diff --git a/.github/workflows/build_test.arcgis.service.yaml b/.github/workflows/build_test.arcgis.service.yaml index cec2cf2d1..8127dc665 100644 --- a/.github/workflows/build_test.arcgis.service.yaml +++ b/.github/workflows/build_test.arcgis.service.yaml @@ -30,6 +30,11 @@ jobs: cache: npm cache-dependency-path: | plugins/arcgis/service/package-lock.json + - name: build mage service + run: | + cd service + npm ci + npm run build - name: test with node ${{ matrix.node }} run: | cd plugins/arcgis/service @@ -50,6 +55,11 @@ jobs: cache: npm cache-dependency-path: | plugins/arcgis/service/package-lock.json + - name: build mage service + run: | + cd service + npm ci + npm run build - name: build service for plugin run: | cd plugins/arcgis/service diff --git a/.github/workflows/build_test.service.yaml b/.github/workflows/build_test.service.yaml index 48733be54..1de129078 100644 --- a/.github/workflows/build_test.service.yaml +++ b/.github/workflows/build_test.service.yaml @@ -1,6 +1,7 @@ name: build and test service on: + push: workflow_dispatch: workflow_call: pull_request: diff --git a/.github/workflows/build_test.web-app.yaml b/.github/workflows/build_test.web-app.yaml index b7f39216a..f46282061 100644 --- a/.github/workflows/build_test.web-app.yaml +++ b/.github/workflows/build_test.web-app.yaml @@ -1,6 +1,7 @@ name: build and test web-app on: + push: workflow_dispatch: workflow_call: pull_request: diff --git a/.github/workflows/container_image.dev.yaml b/.github/workflows/container_image.dev.yaml deleted file mode 100644 index ebe0785ff..000000000 --- a/.github/workflows/container_image.dev.yaml +++ /dev/null @@ -1,72 +0,0 @@ -name: Dev Container Build - -on: - push: - branches: - - "**" - workflow_dispatch: - inputs: - image-tag: - description: | - Supply a tag to push the image to ghcr.io with the given tag. Without a tag, the workflow will not push, - but only build the image. - type: string - required: false - -permissions: - contents: read - packages: write - -jobs: - build-and-push: - runs-on: ubuntu-latest - steps: - - name: Checkout Repo - uses: actions/checkout@v7 - - - name: Login to Github Registry ghcr.io - uses: docker/login-action@v4 - with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - - name: image-info - id: image-info - run: | - image_tag="local" - [[ ${{ github.ref }} = 'refs/heads/develop' ]] && image_tag="dev" - [[ -n "${{ inputs.image-tag }}" ]] && image_tag="${{ inputs.image-tag }}" - image_url="${{ github.repository }}/mage:${image_tag}" - echo "using image url ${image_url}" - echo "tag=${image_tag}" >> $GITHUB_OUTPUT - echo "url=ghcr.io/${image_url}" >> $GITHUB_OUTPUT - - - name: Build Docker image - run: | - docker build \ - -t ${{ steps.image-info.outputs.url }} \ - --annotation "org.opencontainers.image.revision=${{ github.sha }}" . - - - name: Scan the docker image with Trivy - uses: aquasecurity/trivy-action@v0.35.0 - with: - scan-type: 'image' - image-ref: '${{ steps.image-info.outputs.url }}' - ignore-unfixed: true - format: 'table' - output: 'trivy-image-report.txt' - exit-code: '0' - version: 'v0.69.3' - - - name: Upload Trivy report - uses: actions/upload-artifact@v7 - with: - name: trivy-container-security-report - path: trivy-image-report.txt - retention-days: 7 - - - name: Push Image - if: ${{ steps.image-info.outputs.tag != 'local' }} - run: | - docker push ${{ steps.image-info.outputs.url }} diff --git a/.github/workflows/container_image.prod.yaml b/.github/workflows/container_image.prod.yaml deleted file mode 100644 index 30eef6194..000000000 --- a/.github/workflows/container_image.prod.yaml +++ /dev/null @@ -1,72 +0,0 @@ -name: Production Container Build - -on: - workflow_dispatch: - inputs: - image-tag: - description: | - Supply a tag to push the image to ghcr.io with the given tag. Without a tag, the workflow will not push, - but only build the image. If the workflow runs from the develop branch for testing, the image will - implicitly receive the 'dev' tag. - type: string - required: false - -permissions: - contents: read - packages: write - -jobs: - build-and-push: - runs-on: ubuntu-latest - steps: - - name: Login to Github Registry ghcr.io - uses: docker/login-action@v4 - with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - - name: checkout - uses: actions/checkout@v7 - - - name: image-info - id: image-info - run: | - image_tag="local" - [[ ${{ github.ref }} = 'refs/heads/develop' ]] && image_tag="dev" - [[ -n "${{ inputs.image-tag }}" ]] && image_tag="${{ inputs.image-tag }}" - image_url="${{ github.repository }}/mage-prod:${image_tag}" - image_version=$(jq -r .version < ./service/package.json) - echo "using image url ${image_url}" - echo "tag=${image_tag}" >> $GITHUB_OUTPUT - echo "url=ghcr.io/${image_url}" >> $GITHUB_OUTPUT - echo "version=${image_version}" >> $GITHUB_OUTPUT - - - name: Build and push image - uses: docker/build-push-action@v7 - with: - file: ./docker/server/Dockerfile - tags: ${{ steps.image-info.outputs.url }} - platforms: linux/amd64 - push: ${{ steps.image-info.outputs.tag != 'local' }} - annotations: | - org.opencontainers.image.revision=${{ github.sha }} - org.opencontainers.image.version=${{ steps.image-info.outputs.version }} - - - name: Scan the docker image with Trivy - uses: aquasecurity/trivy-action@v0.35.0 - with: - scan-type: 'image' - image-ref: '${{ steps.image-info.outputs.url }}' - ignore-unfixed: true - format: 'table' - output: 'trivy-image-report.txt' - exit-code: '0' - version: 'v0.69.3' - - - name: Upload Trivy report - uses: actions/upload-artifact@v7 - with: - name: trivy-container-security-report - path: trivy-image-report.txt - retention-days: 7 diff --git a/.github/workflows/container_image.yaml b/.github/workflows/container_image.yaml new file mode 100644 index 000000000..b827e4b56 --- /dev/null +++ b/.github/workflows/container_image.yaml @@ -0,0 +1,181 @@ +name: Mage Container Image + +on: + workflow_dispatch: + inputs: + image-packages: + description: | + Indicate whether to build the image with packages built from source locally, or with packages published in + the NPM registry. + type: choice + options: + - local + - published + default: local + image-tag: + description: | + Supply a tag to push the image to the ghcr.io registry. Without a tag, the workflow will not push, + but only build the image. If the workflow runs from the develop branch, the image will implicitly + receive the 'dev' tag. Examples: `6.7.0-beta.1` or `feature-123`. + type: string + required: false + image-name: + description: | + Set the base name of the image, i.e., the last component of the image repository path preceding the tag. + The default is 'mage', which produces the full image path 'ghcr.io/nagegeoint/mage-server/mage'. + type: string + default: mage + version-core: + description: | + The version of the published core packages, @ngageoint/mage.service and @ngageoint/mage.web-app, + to install in the image + type: string + required: false + version-arcgis-plugin: + description: | + The version of the published ArcGIS plugin packages to install in the image + type: string + required: false + version-clamav-plugin: + description: | + The version of the published ClamAV plugin package to install in the image + type: string + required: false + version-image-plugin: + description: | + The version of the published image plugin package to install in the image + type: string + required: false + version-msi-plugin: + description: | + The version of the published MSI plugin package to install in the image + type: string + required: false + version-sftp-plugin: + description: | + The version of the published SFTP plugin packages to install in the image + type: string + required: false + +permissions: + contents: read + packages: read + +jobs: + image-info: + runs-on: ubuntu-latest + outputs: + repo: ${{ steps.image-info.outputs.repo }} + tag: ${{ steps.image-info.outputs.tag }} + url: ${{ steps.image-info.outputs.url }} + version: ${{ steps.image-info.outputs.version }} + dockerfile: ${{ steps.image-info.outputs.dockerfile }} + steps: + - name: install node.js + uses: actions/setup-node@v7 + - name: checkout + uses: actions/checkout@v7 + - name: image-info + id: image-info + run: | + image_tag="local" + [[ ${{ github.ref }} = 'refs/heads/develop' ]] && image_tag="dev" + [[ -n "${{ inputs.image-tag }}" ]] && image_tag="${{ inputs.image-tag }}" + image_repo="ghcr.io/${{ github.repository }}/${{ inputs.image-name }}" + image_url="${image_repo}:${image_tag}" + image_dockerfile="Dockerfile" + image_version=$(jq -r .version < ./service/package.json) + if [[ "${{ inputs.image-packages }}" = "published" ]] + then + image_dockerfile="Dockerfile.published" + image_version=$(npm info @ngageoint/mage.service@${{ inputs.version-core || 'latest' }} version) + fi + echo "using image url ${image_url}" + echo "repo=${image_repo}" >> $GITHUB_OUTPUT + echo "tag=${image_tag}" >> $GITHUB_OUTPUT + echo "url=${image_url}" >> $GITHUB_OUTPUT + echo "version=${image_version}" >> $GITHUB_OUTPUT + echo "dockerfile=./${image_dockerfile}" >> $GITHUB_OUTPUT + + build-push: + needs: image-info + uses: docker/github-builder/.github/workflows/build.yml@v1 + permissions: + contents: read + id-token: write + packages: write + with: + output: image + platforms: linux/amd64,linux/arm64 + file: ${{ needs.image-info.outputs.dockerfile }} + push: ${{ needs.image-info.outputs.tag != 'local' }} + meta-images: ${{ needs.image-info.outputs.repo }} + meta-tags: | + type=raw,value=${{ needs.image-info.outputs.tag }} + meta-annotations: | + org.opencontainers.image.revision=${{ github.sha }} + org.opencontainers.image.version=${{ needs.image-info.outputs.version }} + build-args: | + CORE_VERSION=${{ inputs.version-core || 'latest' }} + ARCGIS_VERSION=${{ inputs.version-arcgis-plugin || 'latest' }} + CLAMAV_VERSION=${{ inputs.version-clamav-plugin || 'latest' }} + IMAGE_VERSION=${{ inputs.version-image-plugin || 'latest' }} + MSI_VERSION=${{ inputs.version-msi-plugin || 'latest' }} + SFTP_VERSION=${{ inputs.version-sftp-plugin || 'latest' }} + secrets: + registry-auths: | + - registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + extract-package-lock: + runs-on: ubuntu-latest + needs: + - image-info + - build-push + steps: + - name: Docker login ghcr.io + uses: docker/login-action@v4 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + - name: Extract package-lock + run: | + docker create --name mage-files ${{ needs.image-info.outputs.url }} + docker cp mage-files:/mage/instance/package-lock.json ./mage-instance.package-lock.json + docker rm mage-files + - name: Upload package-lock + uses: actions/upload-artifact@v7 + with: + path: mage-instance.package-lock.json + name: mage-instance.package-lock.json + + trivy-scan: + needs: + - image-info + - build-push + runs-on: ubuntu-latest + steps: + - name: Docker login ghcr.io + uses: docker/login-action@v4 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + - name: Trivy image scan + uses: aquasecurity/trivy-action@v0.35.0 + with: + scan-type: 'image' + image-ref: '${{ needs.image-info.outputs.url }}' + ignore-unfixed: true + format: 'table' + output: 'trivy-image-report.txt' + exit-code: '0' + version: 'v0.69.3' + - name: Upload Trivy report + uses: actions/upload-artifact@v7 + with: + name: trivy-container-security-report + path: trivy-image-report.txt + retention-days: 7 diff --git a/.gitignore b/.gitignore index bf695aea7..2beb9ba1b 100644 --- a/.gitignore +++ b/.gitignore @@ -8,17 +8,16 @@ node_modules .test_run .vscode/ coverage/ -database/ -docker/database/ -docker/server/resources/ +# these two entries should not be necessary anymore, but remain for now to +# ensure these obsolete directories aren't commited if a clone still has them +/database/ /server npm-debug.log pnpm-debug.log scratch/ *.scratch/ *.scratch.* -/docker/auth-idp/ldap/config/* -/docker/auth-idp/ldap/db/* +*.tsbuildinfo .angular .DS_STORE # this line must be last diff --git a/Dockerfile b/Dockerfile index 318325eff..034947988 100644 --- a/Dockerfile +++ b/Dockerfile @@ -32,8 +32,8 @@ RUN cd ${MAGE_SERVER}/web-app \ RUN cd ${MAGE_SERVER}/plugins/image/service \ && npm link ../../../service \ - && npm run build && \ - cd ${MAGE_PACKAGES} \ + && npm run build \ + && cd ${MAGE_PACKAGES} \ && npm pack ${MAGE_SERVER}/plugins/image/service RUN cd ${MAGE_SERVER}/plugins/sftp/service \ diff --git a/docker/server/Dockerfile b/Dockerfile.published similarity index 64% rename from docker/server/Dockerfile rename to Dockerfile.published index 5ac3e90fb..120ad878e 100644 --- a/docker/server/Dockerfile +++ b/Dockerfile.published @@ -1,3 +1,5 @@ +# Assemble a Mage server instance image from packages published in the NPM registry. + ARG BUILD_IMAGE="node:24-slim" ARG DIST_IMAGE="ghcr.io/ngageoint/mage-server/ironbank/google/distroless13/nodejs-24:nonroot" ARG MAGE_INSTANCE="/mage/instance" @@ -11,20 +13,23 @@ ARG ARCGIS_VERSION="latest" ARG SFTP_VERSION="latest" ARG MSI_VERSION="latest" ARG IMAGE_VERSION="latest" +ARG CLAMAV_VERSION="" ARG MAGE_INSTANCE WORKDIR ${MAGE_INSTANCE} RUN set -eux; \ - npm i --omit=dev @ngageoint/mage.service@${CORE_VERSION}; \ - npm i --omit=dev @ngageoint/mage.web-app@${CORE_VERSION}; \ + npm i --omit dev @ngageoint/mage.service@${CORE_VERSION}; \ + npm i --omit dev @ngageoint/mage.web-app@${CORE_VERSION}; \ [ -n "${ARCGIS_VERSION}" ] && \ - npm i --omit=dev @ngageoint/mage.arcgis.service@${ARCGIS_VERSION} @ngageoint/mage.arcgis.web-app@${ARCGIS_VERSION}; \ + npm i --omit dev @ngageoint/mage.arcgis.service@${ARCGIS_VERSION} @ngageoint/mage.arcgis.web-app@${ARCGIS_VERSION}; \ [ -n "${SFTP_VERSION}" ] && \ - npm i --omit=dev @ngageoint/mage.sftp.service@${SFTP_VERSION} @ngageoint/mage.sftp.web@${SFTP_VERSION}; \ + npm i --omit dev @ngageoint/mage.sftp.service@${SFTP_VERSION} @ngageoint/mage.sftp.web@${SFTP_VERSION}; \ [ -n "${MSI_VERSION}" ] && \ - npm i --omit=dev @ngageoint/mage.nga-msi@${MSI_VERSION}; \ + npm i --omit dev @ngageoint/mage.nga-msi@${MSI_VERSION}; \ [ -n "${IMAGE_VERSION}" ] && \ - npm i --omit=dev @ngageoint/mage.image.service@${IMAGE_VERSION}; \ + npm i --omit dev @ngageoint/mage.image.service@${IMAGE_VERSION}; \ + [ -n "${CLAMAV_VERSION}" ] && \ + npm i --omit dev @ngageoint/mage.clamav.service@${CLAMAV_VERSION}; \ ln -sfn ./node_modules/.bin/mage.service ./mage.service FROM ${DIST_IMAGE} @@ -39,4 +44,4 @@ WORKDIR ${MAGE_INSTANCE} VOLUME /var/lib/mage EXPOSE 4242 -CMD [ "./mage.service" ] \ No newline at end of file +CMD [ "./mage.service" ] diff --git a/docker-compose.yml b/docker-compose.yml index 997683bea..41947fe8f 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -5,20 +5,20 @@ services: image: clamav/clamav:stable platform: linux/amd64 volumes: - - ./clamav-test/data:/var/lib/clamav + - ./docker/clamav:/var/lib/clamav ports: - "3310:3310" networks: - mage.net mage-db: - image: mongodb/mongodb-community-server:8.0-ubi9-slim + image: mongodb/mongodb-community-server:8.3-ubi9-slim # Uncomment the following ports block to allow the mongo client on your # host machine to connect to MongoDB in the Docker container. ports: - 27017:27017 volumes: - - ./database/data:/data/db + - ./docker/mage/db:/data/db networks: - mage.net @@ -26,13 +26,26 @@ services: depends_on: [ mage-db ] build: context: ./ - # dockerfile: Dockerfile-debug + tags: + - mage:local image: mage:local - # entrypoint: ["./node_modules/.bin/mage.service", "--plugin", "@ngageoint/mage.image.service"] Uncomment to specify new entrypoint commands - platform: linux/amd64 + command: + - ./mage.service + - --plugin + - '@ngageoint/mage.image.service' + - --plugin + - '@ngageoint/mage.sftp.service' + - --web-plugin + - '@ngageoint/mage.sftp.web' + - --plugin + - '@ngageoint/mage.arcgis.service' + - --web-plugin + - '@ngageoint/mage.arcgis.web-app' + - --plugin + - '@ngageoint/mage.clamav.service' volumes: - - ./server/resources:/var/lib/mage - # - ./sftp-test/id_rsa:/run/secrets/sftp_key:ro + - ./docker/mage/resources:/var/lib/mage + - ./docker/sftp/key/id_rsa:/run/secrets/mage/sftp_key:ro # Comment the ports block to disallow connections directly to the node # server when running the mage-web-proxy below. ports: @@ -45,26 +58,20 @@ services: environment: MAGE_MONGO_URL: mongodb://mage-db:27017/magedb MAGE_TOKEN_EXPIRATION: "28800" - MAGE_PLUGINS: '{"servicePlugins":["@ngageoint/mage.sftp.service","@ngageoint/mage.arcgis.service","@ngageoint/mage.clamav.service"],"webUIPlugins":["@ngageoint/mage.sftp.web","@ngageoint/mage.arcgis.web-app"]}' # Only used if @ngageoint/mage.clamav.service is enabled above - host/port # for the mage-clamav container this compose file already defines. MAGE_CLAMAV_HOST: mage-clamav MAGE_CLAMAV_PORT: "3310" - MAGE_SFTP_KEY_FILE: /var/lib/mage/sftp_key - - # Uncomment this section to use Docker volumes instead of bind mounts for data directories: - # volumes: - # "mongodb-data": - # "mage-server-resources": + MAGE_SFTP_KEY_FILE: /run/secrets/mage/sftp_key # Uncomment the following block to enable the TLS reverse proxy. You will # also need to generate the key and certificate as the README describes. # mage-web-proxy: # image: nginx # volumes: - # - ./server/web/nginx.conf:/etc/nginx/nginx.conf - # - ./server/web/mage-web.crt:/etc/nginx/ssl/web.crt - # - ./server/web/mage-web.key:/etc/nginx/ssl/web.key + # - ./docker/web-proxy/nginx.conf:/etc/nginx/nginx.conf + # - ./docker/web-proxy/mage-web.crt:/etc/nginx/ssl/web.crt + # - ./docker/web-proxy/mage-web.key:/etc/nginx/ssl/web.key # ports: # - 4280:80 # - 4243:4243 @@ -72,14 +79,14 @@ services: # - mage.net # Local SFTP server for development testing. - # Uploaded zips land in ./sftp-test/upload/ on your Mac. - # Connect the plugin to: host=mage-sftp, port=22, user=magetest, path=/upload + # Uploaded zips land in ./docker/sftp/upload/ on your Mac. + # Connect the plugin to: host=mage-sftp, port=22, user=mage, path=/upload mage-sftp: image: atmoz/sftp - command: magetest::1001 + command: mage::1001 volumes: - - ./sftp-test/id_rsa.pub:/home/magetest/.ssh/keys/id_rsa.pub:ro - - ./sftp-test/upload:/home/magetest/upload + - ./docker/sftp/key/id_rsa.pub:/home/mage/.ssh/keys/id_rsa.pub:ro + - ./docker/sftp/upload:/home/mage/upload ports: - "2222:22" networks: diff --git a/docker/README.md b/docker/README.md index 2999a82e5..0716d3a5d 100644 --- a/docker/README.md +++ b/docker/README.md @@ -2,46 +2,83 @@ ## Mage server image -The Mage Server image contains the the core Mage server Node app that consists +The Mage Server image contains the core Mage server Node app that consists of the ReST web service and the Mage web app. By default, the image also -includes plugins maintained in the [Mage server repository](../plugins/). By -default, the server [Dockerfile](./server/Dockerfile) pulls the latest tagged -versions from the NPM registry. You can override the version using Docker's -`--build-arg` CLI switch to set the package versions you want in the image. -If you are building on Apple Silicon hardware, use `--platform linux/amd64` so -the built image platform matches the base image platform. Here's an example of -building the image with an explicit service version. +includes plugins maintained in the [Mage server repository](../plugins/). There +are two Mage server Dockerfiles. The default [Dockerfile](../Dockerfile) builds +and installs all packages from the local source tree. [`Dockerfile.published`](../Dockerfile.published) +installs published Mage packages from the NPM registry to build a production +release image. + +Build the local development image with the following command from the project +root. ```bash -$ cd ./docker/server -$ docker build --platform linux/amd64 --build-arg service_version=6.2.10 -t mage-server: . +docker build -t mage:local . +``` +Build the production release image with the following command. +```bash +docker build -t mage-server: -f Dockerfile.published . +``` +You can override the package versions in the production image using Docker's +`--build-arg` CLI switch to set the package versions you want in the image. +Here's an example of building the image with an explicit core service and +web-app version. +```bash +docker build --build-arg CORE_VERSION=6.7.0 -t mage-server: -f Dockerfile.published . ``` -The Iron Bank [Dockerfile](./server/Dockerfile.ironbank) uses a different, -hardened [base image](https://ironbank.dso.mil/repomap/details;registry1Path=opensource%252Fnodejs%252Fdebian%252Fnodejs) -from US DoD's [Iron Bank](https://ironbank.dso.mil/about) repository. The -Dockerfile builds exactly the same as the standard Dockerfile. +### Private base image + +Both Dockerfiles default to a hardened base image hosted in this project's GitHub +repository. You will need to create a [personal access token](https://github.com/settings/tokens) the Docker +CLI can use to [access](https://docs.github.com/en/packages/working-with-a-github-packages-registry/working-with-the-container-registry) the GitHub container registry in order to build with that +base image. Alternatively, you can override the base image with an open Node.js +image using a Docker CLI build argument like the following. +```bash +docker build --build-arg DIST_IMAGE=node:26-slim . +``` + +The hardened [base image](https://registry1.dso.mil/harbor/projects/3/repositories/google%2Fdistroless13%2Fnodejs-24/artifacts-tab) +is a copy from [Platform One's](https://p1.dso.mil/) [Iron Bank repository](https://ironbank.dso.mil/about), which is +based on Google's [distroless Node](https://console.cloud.google.com/artifacts/docker/distroless/us/gcr.io/nodejs24-debian13) image. +The [source](https://repo1.dso.mil/dsop/google/distroless13/nodejs-24) for the Iron Bank image is available in Platform +One's [Repo One](https://repo1.dso.mil/) Git hosting service. + +The base image is distroless, hence lacks a shell and familiar Linux command line utilities. To debug the image +or a running container interactively, you'll need to use the `docker debug` command. For example: +```bash +docker debug mage:local +``` ## Docker Compose -You can start a Mage server by using [docker compose](https://docs.docker.com/compose/) to start services -defined in Mage's [Compose file](docker-compose.yml). +You can start a Mage server instance by using [docker compose](https://docs.docker.com/compose/) to start services +defined in Mage's [Compose file](../docker-compose.yml). The first time you run Mage with Docker, execute the following steps from the directory where you cloned the Mage Git repository. ```bash -$ cd ./docker -$ docker compose up -d # build the service images, then create and start the service containers for the first time -``` -If you want to use a Mage server image tagged with a different version than -the `mage-server` Compose service currently defines, prepend the `MAGE_VERSION` -environment variable to the command. -```bash -$ MAGE_VERSION=6.3.0-beta.1 docker compose up -d +docker compose up -d # build the service images, then create and start the service containers for the first time ``` With all the default settings, you should then be able to browse to http://localhost:4242 to interact with the Mage web app. -## Interacting with the containers +### HTTPS/TLS Reverse Proxy + +The Compose file defines a `mage-web-proxy` service based on the `nginx` image. Uncomment this service block to +enable HTTPS connections to the Mage web app and API. This may be required for testing the Mage mobile applications +with a locally running server, as well as facilitate testing that resource URLs and links work properly when the core +Node app is running behind a secure proxy. The nginx container proxy references the configuration from +[web-proxy/nginx.conf](./web-proxy/nginx.conf). + +You'll need to generate a self-signed key-certificate pair for the nginx proxy using `openssl`. The Compose file +expects the certificate and key files at `docker/web-proxy/mage-web.crt` and `docker/web-proxy/mage-web.key`, +respectively. Both files have entries in `docker/web-proxy/.gitignore` to avoid committing the private key to the +repository. There are plenty of tutorials online about creating a self-signed certificate, for example, +https://www.digitalocean.com/community/tutorials/how-to-create-a-self-signed-ssl-certificate-for-nginx-in-ubuntu-16-04. +Simply replace the file paths as appropriate. + +### Interacting with the containers _NOTE:_ All of the following `docker compose` commands described below assume you are operating from the [`docker`](docker) directory. @@ -107,56 +144,3 @@ tokens users > ``` - -## Details - -### App Services - -The Mage Server Docker app consists of three service containers: the MongoDB -database, the Mage Node.js app, and the optional nginx TLS reverse proxy. - -### MongoDB - -The MongoDB image is the official MongoDB image available from [Docker Hub](https://hub.docker.com/_/mongo/). The Compose -file builds that image unmodified, but uses a custom command to launch MongoDB with specific settings. The Compose file -runs MongoDB as the service `mage-db`. - -### Mage server - -The Compose file references a custom, local [Dockerfile](server/Dockerfile) based on the official [Node.js](https://hub.docker.com/_/node/) -image to build the Mage server image. At build time, the Mage server Dockerfile -copies whatever Mage server package tarballs you supply into the Mage server -image, which might not match the default version the Compose currently defines. -If you want the Mage server image that Compose builds to have a different tag, -override the `MAGE_VERSION` default value in the Compose file. -```bash -MAGE_VERSION=6.2.0 docker compose build -``` -By way of the -```yaml -image: "mage-server:${MAGE_VERSION:-6.1.2}" -``` -entry, the Compose file tells Docker to [tag](https://docs.docker.com/get-started/part2/#tag-the-image) -the `mage-server` image Compose builds with the value of `MAGE_VERSION`. - -Be aware that, after overriding the default `MAGE_VERSION` value, if you then -use `docker compose up -d` without the same `MAGE_VERSION=xxx` override, -Compose will simply add a tag for the default version to the previously built -image. So, for example, if the Compose file default for `MAGE_VERSION` is -`6.2.0`, and you -```bash -$ MAGE_VERSION=6.3.0-beta.1 docker compose build -docker compose up -d -``` -you will end up wth one image that has two tags `6.2.0` and `6.3.0-beta.1`. -```bash -$ docker images -REPOSITORY TAG IMAGE ID CREATED SIZE -mage-server 6.2.0 34093daa6c4e 2 hours ago 522MB -mage-server 6.3.0-beta.1 34093daa6c4e 2 hours ago 522MB -``` -Note the same values in the `IMAGE ID` column in the example output, along with -different `TAG` values. - - - diff --git a/docker/auth-idp/.gitignore b/docker/auth-idp/.gitignore new file mode 100644 index 000000000..17b024d1c --- /dev/null +++ b/docker/auth-idp/.gitignore @@ -0,0 +1,2 @@ +/ldap/config/* +/ldap/db/* diff --git a/docker/clamav/.gitignore b/docker/clamav/.gitignore new file mode 100644 index 000000000..d6b7ef32c --- /dev/null +++ b/docker/clamav/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml deleted file mode 100644 index 869ecd969..000000000 --- a/docker/docker-compose.yml +++ /dev/null @@ -1,65 +0,0 @@ -services: - - mage-db: - image: mongo:6.0.28 - volumes: - - ./database/data:/data/db - - ./database/log:/var/log/mongodb - # If you'd rather use a Docker volume than a bind mount, use a volume configuration - # like this instead: - # - mongo-data:/data/db - # Uncomment the following ports block to allow the mongo client on your - # host machine to connect to MongoDB in the Docker container. - # ports: - # - 27017:27017 - networks: - - mage.net - - mage-server: - depends_on: [ mage-db ] - image: "mage:prod" - # image: "mage-server:debug" - build: - context: ./server - # dockerfile: Dockerfile-debug - platform: linux/amd64 - volumes: - - ./server/resources:/var/lib/mage - # If you'd rather use a Docker volume than a bind mount, use a volume configuration - # like this instead: - # - server-resources:/var/lib/mage - # Comment the ports block to disallow connections directly to the node - # server when running the mage-web-proxy below. - ports: - - 4242:4242 - # Uncomment to allow debuggers to attach the Node process inside the - # container on port 14242 - # - 14242:14242 - networks: - - mage.net - environment: - MAGE_MONGO_URL: mongodb://mage-db:27017/magedb - MAGE_TOKEN_EXPIRATION: "28800" - - # Uncomment the following block to enable the TLS reverse proxy. You will - # also need to generate the key and certificate as the README describes. - # mage-web-proxy: - # image: nginx - # volumes: - # - ./web/nginx.conf:/etc/nginx/nginx.conf - # - ./web/mage-web.crt:/etc/nginx/ssl/web.crt - # - ./web/mage-web.key:/etc/nginx/ssl/web.key - # ports: - # - 4280:80 - # - 4243:4243 - # networks: - # - mage.net - -networks: - mage.net: - driver: bridge - -# Uncomment this section to use Docker volumes instead of bind mounts for data directories: -# volumes: -# "mongo-data": -# "server-resources": diff --git a/docker/mage/.gitignore b/docker/mage/.gitignore new file mode 100644 index 000000000..55002c3ea --- /dev/null +++ b/docker/mage/.gitignore @@ -0,0 +1,2 @@ +/db/ +/resources/ diff --git a/docker/server/.gitignore b/docker/server/.gitignore deleted file mode 100644 index 0d1d6c364..000000000 --- a/docker/server/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -# ignore npm package tarballs and package.json that image build script downloads -/*.tgz -/package.json \ No newline at end of file diff --git a/docker/sftp/.gitignore b/docker/sftp/.gitignore new file mode 100644 index 000000000..5cbdd9e71 --- /dev/null +++ b/docker/sftp/.gitignore @@ -0,0 +1,3 @@ +/upload +/key + diff --git a/docker/web/.gitignore b/docker/web-proxy/.gitignore similarity index 100% rename from docker/web/.gitignore rename to docker/web-proxy/.gitignore diff --git a/docker/web/nginx.conf b/docker/web-proxy/nginx.conf similarity index 100% rename from docker/web/nginx.conf rename to docker/web-proxy/nginx.conf diff --git a/package-lock.json b/package-lock.json index f02bc3ea4..aab91bf72 100644 --- a/package-lock.json +++ b/package-lock.json @@ -99,6 +99,15 @@ } } }, + "node_modules/@angular-architects/module-federation/node_modules/zone.js": { + "version": "0.16.3", + "resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.16.3.tgz", + "integrity": "sha512-ihXL9+vhYyEhXz4TDNpHeAOZN9FVrbog0Il64OIEI28UP/n5AaI6gsccRuOHBfx+206agzyoK527bYIO0Foy6A==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true + }, "node_modules/@angular/animations": { "version": "18.2.14", "resolved": "https://registry.npmjs.org/@angular/animations/-/animations-18.2.14.tgz", diff --git a/plugins/arcgis/CHANGELOG.md b/plugins/arcgis/CHANGELOG.md new file mode 100644 index 000000000..f0a57a7f3 --- /dev/null +++ b/plugins/arcgis/CHANGELOG.md @@ -0,0 +1,28 @@ +# Change Log + +All notable changes to the ArcGIS plugin will be documented in this file. +The plugin adheres to [Semantic Versioning](http://semver.org/). + +--- + +## [1.0.0-beta.4] +### Service +#### Features +* Looks up push status for each observation as it's sent to ArcGIS +* Added a "Reset Config" action that deletes the entire ArcGIS plugin configuration +#### Bug Fixes +* Fixed local `@ngageoint/mage.service` linking so `npm ci` works correctly when developing the plugin against the local source tree +* Improved handling of attribute field mappings to avoid collisions and errors + +### Web App +#### Features +* Replaced the Events tab's per-event "edit layers/filter" popup with an inline expandable section +* Push Status table adds an "Attachments" column showing sent/failed counts per observation, with a hover tooltip listing each attachment's name and status +* New ability to browse a protal URL to lookup your feature service layer +* Field Mappings and Attributes now use constrained dropdowns instead of free-text entry for ArcGIS attribute names, with reserved-name warnings, to prevent typos and case mismatches between a field mapping and its attribute configuration +* Added a "Reset Config" button (Processing tab) +* General button and dialog styling pass for consistency +* UI usability improvements +#### Bug Fixes +* Fixed a change-detection bug where repeatedly interacting with an event's sync-after date/time filter could freeze the page +* Events list no longer shows stray dividers for events hidden by the filter box diff --git a/plugins/arcgis/README.md b/plugins/arcgis/README.md index bda70a3ec..b65363896 100644 --- a/plugins/arcgis/README.md +++ b/plugins/arcgis/README.md @@ -50,6 +50,8 @@ plugins/arcgis/ ## Authentication methods +Users can add a feature service URL directly, or select the option to browse a portal URL to discover avialable services + The plugin supports three authentication methods for connecting to an ArcGIS Feature Service: | Method | When to use | @@ -79,7 +81,6 @@ The plugin config is stored in the MAGE plugin state repository (MongoDB). All f | `idSeparator` | `'-'` | Separator between observation ID and event ID | | `eventIdField` | `'event_id'` | ArcGIS attribute for the MAGE event ID | | `eventNameField` | `'event_name'` | ArcGIS attribute for the MAGE event name | -| `lastEditedDateField` | `'last_edited_date'` | ArcGIS attribute for last edit timestamp | | `lastModifiedField` | `'last_modified'` | ArcGIS attribute for the MAGE observation's last modified time | | `userIdField` | `'user_id'` | ArcGIS attribute for the MAGE user ID | | `usernameField` | `'username'` | ArcGIS attribute for the MAGE username | diff --git a/plugins/arcgis/service/jest.config.ts b/plugins/arcgis/service/jest.config.ts new file mode 100644 index 000000000..21e540c30 --- /dev/null +++ b/plugins/arcgis/service/jest.config.ts @@ -0,0 +1,18 @@ +import type {Config} from 'jest'; +import { createDefaultPreset } from 'ts-jest'; + +const tsJestTransformCfg = createDefaultPreset().transform; + +const config:Config = { + testEnvironment: "node", + transform: tsJestTransformCfg, + roots: ['/src'], + moduleNameMapper: { + // This is needed to ensure that all imports of mongoose go to the same instance, + // so the mocking logic is working on the same mongoose singletons that are + // used by whatever code is using mongoose. + '^mongoose$': '/node_modules/mongoose' + } +}; + +export default config; diff --git a/plugins/arcgis/service/package-lock.json b/plugins/arcgis/service/package-lock.json index 848a83f2f..75ef37c1e 100644 --- a/plugins/arcgis/service/package-lock.json +++ b/plugins/arcgis/service/package-lock.json @@ -10,19 +10,30 @@ "license": "Apache-2.0", "dependencies": { "@esri/arcgis-rest-feature-service": "4.1.0", + "@esri/arcgis-rest-portal": "4.3.0", "@esri/arcgis-rest-request": "4.3.0", "@terraformer/arcgis": "2.1.2", - "form-data": "4.0.6" + "@terraformer/wkt": "2.2.1", + "file-type": "16.5.4", + "form-data": "4.0.6", + "mime-types": "3.0.2" }, "devDependencies": { "@eslint/js": "9.24.0", + "@ngageoint/mage.service": "file:../../../service", "@types/express": "4.17.21", "@types/geojson": "7946.0.16", "@types/jest": "^30.0.0", + "@types/mime-types": "3.0.1", + "@types/terraformer__wkt": "2.0.3", "eslint": "9.24.0", "eslint-plugin-jsdoc": "50.6.9", "jest": "^30.2.0", + "mockingoose": "2.16.2", + "mongoose": "8.24.1", "nock": "12.0.3", + "ts-jest": "29.4.6", + "ts-node": "10.9.2", "typescript": "5.8.2", "typescript-eslint": "8.29.1" }, @@ -31,6 +42,121 @@ "express": "4.22.2" } }, + "../../../service": { + "name": "@ngageoint/mage.service", + "version": "6.7.1-beta.1", + "dev": true, + "dependencies": { + "@ngageoint/geopackage": "^4.2.8", + "@ngageoint/mongodb-migrations": "^2.0.0", + "@node-saml/passport-saml": "^5.1.0", + "@turf/centroid": "7.2.0", + "@turf/kinks": "7.2.0", + "@turf/rewind": "^7.3.5", + "@types/geojson": "7946.0.16", + "@types/json-schema": "7.0.15", + "@types/mime-types": "3.0.1", + "@xmldom/xmldom": "0.9.10", + "adm-zip": "0.6.0", + "archiver": "^7.0.1", + "async": "3.2.6", + "async-lock": "1.4.1", + "base-64": "1.0.0", + "better-sqlite3": "^12.11.1", + "busboy": "1.6.0", + "captcha-canvas": "3.4.0", + "commander": "12.0.0", + "express": "4.22.2", + "express-session": "1.18.1", + "file-type": "16.5.4", + "fs-extra": "11.3.4", + "geojson": "0.5.0", + "geojson-validation": "1.0.2", + "json2csv": "5.0.7", + "jsonwebtoken": "9.0.2", + "ldapts": "^8.1.8", + "lodash": "4.18.1", + "mgrs": "1.0.0", + "mime-types": "2.1.35", + "moment": "2.30.1", + "mongoose": "8.24.1", + "multer": "2.2.0", + "node-fetch": "2.7.0", + "passport": "0.7.0", + "passport-anonymous": "1.0.1", + "passport-http-bearer": "1.0.1", + "passport-local": "1.0.0", + "passport-oauth2": "1.8.0", + "passport-openidconnect": "0.1.2", + "pug": "3.0.3", + "rfc5646": "3.0.0", + "skia-canvas": "3.0.8", + "superagent": "8.1.2", + "uniqid": "5.4.0", + "winston": "3.19.0", + "wkx": "0.5.0", + "wms-capabilities": "0.6.0", + "xmlbuilder2": "4.0.3", + "yaml": "2.8.3" + }, + "bin": { + "mage.service": "bin/mage.service.js" + }, + "devDependencies": { + "@fluffy-spoon/substitute": "1.208.0", + "@types/archiver": "^7.0.0", + "@types/async": "3.2.24", + "@types/busboy": "1.5.4", + "@types/chai": "4.3.20", + "@types/chai-as-promised": "7.1.8", + "@types/deep-equal": "1.0.4", + "@types/express": "4.17.21", + "@types/express-serve-static-core": "4.19.6", + "@types/express-session": "~1.18.0", + "@types/fs-extra": "8.1.5", + "@types/json2csv": "5.0.7", + "@types/lodash": "4.17.16", + "@types/mocha": "7.0.2", + "@types/multer": "2.1.0", + "@types/passport": "1.0.17", + "@types/sinon": "9.0.11", + "@types/sinon-chai": "3.2.12", + "@types/superagent": "8.1.9", + "@types/supertest": "2.0.16", + "@types/uniqid": "5.3.4", + "@typescript-eslint/eslint-plugin": "6.21.0", + "@typescript-eslint/parser": "6.21.0", + "ajv": "6.12.6", + "axios": "1.18.1", + "chai": "4.5.0", + "chai-as-promised": "7.1.2", + "cpy-cli": "5.0.0", + "deep-equal": "2.2.3", + "eslint": "8.57.1", + "eslint-config-prettier": "6.15.0", + "eslint-plugin-prettier": "3.4.1", + "iconv-lite": "0.6.3", + "jszip": "3.10.1", + "mocha": "10.8.2", + "mock-fs": "4.14.0", + "mongodb-memory-server": "^11.2.0", + "npm-run-all": "4.1.5", + "nyc": "14.1.1", + "openapi-enforcer": "1.23.0", + "prettier": "1.19.1", + "proxyquire": "2.1.3", + "rimraf": "5.0.10", + "sinon": "7.5.0", + "sinon-chai": "3.7.0", + "sinon-mongoose": "2.3.0", + "supertest": "6.3.4", + "typescript": "^5.9.3" + }, + "engines": { + "node": ">= 22.0.0", + "npm": ">= 10.0.0" + } + }, "node_modules/@babel/code-frame": { "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz", @@ -98,14 +224,14 @@ } }, "node_modules/@babel/generator": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.7.tgz", - "integrity": "sha512-DkXD5OJQaAQIdZ1bt3UZdEnHAn9Imd3IVBdX03UFe+ony9Ojw5pzr9YVKGDY1jt+Gcn/FnGkNf8r+Vj5NOJWtQ==", + "version": "7.29.8", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.8.tgz", + "integrity": "sha512-gZbepsdh3WDtgZKWL+vTPh71LSBrm/Y4/QDZBVCcYfmeTEEuoOYwlSy+G1StfJg+/Zy550u/3TATbm7qDbbMtg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/parser": "^7.29.7", - "@babel/types": "^7.29.7", + "@babel/parser": "^7.29.8", + "@babel/types": "^7.29.8", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" @@ -238,13 +364,13 @@ } }, "node_modules/@babel/parser": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.7.tgz", - "integrity": "sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg==", + "version": "7.29.8", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.8.tgz", + "integrity": "sha512-E8lTAYNB1KW+FH+VGJuZM1ioAx2E6oVlvQFRrf5P8ZZmsiJXYAD9vTFV7yyEURNzgh1dFqMZuO6tUwcARbqFCA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/types": "^7.29.7" + "@babel/types": "^7.29.8" }, "bin": { "parser": "bin/babel-parser.js" @@ -508,18 +634,18 @@ } }, "node_modules/@babel/traverse": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.7.tgz", - "integrity": "sha512-EhlfNQtZ+NK22w5BM61ciuiq1m58ed33Wr1Xan//ZRTy6hgjnwyCffRYwzsGXdASJSUJ1guZILsErh1eQcl+zw==", + "version": "7.29.8", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.8.tgz", + "integrity": "sha512-I5z7H3bf/41ktsNVLtpN0wAa336HkqIHQ5BuPLEhTkt1jVSyZpeNKIzTgEWmlxjdg81R0IgUCcaE+Ok3NvrfZg==", "dev": true, "license": "MIT", "dependencies": { "@babel/code-frame": "^7.29.7", - "@babel/generator": "^7.29.7", + "@babel/generator": "^7.29.8", "@babel/helper-globals": "^7.29.7", - "@babel/parser": "^7.29.7", + "@babel/parser": "^7.29.8", "@babel/template": "^7.29.7", - "@babel/types": "^7.29.7", + "@babel/types": "^7.29.8", "debug": "^4.3.1" }, "engines": { @@ -527,9 +653,9 @@ } }, "node_modules/@babel/types": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.7.tgz", - "integrity": "sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==", + "version": "7.29.8", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.8.tgz", + "integrity": "sha512-Vj1jF3cPfxg7OAfoI7QnVKLoILlm2JF9pnVHrX8qx7AHMiYWT+NDAA7jChlNgRS4WTLc/fD1lXLmPixluj+3Gg==", "dev": true, "license": "MIT", "dependencies": { @@ -547,6 +673,30 @@ "dev": true, "license": "MIT" }, + "node_modules/@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "0.3.9" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@cspotcode/source-map-support/node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, "node_modules/@emnapi/core": { "version": "1.10.0", "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.10.0.tgz", @@ -597,9 +747,9 @@ } }, "node_modules/@eslint-community/eslint-utils": { - "version": "4.9.1", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", - "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==", + "version": "4.10.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.10.1.tgz", + "integrity": "sha512-cuadcxVFE8sDK6iWJbs8Sn0av2Nrh2QSGQhVlBW9AaAHqHwjWsZHT8LJ4hFGPh7ASBV2deFdM7H/DPjulmh8rg==", "dev": true, "license": "MIT", "dependencies": { @@ -677,9 +827,9 @@ } }, "node_modules/@eslint/eslintrc": { - "version": "3.3.5", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.5.tgz", - "integrity": "sha512-4IlJx0X0qftVsN5E+/vGujTRIFtwuLbNsVUe7TO6zYPDR1O6nFwvwhIKEKSrl6dZchmYBITazxKoUYOjdtjlRg==", + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.6.tgz", + "integrity": "sha512-l2Ul9PrHsPCKcEY/ac7VgFj9D80C7S68sOKc618SyHDPK36s1XcFebXY0iTzUVn4Yq+YbwvSnDmCz9yxjX+QrA==", "dev": true, "license": "MIT", "dependencies": { @@ -689,7 +839,7 @@ "globals": "^14.0.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", - "js-yaml": "^4.1.1", + "js-yaml": "^4.3.0", "minimatch": "^3.1.5", "strip-json-comments": "^3.1.1" }, @@ -764,29 +914,28 @@ } }, "node_modules/@esri/arcgis-rest-fetch": { - "version": "4.10.3", - "resolved": "https://registry.npmjs.org/@esri/arcgis-rest-fetch/-/arcgis-rest-fetch-4.10.3.tgz", - "integrity": "sha512-bYrL+/DruzL26qX/LXYCLxto8ZpGq+/tSMFRo41kQO1QOE3RqH7+GN8ovQcJU9X3AzOLi/u0hfBXr7bUjbb7IA==", + "version": "4.11.0", + "resolved": "https://registry.npmjs.org/@esri/arcgis-rest-fetch/-/arcgis-rest-fetch-4.11.0.tgz", + "integrity": "sha512-EcpT2EwFX5GmUwdzwQ3nfUz7M4Mo6z2fbWP3/5/tZefVz+TnWWgceudIiWy45AinuEHiJS5rCgOkNS7vmTabsA==", "license": "Apache-2.0", "dependencies": { "node-fetch": "^3.2.10" } }, "node_modules/@esri/arcgis-rest-form-data": { - "version": "4.10.3", - "resolved": "https://registry.npmjs.org/@esri/arcgis-rest-form-data/-/arcgis-rest-form-data-4.10.3.tgz", - "integrity": "sha512-6jjXTqLOFMU8/WfXyzuVfq2BhIMdR09zyjJeb0jhDzxrfFfs9IfgokLLF/LlPd60CVPjG04cPqCAW2RhRzkzZA==", + "version": "4.11.0", + "resolved": "https://registry.npmjs.org/@esri/arcgis-rest-form-data/-/arcgis-rest-form-data-4.11.0.tgz", + "integrity": "sha512-Gm9ojfnDjH3meBHZ077otEexnMJDJXkMnVqrycMWEqzNU/7saBgtHvfscF6xwQ8JZ+Jh0VeGQ7ECUQ3xNkIPcA==", "license": "Apache-2.0", "dependencies": { "formdata-node": "^4.1.0" } }, "node_modules/@esri/arcgis-rest-portal": { - "version": "4.10.3", - "resolved": "https://registry.npmjs.org/@esri/arcgis-rest-portal/-/arcgis-rest-portal-4.10.3.tgz", - "integrity": "sha512-o5iwxSDS2M8lwLkNtaJTKqyN3NHv2Ne5bL+7tfdcmmHukcGMuuSroRG/+IT3CXOS7gk9sYVdR0cMEPHs1lTWNQ==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@esri/arcgis-rest-portal/-/arcgis-rest-portal-4.3.0.tgz", + "integrity": "sha512-3cAXfTD2ki3U1FNWmw9VPVZX11qD39POds42QFHifnkFZw+zA/yVeRjlPaGbXRE6JJfdIhDFzKVMvSPhRiFs2w==", "license": "Apache-2.0", - "peer": true, "dependencies": { "tslib": "^2.3.0" }, @@ -938,9 +1087,9 @@ } }, "node_modules/@istanbuljs/load-nyc-config/node_modules/js-yaml": { - "version": "3.14.2", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.2.tgz", - "integrity": "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==", + "version": "3.15.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.15.1.tgz", + "integrity": "sha512-S99WuO3HlhO3XN41EtYUNl9zzXjoJx7QvmipxsJVxtCBT0YHEFy+iOJhjSvrmV12nYhWpZaM8lPHkJm0yUMbag==", "dev": true, "license": "MIT", "dependencies": { @@ -1404,13454 +1553,190 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@mongodb-js/saslprep": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.5.0.tgz", + "integrity": "sha512-Hk1SKJCMcCos38+vqDnZzlIo4XRj9yCGzYkjB4LcqpeXRIYfia1UWTz+VrueLxoU+uSRJzgkufxoRZg8gi52YA==", + "dev": true, + "license": "MIT", + "dependencies": { + "sparse-bitfield": "^3.0.3" + } + }, "node_modules/@napi-rs/wasm-runtime": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.5.tgz", - "integrity": "sha512-AWPoBRJ9tsnVhor4sjO7rkni+7p+2IAEFj6cx06UgP10jkQHqay/36uRV/bFkgrh18D9vb4cr8Q0Pthskgzy+Q==", + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.2.3.tgz", + "integrity": "sha512-UMduMbqO5s5zF2NkNacMT/yK5Y5QiKvWr2+50bzIIxFDwVJ2h49b+oyjaCGPhJxd2/gC2x39EHv/gHVuu36x2Q==", "dev": true, "license": "MIT", "optional": true, "dependencies": { - "@tybys/wasm-util": "^0.10.2" + "@tybys/wasm-util": "^0.10.3" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=23.5.0" }, "funding": { "type": "github", "url": "https://github.com/sponsors/Brooooooklyn" }, "peerDependencies": { - "@emnapi/core": "^1.7.1", - "@emnapi/runtime": "^1.7.1" + "@emnapi/core": "^1.7.1 || ^2.0.0-alpha.4", + "@emnapi/runtime": "^1.7.1 || ^2.0.0-alpha.4" } }, "node_modules/@ngageoint/mage.service": { - "version": "6.5.8", - "resolved": "https://registry.npmjs.org/@ngageoint/mage.service/-/mage.service-6.5.8.tgz", - "integrity": "sha512-mjMadP/nmVFHRbuvucX4jqnn2yWJKlob7HntoXzURmj7yd09/KiDGKVrehC9vISjGHCvSDplTnGvC6UrJ4AUvQ==", - "hasShrinkwrap": true, - "peer": true, + "resolved": "../../../service", + "link": true + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "license": "MIT", "dependencies": { - "@ngageoint/geopackage": "4.2.6", - "@ngageoint/mongodb-migrations": "1.0.0", - "@node-saml/passport-saml": "4.0.4", - "@turf/centroid": "7.2.0", - "@turf/kinks": "7.2.0", - "@types/geojson": "7946.0.16", - "@types/json-schema": "7.0.15", - "@types/mime-types": "2.1.4", - "@xmldom/xmldom": "0.9.8", - "adm-zip": "0.5.16", - "archiver": "5.3.2", - "async": "3.2.6", - "async-lock": "1.4.1", - "base-64": "1.0.0", - "busboy": "1.6.0", - "cfenv": "1.2.4", - "commander": "8.3.0", - "express": "4.21.2", - "express-session": "1.18.1", - "file-type": "16.5.4", - "fs-extra": "4.0.3", - "geojson": "0.5.0", - "geojson-validation": "1.0.2", - "json2csv": "4.5.4", - "jsonwebtoken": "9.0.2", - "lodash": "4.17.21", - "mgrs": "1.0.0", - "mime-types": "2.1.35", - "moment": "2.30.1", - "mongoose": "6.13.8", - "multer": "1.4.5-lts.1", - "node-fetch": "2.7.0", - "passport": "0.7.0", - "passport-anonymous": "1.0.1", - "passport-http-bearer": "1.0.1", - "passport-ldapauth": "3.0.1", - "passport-local": "1.0.0", - "passport-oauth2": "1.8.0", - "passport-openidconnect": "0.1.2", - "pug": "3.0.3", - "rfc5646": "3.0.0", - "superagent": "8.1.2", - "svg-captcha": "1.4.0", - "uniqid": "5.4.0", - "walk": "2.3.15", - "winston": "1.1.2", - "wkx": "0.5.0", - "wms-capabilities": "0.6.0", - "xmlbuilder2": "3.1.1", - "xpath": "0.0.34", - "yaml": "1.10.2" - }, - "bin": { - "mage.service": "bin/mage.service.js" + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" }, "engines": { - "node": ">= 16", - "npm": ">= 8" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@aws-crypto/sha256-browser": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-browser/-/sha256-browser-5.2.0.tgz", - "integrity": "sha512-AXfN/lGotSQwu6HNcEsIASo7kWXZ5HYWvfOmSNKDsEqC4OashTp8alTmaz+F7TC2L083SFv5RdB+qU3Vs1kZqw==", - "optional": true, - "peer": true, - "dependencies": { - "@aws-crypto/sha256-js": "^5.2.0", - "@aws-crypto/supports-web-crypto": "^5.2.0", - "@aws-crypto/util": "^5.2.0", - "@aws-sdk/types": "^3.222.0", - "@aws-sdk/util-locate-window": "^3.0.0", - "@smithy/util-utf8": "^2.0.0", - "tslib": "^2.6.2" + "node": ">= 8" } }, - "node_modules/@ngageoint/mage.service/node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/is-array-buffer": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz", - "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==", - "optional": true, - "peer": true, - "dependencies": { - "tslib": "^2.6.2" - }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "license": "MIT", "engines": { - "node": ">=14.0.0" + "node": ">= 8" } }, - "node_modules/@ngageoint/mage.service/node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/util-buffer-from": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz", - "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==", - "optional": true, - "peer": true, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "license": "MIT", "dependencies": { - "@smithy/is-array-buffer": "^2.2.0", - "tslib": "^2.6.2" + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" }, "engines": { - "node": ">=14.0.0" + "node": ">= 8" } }, - "node_modules/@ngageoint/mage.service/node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/util-utf8": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", - "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "license": "MIT", "optional": true, - "peer": true, - "dependencies": { - "@smithy/util-buffer-from": "^2.2.0", - "tslib": "^2.6.2" - }, "engines": { - "node": ">=14.0.0" + "node": ">=14" } }, - "node_modules/@ngageoint/mage.service/node_modules/@aws-crypto/sha256-js": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-js/-/sha256-js-5.2.0.tgz", - "integrity": "sha512-FFQQyu7edu4ufvIZ+OadFpHHOt+eSTBaYaki44c+akjg7qZg9oOQeLlk77F6tSYqjDAFClrHJk9tMf0HdVyOvA==", - "optional": true, - "peer": true, - "dependencies": { - "@aws-crypto/util": "^5.2.0", - "@aws-sdk/types": "^3.222.0", - "tslib": "^2.6.2" - }, + "node_modules/@pkgr/core": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.1.2.tgz", + "integrity": "sha512-fdDH1LSGfZdTH2sxdpVMw31BanV28K/Gry0cVFxaNP77neJSkd82mM8ErPNYs9e+0O7SdHBLTDzDgwUuy18RnQ==", + "dev": true, + "license": "MIT", "engines": { - "node": ">=16.0.0" + "node": "^12.20.0 || ^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/unts" } }, - "node_modules/@ngageoint/mage.service/node_modules/@aws-crypto/supports-web-crypto": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/supports-web-crypto/-/supports-web-crypto-5.2.0.tgz", - "integrity": "sha512-iAvUotm021kM33eCdNfwIN//F77/IADDSs58i+MDaOqFrVjZo9bAal0NK7HurRuWLLpF1iLX7gbWrjHjeo+YFg==", - "optional": true, - "peer": true, - "dependencies": { - "tslib": "^2.6.2" - } + "node_modules/@sinclair/typebox": { + "version": "0.34.52", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.52.tgz", + "integrity": "sha512-XiMQh7qqVlxZzcVD+kkGMNGMzcTrDMLWI7S4x7z1MkCkbDPrekpZXEUK0eZqZFMuHQg2a2DZOcDIh9o5v3Gonw==", + "dev": true, + "license": "MIT" }, - "node_modules/@ngageoint/mage.service/node_modules/@aws-crypto/util": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/util/-/util-5.2.0.tgz", - "integrity": "sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ==", - "optional": true, - "peer": true, + "node_modules/@sinonjs/commons": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", + "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", + "dev": true, + "license": "BSD-3-Clause", "dependencies": { - "@aws-sdk/types": "^3.222.0", - "@smithy/util-utf8": "^2.0.0", - "tslib": "^2.6.2" + "type-detect": "4.0.8" } }, - "node_modules/@ngageoint/mage.service/node_modules/@aws-crypto/util/node_modules/@smithy/is-array-buffer": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz", - "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==", - "optional": true, - "peer": true, + "node_modules/@sinonjs/fake-timers": { + "version": "15.4.0", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-15.4.0.tgz", + "integrity": "sha512-DsG+8/LscQIQg68J6Ef3dv10u6nVyetYn923s3/sus5eaGfTo1of5WMZSLf0UJc9KDuKPilPH0UDJCjvNbDNCA==", + "dev": true, + "license": "BSD-3-Clause", "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=14.0.0" + "@sinonjs/commons": "^3.0.1" } }, - "node_modules/@ngageoint/mage.service/node_modules/@aws-crypto/util/node_modules/@smithy/util-buffer-from": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz", - "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==", - "optional": true, - "peer": true, + "node_modules/@terraformer/arcgis": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@terraformer/arcgis/-/arcgis-2.1.2.tgz", + "integrity": "sha512-IvdfqehcNAUtKU1OFMKwPT8EvdKlVFZ7q7ZKzkIF8XzYZIVsZLuXuOS1UBdRh5u/o+X5Gax7jiZhD8U/4TV+Jw==", + "license": "MIT", "dependencies": { - "@smithy/is-array-buffer": "^2.2.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=14.0.0" + "@terraformer/common": "^2.1.2" } }, - "node_modules/@ngageoint/mage.service/node_modules/@aws-crypto/util/node_modules/@smithy/util-utf8": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", - "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", - "optional": true, - "peer": true, - "dependencies": { - "@smithy/util-buffer-from": "^2.2.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=14.0.0" - } + "node_modules/@terraformer/common": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/@terraformer/common/-/common-2.2.2.tgz", + "integrity": "sha512-W+O/hblr5g1RBzkehaEfF/EdCerkG7j6g2cGBawp2B2zHXdCdR7NrXc0Sh106SFFJyadQZ62On36S+8wHF4Kag==", + "license": "MIT" }, - "node_modules/@ngageoint/mage.service/node_modules/@aws-sdk/client-cognito-identity": { - "version": "3.606.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-cognito-identity/-/client-cognito-identity-3.606.0.tgz", - "integrity": "sha512-CJ3kovUg7HAn3trqo0WxVw3PJoaHxiGU1U+Ok8Vx/sL81+auyyiasT09M/NcchRqwAooKvUi44sVD0ih7Zi9Nw==", - "optional": true, - "peer": true, - "dependencies": { - "@aws-crypto/sha256-browser": "5.2.0", - "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/client-sso-oidc": "3.606.0", - "@aws-sdk/client-sts": "3.606.0", - "@aws-sdk/core": "3.598.0", - "@aws-sdk/credential-provider-node": "3.600.0", - "@aws-sdk/middleware-host-header": "3.598.0", - "@aws-sdk/middleware-logger": "3.598.0", - "@aws-sdk/middleware-recursion-detection": "3.598.0", - "@aws-sdk/middleware-user-agent": "3.598.0", - "@aws-sdk/region-config-resolver": "3.598.0", - "@aws-sdk/types": "3.598.0", - "@aws-sdk/util-endpoints": "3.598.0", - "@aws-sdk/util-user-agent-browser": "3.598.0", - "@aws-sdk/util-user-agent-node": "3.598.0", - "@smithy/config-resolver": "^3.0.2", - "@smithy/core": "^2.2.1", - "@smithy/fetch-http-handler": "^3.0.2", - "@smithy/hash-node": "^3.0.1", - "@smithy/invalid-dependency": "^3.0.1", - "@smithy/middleware-content-length": "^3.0.1", - "@smithy/middleware-endpoint": "^3.0.2", - "@smithy/middleware-retry": "^3.0.4", - "@smithy/middleware-serde": "^3.0.1", - "@smithy/middleware-stack": "^3.0.1", - "@smithy/node-config-provider": "^3.1.1", - "@smithy/node-http-handler": "^3.0.1", - "@smithy/protocol-http": "^4.0.1", - "@smithy/smithy-client": "^3.1.2", - "@smithy/types": "^3.1.0", - "@smithy/url-parser": "^3.0.1", - "@smithy/util-base64": "^3.0.0", - "@smithy/util-body-length-browser": "^3.0.0", - "@smithy/util-body-length-node": "^3.0.0", - "@smithy/util-defaults-mode-browser": "^3.0.4", - "@smithy/util-defaults-mode-node": "^3.0.4", - "@smithy/util-endpoints": "^2.0.2", - "@smithy/util-middleware": "^3.0.1", - "@smithy/util-retry": "^3.0.1", - "@smithy/util-utf8": "^3.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } + "node_modules/@terraformer/wkt": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@terraformer/wkt/-/wkt-2.2.1.tgz", + "integrity": "sha512-XDUsW/lvbMzFi7GIuRD9+UqR4QyP+5C+TugeJLMDczKIRbaHoE9J3N8zLSdyOGmnJL9B6xTS3YMMlBnMU0Ar5A==", + "license": "MIT" }, - "node_modules/@ngageoint/mage.service/node_modules/@aws-sdk/client-sso": { - "version": "3.598.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.598.0.tgz", - "integrity": "sha512-nOI5lqPYa+YZlrrzwAJywJSw3MKVjvu6Ge2fCqQUNYMfxFB0NAaDFnl0EPjXi+sEbtCuz/uWE77poHbqiZ+7Iw==", - "optional": true, - "peer": true, - "dependencies": { - "@aws-crypto/sha256-browser": "5.2.0", - "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "3.598.0", - "@aws-sdk/middleware-host-header": "3.598.0", - "@aws-sdk/middleware-logger": "3.598.0", - "@aws-sdk/middleware-recursion-detection": "3.598.0", - "@aws-sdk/middleware-user-agent": "3.598.0", - "@aws-sdk/region-config-resolver": "3.598.0", - "@aws-sdk/types": "3.598.0", - "@aws-sdk/util-endpoints": "3.598.0", - "@aws-sdk/util-user-agent-browser": "3.598.0", - "@aws-sdk/util-user-agent-node": "3.598.0", - "@smithy/config-resolver": "^3.0.2", - "@smithy/core": "^2.2.1", - "@smithy/fetch-http-handler": "^3.0.2", - "@smithy/hash-node": "^3.0.1", - "@smithy/invalid-dependency": "^3.0.1", - "@smithy/middleware-content-length": "^3.0.1", - "@smithy/middleware-endpoint": "^3.0.2", - "@smithy/middleware-retry": "^3.0.4", - "@smithy/middleware-serde": "^3.0.1", - "@smithy/middleware-stack": "^3.0.1", - "@smithy/node-config-provider": "^3.1.1", - "@smithy/node-http-handler": "^3.0.1", - "@smithy/protocol-http": "^4.0.1", - "@smithy/smithy-client": "^3.1.2", - "@smithy/types": "^3.1.0", - "@smithy/url-parser": "^3.0.1", - "@smithy/util-base64": "^3.0.0", - "@smithy/util-body-length-browser": "^3.0.0", - "@smithy/util-body-length-node": "^3.0.0", - "@smithy/util-defaults-mode-browser": "^3.0.4", - "@smithy/util-defaults-mode-node": "^3.0.4", - "@smithy/util-endpoints": "^2.0.2", - "@smithy/util-middleware": "^3.0.1", - "@smithy/util-retry": "^3.0.1", - "@smithy/util-utf8": "^3.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@aws-sdk/client-sso-oidc": { - "version": "3.606.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso-oidc/-/client-sso-oidc-3.606.0.tgz", - "integrity": "sha512-gL1FHPS6hwgMNS/A+Qh5bUyHOeRVOqdb7c6+i+9gR3wtGvt2lvoSm8w5DhS08Xiiacz2AqYRDEapp0xuyCrbBQ==", - "optional": true, - "peer": true, - "dependencies": { - "@aws-crypto/sha256-browser": "5.2.0", - "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "3.598.0", - "@aws-sdk/credential-provider-node": "3.600.0", - "@aws-sdk/middleware-host-header": "3.598.0", - "@aws-sdk/middleware-logger": "3.598.0", - "@aws-sdk/middleware-recursion-detection": "3.598.0", - "@aws-sdk/middleware-user-agent": "3.598.0", - "@aws-sdk/region-config-resolver": "3.598.0", - "@aws-sdk/types": "3.598.0", - "@aws-sdk/util-endpoints": "3.598.0", - "@aws-sdk/util-user-agent-browser": "3.598.0", - "@aws-sdk/util-user-agent-node": "3.598.0", - "@smithy/config-resolver": "^3.0.2", - "@smithy/core": "^2.2.1", - "@smithy/fetch-http-handler": "^3.0.2", - "@smithy/hash-node": "^3.0.1", - "@smithy/invalid-dependency": "^3.0.1", - "@smithy/middleware-content-length": "^3.0.1", - "@smithy/middleware-endpoint": "^3.0.2", - "@smithy/middleware-retry": "^3.0.4", - "@smithy/middleware-serde": "^3.0.1", - "@smithy/middleware-stack": "^3.0.1", - "@smithy/node-config-provider": "^3.1.1", - "@smithy/node-http-handler": "^3.0.1", - "@smithy/protocol-http": "^4.0.1", - "@smithy/smithy-client": "^3.1.2", - "@smithy/types": "^3.1.0", - "@smithy/url-parser": "^3.0.1", - "@smithy/util-base64": "^3.0.0", - "@smithy/util-body-length-browser": "^3.0.0", - "@smithy/util-body-length-node": "^3.0.0", - "@smithy/util-defaults-mode-browser": "^3.0.4", - "@smithy/util-defaults-mode-node": "^3.0.4", - "@smithy/util-endpoints": "^2.0.2", - "@smithy/util-middleware": "^3.0.1", - "@smithy/util-retry": "^3.0.1", - "@smithy/util-utf8": "^3.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - }, - "peerDependencies": { - "@aws-sdk/client-sts": "^3.606.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@aws-sdk/client-sts": { - "version": "3.606.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sts/-/client-sts-3.606.0.tgz", - "integrity": "sha512-b11mAhjrkm3MMiAPoMGcmd6vsaz2120lg8rHG/NZCo9vB1K6Kc7WP+a1Q05TRMseer2egTtpWJfn44aVO97VqA==", - "optional": true, - "peer": true, - "dependencies": { - "@aws-crypto/sha256-browser": "5.2.0", - "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/client-sso-oidc": "3.606.0", - "@aws-sdk/core": "3.598.0", - "@aws-sdk/credential-provider-node": "3.600.0", - "@aws-sdk/middleware-host-header": "3.598.0", - "@aws-sdk/middleware-logger": "3.598.0", - "@aws-sdk/middleware-recursion-detection": "3.598.0", - "@aws-sdk/middleware-user-agent": "3.598.0", - "@aws-sdk/region-config-resolver": "3.598.0", - "@aws-sdk/types": "3.598.0", - "@aws-sdk/util-endpoints": "3.598.0", - "@aws-sdk/util-user-agent-browser": "3.598.0", - "@aws-sdk/util-user-agent-node": "3.598.0", - "@smithy/config-resolver": "^3.0.2", - "@smithy/core": "^2.2.1", - "@smithy/fetch-http-handler": "^3.0.2", - "@smithy/hash-node": "^3.0.1", - "@smithy/invalid-dependency": "^3.0.1", - "@smithy/middleware-content-length": "^3.0.1", - "@smithy/middleware-endpoint": "^3.0.2", - "@smithy/middleware-retry": "^3.0.4", - "@smithy/middleware-serde": "^3.0.1", - "@smithy/middleware-stack": "^3.0.1", - "@smithy/node-config-provider": "^3.1.1", - "@smithy/node-http-handler": "^3.0.1", - "@smithy/protocol-http": "^4.0.1", - "@smithy/smithy-client": "^3.1.2", - "@smithy/types": "^3.1.0", - "@smithy/url-parser": "^3.0.1", - "@smithy/util-base64": "^3.0.0", - "@smithy/util-body-length-browser": "^3.0.0", - "@smithy/util-body-length-node": "^3.0.0", - "@smithy/util-defaults-mode-browser": "^3.0.4", - "@smithy/util-defaults-mode-node": "^3.0.4", - "@smithy/util-endpoints": "^2.0.2", - "@smithy/util-middleware": "^3.0.1", - "@smithy/util-retry": "^3.0.1", - "@smithy/util-utf8": "^3.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@aws-sdk/core": { - "version": "3.598.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.598.0.tgz", - "integrity": "sha512-HaSjt7puO5Cc7cOlrXFCW0rtA0BM9lvzjl56x0A20Pt+0wxXGeTOZZOkXQIepbrFkV2e/HYukuT9e99vXDm59g==", - "optional": true, - "peer": true, - "dependencies": { - "@smithy/core": "^2.2.1", - "@smithy/protocol-http": "^4.0.1", - "@smithy/signature-v4": "^3.1.0", - "@smithy/smithy-client": "^3.1.2", - "@smithy/types": "^3.1.0", - "fast-xml-parser": "4.2.5", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@aws-sdk/credential-provider-cognito-identity": { - "version": "3.606.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-cognito-identity/-/credential-provider-cognito-identity-3.606.0.tgz", - "integrity": "sha512-4wGvXilFLkozs4/dMnn9NvxZbL9oyyReoF9aR3kGUZ0QVO8cCBp/Zkr8IXZifhVBo9/esJdMFnR9lEXR7Yuleg==", - "optional": true, - "peer": true, - "dependencies": { - "@aws-sdk/client-cognito-identity": "3.606.0", - "@aws-sdk/types": "3.598.0", - "@smithy/property-provider": "^3.1.1", - "@smithy/types": "^3.1.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@aws-sdk/credential-provider-env": { - "version": "3.598.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.598.0.tgz", - "integrity": "sha512-vi1khgn7yXzLCcgSIzQrrtd2ilUM0dWodxj3PQ6BLfP0O+q1imO3hG1nq7DVyJtq7rFHs6+9N8G4mYvTkxby2w==", - "optional": true, - "peer": true, - "dependencies": { - "@aws-sdk/types": "3.598.0", - "@smithy/property-provider": "^3.1.1", - "@smithy/types": "^3.1.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@aws-sdk/credential-provider-http": { - "version": "3.598.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.598.0.tgz", - "integrity": "sha512-N7cIafi4HVlQvEgvZSo1G4T9qb/JMLGMdBsDCT5XkeJrF0aptQWzTFH0jIdZcLrMYvzPcuEyO3yCBe6cy/ba0g==", - "optional": true, - "peer": true, - "dependencies": { - "@aws-sdk/types": "3.598.0", - "@smithy/fetch-http-handler": "^3.0.2", - "@smithy/node-http-handler": "^3.0.1", - "@smithy/property-provider": "^3.1.1", - "@smithy/protocol-http": "^4.0.1", - "@smithy/smithy-client": "^3.1.2", - "@smithy/types": "^3.1.0", - "@smithy/util-stream": "^3.0.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@aws-sdk/credential-provider-ini": { - "version": "3.598.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.598.0.tgz", - "integrity": "sha512-/ppcIVUbRwDIwJDoYfp90X3+AuJo2mvE52Y1t2VSrvUovYn6N4v95/vXj6LS8CNDhz2jvEJYmu+0cTMHdhI6eA==", - "optional": true, - "peer": true, - "dependencies": { - "@aws-sdk/credential-provider-env": "3.598.0", - "@aws-sdk/credential-provider-http": "3.598.0", - "@aws-sdk/credential-provider-process": "3.598.0", - "@aws-sdk/credential-provider-sso": "3.598.0", - "@aws-sdk/credential-provider-web-identity": "3.598.0", - "@aws-sdk/types": "3.598.0", - "@smithy/credential-provider-imds": "^3.1.1", - "@smithy/property-provider": "^3.1.1", - "@smithy/shared-ini-file-loader": "^3.1.1", - "@smithy/types": "^3.1.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - }, - "peerDependencies": { - "@aws-sdk/client-sts": "^3.598.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@aws-sdk/credential-provider-node": { - "version": "3.600.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.600.0.tgz", - "integrity": "sha512-1pC7MPMYD45J7yFjA90SxpR0yaSvy+yZiq23aXhAPZLYgJBAxHLu0s0mDCk/piWGPh8+UGur5K0bVdx4B1D5hw==", - "optional": true, - "peer": true, - "dependencies": { - "@aws-sdk/credential-provider-env": "3.598.0", - "@aws-sdk/credential-provider-http": "3.598.0", - "@aws-sdk/credential-provider-ini": "3.598.0", - "@aws-sdk/credential-provider-process": "3.598.0", - "@aws-sdk/credential-provider-sso": "3.598.0", - "@aws-sdk/credential-provider-web-identity": "3.598.0", - "@aws-sdk/types": "3.598.0", - "@smithy/credential-provider-imds": "^3.1.1", - "@smithy/property-provider": "^3.1.1", - "@smithy/shared-ini-file-loader": "^3.1.1", - "@smithy/types": "^3.1.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@aws-sdk/credential-provider-process": { - "version": "3.598.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.598.0.tgz", - "integrity": "sha512-rM707XbLW8huMk722AgjVyxu2tMZee++fNA8TJVNgs1Ma02Wx6bBrfIvlyK0rCcIRb0WdQYP6fe3Xhiu4e8IBA==", - "optional": true, - "peer": true, - "dependencies": { - "@aws-sdk/types": "3.598.0", - "@smithy/property-provider": "^3.1.1", - "@smithy/shared-ini-file-loader": "^3.1.1", - "@smithy/types": "^3.1.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@aws-sdk/credential-provider-sso": { - "version": "3.598.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.598.0.tgz", - "integrity": "sha512-5InwUmrAuqQdOOgxTccRayMMkSmekdLk6s+az9tmikq0QFAHUCtofI+/fllMXSR9iL6JbGYi1940+EUmS4pHJA==", - "optional": true, - "peer": true, - "dependencies": { - "@aws-sdk/client-sso": "3.598.0", - "@aws-sdk/token-providers": "3.598.0", - "@aws-sdk/types": "3.598.0", - "@smithy/property-provider": "^3.1.1", - "@smithy/shared-ini-file-loader": "^3.1.1", - "@smithy/types": "^3.1.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@aws-sdk/credential-provider-web-identity": { - "version": "3.598.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.598.0.tgz", - "integrity": "sha512-GV5GdiMbz5Tz9JO4NJtRoFXjW0GPEujA0j+5J/B723rTN+REHthJu48HdBKouHGhdzkDWkkh1bu52V02Wprw8w==", - "optional": true, - "peer": true, - "dependencies": { - "@aws-sdk/types": "3.598.0", - "@smithy/property-provider": "^3.1.1", - "@smithy/types": "^3.1.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - }, - "peerDependencies": { - "@aws-sdk/client-sts": "^3.598.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@aws-sdk/credential-providers": { - "version": "3.606.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-providers/-/credential-providers-3.606.0.tgz", - "integrity": "sha512-34hswGNDWBFvp4Hi4Gv9DIJ4Ks0Nbg8w3emFsPVHLqqI6X2Wd0hJTf+mi1kMhy/AQXt5LisKLw6wjNIKD2+KGg==", - "optional": true, - "peer": true, - "dependencies": { - "@aws-sdk/client-cognito-identity": "3.606.0", - "@aws-sdk/client-sso": "3.598.0", - "@aws-sdk/client-sts": "3.606.0", - "@aws-sdk/credential-provider-cognito-identity": "3.606.0", - "@aws-sdk/credential-provider-env": "3.598.0", - "@aws-sdk/credential-provider-http": "3.598.0", - "@aws-sdk/credential-provider-ini": "3.598.0", - "@aws-sdk/credential-provider-node": "3.600.0", - "@aws-sdk/credential-provider-process": "3.598.0", - "@aws-sdk/credential-provider-sso": "3.598.0", - "@aws-sdk/credential-provider-web-identity": "3.598.0", - "@aws-sdk/types": "3.598.0", - "@smithy/credential-provider-imds": "^3.1.1", - "@smithy/property-provider": "^3.1.1", - "@smithy/types": "^3.1.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@aws-sdk/middleware-host-header": { - "version": "3.598.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.598.0.tgz", - "integrity": "sha512-WiaG059YBQwQraNejLIi0gMNkX7dfPZ8hDIhvMr5aVPRbaHH8AYF3iNSsXYCHvA2Cfa1O9haYXsuMF9flXnCmA==", - "optional": true, - "peer": true, - "dependencies": { - "@aws-sdk/types": "3.598.0", - "@smithy/protocol-http": "^4.0.1", - "@smithy/types": "^3.1.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@aws-sdk/middleware-logger": { - "version": "3.598.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.598.0.tgz", - "integrity": "sha512-bxBjf/VYiu3zfu8SYM2S9dQQc3tz5uBAOcPz/Bt8DyyK3GgOpjhschH/2XuUErsoUO1gDJqZSdGOmuHGZQn00Q==", - "optional": true, - "peer": true, - "dependencies": { - "@aws-sdk/types": "3.598.0", - "@smithy/types": "^3.1.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@aws-sdk/middleware-recursion-detection": { - "version": "3.598.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.598.0.tgz", - "integrity": "sha512-vjT9BeFY9FeN0f8hm2l6F53tI0N5bUq6RcDkQXKNabXBnQxKptJRad6oP2X5y3FoVfBLOuDkQgiC2940GIPxtQ==", - "optional": true, - "peer": true, - "dependencies": { - "@aws-sdk/types": "3.598.0", - "@smithy/protocol-http": "^4.0.1", - "@smithy/types": "^3.1.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@aws-sdk/middleware-user-agent": { - "version": "3.598.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.598.0.tgz", - "integrity": "sha512-4tjESlHG5B5MdjUaLK7tQs/miUtHbb6deauQx8ryqSBYOhfHVgb1ZnzvQR0bTrhpqUg0WlybSkDaZAICf9xctg==", - "optional": true, - "peer": true, - "dependencies": { - "@aws-sdk/types": "3.598.0", - "@aws-sdk/util-endpoints": "3.598.0", - "@smithy/protocol-http": "^4.0.1", - "@smithy/types": "^3.1.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@aws-sdk/region-config-resolver": { - "version": "3.598.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.598.0.tgz", - "integrity": "sha512-oYXhmTokSav4ytmWleCr3rs/1nyvZW/S0tdi6X7u+dLNL5Jee+uMxWGzgOrWK6wrQOzucLVjS4E/wA11Kv2GTw==", - "optional": true, - "peer": true, - "dependencies": { - "@aws-sdk/types": "3.598.0", - "@smithy/node-config-provider": "^3.1.1", - "@smithy/types": "^3.1.0", - "@smithy/util-config-provider": "^3.0.0", - "@smithy/util-middleware": "^3.0.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@aws-sdk/token-providers": { - "version": "3.598.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.598.0.tgz", - "integrity": "sha512-TKY1EVdHVBnZqpyxyTHdpZpa1tUpb6nxVeRNn1zWG8QB5MvH4ALLd/jR+gtmWDNQbIG4cVuBOZFVL8hIYicKTA==", - "optional": true, - "peer": true, - "dependencies": { - "@aws-sdk/types": "3.598.0", - "@smithy/property-provider": "^3.1.1", - "@smithy/shared-ini-file-loader": "^3.1.1", - "@smithy/types": "^3.1.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - }, - "peerDependencies": { - "@aws-sdk/client-sso-oidc": "^3.598.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@aws-sdk/types": { - "version": "3.598.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.598.0.tgz", - "integrity": "sha512-742uRl6z7u0LFmZwDrFP6r1wlZcgVPw+/TilluDJmCAR8BgRw3IR+743kUXKBGd8QZDRW2n6v/PYsi/AWCDDMQ==", - "optional": true, - "peer": true, - "dependencies": { - "@smithy/types": "^3.1.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@aws-sdk/util-endpoints": { - "version": "3.598.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.598.0.tgz", - "integrity": "sha512-Qo9UoiVVZxcOEdiOMZg3xb1mzkTxrhd4qSlg5QQrfWPJVx/QOg+Iy0NtGxPtHtVZNHZxohYwDwV/tfsnDSE2gQ==", - "optional": true, - "peer": true, - "dependencies": { - "@aws-sdk/types": "3.598.0", - "@smithy/types": "^3.1.0", - "@smithy/util-endpoints": "^2.0.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@aws-sdk/util-locate-window": { - "version": "3.568.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-locate-window/-/util-locate-window-3.568.0.tgz", - "integrity": "sha512-3nh4TINkXYr+H41QaPelCceEB2FXP3fxp93YZXB/kqJvX0U9j0N0Uk45gvsjmEPzG8XxkPEeLIfT2I1M7A6Lig==", - "optional": true, - "peer": true, - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@aws-sdk/util-user-agent-browser": { - "version": "3.598.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.598.0.tgz", - "integrity": "sha512-36Sxo6F+ykElaL1mWzWjlg+1epMpSe8obwhCN1yGE7Js9ywy5U6k6l+A3q3YM9YRbm740sNxncbwLklMvuhTKw==", - "optional": true, - "peer": true, - "dependencies": { - "@aws-sdk/types": "3.598.0", - "@smithy/types": "^3.1.0", - "bowser": "^2.11.0", - "tslib": "^2.6.2" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@aws-sdk/util-user-agent-node": { - "version": "3.598.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.598.0.tgz", - "integrity": "sha512-oyWGcOlfTdzkC6SVplyr0AGh54IMrDxbhg5RxJ5P+V4BKfcDoDcZV9xenUk9NsOi9MuUjxMumb9UJGkDhM1m0A==", - "optional": true, - "peer": true, - "dependencies": { - "@aws-sdk/types": "3.598.0", - "@smithy/node-config-provider": "^3.1.1", - "@smithy/types": "^3.1.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - }, - "peerDependencies": { - "aws-crt": ">=1.0.0" - }, - "peerDependenciesMeta": { - "aws-crt": { - "optional": true - } - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@babel/code-frame": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.7.tgz", - "integrity": "sha512-BcYH1CVJBO9tvyIZ2jVeXgSIMvGZ2FDRvDdOIVQyuklNKSsx+eppDEBq/g47Ayw+RqNFE+URvOShmf+f/qwAlA==", - "extraneous": true, - "dependencies": { - "@babel/highlight": "^7.24.7", - "picocolors": "^1.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@babel/generator": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.24.7.tgz", - "integrity": "sha512-oipXieGC3i45Y1A41t4tAqpnEZWgB/lC6Ehh6+rOviR5XWpTtMmLN+fGjz9vOiNRt0p6RtO6DtD0pdU3vpqdSA==", - "extraneous": true, - "dependencies": { - "@babel/types": "^7.24.7", - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.25", - "jsesc": "^2.5.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@babel/helper-environment-visitor": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.24.7.tgz", - "integrity": "sha512-DoiN84+4Gnd0ncbBOM9AZENV4a5ZiL39HYMyZJGZ/AZEykHYdJw0wW3kdcsh9/Kn+BRXHLkkklZ51ecPKmI1CQ==", - "extraneous": true, - "dependencies": { - "@babel/types": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@babel/helper-function-name": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.24.7.tgz", - "integrity": "sha512-FyoJTsj/PEUWu1/TYRiXTIHc8lbw+TDYkZuoE43opPS5TrI7MyONBE1oNvfguEXAD9yhQRrVBnXdXzSLQl9XnA==", - "extraneous": true, - "dependencies": { - "@babel/template": "^7.24.7", - "@babel/types": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@babel/helper-hoist-variables": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.24.7.tgz", - "integrity": "sha512-MJJwhkoGy5c4ehfoRyrJ/owKeMl19U54h27YYftT0o2teQ3FJ3nQUf/I3LlJsX4l3qlw7WRXUmiyajvHXoTubQ==", - "extraneous": true, - "dependencies": { - "@babel/types": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@babel/helper-split-export-declaration": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.24.7.tgz", - "integrity": "sha512-oy5V7pD+UvfkEATUKvIjvIAH/xCzfsFVw7ygW2SI6NClZzquT+mwdTfgfdbUiceh6iQO0CHtCPsyze/MZ2YbAA==", - "extraneous": true, - "dependencies": { - "@babel/types": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@babel/helper-string-parser": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.24.7.tgz", - "integrity": "sha512-7MbVt6xrwFQbunH2DNQsAP5sTGxfqQtErvBIvIMi6EQnbgUOuVYanvREcmFrOPhoXBrTtjhhP+lW+o5UfK+tDg==", - "peer": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@babel/helper-validator-identifier": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.7.tgz", - "integrity": "sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w==", - "peer": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@babel/highlight": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.7.tgz", - "integrity": "sha512-EStJpq4OuY8xYfhGVXngigBJRWxftKX9ksiGDnmlY3o7B/V7KIAc9X4oiK87uPJSc/vs5L869bem5fhZa8caZw==", - "extraneous": true, - "dependencies": { - "@babel/helper-validator-identifier": "^7.24.7", - "chalk": "^2.4.2", - "js-tokens": "^4.0.0", - "picocolors": "^1.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@babel/highlight/node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "extraneous": true, - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@babel/highlight/node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "extraneous": true, - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@babel/highlight/node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "extraneous": true, - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@babel/highlight/node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "extraneous": true - }, - "node_modules/@ngageoint/mage.service/node_modules/@babel/highlight/node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "extraneous": true, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@babel/highlight/node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "extraneous": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@babel/highlight/node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "extraneous": true, - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@babel/parser": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.7.tgz", - "integrity": "sha512-9uUYRm6OqQrCqQdG1iCBwBPZgN8ciDBro2nIOFaiRz1/BCxaI7CNvQbDHvsArAC7Tw9Hda/B3U+6ui9u4HWXPw==", - "peer": true, - "bin": { - "parser": "bin/babel-parser.js" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@babel/template": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.24.7.tgz", - "integrity": "sha512-jYqfPrU9JTF0PmPy1tLYHW4Mp4KlgxJD9l2nP9fD6yT/ICi554DmrWBAEYpIelzjHf1msDP3PxJIRt/nFNfBig==", - "extraneous": true, - "dependencies": { - "@babel/code-frame": "^7.24.7", - "@babel/parser": "^7.24.7", - "@babel/types": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@babel/traverse": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.24.7.tgz", - "integrity": "sha512-yb65Ed5S/QAcewNPh0nZczy9JdYXkkAbIsEo+P7BE7yO3txAY30Y/oPa3QkQ5It3xVG2kpKMg9MsdxZaO31uKA==", - "extraneous": true, - "dependencies": { - "@babel/code-frame": "^7.24.7", - "@babel/generator": "^7.24.7", - "@babel/helper-environment-visitor": "^7.24.7", - "@babel/helper-function-name": "^7.24.7", - "@babel/helper-hoist-variables": "^7.24.7", - "@babel/helper-split-export-declaration": "^7.24.7", - "@babel/parser": "^7.24.7", - "@babel/types": "^7.24.7", - "debug": "^4.3.1", - "globals": "^11.1.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@babel/traverse/node_modules/globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "extraneous": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@babel/types": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.24.7.tgz", - "integrity": "sha512-XEFXSlxiG5td2EJRe8vOmRbaXVgfcBlszKujvVmWIK/UpywWljQCfzAv3RQCGujWQ1RD4YYWEAqDXfuJiy8f5Q==", - "peer": true, - "dependencies": { - "@babel/helper-string-parser": "^7.24.7", - "@babel/helper-validator-identifier": "^7.24.7", - "to-fast-properties": "^2.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@eslint-community/eslint-utils": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", - "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", - "extraneous": true, - "dependencies": { - "eslint-visitor-keys": "^3.3.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@eslint-community/regexpp": { - "version": "4.11.0", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.11.0.tgz", - "integrity": "sha512-G/M/tIiMrTAxEWRfLfQJMmGNX28IxBg4PBz8XqQhqUHLFI6TL2htpIB1iQCj144V5ee/JaKyT9/WZ0MGZWfA7A==", - "extraneous": true, - "engines": { - "node": "^12.0.0 || ^14.0.0 || >=16.0.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@eslint/eslintrc": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", - "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", - "extraneous": true, - "dependencies": { - "ajv": "^6.12.4", - "debug": "^4.3.2", - "espree": "^9.6.0", - "globals": "^13.19.0", - "ignore": "^5.2.0", - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.0", - "minimatch": "^3.1.2", - "strip-json-comments": "^3.1.1" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@eslint/eslintrc/node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "extraneous": true - }, - "node_modules/@ngageoint/mage.service/node_modules/@eslint/eslintrc/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "extraneous": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@eslint/eslintrc/node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "extraneous": true, - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@eslint/eslintrc/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "extraneous": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@eslint/js": { - "version": "8.57.1", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.1.tgz", - "integrity": "sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==", - "extraneous": true, - "license": "MIT", - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@fluffy-spoon/substitute": { - "version": "1.208.0", - "resolved": "https://registry.npmjs.org/@fluffy-spoon/substitute/-/substitute-1.208.0.tgz", - "integrity": "sha512-BU5vKRoK4OYlKzDtyg4HbtWnUNLOvV0ntqEZIphz+mq2G0HlVFywwJ7M+FbIcnJVDbUReS01FyL5x8R01r7zBg==", - "extraneous": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/substitute-js#section-contribute" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@humanwhocodes/config-array": { - "version": "0.13.0", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz", - "integrity": "sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==", - "deprecated": "Use @eslint/config-array instead", - "extraneous": true, - "license": "Apache-2.0", - "dependencies": { - "@humanwhocodes/object-schema": "^2.0.3", - "debug": "^4.3.1", - "minimatch": "^3.0.5" - }, - "engines": { - "node": ">=10.10.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@humanwhocodes/config-array/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "extraneous": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@humanwhocodes/config-array/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "extraneous": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@humanwhocodes/module-importer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", - "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", - "extraneous": true, - "engines": { - "node": ">=12.22" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@humanwhocodes/object-schema": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", - "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", - "deprecated": "Use @eslint/object-schema instead", - "extraneous": true, - "license": "BSD-3-Clause" - }, - "node_modules/@ngageoint/mage.service/node_modules/@isaacs/cliui": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", - "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", - "extraneous": true, - "dependencies": { - "string-width": "^5.1.2", - "string-width-cjs": "npm:string-width@^4.2.0", - "strip-ansi": "^7.0.1", - "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", - "wrap-ansi": "^8.1.0", - "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@isaacs/cliui/node_modules/ansi-regex": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", - "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", - "extraneous": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@isaacs/cliui/node_modules/ansi-styles": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", - "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", - "extraneous": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@isaacs/cliui/node_modules/emoji-regex": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", - "extraneous": true - }, - "node_modules/@ngageoint/mage.service/node_modules/@isaacs/cliui/node_modules/string-width": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", - "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", - "extraneous": true, - "dependencies": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^9.2.2", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@isaacs/cliui/node_modules/strip-ansi": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", - "extraneous": true, - "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@isaacs/cliui/node_modules/wrap-ansi": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", - "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", - "extraneous": true, - "dependencies": { - "ansi-styles": "^6.1.0", - "string-width": "^5.0.1", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@jridgewell/gen-mapping": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", - "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", - "extraneous": true, - "dependencies": { - "@jridgewell/set-array": "^1.2.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.24" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@jridgewell/resolve-uri": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", - "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", - "extraneous": true, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@jridgewell/set-array": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", - "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", - "extraneous": true, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@jridgewell/sourcemap-codec": { - "version": "1.4.15", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", - "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", - "extraneous": true - }, - "node_modules/@ngageoint/mage.service/node_modules/@jridgewell/trace-mapping": { - "version": "0.3.25", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", - "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", - "extraneous": true, - "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@mongodb-js/saslprep": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.1.7.tgz", - "integrity": "sha512-dCHW/oEX0KJ4NjDULBo3JiOaK5+6axtpBbS+ao2ZInoAL9/YRQLhXzSNAFz7hP4nzLkIqsfYAK/PDE3+XHny0Q==", - "optional": true, - "peer": true, - "dependencies": { - "sparse-bitfield": "^3.0.3" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@ngageoint/geopackage": { - "version": "4.2.6", - "resolved": "https://registry.npmjs.org/@ngageoint/geopackage/-/geopackage-4.2.6.tgz", - "integrity": "sha512-HT0WAhAySTv+p462SUkvOKlXoPWDI6LETdBqNl0mC55YxE7PSvPByRIh3r/ajqSirhJeydaoN2wCw0nw+es2bA==", - "peer": true, - "dependencies": { - "@turf/bbox": "6.3.0", - "@turf/boolean-clockwise": "6.5.0", - "@turf/boolean-point-in-polygon": "6.5.0", - "@turf/boolean-within": "6.5.0", - "@turf/distance": "6.5.0", - "@turf/helpers": "6.5.0", - "@turf/intersect": "6.5.0", - "@turf/line-intersect": "6.5.0", - "@turf/point-to-line-distance": "6.5.0", - "@turf/polygon-to-line": "6.5.0", - "@types/geojson": "7946.0.8", - "@types/proj4": "2.5.2", - "file-type": "^16.5.4", - "image-size": "0.8.3", - "lodash": "4.17.21", - "proj4": "2.8.0", - "reproject": "1.2.5", - "rtree-sql.js": "1.7.0", - "simplify-js": "1.2.4", - "webworkify": "1.5.0", - "wkx": "0.4.8" - }, - "bin": { - "geopackage": "cli" - }, - "optionalDependencies": { - "better-sqlite3": "^9.1.1", - "chalk": "4.1.1", - "inquirer": "8.0.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@ngageoint/geopackage/node_modules/@types/geojson": { - "version": "7946.0.8", - "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.8.tgz", - "integrity": "sha512-1rkryxURpr6aWP7R786/UQOkJ3PcpQiWkAXBmdWc7ryFWqN6a4xfK7BtjXvFBKO9LjQ+MWQSWxYeZX1OApnArA==", - "peer": true - }, - "node_modules/@ngageoint/mage.service/node_modules/@ngageoint/geopackage/node_modules/better-sqlite3": { - "version": "9.6.0", - "resolved": "https://registry.npmjs.org/better-sqlite3/-/better-sqlite3-9.6.0.tgz", - "integrity": "sha512-yR5HATnqeYNVnkaUTf4bOP2dJSnyhP4puJN/QPRyx4YkBEEUxib422n2XzPqDEHjQQqazoYoADdAm5vE15+dAQ==", - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "bindings": "^1.5.0", - "prebuild-install": "^7.1.1" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@ngageoint/geopackage/node_modules/wkx": { - "version": "0.4.8", - "resolved": "https://registry.npmjs.org/wkx/-/wkx-0.4.8.tgz", - "integrity": "sha512-ikPXMM9IR/gy/LwiOSqWlSL3X/J5uk9EO2hHNRXS41eTLXaUFEVw9fn/593jW/tE5tedNg8YjT5HkCa4FqQZyQ==", - "license": "MIT", - "peer": true, - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@ngageoint/mongodb-migrations": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@ngageoint/mongodb-migrations/-/mongodb-migrations-1.0.0.tgz", - "integrity": "sha512-SOjJk+xuPfMmevyZOhRCm3odFFW//zg8tKSkiKfgAslMIaZUII6rbrBQRjBPqyFewqg/dabY4xYnXelZSY7/cw==", - "peer": true, - "dependencies": { - "bluebird": "^3.7.2", - "coffeescript": "^2.7.0", - "lodash": "^4.17.21", - "mkdirp": "^0.5.6", - "mongodb": "^4.8.1", - "nomnom": "^1.8.1" - }, - "bin": { - "mm": "bin/mm" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@node-saml/node-saml": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/@node-saml/node-saml/-/node-saml-4.0.5.tgz", - "integrity": "sha512-J5DglElbY1tjOuaR1NPtjOXkXY5bpUhDoKVoeucYN98A3w4fwgjIOPqIGcb6cQsqFq2zZ6vTCeKn5C/hvefSaw==", - "peer": true, - "dependencies": { - "@types/debug": "^4.1.7", - "@types/passport": "^1.0.11", - "@types/xml-crypto": "^1.4.2", - "@types/xml-encryption": "^1.2.1", - "@types/xml2js": "^0.4.11", - "@xmldom/xmldom": "^0.8.6", - "debug": "^4.3.4", - "xml-crypto": "^3.0.1", - "xml-encryption": "^3.0.2", - "xml2js": "^0.5.0", - "xmlbuilder": "^15.1.1" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@node-saml/node-saml/node_modules/@xmldom/xmldom": { - "version": "0.8.10", - "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.10.tgz", - "integrity": "sha512-2WALfTl4xo2SkGCYRt6rDTFfk9R1czmBvUQy12gK2KuRKIpWEhcbbzy8EZXtz/jkRqHX8bFEc6FC1HjX4TUWYw==", - "license": "MIT", - "peer": true, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@node-saml/passport-saml": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/@node-saml/passport-saml/-/passport-saml-4.0.4.tgz", - "integrity": "sha512-xFw3gw0yo+K1mzlkW15NeBF7cVpRHN/4vpjmBKzov5YFImCWh/G0LcTZ8krH3yk2/eRPc3Or8LRPudVJBjmYaw==", - "peer": true, - "dependencies": { - "@node-saml/node-saml": "^4.0.4", - "@types/express": "^4.17.14", - "@types/passport": "^1.0.11", - "@types/passport-strategy": "^0.2.35", - "passport": "^0.6.0", - "passport-strategy": "^1.0.0" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@node-saml/passport-saml/node_modules/passport": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/passport/-/passport-0.6.0.tgz", - "integrity": "sha512-0fe+p3ZnrWRW74fe8+SvCyf4a3Pb2/h7gFkQ8yTJpAO50gDzlfjZUZTO1k5Eg9kUct22OxHLqDZoKUWRHOh9ug==", - "license": "MIT", - "peer": true, - "dependencies": { - "passport-strategy": "1.x.x", - "pause": "0.0.1", - "utils-merge": "^1.0.1" - }, - "engines": { - "node": ">= 0.4.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/jaredhanson" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "extraneous": true, - "dependencies": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "extraneous": true, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "extraneous": true, - "dependencies": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@oozcitak/dom": { - "version": "1.15.10", - "resolved": "https://registry.npmjs.org/@oozcitak/dom/-/dom-1.15.10.tgz", - "integrity": "sha512-0JT29/LaxVgRcGKvHmSrUTEvZ8BXvZhGl2LASRUgHqDTC1M5g1pLmVv56IYNyt3bG2CUjDkc67wnyZC14pbQrQ==", - "peer": true, - "dependencies": { - "@oozcitak/infra": "1.0.8", - "@oozcitak/url": "1.0.4", - "@oozcitak/util": "8.3.8" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@oozcitak/infra": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/@oozcitak/infra/-/infra-1.0.8.tgz", - "integrity": "sha512-JRAUc9VR6IGHOL7OGF+yrvs0LO8SlqGnPAMqyzOuFZPSZSXI7Xf2O9+awQPSMXgIWGtgUf/dA6Hs6X6ySEaWTg==", - "peer": true, - "dependencies": { - "@oozcitak/util": "8.3.8" - }, - "engines": { - "node": ">=6.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@oozcitak/url": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@oozcitak/url/-/url-1.0.4.tgz", - "integrity": "sha512-kDcD8y+y3FCSOvnBI6HJgl00viO/nGbQoCINmQ0h98OhnGITrWR3bOGfwYCthgcrV8AnTJz8MzslTQbC3SOAmw==", - "peer": true, - "dependencies": { - "@oozcitak/infra": "1.0.8", - "@oozcitak/util": "8.3.8" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@oozcitak/util": { - "version": "8.3.8", - "resolved": "https://registry.npmjs.org/@oozcitak/util/-/util-8.3.8.tgz", - "integrity": "sha512-T8TbSnGsxo6TDBJx/Sgv/BlVJL3tshxZP7Aq5R1mSnM5OcHY2dQaxLMu2+E8u3gN0MLOzdjurqN4ZRVuzQycOQ==", - "peer": true, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@pkgjs/parseargs": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", - "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", - "extraneous": true, - "engines": { - "node": ">=14" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@sinonjs/commons": { - "version": "1.8.6", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.6.tgz", - "integrity": "sha512-Ky+XkAkqPZSm3NLBeUng77EBQl3cmeJhITaGHdYH8kjVB+aun3S4XBRti2zt17mtt0mIUDiNxYeoJm6drVvBJQ==", - "extraneous": true, - "dependencies": { - "type-detect": "4.0.8" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@sinonjs/formatio": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/@sinonjs/formatio/-/formatio-3.2.2.tgz", - "integrity": "sha512-B8SEsgd8gArBLMD6zpRw3juQ2FVSsmdd7qlevyDqzS9WTCtvF55/gAL+h6gue8ZvPYcdiPdvueM/qm//9XzyTQ==", - "extraneous": true, - "dependencies": { - "@sinonjs/commons": "^1", - "@sinonjs/samsam": "^3.1.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@sinonjs/samsam": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-3.3.3.tgz", - "integrity": "sha512-bKCMKZvWIjYD0BLGnNrxVuw4dkWCYsLqFOUWw8VgKF/+5Y+mE7LfHWPIYoDXowH+3a9LsWDMo0uAP8YDosPvHQ==", - "extraneous": true, - "dependencies": { - "@sinonjs/commons": "^1.3.0", - "array-from": "^2.1.1", - "lodash": "^4.17.15" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@sinonjs/text-encoding": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/@sinonjs/text-encoding/-/text-encoding-0.7.2.tgz", - "integrity": "sha512-sXXKG+uL9IrKqViTtao2Ws6dy0znu9sOaP1di/jKGW1M6VssO8vlpXCQcpZ+jisQ1tTFAC5Jo/EOzFbggBagFQ==", - "extraneous": true - }, - "node_modules/@ngageoint/mage.service/node_modules/@smithy/abort-controller": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-3.1.1.tgz", - "integrity": "sha512-MBJBiidoe+0cTFhyxT8g+9g7CeVccLM0IOKKUMCNQ1CNMJ/eIfoo0RTfVrXOONEI1UCN1W+zkiHSbzUNE9dZtQ==", - "optional": true, - "peer": true, - "dependencies": { - "@smithy/types": "^3.3.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@smithy/config-resolver": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-3.0.4.tgz", - "integrity": "sha512-VwiOk7TwXoE7NlNguV/aPq1hFH72tqkHCw8eWXbr2xHspRyyv9DLpLXhq+Ieje+NwoqXrY0xyQjPXdOE6cGcHA==", - "optional": true, - "peer": true, - "dependencies": { - "@smithy/node-config-provider": "^3.1.3", - "@smithy/types": "^3.3.0", - "@smithy/util-config-provider": "^3.0.0", - "@smithy/util-middleware": "^3.0.3", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@smithy/core": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/@smithy/core/-/core-2.2.4.tgz", - "integrity": "sha512-qdY3LpMOUyLM/gfjjMQZui+UTNS7kBRDWlvyIhVOql5dn2J3isk9qUTBtQ1CbDH8MTugHis1zu3h4rH+Qmmh4g==", - "optional": true, - "peer": true, - "dependencies": { - "@smithy/middleware-endpoint": "^3.0.4", - "@smithy/middleware-retry": "^3.0.7", - "@smithy/middleware-serde": "^3.0.3", - "@smithy/protocol-http": "^4.0.3", - "@smithy/smithy-client": "^3.1.5", - "@smithy/types": "^3.3.0", - "@smithy/util-middleware": "^3.0.3", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@smithy/credential-provider-imds": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-3.1.3.tgz", - "integrity": "sha512-U1Yrv6hx/mRK6k8AncuI6jLUx9rn0VVSd9NPEX6pyYFBfkSkChOc/n4zUb8alHUVg83TbI4OdZVo1X0Zfj3ijA==", - "optional": true, - "peer": true, - "dependencies": { - "@smithy/node-config-provider": "^3.1.3", - "@smithy/property-provider": "^3.1.3", - "@smithy/types": "^3.3.0", - "@smithy/url-parser": "^3.0.3", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@smithy/fetch-http-handler": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-3.2.0.tgz", - "integrity": "sha512-vFvDxMrc6sO5Atec8PaISckMcAwsCrRhYxwUylg97bRT2KZoumOF7qk5+6EVUtuM1IG9AJV5aqXnHln9ZdXHpg==", - "optional": true, - "peer": true, - "dependencies": { - "@smithy/protocol-http": "^4.0.3", - "@smithy/querystring-builder": "^3.0.3", - "@smithy/types": "^3.3.0", - "@smithy/util-base64": "^3.0.0", - "tslib": "^2.6.2" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@smithy/hash-node": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-3.0.3.tgz", - "integrity": "sha512-2ctBXpPMG+B3BtWSGNnKELJ7SH9e4TNefJS0cd2eSkOOROeBnnVBnAy9LtJ8tY4vUEoe55N4CNPxzbWvR39iBw==", - "optional": true, - "peer": true, - "dependencies": { - "@smithy/types": "^3.3.0", - "@smithy/util-buffer-from": "^3.0.0", - "@smithy/util-utf8": "^3.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@smithy/invalid-dependency": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-3.0.3.tgz", - "integrity": "sha512-ID1eL/zpDULmHJbflb864k72/SNOZCADRc9i7Exq3RUNJw6raWUSlFEQ+3PX3EYs++bTxZB2dE9mEHTQLv61tw==", - "optional": true, - "peer": true, - "dependencies": { - "@smithy/types": "^3.3.0", - "tslib": "^2.6.2" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@smithy/is-array-buffer": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-3.0.0.tgz", - "integrity": "sha512-+Fsu6Q6C4RSJiy81Y8eApjEB5gVtM+oFKTffg+jSuwtvomJJrhUJBu2zS8wjXSgH/g1MKEWrzyChTBe6clb5FQ==", - "optional": true, - "peer": true, - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@smithy/middleware-content-length": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-3.0.3.tgz", - "integrity": "sha512-Dbz2bzexReYIQDWMr+gZhpwBetNXzbhnEMhYKA6urqmojO14CsXjnsoPYO8UL/xxcawn8ZsuVU61ElkLSltIUQ==", - "optional": true, - "peer": true, - "dependencies": { - "@smithy/protocol-http": "^4.0.3", - "@smithy/types": "^3.3.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@smithy/middleware-endpoint": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-3.0.4.tgz", - "integrity": "sha512-whUJMEPwl3ANIbXjBXZVdJNgfV2ZU8ayln7xUM47rXL2txuenI7jQ/VFFwCzy5lCmXScjp6zYtptW5Evud8e9g==", - "optional": true, - "peer": true, - "dependencies": { - "@smithy/middleware-serde": "^3.0.3", - "@smithy/node-config-provider": "^3.1.3", - "@smithy/shared-ini-file-loader": "^3.1.3", - "@smithy/types": "^3.3.0", - "@smithy/url-parser": "^3.0.3", - "@smithy/util-middleware": "^3.0.3", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@smithy/middleware-retry": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-3.0.7.tgz", - "integrity": "sha512-f5q7Y09G+2h5ivkSx5CHvlAT4qRR3jBFEsfXyQ9nFNiWQlr8c48blnu5cmbTQ+p1xmIO14UXzKoF8d7Tm0Gsjw==", - "optional": true, - "peer": true, - "dependencies": { - "@smithy/node-config-provider": "^3.1.3", - "@smithy/protocol-http": "^4.0.3", - "@smithy/service-error-classification": "^3.0.3", - "@smithy/smithy-client": "^3.1.5", - "@smithy/types": "^3.3.0", - "@smithy/util-middleware": "^3.0.3", - "@smithy/util-retry": "^3.0.3", - "tslib": "^2.6.2", - "uuid": "^9.0.1" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@smithy/middleware-serde": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-3.0.3.tgz", - "integrity": "sha512-puUbyJQBcg9eSErFXjKNiGILJGtiqmuuNKEYNYfUD57fUl4i9+mfmThtQhvFXU0hCVG0iEJhvQUipUf+/SsFdA==", - "optional": true, - "peer": true, - "dependencies": { - "@smithy/types": "^3.3.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@smithy/middleware-stack": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-3.0.3.tgz", - "integrity": "sha512-r4klY9nFudB0r9UdSMaGSyjyQK5adUyPnQN/ZM6M75phTxOdnc/AhpvGD1fQUvgmqjQEBGCwpnPbDm8pH5PapA==", - "optional": true, - "peer": true, - "dependencies": { - "@smithy/types": "^3.3.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@smithy/node-config-provider": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-3.1.3.tgz", - "integrity": "sha512-rxdpAZczzholz6CYZxtqDu/aKTxATD5DAUDVj7HoEulq+pDSQVWzbg0btZDlxeFfa6bb2b5tUvgdX5+k8jUqcg==", - "optional": true, - "peer": true, - "dependencies": { - "@smithy/property-provider": "^3.1.3", - "@smithy/shared-ini-file-loader": "^3.1.3", - "@smithy/types": "^3.3.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@smithy/node-http-handler": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-3.1.1.tgz", - "integrity": "sha512-L71NLyPeP450r2J/mfu1jMc//Z1YnqJt2eSNw7uhiItaONnBLDA68J5jgxq8+MBDsYnFwNAIc7dBG1ImiWBiwg==", - "optional": true, - "peer": true, - "dependencies": { - "@smithy/abort-controller": "^3.1.1", - "@smithy/protocol-http": "^4.0.3", - "@smithy/querystring-builder": "^3.0.3", - "@smithy/types": "^3.3.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@smithy/property-provider": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-3.1.3.tgz", - "integrity": "sha512-zahyOVR9Q4PEoguJ/NrFP4O7SMAfYO1HLhB18M+q+Z4KFd4V2obiMnlVoUFzFLSPeVt1POyNWneHHrZaTMoc/g==", - "optional": true, - "peer": true, - "dependencies": { - "@smithy/types": "^3.3.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@smithy/protocol-http": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-4.0.3.tgz", - "integrity": "sha512-x5jmrCWwQlx+Zv4jAtc33ijJ+vqqYN+c/ZkrnpvEe/uDas7AT7A/4Rc2CdfxgWv4WFGmEqODIrrUToPN6DDkGw==", - "optional": true, - "peer": true, - "dependencies": { - "@smithy/types": "^3.3.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@smithy/querystring-builder": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-3.0.3.tgz", - "integrity": "sha512-vyWckeUeesFKzCDaRwWLUA1Xym9McaA6XpFfAK5qI9DKJ4M33ooQGqvM4J+LalH4u/Dq9nFiC8U6Qn1qi0+9zw==", - "optional": true, - "peer": true, - "dependencies": { - "@smithy/types": "^3.3.0", - "@smithy/util-uri-escape": "^3.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@smithy/querystring-parser": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-3.0.3.tgz", - "integrity": "sha512-zahM1lQv2YjmznnfQsWbYojFe55l0SLG/988brlLv1i8z3dubloLF+75ATRsqPBboUXsW6I9CPGE5rQgLfY0vQ==", - "optional": true, - "peer": true, - "dependencies": { - "@smithy/types": "^3.3.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@smithy/service-error-classification": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-3.0.3.tgz", - "integrity": "sha512-Jn39sSl8cim/VlkLsUhRFq/dKDnRUFlfRkvhOJaUbLBXUsLRLNf9WaxDv/z9BjuQ3A6k/qE8af1lsqcwm7+DaQ==", - "optional": true, - "peer": true, - "dependencies": { - "@smithy/types": "^3.3.0" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@smithy/shared-ini-file-loader": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-3.1.3.tgz", - "integrity": "sha512-Z8Y3+08vgoDgl4HENqNnnzSISAaGrF2RoKupoC47u2wiMp+Z8P/8mDh1CL8+8ujfi2U5naNvopSBmP/BUj8b5w==", - "optional": true, - "peer": true, - "dependencies": { - "@smithy/types": "^3.3.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@smithy/signature-v4": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-3.1.2.tgz", - "integrity": "sha512-3BcPylEsYtD0esM4Hoyml/+s7WP2LFhcM3J2AGdcL2vx9O60TtfpDOL72gjb4lU8NeRPeKAwR77YNyyGvMbuEA==", - "optional": true, - "peer": true, - "dependencies": { - "@smithy/is-array-buffer": "^3.0.0", - "@smithy/types": "^3.3.0", - "@smithy/util-hex-encoding": "^3.0.0", - "@smithy/util-middleware": "^3.0.3", - "@smithy/util-uri-escape": "^3.0.0", - "@smithy/util-utf8": "^3.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@smithy/smithy-client": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-3.1.5.tgz", - "integrity": "sha512-x9bL9Mx2CT2P1OiUlHM+ZNpbVU6TgT32f9CmTRzqIHA7M4vYrROCWEoC3o4xHNJASoGd4Opos3cXYPgh+/m4Ww==", - "optional": true, - "peer": true, - "dependencies": { - "@smithy/middleware-endpoint": "^3.0.4", - "@smithy/middleware-stack": "^3.0.3", - "@smithy/protocol-http": "^4.0.3", - "@smithy/types": "^3.3.0", - "@smithy/util-stream": "^3.0.5", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@smithy/types": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/@smithy/types/-/types-3.3.0.tgz", - "integrity": "sha512-IxvBBCTFDHbVoK7zIxqA1ZOdc4QfM5HM7rGleCuHi7L1wnKv5Pn69xXJQ9hgxH60ZVygH9/JG0jRgtUncE3QUA==", - "optional": true, - "peer": true, - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@smithy/url-parser": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-3.0.3.tgz", - "integrity": "sha512-pw3VtZtX2rg+s6HMs6/+u9+hu6oY6U7IohGhVNnjbgKy86wcIsSZwgHrFR+t67Uyxvp4Xz3p3kGXXIpTNisq8A==", - "optional": true, - "peer": true, - "dependencies": { - "@smithy/querystring-parser": "^3.0.3", - "@smithy/types": "^3.3.0", - "tslib": "^2.6.2" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@smithy/util-base64": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-base64/-/util-base64-3.0.0.tgz", - "integrity": "sha512-Kxvoh5Qtt0CDsfajiZOCpJxgtPHXOKwmM+Zy4waD43UoEMA+qPxxa98aE/7ZhdnBFZFXMOiBR5xbcaMhLtznQQ==", - "optional": true, - "peer": true, - "dependencies": { - "@smithy/util-buffer-from": "^3.0.0", - "@smithy/util-utf8": "^3.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@smithy/util-body-length-browser": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-body-length-browser/-/util-body-length-browser-3.0.0.tgz", - "integrity": "sha512-cbjJs2A1mLYmqmyVl80uoLTJhAcfzMOyPgjwAYusWKMdLeNtzmMz9YxNl3/jRLoxSS3wkqkf0jwNdtXWtyEBaQ==", - "optional": true, - "peer": true, - "dependencies": { - "tslib": "^2.6.2" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@smithy/util-body-length-node": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-body-length-node/-/util-body-length-node-3.0.0.tgz", - "integrity": "sha512-Tj7pZ4bUloNUP6PzwhN7K386tmSmEET9QtQg0TgdNOnxhZvCssHji+oZTUIuzxECRfG8rdm2PMw2WCFs6eIYkA==", - "optional": true, - "peer": true, - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@smithy/util-buffer-from": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-3.0.0.tgz", - "integrity": "sha512-aEOHCgq5RWFbP+UDPvPot26EJHjOC+bRgse5A8V3FSShqd5E5UN4qc7zkwsvJPPAVsf73QwYcHN1/gt/rtLwQA==", - "optional": true, - "peer": true, - "dependencies": { - "@smithy/is-array-buffer": "^3.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@smithy/util-config-provider": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-config-provider/-/util-config-provider-3.0.0.tgz", - "integrity": "sha512-pbjk4s0fwq3Di/ANL+rCvJMKM5bzAQdE5S/6RL5NXgMExFAi6UgQMPOm5yPaIWPpr+EOXKXRonJ3FoxKf4mCJQ==", - "optional": true, - "peer": true, - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@smithy/util-defaults-mode-browser": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-3.0.7.tgz", - "integrity": "sha512-Q2txLyvQyGfmjsaDbVV7Sg8psefpFcrnlGapDzXGFRPFKRBeEg6OvFK8FljqjeHSaCZ6/UuzQExUPqBR/2qlDA==", - "optional": true, - "peer": true, - "dependencies": { - "@smithy/property-provider": "^3.1.3", - "@smithy/smithy-client": "^3.1.5", - "@smithy/types": "^3.3.0", - "bowser": "^2.11.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">= 10.0.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@smithy/util-defaults-mode-node": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-3.0.7.tgz", - "integrity": "sha512-F4Qcj1fG6MGi2BSWCslfsMSwllws/WzYONBGtLybyY+halAcXdWhcew+mej8M5SKd5hqPYp4f7b+ABQEaeytgg==", - "optional": true, - "peer": true, - "dependencies": { - "@smithy/config-resolver": "^3.0.4", - "@smithy/credential-provider-imds": "^3.1.3", - "@smithy/node-config-provider": "^3.1.3", - "@smithy/property-provider": "^3.1.3", - "@smithy/smithy-client": "^3.1.5", - "@smithy/types": "^3.3.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">= 10.0.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@smithy/util-endpoints": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-2.0.4.tgz", - "integrity": "sha512-ZAtNf+vXAsgzgRutDDiklU09ZzZiiV/nATyqde4Um4priTmasDH+eLpp3tspL0hS2dEootyFMhu1Y6Y+tzpWBQ==", - "optional": true, - "peer": true, - "dependencies": { - "@smithy/node-config-provider": "^3.1.3", - "@smithy/types": "^3.3.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@smithy/util-hex-encoding": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-hex-encoding/-/util-hex-encoding-3.0.0.tgz", - "integrity": "sha512-eFndh1WEK5YMUYvy3lPlVmYY/fZcQE1D8oSf41Id2vCeIkKJXPcYDCZD+4+xViI6b1XSd7tE+s5AmXzz5ilabQ==", - "optional": true, - "peer": true, - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@smithy/util-middleware": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-3.0.3.tgz", - "integrity": "sha512-l+StyYYK/eO3DlVPbU+4Bi06Jjal+PFLSMmlWM1BEwyLxZ3aKkf1ROnoIakfaA7mC6uw3ny7JBkau4Yc+5zfWw==", - "optional": true, - "peer": true, - "dependencies": { - "@smithy/types": "^3.3.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@smithy/util-retry": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-3.0.3.tgz", - "integrity": "sha512-AFw+hjpbtVApzpNDhbjNG5NA3kyoMs7vx0gsgmlJF4s+yz1Zlepde7J58zpIRIsdjc+emhpAITxA88qLkPF26w==", - "optional": true, - "peer": true, - "dependencies": { - "@smithy/service-error-classification": "^3.0.3", - "@smithy/types": "^3.3.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@smithy/util-stream": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-3.0.5.tgz", - "integrity": "sha512-xC3L5PKMAT/Bh8fmHNXP9sdQ4+4aKVUU3EEJ2CF/lLk7R+wtMJM+v/1B4en7jO++Wa5spGzFDBCl0QxgbUc5Ug==", - "optional": true, - "peer": true, - "dependencies": { - "@smithy/fetch-http-handler": "^3.2.0", - "@smithy/node-http-handler": "^3.1.1", - "@smithy/types": "^3.3.0", - "@smithy/util-base64": "^3.0.0", - "@smithy/util-buffer-from": "^3.0.0", - "@smithy/util-hex-encoding": "^3.0.0", - "@smithy/util-utf8": "^3.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@smithy/util-uri-escape": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-uri-escape/-/util-uri-escape-3.0.0.tgz", - "integrity": "sha512-LqR7qYLgZTD7nWLBecUi4aqolw8Mhza9ArpNEQ881MJJIU2sE5iHCK6TdyqqzcDLy0OPe10IY4T8ctVdtynubg==", - "optional": true, - "peer": true, - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@smithy/util-utf8": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-3.0.0.tgz", - "integrity": "sha512-rUeT12bxFnplYDe815GXbq/oixEGHfRFFtcTF3YdDi/JaENIM6aSYYLJydG83UNzLXeRI5K8abYd/8Sp/QM0kA==", - "optional": true, - "peer": true, - "dependencies": { - "@smithy/util-buffer-from": "^3.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@tokenizer/token": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/@tokenizer/token/-/token-0.3.0.tgz", - "integrity": "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==", - "peer": true - }, - "node_modules/@ngageoint/mage.service/node_modules/@turf/bbox": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/@turf/bbox/-/bbox-6.3.0.tgz", - "integrity": "sha512-N4ue5Xopu1qieSHP2MA/CJGWHPKaTrVXQJjzHRNcY1vtsO126xbSaJhWUrFc5x5vVkXp0dcucGryO0r5m4o/KA==", - "peer": true, - "dependencies": { - "@turf/helpers": "^6.3.0", - "@turf/meta": "^6.3.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@turf/bearing": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/@turf/bearing/-/bearing-6.5.0.tgz", - "integrity": "sha512-dxINYhIEMzgDOztyMZc20I7ssYVNEpSv04VbMo5YPQsqa80KO3TFvbuCahMsCAW5z8Tncc8dwBlEFrmRjJG33A==", - "peer": true, - "dependencies": { - "@turf/helpers": "^6.5.0", - "@turf/invariant": "^6.5.0" - }, - "funding": { - "url": "https://opencollective.com/turf" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@turf/boolean-clockwise": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/@turf/boolean-clockwise/-/boolean-clockwise-6.5.0.tgz", - "integrity": "sha512-45+C7LC5RMbRWrxh3Z0Eihsc8db1VGBO5d9BLTOAwU4jR6SgsunTfRWR16X7JUwIDYlCVEmnjcXJNi/kIU3VIw==", - "peer": true, - "dependencies": { - "@turf/helpers": "^6.5.0", - "@turf/invariant": "^6.5.0" - }, - "funding": { - "url": "https://opencollective.com/turf" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@turf/boolean-point-in-polygon": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/@turf/boolean-point-in-polygon/-/boolean-point-in-polygon-6.5.0.tgz", - "integrity": "sha512-DtSuVFB26SI+hj0SjrvXowGTUCHlgevPAIsukssW6BG5MlNSBQAo70wpICBNJL6RjukXg8d2eXaAWuD/CqL00A==", - "peer": true, - "dependencies": { - "@turf/helpers": "^6.5.0", - "@turf/invariant": "^6.5.0" - }, - "funding": { - "url": "https://opencollective.com/turf" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@turf/boolean-point-on-line": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/@turf/boolean-point-on-line/-/boolean-point-on-line-6.5.0.tgz", - "integrity": "sha512-A1BbuQ0LceLHvq7F/P7w3QvfpmZqbmViIUPHdNLvZimFNLo4e6IQunmzbe+8aSStH9QRZm3VOflyvNeXvvpZEQ==", - "peer": true, - "dependencies": { - "@turf/helpers": "^6.5.0", - "@turf/invariant": "^6.5.0" - }, - "funding": { - "url": "https://opencollective.com/turf" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@turf/boolean-within": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/@turf/boolean-within/-/boolean-within-6.5.0.tgz", - "integrity": "sha512-YQB3oU18Inx35C/LU930D36RAVe7LDXk1kWsQ8mLmuqYn9YdPsDQTMTkLJMhoQ8EbN7QTdy333xRQ4MYgToteQ==", - "peer": true, - "dependencies": { - "@turf/bbox": "^6.5.0", - "@turf/boolean-point-in-polygon": "^6.5.0", - "@turf/boolean-point-on-line": "^6.5.0", - "@turf/helpers": "^6.5.0", - "@turf/invariant": "^6.5.0" - }, - "funding": { - "url": "https://opencollective.com/turf" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@turf/boolean-within/node_modules/@turf/bbox": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/@turf/bbox/-/bbox-6.5.0.tgz", - "integrity": "sha512-RBbLaao5hXTYyyg577iuMtDB8ehxMlUqHEJiMs8jT1GHkFhr6sYre3lmLsPeYEi/ZKj5TP5tt7fkzNdJ4GIVyw==", - "peer": true, - "dependencies": { - "@turf/helpers": "^6.5.0", - "@turf/meta": "^6.5.0" - }, - "funding": { - "url": "https://opencollective.com/turf" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@turf/centroid": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@turf/centroid/-/centroid-7.2.0.tgz", - "integrity": "sha512-yJqDSw25T7P48au5KjvYqbDVZ7qVnipziVfZ9aSo7P2/jTE7d4BP21w0/XLi3T/9bry/t9PR1GDDDQljN4KfDw==", - "license": "MIT", - "peer": true, - "dependencies": { - "@turf/helpers": "^7.2.0", - "@turf/meta": "^7.2.0", - "@types/geojson": "^7946.0.10", - "tslib": "^2.8.1" - }, - "funding": { - "url": "https://opencollective.com/turf" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@turf/centroid/node_modules/@turf/helpers": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@turf/helpers/-/helpers-7.2.0.tgz", - "integrity": "sha512-cXo7bKNZoa7aC7ydLmUR02oB3IgDe7MxiPuRz3cCtYQHn+BJ6h1tihmamYDWWUlPHgSNF0i3ATc4WmDECZafKw==", - "license": "MIT", - "peer": true, - "dependencies": { - "@types/geojson": "^7946.0.10", - "tslib": "^2.8.1" - }, - "funding": { - "url": "https://opencollective.com/turf" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@turf/centroid/node_modules/@turf/meta": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@turf/meta/-/meta-7.2.0.tgz", - "integrity": "sha512-igzTdHsQc8TV1RhPuOLVo74Px/hyPrVgVOTgjWQZzt3J9BVseCdpfY/0cJBdlSRI4S/yTmmHl7gAqjhpYH5Yaw==", - "license": "MIT", - "peer": true, - "dependencies": { - "@turf/helpers": "^7.2.0", - "@types/geojson": "^7946.0.10" - }, - "funding": { - "url": "https://opencollective.com/turf" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@turf/clone": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/@turf/clone/-/clone-6.5.0.tgz", - "integrity": "sha512-mzVtTFj/QycXOn6ig+annKrM6ZlimreKYz6f/GSERytOpgzodbQyOgkfwru100O1KQhhjSudKK4DsQ0oyi9cTw==", - "peer": true, - "dependencies": { - "@turf/helpers": "^6.5.0" - }, - "funding": { - "url": "https://opencollective.com/turf" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@turf/distance": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/@turf/distance/-/distance-6.5.0.tgz", - "integrity": "sha512-xzykSLfoURec5qvQJcfifw/1mJa+5UwByZZ5TZ8iaqjGYN0vomhV9aiSLeYdUGtYRESZ+DYC/OzY+4RclZYgMg==", - "peer": true, - "dependencies": { - "@turf/helpers": "^6.5.0", - "@turf/invariant": "^6.5.0" - }, - "funding": { - "url": "https://opencollective.com/turf" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@turf/helpers": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/@turf/helpers/-/helpers-6.5.0.tgz", - "integrity": "sha512-VbI1dV5bLFzohYYdgqwikdMVpe7pJ9X3E+dlr425wa2/sMJqYDhTO++ec38/pcPvPE6oD9WEEeU3Xu3gza+VPw==", - "peer": true, - "funding": { - "url": "https://opencollective.com/turf" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@turf/intersect": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/@turf/intersect/-/intersect-6.5.0.tgz", - "integrity": "sha512-2legGJeKrfFkzntcd4GouPugoqPUjexPZnOvfez+3SfIMrHvulw8qV8u7pfVyn2Yqs53yoVCEjS5sEpvQ5YRQg==", - "peer": true, - "dependencies": { - "@turf/helpers": "^6.5.0", - "@turf/invariant": "^6.5.0", - "polygon-clipping": "^0.15.3" - }, - "funding": { - "url": "https://opencollective.com/turf" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@turf/invariant": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/@turf/invariant/-/invariant-6.5.0.tgz", - "integrity": "sha512-Wv8PRNCtPD31UVbdJE/KVAWKe7l6US+lJItRR/HOEW3eh+U/JwRCSUl/KZ7bmjM/C+zLNoreM2TU6OoLACs4eg==", - "peer": true, - "dependencies": { - "@turf/helpers": "^6.5.0" - }, - "funding": { - "url": "https://opencollective.com/turf" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@turf/kinks": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@turf/kinks/-/kinks-7.2.0.tgz", - "integrity": "sha512-BtxDxGewJR0Q5WR9HKBSxZhirFX+GEH1rD7/EvgDsHS8e1Y5/vNQQUmXdURjdPa4StzaUBsWRU5T3A356gLbPA==", - "license": "MIT", - "peer": true, - "dependencies": { - "@turf/helpers": "^7.2.0", - "@types/geojson": "^7946.0.10", - "tslib": "^2.8.1" - }, - "funding": { - "url": "https://opencollective.com/turf" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@turf/kinks/node_modules/@turf/helpers": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@turf/helpers/-/helpers-7.2.0.tgz", - "integrity": "sha512-cXo7bKNZoa7aC7ydLmUR02oB3IgDe7MxiPuRz3cCtYQHn+BJ6h1tihmamYDWWUlPHgSNF0i3ATc4WmDECZafKw==", - "license": "MIT", - "peer": true, - "dependencies": { - "@types/geojson": "^7946.0.10", - "tslib": "^2.8.1" - }, - "funding": { - "url": "https://opencollective.com/turf" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@turf/line-intersect": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/@turf/line-intersect/-/line-intersect-6.5.0.tgz", - "integrity": "sha512-CS6R1tZvVQD390G9Ea4pmpM6mJGPWoL82jD46y0q1KSor9s6HupMIo1kY4Ny+AEYQl9jd21V3Scz20eldpbTVA==", - "peer": true, - "dependencies": { - "@turf/helpers": "^6.5.0", - "@turf/invariant": "^6.5.0", - "@turf/line-segment": "^6.5.0", - "@turf/meta": "^6.5.0", - "geojson-rbush": "3.x" - }, - "funding": { - "url": "https://opencollective.com/turf" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@turf/line-segment": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/@turf/line-segment/-/line-segment-6.5.0.tgz", - "integrity": "sha512-jI625Ho4jSuJESNq66Mmi290ZJ5pPZiQZruPVpmHkUw257Pew0alMmb6YrqYNnLUuiVVONxAAKXUVeeUGtycfw==", - "peer": true, - "dependencies": { - "@turf/helpers": "^6.5.0", - "@turf/invariant": "^6.5.0", - "@turf/meta": "^6.5.0" - }, - "funding": { - "url": "https://opencollective.com/turf" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@turf/meta": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/@turf/meta/-/meta-6.5.0.tgz", - "integrity": "sha512-RrArvtsV0vdsCBegoBtOalgdSOfkBrTJ07VkpiCnq/491W67hnMWmDu7e6Ztw0C3WldRYTXkg3SumfdzZxLBHA==", - "peer": true, - "dependencies": { - "@turf/helpers": "^6.5.0" - }, - "funding": { - "url": "https://opencollective.com/turf" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@turf/point-to-line-distance": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/@turf/point-to-line-distance/-/point-to-line-distance-6.5.0.tgz", - "integrity": "sha512-opHVQ4vjUhNBly1bob6RWy+F+hsZDH9SA0UW36pIRzfpu27qipU18xup0XXEePfY6+wvhF6yL/WgCO2IbrLqEA==", - "peer": true, - "dependencies": { - "@turf/bearing": "^6.5.0", - "@turf/distance": "^6.5.0", - "@turf/helpers": "^6.5.0", - "@turf/invariant": "^6.5.0", - "@turf/meta": "^6.5.0", - "@turf/projection": "^6.5.0", - "@turf/rhumb-bearing": "^6.5.0", - "@turf/rhumb-distance": "^6.5.0" - }, - "funding": { - "url": "https://opencollective.com/turf" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@turf/polygon-to-line": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/@turf/polygon-to-line/-/polygon-to-line-6.5.0.tgz", - "integrity": "sha512-5p4n/ij97EIttAq+ewSnKt0ruvuM+LIDzuczSzuHTpq4oS7Oq8yqg5TQ4nzMVuK41r/tALCk7nAoBuw3Su4Gcw==", - "peer": true, - "dependencies": { - "@turf/helpers": "^6.5.0", - "@turf/invariant": "^6.5.0" - }, - "funding": { - "url": "https://opencollective.com/turf" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@turf/projection": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/@turf/projection/-/projection-6.5.0.tgz", - "integrity": "sha512-/Pgh9mDvQWWu8HRxqpM+tKz8OzgauV+DiOcr3FCjD6ubDnrrmMJlsf6fFJmggw93mtVPrZRL6yyi9aYCQBOIvg==", - "peer": true, - "dependencies": { - "@turf/clone": "^6.5.0", - "@turf/helpers": "^6.5.0", - "@turf/meta": "^6.5.0" - }, - "funding": { - "url": "https://opencollective.com/turf" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@turf/rhumb-bearing": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/@turf/rhumb-bearing/-/rhumb-bearing-6.5.0.tgz", - "integrity": "sha512-jMyqiMRK4hzREjQmnLXmkJ+VTNTx1ii8vuqRwJPcTlKbNWfjDz/5JqJlb5NaFDcdMpftWovkW5GevfnuzHnOYA==", - "peer": true, - "dependencies": { - "@turf/helpers": "^6.5.0", - "@turf/invariant": "^6.5.0" - }, - "funding": { - "url": "https://opencollective.com/turf" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@turf/rhumb-distance": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/@turf/rhumb-distance/-/rhumb-distance-6.5.0.tgz", - "integrity": "sha512-oKp8KFE8E4huC2Z1a1KNcFwjVOqa99isxNOwfo4g3SUABQ6NezjKDDrnvC4yI5YZ3/huDjULLBvhed45xdCrzg==", - "peer": true, - "dependencies": { - "@turf/helpers": "^6.5.0", - "@turf/invariant": "^6.5.0" - }, - "funding": { - "url": "https://opencollective.com/turf" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@types/adm-zip": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/@types/adm-zip/-/adm-zip-0.5.7.tgz", - "integrity": "sha512-DNEs/QvmyRLurdQPChqq0Md4zGvPwHerAJYWk9l2jCbD1VPpnzRJorOdiq4zsw09NFbYnhfsoEhWtxIzXpn2yw==", - "extraneous": true, - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@types/archiver": { - "version": "5.3.4", - "resolved": "https://registry.npmjs.org/@types/archiver/-/archiver-5.3.4.tgz", - "integrity": "sha512-Lj7fLBIMwYFgViVVZHEdExZC3lVYsl+QL0VmdNdIzGZH544jHveYWij6qdnBgJQDnR7pMKliN9z2cPZFEbhyPw==", - "extraneous": true, - "dependencies": { - "@types/readdir-glob": "*" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@types/async": { - "version": "3.2.24", - "resolved": "https://registry.npmjs.org/@types/async/-/async-3.2.24.tgz", - "integrity": "sha512-8iHVLHsCCOBKjCF2KwFe0p9Z3rfM9mL+sSP8btyR5vTjJRAqpBYD28/ZLgXPf0pjG1VxOvtCV/BgXkQbpSe8Hw==", - "extraneous": true, - "license": "MIT" - }, - "node_modules/@ngageoint/mage.service/node_modules/@types/body-parser": { - "version": "1.19.5", - "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz", - "integrity": "sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==", - "peer": true, - "dependencies": { - "@types/connect": "*", - "@types/node": "*" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@types/bson": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/@types/bson/-/bson-1.0.11.tgz", - "integrity": "sha512-j+UcCWI+FsbI5/FQP/Kj2CXyplWAz39ktHFkXk84h7dNblKRSoNJs95PZFRd96NQGqsPEPgeclqnznWZr14ZDA==", - "extraneous": true, - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@types/busboy": { - "version": "1.5.4", - "resolved": "https://registry.npmjs.org/@types/busboy/-/busboy-1.5.4.tgz", - "integrity": "sha512-kG7WrUuAKK0NoyxfQHsVE6j1m01s6kMma64E+OZenQABMQyTJop1DumUWcLwAQ2JzpefU7PDYoRDKl8uZosFjw==", - "extraneous": true, - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@types/chai": { - "version": "4.3.20", - "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.20.tgz", - "integrity": "sha512-/pC9HAB5I/xMlc5FP77qjCnI16ChlJfW0tGa0IUcFn38VJrTV6DeZ60NU5KZBtaOZqjdpwTWohz5HU1RrhiYxQ==", - "extraneous": true, - "license": "MIT" - }, - "node_modules/@ngageoint/mage.service/node_modules/@types/chai-as-promised": { - "version": "7.1.8", - "resolved": "https://registry.npmjs.org/@types/chai-as-promised/-/chai-as-promised-7.1.8.tgz", - "integrity": "sha512-ThlRVIJhr69FLlh6IctTXFkmhtP3NpMZ2QGq69StYLyKZFp/HOp1VdKZj7RvfNWYYcJ1xlbLGLLWj1UvP5u/Gw==", - "extraneous": true, - "license": "MIT", - "dependencies": { - "@types/chai": "*" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@types/connect": { - "version": "3.4.38", - "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", - "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", - "peer": true, - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@types/cookiejar": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@types/cookiejar/-/cookiejar-2.1.5.tgz", - "integrity": "sha512-he+DHOWReW0nghN24E1WUqM0efK4kI9oTqDm6XmK8ZPe2djZ90BSNdGnIyCLzCPw7/pogPlGbzI2wHGGmi4O/Q==", - "extraneous": true - }, - "node_modules/@ngageoint/mage.service/node_modules/@types/debug": { - "version": "4.1.12", - "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz", - "integrity": "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==", - "peer": true, - "dependencies": { - "@types/ms": "*" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@types/deep-equal": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@types/deep-equal/-/deep-equal-1.0.4.tgz", - "integrity": "sha512-tqdiS4otQP4KmY0PR3u6KbZ5EWvhNdUoS/jc93UuK23C220lOZ/9TvjfxdPcKvqwwDVtmtSCrnr0p/2dirAxkA==", - "extraneous": true, - "license": "MIT" - }, - "node_modules/@ngageoint/mage.service/node_modules/@types/express": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.21.tgz", - "integrity": "sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==", - "license": "MIT", - "peer": true, - "dependencies": { - "@types/body-parser": "*", - "@types/express-serve-static-core": "^4.17.33", - "@types/qs": "*", - "@types/serve-static": "*" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@types/express-serve-static-core": { - "version": "4.19.6", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.6.tgz", - "integrity": "sha512-N4LZ2xG7DatVqhCZzOGb1Yi5lMbXSZcmdLDe9EzSndPV2HpWYWzRbaerl2n27irrm94EPpprqa8KpskPT085+A==", - "license": "MIT", - "peer": true, - "dependencies": { - "@types/node": "*", - "@types/qs": "*", - "@types/range-parser": "*", - "@types/send": "*" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@types/fs-extra": { - "version": "8.1.5", - "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-8.1.5.tgz", - "integrity": "sha512-0dzKcwO+S8s2kuF5Z9oUWatQJj5Uq/iqphEtE3GQJVRRYm/tD1LglU2UnXi2A8jLq5umkGouOXOR9y0n613ZwQ==", - "extraneous": true, - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@types/geojson": { - "version": "7946.0.16", - "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.16.tgz", - "integrity": "sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==", - "license": "MIT", - "peer": true - }, - "node_modules/@ngageoint/mage.service/node_modules/@types/http-errors": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.4.tgz", - "integrity": "sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==", - "peer": true - }, - "node_modules/@ngageoint/mage.service/node_modules/@types/json-schema": { - "version": "7.0.15", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", - "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", - "license": "MIT", - "peer": true - }, - "node_modules/@ngageoint/mage.service/node_modules/@types/json2csv": { - "version": "4.5.1", - "resolved": "https://registry.npmjs.org/@types/json2csv/-/json2csv-4.5.1.tgz", - "integrity": "sha512-sZbRdrTddxYzhydkC5eMEbRXU8pjcV8Zyxf2If7YO3GVxWY18fs0P5YJyjRfkMKTAsA8ynTRQWhvWbHhBzBFVA==", - "extraneous": true, - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@types/ldapjs": { - "version": "2.2.5", - "resolved": "https://registry.npmjs.org/@types/ldapjs/-/ldapjs-2.2.5.tgz", - "integrity": "sha512-Lv/nD6QDCmcT+V1vaTRnEKE8UgOilVv5pHcQuzkU1LcRe4mbHHuUo/KHi0LKrpdHhQY8FJzryF38fcVdeUIrzg==", - "peer": true, - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@types/lodash": { - "version": "4.17.16", - "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.16.tgz", - "integrity": "sha512-HX7Em5NYQAXKW+1T+FiuG27NGwzJfCX3s1GjOa7ujxZa52kjJLOr4FUxT+giF6Tgxv1e+/czV/iTtBw27WTU9g==", - "extraneous": true, - "license": "MIT" - }, - "node_modules/@ngageoint/mage.service/node_modules/@types/methods": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/@types/methods/-/methods-1.1.4.tgz", - "integrity": "sha512-ymXWVrDiCxTBE3+RIrrP533E70eA+9qu7zdWoHuOmGujkYtzf4HQF96b8nwHLqhuf4ykX61IGRIB38CC6/sImQ==", - "extraneous": true - }, - "node_modules/@ngageoint/mage.service/node_modules/@types/mime": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", - "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==", - "peer": true - }, - "node_modules/@ngageoint/mage.service/node_modules/@types/mime-types": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@types/mime-types/-/mime-types-2.1.4.tgz", - "integrity": "sha512-lfU4b34HOri+kAY5UheuFMWPDOI+OPceBSHZKp69gEyTL/mmJ4cnU6Y/rlme3UL3GyOn6Y42hyIEw0/q8sWx5w==", - "license": "MIT", - "peer": true - }, - "node_modules/@ngageoint/mage.service/node_modules/@types/mocha": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-7.0.2.tgz", - "integrity": "sha512-ZvO2tAcjmMi8V/5Z3JsyofMe3hasRcaw88cto5etSVMwVQfeivGAlEYmaQgceUSVYFofVjT+ioHsATjdWcFt1w==", - "extraneous": true - }, - "node_modules/@ngageoint/mage.service/node_modules/@types/ms": { - "version": "0.7.34", - "resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.34.tgz", - "integrity": "sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g==", - "peer": true - }, - "node_modules/@ngageoint/mage.service/node_modules/@types/multer": { - "version": "1.4.12", - "resolved": "https://registry.npmjs.org/@types/multer/-/multer-1.4.12.tgz", - "integrity": "sha512-pQ2hoqvXiJt2FP9WQVLPRO+AmiIm/ZYkavPlIQnx282u4ZrVdztx0pkh3jjpQt0Kz+YI0YhSG264y08UJKoUQg==", - "extraneous": true, - "license": "MIT", - "dependencies": { - "@types/express": "*" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@types/node": { - "version": "18.19.80", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.80.tgz", - "integrity": "sha512-kEWeMwMeIvxYkeg1gTc01awpwLbfMRZXdIhwRcakd/KlK53jmRC26LqcbIt7fnAQTu5GzlnWmzA3H6+l1u6xxQ==", - "license": "MIT", - "peer": true, - "dependencies": { - "undici-types": "~5.26.4" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@types/node-fetch": { - "version": "2.6.12", - "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.12.tgz", - "integrity": "sha512-8nneRWKCg3rMtF69nLQJnOYUcbafYeFSjqkw3jCRLsqkWFlHaoQrr5mXmofFGOx3DKn7UfmBMyov8ySvLRVldA==", - "extraneous": true, - "license": "MIT", - "dependencies": { - "@types/node": "*", - "form-data": "^4.0.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@types/passport": { - "version": "1.0.17", - "resolved": "https://registry.npmjs.org/@types/passport/-/passport-1.0.17.tgz", - "integrity": "sha512-aciLyx+wDwT2t2/kJGJR2AEeBz0nJU4WuRX04Wu9Dqc5lSUtwu0WERPHYsLhF9PtseiAMPBGNUOtFjxZ56prsg==", - "license": "MIT", - "peer": true, - "dependencies": { - "@types/express": "*" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@types/passport-strategy": { - "version": "0.2.38", - "resolved": "https://registry.npmjs.org/@types/passport-strategy/-/passport-strategy-0.2.38.tgz", - "integrity": "sha512-GC6eMqqojOooq993Tmnmp7AUTbbQSgilyvpCYQjT+H6JfG/g6RGc7nXEniZlp0zyKJ0WUdOiZWLBZft9Yug1uA==", - "peer": true, - "dependencies": { - "@types/express": "*", - "@types/passport": "*" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@types/proj4": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/@types/proj4/-/proj4-2.5.2.tgz", - "integrity": "sha512-/Nmfn9p08yaYw6xo5f2b0L+2oHk2kZeOkp5v+4VCeNfq+ETlLQbmHmC97/pjDIEZy8jxwz7pdPpwNzDHM5cuJw==", - "peer": true - }, - "node_modules/@ngageoint/mage.service/node_modules/@types/qs": { - "version": "6.9.15", - "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.15.tgz", - "integrity": "sha512-uXHQKES6DQKKCLh441Xv/dwxOq1TVS3JPUMlEqoEglvlhR6Mxnlew/Xq/LRVHpLyk7iK3zODe1qYHIMltO7XGg==", - "peer": true - }, - "node_modules/@ngageoint/mage.service/node_modules/@types/range-parser": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", - "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", - "peer": true - }, - "node_modules/@ngageoint/mage.service/node_modules/@types/readdir-glob": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/@types/readdir-glob/-/readdir-glob-1.1.5.tgz", - "integrity": "sha512-raiuEPUYqXu+nvtY2Pe8s8FEmZ3x5yAH4VkLdihcPdalvsHltomrRC9BzuStrJ9yk06470hS0Crw0f1pXqD+Hg==", - "extraneous": true, - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@types/semver": { - "version": "7.5.8", - "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.8.tgz", - "integrity": "sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==", - "extraneous": true, - "license": "MIT" - }, - "node_modules/@ngageoint/mage.service/node_modules/@types/send": { - "version": "0.17.4", - "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.4.tgz", - "integrity": "sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==", - "peer": true, - "dependencies": { - "@types/mime": "^1", - "@types/node": "*" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@types/serve-static": { - "version": "1.15.7", - "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.7.tgz", - "integrity": "sha512-W8Ym+h8nhuRwaKPaDw34QUkwsGi6Rc4yYqvKFo5rm2FUEhCFbzVWrxXUxuKK8TASjWsysJY0nsmNCGhCOIsrOw==", - "peer": true, - "dependencies": { - "@types/http-errors": "*", - "@types/node": "*", - "@types/send": "*" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@types/sinon": { - "version": "9.0.11", - "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-9.0.11.tgz", - "integrity": "sha512-PwP4UY33SeeVKodNE37ZlOsR9cReypbMJOhZ7BVE0lB+Hix3efCOxiJWiE5Ia+yL9Cn2Ch72EjFTRze8RZsNtg==", - "extraneous": true, - "license": "MIT", - "dependencies": { - "@types/sinonjs__fake-timers": "*" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@types/sinon-chai": { - "version": "3.2.12", - "resolved": "https://registry.npmjs.org/@types/sinon-chai/-/sinon-chai-3.2.12.tgz", - "integrity": "sha512-9y0Gflk3b0+NhQZ/oxGtaAJDvRywCa5sIyaVnounqLvmf93yBF4EgIRspePtkMs3Tr844nCclYMlcCNmLCvjuQ==", - "extraneous": true, - "license": "MIT", - "dependencies": { - "@types/chai": "*", - "@types/sinon": "*" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@types/sinonjs__fake-timers": { - "version": "8.1.5", - "resolved": "https://registry.npmjs.org/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-8.1.5.tgz", - "integrity": "sha512-mQkU2jY8jJEF7YHjHvsQO8+3ughTL1mcnn96igfhONmR+fUPSKIkefQYpSe8bsly2Ep7oQbn/6VG5/9/0qcArQ==", - "extraneous": true - }, - "node_modules/@ngageoint/mage.service/node_modules/@types/superagent": { - "version": "8.1.9", - "resolved": "https://registry.npmjs.org/@types/superagent/-/superagent-8.1.9.tgz", - "integrity": "sha512-pTVjI73witn+9ILmoJdajHGW2jkSaOzhiFYF1Rd3EQ94kymLqB9PjD9ISg7WaALC7+dCHT0FGe9T2LktLq/3GQ==", - "extraneous": true, - "license": "MIT", - "dependencies": { - "@types/cookiejar": "^2.1.5", - "@types/methods": "^1.1.4", - "@types/node": "*", - "form-data": "^4.0.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@types/supertest": { - "version": "2.0.16", - "resolved": "https://registry.npmjs.org/@types/supertest/-/supertest-2.0.16.tgz", - "integrity": "sha512-6c2ogktZ06tr2ENoZivgm7YnprnhYE4ZoXGMY+oA7IuAf17M8FWvujXZGmxLv8y0PTyts4x5A+erSwVUFA8XSg==", - "extraneous": true, - "dependencies": { - "@types/superagent": "*" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@types/uniqid": { - "version": "5.3.4", - "resolved": "https://registry.npmjs.org/@types/uniqid/-/uniqid-5.3.4.tgz", - "integrity": "sha512-AgC+o3/8/QEHuU3w5w2jZ8auQtjSJ/s8G8RfEk9CYLogK1RGXqxhHH0wOEAu8uHXjvj8oh/dRtfgok4IHKxh/Q==", - "extraneous": true, - "license": "MIT" - }, - "node_modules/@ngageoint/mage.service/node_modules/@types/webidl-conversions": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/@types/webidl-conversions/-/webidl-conversions-7.0.3.tgz", - "integrity": "sha512-CiJJvcRtIgzadHCYXw7dqEnMNRjhGZlYK05Mj9OyktqV8uVT8fD2BFOB7S1uwBE3Kj2Z+4UyPmFw/Ixgw/LAlA==", - "peer": true - }, - "node_modules/@ngageoint/mage.service/node_modules/@types/whatwg-url": { - "version": "8.2.2", - "resolved": "https://registry.npmjs.org/@types/whatwg-url/-/whatwg-url-8.2.2.tgz", - "integrity": "sha512-FtQu10RWgn3D9U4aazdwIE2yzphmTJREDqNdODHrbrZmmMqI0vMheC/6NE/J1Yveaj8H+ela+YwWTjq5PGmuhA==", - "peer": true, - "dependencies": { - "@types/node": "*", - "@types/webidl-conversions": "*" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@types/xml-crypto": { - "version": "1.4.6", - "resolved": "https://registry.npmjs.org/@types/xml-crypto/-/xml-crypto-1.4.6.tgz", - "integrity": "sha512-A6jEW2FxLZo1CXsRWnZHUX2wzR3uDju2Bozt6rDbSmU/W8gkilaVbwFEVN0/NhnUdMVzwYobWtM6bU1QJJFb7Q==", - "peer": true, - "dependencies": { - "@types/node": "*", - "xpath": "0.0.27" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@types/xml-crypto/node_modules/xpath": { - "version": "0.0.27", - "resolved": "https://registry.npmjs.org/xpath/-/xpath-0.0.27.tgz", - "integrity": "sha512-fg03WRxtkCV6ohClePNAECYsmpKKTv5L8y/X3Dn1hQrec3POx2jHZ/0P2qQ6HvsrU1BmeqXcof3NGGueG6LxwQ==", - "peer": true, - "engines": { - "node": ">=0.6.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@types/xml-encryption": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@types/xml-encryption/-/xml-encryption-1.2.4.tgz", - "integrity": "sha512-I69K/WW1Dv7j6O3jh13z0X8sLWJRXbu5xnHDl9yHzUNDUBtUoBY058eb5s+x/WG6yZC1h8aKdI2EoyEPjyEh+Q==", - "peer": true, - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@types/xml2js": { - "version": "0.4.14", - "resolved": "https://registry.npmjs.org/@types/xml2js/-/xml2js-0.4.14.tgz", - "integrity": "sha512-4YnrRemBShWRO2QjvUin8ESA41rH+9nQGLUGZV/1IDhi3SL9OhdpNC/MrulTWuptXKwhx/aDxE7toV0f/ypIXQ==", - "peer": true, - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@typescript-eslint/eslint-plugin": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.21.0.tgz", - "integrity": "sha512-oy9+hTPCUFpngkEZUSzbf9MxI65wbKFoQYsgPdILTfbUldp5ovUuphZVe4i30emU9M/kP+T64Di0mxl7dSw3MA==", - "extraneous": true, - "license": "MIT", - "dependencies": { - "@eslint-community/regexpp": "^4.5.1", - "@typescript-eslint/scope-manager": "6.21.0", - "@typescript-eslint/type-utils": "6.21.0", - "@typescript-eslint/utils": "6.21.0", - "@typescript-eslint/visitor-keys": "6.21.0", - "debug": "^4.3.4", - "graphemer": "^1.4.0", - "ignore": "^5.2.4", - "natural-compare": "^1.4.0", - "semver": "^7.5.4", - "ts-api-utils": "^1.0.1" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "@typescript-eslint/parser": "^6.0.0 || ^6.0.0-alpha", - "eslint": "^7.0.0 || ^8.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@typescript-eslint/parser": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.21.0.tgz", - "integrity": "sha512-tbsV1jPne5CkFQCgPBcDOt30ItF7aJoZL997JSF7MhGQqOeT3svWRYxiqlfA5RUdlHN6Fi+EI9bxqbdyAUZjYQ==", - "extraneous": true, - "license": "BSD-2-Clause", - "dependencies": { - "@typescript-eslint/scope-manager": "6.21.0", - "@typescript-eslint/types": "6.21.0", - "@typescript-eslint/typescript-estree": "6.21.0", - "@typescript-eslint/visitor-keys": "6.21.0", - "debug": "^4.3.4" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^7.0.0 || ^8.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@typescript-eslint/scope-manager": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.21.0.tgz", - "integrity": "sha512-OwLUIWZJry80O99zvqXVEioyniJMa+d2GrqpUTqi5/v5D5rOrppJVBPa0yKCblcigC0/aYAzxxqQ1B+DS2RYsg==", - "extraneous": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "6.21.0", - "@typescript-eslint/visitor-keys": "6.21.0" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@typescript-eslint/type-utils": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.21.0.tgz", - "integrity": "sha512-rZQI7wHfao8qMX3Rd3xqeYSMCL3SoiSQLBATSiVKARdFGCYSRvmViieZjqc58jKgs8Y8i9YvVVhRbHSTA4VBag==", - "extraneous": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/typescript-estree": "6.21.0", - "@typescript-eslint/utils": "6.21.0", - "debug": "^4.3.4", - "ts-api-utils": "^1.0.1" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^7.0.0 || ^8.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@typescript-eslint/types": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.21.0.tgz", - "integrity": "sha512-1kFmZ1rOm5epu9NZEZm1kckCDGj5UJEf7P1kliH4LKu/RkwpsfqqGmY2OOcUs18lSlQBKLDYBOGxRVtrMN5lpg==", - "extraneous": true, - "license": "MIT", - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@typescript-eslint/typescript-estree": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.21.0.tgz", - "integrity": "sha512-6npJTkZcO+y2/kr+z0hc4HwNfrrP4kNYh57ek7yCNlrBjWQ1Y0OS7jiZTkgumrvkX5HkEKXFZkkdFNkaW2wmUQ==", - "extraneous": true, - "license": "BSD-2-Clause", - "dependencies": { - "@typescript-eslint/types": "6.21.0", - "@typescript-eslint/visitor-keys": "6.21.0", - "debug": "^4.3.4", - "globby": "^11.1.0", - "is-glob": "^4.0.3", - "minimatch": "9.0.3", - "semver": "^7.5.4", - "ts-api-utils": "^1.0.1" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@typescript-eslint/utils": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.21.0.tgz", - "integrity": "sha512-NfWVaC8HP9T8cbKQxHcsJBY5YE1O33+jpMwN45qzWWaPDZgLIbo12toGMWnmhvCpd3sIxkpDw3Wv1B3dYrbDQQ==", - "extraneous": true, - "license": "MIT", - "dependencies": { - "@eslint-community/eslint-utils": "^4.4.0", - "@types/json-schema": "^7.0.12", - "@types/semver": "^7.5.0", - "@typescript-eslint/scope-manager": "6.21.0", - "@typescript-eslint/types": "6.21.0", - "@typescript-eslint/typescript-estree": "6.21.0", - "semver": "^7.5.4" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^7.0.0 || ^8.0.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@typescript-eslint/visitor-keys": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.21.0.tgz", - "integrity": "sha512-JJtkDduxLi9bivAB+cYOVMtbkqdPOhZ+ZI5LC47MIRrDV4Yn2o+ZnW10Nkmr28xRpSpdJ6Sm42Hjf2+REYXm0A==", - "extraneous": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "6.21.0", - "eslint-visitor-keys": "^3.4.1" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/@ungap/structured-clone": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz", - "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==", - "extraneous": true, - "license": "ISC" - }, - "node_modules/@ngageoint/mage.service/node_modules/@xmldom/xmldom": { - "version": "0.9.8", - "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.9.8.tgz", - "integrity": "sha512-p96FSY54r+WJ50FIOsCOjyj/wavs8921hG5+kVMmZgKcvIKxMXHTrjNJvRgWa/zuX3B6t2lijLNFaOyuxUH+2A==", - "license": "MIT", - "peer": true, - "engines": { - "node": ">=14.6" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/abstract-logging": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/abstract-logging/-/abstract-logging-2.0.1.tgz", - "integrity": "sha512-2BjRTZxTPvheOvGbBslFSYOUkr+SjPtOnrLP33f+VIWLzezQpZcqVg7ja3L4dBXmzzgwT+a029jRx5PCi3JuiA==", - "peer": true - }, - "node_modules/@ngageoint/mage.service/node_modules/accepts": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", - "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", - "peer": true, - "dependencies": { - "mime-types": "~2.1.34", - "negotiator": "0.6.3" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/acorn": { - "version": "8.12.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.0.tgz", - "integrity": "sha512-RTvkC4w+KNXrM39/lWCUaG0IbRkWdCv7W/IOW9oU6SawyxulvkQy5HQPVTKxEjczcUvapcrw3cFx/60VN/NRNw==", - "extraneous": true, - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/acorn-jsx": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "extraneous": true, - "peerDependencies": { - "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/adm-zip": { - "version": "0.5.16", - "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.5.16.tgz", - "integrity": "sha512-TGw5yVi4saajsSEgz25grObGHEUaDrniwvA2qwSC060KfqGPdglhvPMA2lPIoxs3PQIItj2iag35fONcQqgUaQ==", - "license": "MIT", - "peer": true, - "engines": { - "node": ">=12.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/agent-base": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.3.tgz", - "integrity": "sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==", - "extraneous": true, - "license": "MIT", - "engines": { - "node": ">= 14" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/aggregate-error": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-4.0.1.tgz", - "integrity": "sha512-0poP0T7el6Vq3rstR8Mn4V/IQrpBLO6POkUSrN7RhyY+GF/InCFShQzsQ39T25gkHhLgSLByyAz+Kjb+c2L98w==", - "extraneous": true, - "dependencies": { - "clean-stack": "^4.0.0", - "indent-string": "^5.0.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "extraneous": true, - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/ansi-colors": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", - "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", - "extraneous": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/ansi-escapes": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", - "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", - "optional": true, - "peer": true, - "dependencies": { - "type-fest": "^0.21.3" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/ansi-escapes/node_modules/type-fest": { - "version": "0.21.3", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", - "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", - "optional": true, - "peer": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "optional": true, - "peer": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "optional": true, - "peer": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/anymatch": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", - "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", - "extraneous": true, - "license": "ISC", - "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/append-field": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/append-field/-/append-field-1.0.0.tgz", - "integrity": "sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw==", - "peer": true - }, - "node_modules/@ngageoint/mage.service/node_modules/append-transform": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/append-transform/-/append-transform-1.0.0.tgz", - "integrity": "sha512-P009oYkeHyU742iSZJzZZywj4QRJdnTWffaKuJQLablCZ1uz6/cW4yaRgcDaoQ+uwOxxnt0gRUcwfsNP2ri0gw==", - "extraneous": true, - "dependencies": { - "default-require-extensions": "^2.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/archiver": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/archiver/-/archiver-5.3.2.tgz", - "integrity": "sha512-+25nxyyznAXF7Nef3y0EbBeqmGZgeN/BxHX29Rs39djAfaFalmQ89SE6CWyDCHzGL0yt/ycBtNOmGTW0FyGWNw==", - "license": "MIT", - "peer": true, - "dependencies": { - "archiver-utils": "^2.1.0", - "async": "^3.2.4", - "buffer-crc32": "^0.2.1", - "readable-stream": "^3.6.0", - "readdir-glob": "^1.1.2", - "tar-stream": "^2.2.0", - "zip-stream": "^4.1.0" - }, - "engines": { - "node": ">= 10" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/archiver-utils": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-2.1.0.tgz", - "integrity": "sha512-bEL/yUb/fNNiNTuUz979Z0Yg5L+LzLxGJz8x79lYmR54fmTIb6ob/hNQgkQnIUDWIFjZVQwl9Xs356I6BAMHfw==", - "peer": true, - "dependencies": { - "glob": "^7.1.4", - "graceful-fs": "^4.2.0", - "lazystream": "^1.0.0", - "lodash.defaults": "^4.2.0", - "lodash.difference": "^4.5.0", - "lodash.flatten": "^4.4.0", - "lodash.isplainobject": "^4.0.6", - "lodash.union": "^4.6.0", - "normalize-path": "^3.0.0", - "readable-stream": "^2.0.0" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/archiver-utils/node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", - "peer": true - }, - "node_modules/@ngageoint/mage.service/node_modules/archiver-utils/node_modules/readable-stream": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", - "peer": true, - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/archiver-utils/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "peer": true - }, - "node_modules/@ngageoint/mage.service/node_modules/archiver-utils/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "peer": true, - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/archy": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz", - "integrity": "sha512-Xg+9RwCg/0p32teKdGMPTPnVXKD0w3DfHnFTficozsAgsvq2XenPJq/MYpzzQ/v8zrOyJn6Ds39VA4JIDwFfqw==", - "extraneous": true - }, - "node_modules/@ngageoint/mage.service/node_modules/argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "peer": true, - "dependencies": { - "sprintf-js": "~1.0.2" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/array-buffer-byte-length": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.1.tgz", - "integrity": "sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg==", - "extraneous": true, - "dependencies": { - "call-bind": "^1.0.5", - "is-array-buffer": "^3.0.4" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/array-flatten": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", - "peer": true - }, - "node_modules/@ngageoint/mage.service/node_modules/array-from": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/array-from/-/array-from-2.1.1.tgz", - "integrity": "sha512-GQTc6Uupx1FCavi5mPzBvVT7nEOeWMmUA9P95wpfpW1XwMSKs+KaymD5C2Up7KAUKg/mYwbsUYzdZWcoajlNZg==", - "extraneous": true - }, - "node_modules/@ngageoint/mage.service/node_modules/array-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", - "extraneous": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/arraybuffer.prototype.slice": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.3.tgz", - "integrity": "sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A==", - "extraneous": true, - "dependencies": { - "array-buffer-byte-length": "^1.0.1", - "call-bind": "^1.0.5", - "define-properties": "^1.2.1", - "es-abstract": "^1.22.3", - "es-errors": "^1.2.1", - "get-intrinsic": "^1.2.3", - "is-array-buffer": "^3.0.4", - "is-shared-array-buffer": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/arrify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/arrify/-/arrify-3.0.0.tgz", - "integrity": "sha512-tLkvA81vQG/XqE2mjDkGQHoOINtMHtysSnemrmoGe6PydDPMRbVugqyk4A6V/WDWEfm3l+0d8anA9r8cv/5Jaw==", - "extraneous": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/asap": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", - "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==", - "peer": true - }, - "node_modules/@ngageoint/mage.service/node_modules/asn1": { - "version": "0.2.6", - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz", - "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==", - "peer": true, - "dependencies": { - "safer-buffer": "~2.1.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/assert-never": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/assert-never/-/assert-never-1.2.1.tgz", - "integrity": "sha512-TaTivMB6pYI1kXwrFlEhLeGfOqoDNdTxjCdwRfFFkEA30Eu+k48W34nlok2EYWJfFFzqaEmichdNM7th6M5HNw==", - "peer": true - }, - "node_modules/@ngageoint/mage.service/node_modules/assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==", - "peer": true, - "engines": { - "node": ">=0.8" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/assertion-error": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", - "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", - "extraneous": true, - "engines": { - "node": "*" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/async": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", - "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", - "license": "MIT", - "peer": true - }, - "node_modules/@ngageoint/mage.service/node_modules/async-lock": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/async-lock/-/async-lock-1.4.1.tgz", - "integrity": "sha512-Az2ZTpuytrtqENulXwO3GGv1Bztugx6TT37NIo7imr/Qo0gsYiGtSdBa2B6fsXhTpVZDNfu1Qn3pk531e3q+nQ==", - "peer": true - }, - "node_modules/@ngageoint/mage.service/node_modules/async-mutex": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/async-mutex/-/async-mutex-0.4.1.tgz", - "integrity": "sha512-WfoBo4E/TbCX1G95XTjbWTE3X2XLG0m1Xbv2cwOtuPdyH9CZvnaA5nCt1ucjaKEgW2A5IF71hxrRhr83Je5xjA==", - "extraneous": true, - "license": "MIT", - "dependencies": { - "tslib": "^2.4.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", - "peer": true - }, - "node_modules/@ngageoint/mage.service/node_modules/available-typed-arrays": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", - "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", - "extraneous": true, - "dependencies": { - "possible-typed-array-names": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/aws-sign2": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", - "integrity": "sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA==", - "extraneous": true, - "engines": { - "node": "*" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/aws4": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.13.0.tgz", - "integrity": "sha512-3AungXC4I8kKsS9PuS4JH2nc+0bVY/mjgrephHTIi8fpEeGsTHBUJeosp0Wc1myYMElmD0B3Oc4XL/HVJ4PV2g==", - "extraneous": true - }, - "node_modules/@ngageoint/mage.service/node_modules/axios": { - "version": "1.8.4", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.8.4.tgz", - "integrity": "sha512-eBSYY4Y68NNlHbHBMdeDmKNtDgXWhQsJcGqzO3iLUM0GraQFSS9cVgPX5I9b3lbdFKyYoAEGAZF1DwhTaljNAw==", - "extraneous": true, - "license": "MIT", - "dependencies": { - "follow-redirects": "^1.15.6", - "form-data": "^4.0.0", - "proxy-from-env": "^1.1.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/b4a": { - "version": "1.6.7", - "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.6.7.tgz", - "integrity": "sha512-OnAYlL5b7LEkALw87fUVafQw5rVR9RjwGd4KUwNQ6DrrNmaVaUCgLipfVlzrPQ4tWOR9P0IXGNOx50jYCCdSJg==", - "extraneous": true, - "license": "Apache-2.0" - }, - "node_modules/@ngageoint/mage.service/node_modules/babel-walk": { - "version": "3.0.0-canary-5", - "resolved": "https://registry.npmjs.org/babel-walk/-/babel-walk-3.0.0-canary-5.tgz", - "integrity": "sha512-GAwkz0AihzY5bkwIY5QDR+LvsRQgB/B+1foMPvi0FZPMl5fjD7ICiznUiBdLYMH1QYe6vqu4gWYytZOccLouFw==", - "peer": true, - "dependencies": { - "@babel/types": "^7.9.6" - }, - "engines": { - "node": ">= 10.0.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/backoff": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/backoff/-/backoff-2.5.0.tgz", - "integrity": "sha512-wC5ihrnUXmR2douXmXLCe5O3zg3GKIyvRi/hi58a/XyRxVI+3/yM0PYueQOZXPXQ9pxBislYkw+sF9b7C/RuMA==", - "peer": true, - "dependencies": { - "precond": "0.2" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "peer": true - }, - "node_modules/@ngageoint/mage.service/node_modules/bare-events": { - "version": "2.5.4", - "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.5.4.tgz", - "integrity": "sha512-+gFfDkR8pj4/TrWCGUGWmJIkBwuxPS5F+a5yWjOHQt2hHvNZd5YLzadjmDUtFmMM4y429bnKLa8bYBMHcYdnQA==", - "extraneous": true, - "license": "Apache-2.0" - }, - "node_modules/@ngageoint/mage.service/node_modules/base-64": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/base-64/-/base-64-1.0.0.tgz", - "integrity": "sha512-kwDPIFCGx0NZHog36dj+tHiwP4QMzsZ3AgMViUBKI0+V5n4U0ufTCUMhnQ04diaRI8EX/QcPfql7zlhZ7j4zgg==", - "peer": true - }, - "node_modules/@ngageoint/mage.service/node_modules/base64-js": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "peer": true - }, - "node_modules/@ngageoint/mage.service/node_modules/base64url": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/base64url/-/base64url-3.0.1.tgz", - "integrity": "sha512-ir1UPr3dkwexU7FdV8qBBbNDRUhMmIekYMFZfi+C/sLNnRESKPl23nB9b2pltqfOQNnGzsDdId90AEtG5tCx4A==", - "peer": true, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/bcrypt-pbkdf": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", - "integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==", - "extraneous": true, - "dependencies": { - "tweetnacl": "^0.14.3" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/bcryptjs": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/bcryptjs/-/bcryptjs-2.4.3.tgz", - "integrity": "sha512-V/Hy/X9Vt7f3BbPJEi8BdVFMByHi+jNXrYkW3huaybV/kQ0KJg0Y6PkEMbn+zeT+i+SiKZ/HMqJGIIt4LZDqNQ==", - "peer": true - }, - "node_modules/@ngageoint/mage.service/node_modules/binary-extensions": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", - "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", - "extraneous": true, - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/bindings": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", - "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", - "optional": true, - "peer": true, - "dependencies": { - "file-uri-to-path": "1.0.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/bl": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", - "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", - "peer": true, - "dependencies": { - "buffer": "^5.5.0", - "inherits": "^2.0.4", - "readable-stream": "^3.4.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/bluebird": { - "version": "3.7.2", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", - "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", - "peer": true - }, - "node_modules/@ngageoint/mage.service/node_modules/body-parser": { - "version": "1.20.3", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz", - "integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==", - "license": "MIT", - "peer": true, - "dependencies": { - "bytes": "3.1.2", - "content-type": "~1.0.5", - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "on-finished": "2.4.1", - "qs": "6.13.0", - "raw-body": "2.5.2", - "type-is": "~1.6.18", - "unpipe": "1.0.0" - }, - "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/body-parser/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "peer": true, - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/body-parser/node_modules/iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "peer": true, - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/body-parser/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "peer": true - }, - "node_modules/@ngageoint/mage.service/node_modules/bowser": { - "version": "2.11.0", - "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.11.0.tgz", - "integrity": "sha512-AlcaJBi/pqqJBIQ8U9Mcpc9i8Aqxn88Skv5d+xBX006BY5u8N3mGLHa5Lgppa7L/HfwgwLgZ6NYs+Ag6uUmJRA==", - "optional": true, - "peer": true - }, - "node_modules/@ngageoint/mage.service/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "peer": true, - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/braces": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", - "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", - "extraneous": true, - "dependencies": { - "fill-range": "^7.1.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/browser-stdout": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", - "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", - "extraneous": true - }, - "node_modules/@ngageoint/mage.service/node_modules/bson": { - "version": "4.7.2", - "resolved": "https://registry.npmjs.org/bson/-/bson-4.7.2.tgz", - "integrity": "sha512-Ry9wCtIZ5kGqkJoi6aD8KjxFZEx78guTQDnpXWiNthsxzrxAK/i8E6pCHAIZTbaEFWcOCvbecMukfK7XUvyLpQ==", - "peer": true, - "dependencies": { - "buffer": "^5.6.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/buffer": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", - "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "peer": true, - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.1.13" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/buffer-crc32": { - "version": "0.2.13", - "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", - "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", - "peer": true, - "engines": { - "node": "*" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/buffer-equal-constant-time": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", - "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==", - "peer": true - }, - "node_modules/@ngageoint/mage.service/node_modules/buffer-from": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", - "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", - "peer": true - }, - "node_modules/@ngageoint/mage.service/node_modules/busboy": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", - "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==", - "peer": true, - "dependencies": { - "streamsearch": "^1.1.0" - }, - "engines": { - "node": ">=10.16.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/bytes": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", - "license": "MIT", - "peer": true, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/caching-transform": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/caching-transform/-/caching-transform-3.0.2.tgz", - "integrity": "sha512-Mtgcv3lh3U0zRii/6qVgQODdPA4G3zhG+jtbCWj39RXuUFTMzH0vcdMtaJS1jPowd+It2Pqr6y3NJMQqOqCE2w==", - "extraneous": true, - "dependencies": { - "hasha": "^3.0.0", - "make-dir": "^2.0.0", - "package-hash": "^3.0.0", - "write-file-atomic": "^2.4.2" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/caching-transform/node_modules/make-dir": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", - "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", - "extraneous": true, - "dependencies": { - "pify": "^4.0.1", - "semver": "^5.6.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/caching-transform/node_modules/pify": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", - "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", - "extraneous": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/caching-transform/node_modules/semver": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", - "extraneous": true, - "bin": { - "semver": "bin/semver" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/call-bind": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", - "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", - "peer": true, - "dependencies": { - "es-define-property": "^1.0.0", - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.4", - "set-function-length": "^1.2.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/call-bind-apply-helpers": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", - "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", - "license": "MIT", - "peer": true, - "dependencies": { - "es-errors": "^1.3.0", - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/call-bound": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", - "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", - "license": "MIT", - "peer": true, - "dependencies": { - "call-bind-apply-helpers": "^1.0.2", - "get-intrinsic": "^1.3.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "extraneous": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/camelcase": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", - "extraneous": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/caseless": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", - "integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==", - "extraneous": true - }, - "node_modules/@ngageoint/mage.service/node_modules/cfenv": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/cfenv/-/cfenv-1.2.4.tgz", - "integrity": "sha512-jWQ+3UXZauYyOXwHpMm74C0wM7+LDQmgMxWBGchg4as7+YyTL0pyx/CZ3dEvJyZVOB4SgKATc5naJky6cd9zYw==", - "license": "Apache-2.0", - "peer": true, - "dependencies": { - "js-yaml": "4.0.x", - "ports": "1.1.x", - "underscore": "1.12.x" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/chai": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/chai/-/chai-4.5.0.tgz", - "integrity": "sha512-RITGBfijLkBddZvnn8jdqoTypxvqbOLYQkGGxXzeFjVHvudaPw0HNFD9x928/eUwYWd2dPCugVqspGALTZZQKw==", - "extraneous": true, - "license": "MIT", - "dependencies": { - "assertion-error": "^1.1.0", - "check-error": "^1.0.3", - "deep-eql": "^4.1.3", - "get-func-name": "^2.0.2", - "loupe": "^2.3.6", - "pathval": "^1.1.1", - "type-detect": "^4.1.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/chai-as-promised": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/chai-as-promised/-/chai-as-promised-7.1.2.tgz", - "integrity": "sha512-aBDHZxRzYnUYuIAIPBH2s511DjlKPzXNlXSGFC8CwmroWQLfrW0LtE1nK3MAwwNhJPa9raEjNCmRoFpG0Hurdw==", - "extraneous": true, - "license": "WTFPL", - "dependencies": { - "check-error": "^1.0.2" - }, - "peerDependencies": { - "chai": ">= 2.1.2 < 6" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/chai/node_modules/type-detect": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.1.0.tgz", - "integrity": "sha512-Acylog8/luQ8L7il+geoSxhEkazvkslg7PSNKOX59mbB9cOveP5aq9h74Y7YU8yDpJwetzQQrfIwtf4Wp4LKcw==", - "extraneous": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/chalk": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", - "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", - "optional": true, - "peer": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/character-parser": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/character-parser/-/character-parser-2.2.0.tgz", - "integrity": "sha512-+UqJQjFEFaTAs3bNsF2j2kEN1baG/zghZbdqoYEDxGZtJo9LBzl1A+m0D4n3qKx8N2FNv8/Xp6yV9mQmBuptaw==", - "peer": true, - "dependencies": { - "is-regex": "^1.0.3" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/chardet": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", - "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", - "optional": true, - "peer": true - }, - "node_modules/@ngageoint/mage.service/node_modules/check-error": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.3.tgz", - "integrity": "sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==", - "extraneous": true, - "dependencies": { - "get-func-name": "^2.0.2" - }, - "engines": { - "node": "*" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/chokidar": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", - "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", - "extraneous": true, - "funding": [ - { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - ], - "license": "MIT", - "dependencies": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - }, - "engines": { - "node": ">= 8.10.0" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/chokidar/node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "extraneous": true, - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/chownr": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", - "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", - "optional": true, - "peer": true - }, - "node_modules/@ngageoint/mage.service/node_modules/clean-stack": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-4.2.0.tgz", - "integrity": "sha512-LYv6XPxoyODi36Dp976riBtSY27VmFo+MKqEU9QCCWyTrdEPDog+RWA7xQWHi6Vbp61j5c4cdzzX1NidnwtUWg==", - "extraneous": true, - "dependencies": { - "escape-string-regexp": "5.0.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/clean-stack/node_modules/escape-string-regexp": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", - "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", - "extraneous": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/cli-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", - "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", - "optional": true, - "peer": true, - "dependencies": { - "restore-cursor": "^3.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/cli-width": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz", - "integrity": "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==", - "optional": true, - "peer": true, - "engines": { - "node": ">= 10" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/cliui": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", - "extraneous": true, - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^7.0.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/coffeescript": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/coffeescript/-/coffeescript-2.7.0.tgz", - "integrity": "sha512-hzWp6TUE2d/jCcN67LrW1eh5b/rSDKQK6oD6VMLlggYVUUFexgTH9z3dNYihzX4RMhze5FTUsUmOXViJKFQR/A==", - "peer": true, - "bin": { - "cake": "bin/cake", - "coffee": "bin/coffee" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "optional": true, - "peer": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "optional": true, - "peer": true - }, - "node_modules/@ngageoint/mage.service/node_modules/colors": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/colors/-/colors-1.0.3.tgz", - "integrity": "sha512-pFGrxThWcWQ2MsAz6RtgeWe4NK2kUE1WfsrvvlctdII745EW9I0yflqhe7++M5LEc7bV2c/9/5zc8sFcpL0Drw==", - "peer": true, - "engines": { - "node": ">=0.1.90" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "peer": true, - "dependencies": { - "delayed-stream": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/commander": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", - "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==", - "license": "MIT", - "peer": true, - "engines": { - "node": ">= 12" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/commondir": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", - "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==", - "extraneous": true - }, - "node_modules/@ngageoint/mage.service/node_modules/component-emitter": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.1.tgz", - "integrity": "sha512-T0+barUSQRTUQASh8bx02dl+DhF54GtIDY13Y3m9oWTklKbb3Wv974meRpeZ3lp1JpLVECWWNHC4vaG2XHXouQ==", - "peer": true, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/compress-commons": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/compress-commons/-/compress-commons-4.1.2.tgz", - "integrity": "sha512-D3uMHtGc/fcO1Gt1/L7i1e33VOvD4A9hfQLP+6ewd+BvG/gQ84Yh4oftEhAdjSMgBgwGL+jsppT7JYNpo6MHHg==", - "peer": true, - "dependencies": { - "buffer-crc32": "^0.2.13", - "crc32-stream": "^4.0.2", - "normalize-path": "^3.0.0", - "readable-stream": "^3.6.0" - }, - "engines": { - "node": ">= 10" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "peer": true - }, - "node_modules/@ngageoint/mage.service/node_modules/concat-stream": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", - "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", - "engines": [ - "node >= 0.8" - ], - "peer": true, - "dependencies": { - "buffer-from": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^2.2.2", - "typedarray": "^0.0.6" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/concat-stream/node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", - "peer": true - }, - "node_modules/@ngageoint/mage.service/node_modules/concat-stream/node_modules/readable-stream": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", - "peer": true, - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/concat-stream/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "peer": true - }, - "node_modules/@ngageoint/mage.service/node_modules/concat-stream/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "peer": true, - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/constantinople": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/constantinople/-/constantinople-4.0.1.tgz", - "integrity": "sha512-vCrqcSIq4//Gx74TXXCGnHpulY1dskqLTFGDmhrGxzeXL8lF8kvXv6mpNWlJj1uD4DW23D4ljAqbY4RRaaUZIw==", - "peer": true, - "dependencies": { - "@babel/parser": "^7.6.0", - "@babel/types": "^7.6.1" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/content-disposition": { - "version": "0.5.4", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", - "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", - "peer": true, - "dependencies": { - "safe-buffer": "5.2.1" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/content-type": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", - "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", - "peer": true, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/convert-source-map": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", - "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", - "extraneous": true - }, - "node_modules/@ngageoint/mage.service/node_modules/cookie": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz", - "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==", - "license": "MIT", - "peer": true, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/cookie-signature": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", - "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==", - "peer": true - }, - "node_modules/@ngageoint/mage.service/node_modules/cookiejar": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.4.tgz", - "integrity": "sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw==", - "peer": true - }, - "node_modules/@ngageoint/mage.service/node_modules/core-util-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==", - "peer": true - }, - "node_modules/@ngageoint/mage.service/node_modules/coveralls": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/coveralls/-/coveralls-3.1.1.tgz", - "integrity": "sha512-+dxnG2NHncSD1NrqbSM3dn/lE57O6Qf/koe9+I7c+wzkqRmEvcp0kgJdxKInzYzkICKkFMZsX3Vct3++tsF9ww==", - "extraneous": true, - "license": "BSD-2-Clause", - "dependencies": { - "js-yaml": "^3.13.1", - "lcov-parse": "^1.0.0", - "log-driver": "^1.2.7", - "minimist": "^1.2.5", - "request": "^2.88.2" - }, - "bin": { - "coveralls": "bin/coveralls.js" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/coveralls/node_modules/js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", - "extraneous": true, - "dependencies": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/cp-file": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/cp-file/-/cp-file-10.0.0.tgz", - "integrity": "sha512-vy2Vi1r2epK5WqxOLnskeKeZkdZvTKfFZQCplE3XWsP+SUJyd5XAUFC9lFgTjjXJF2GMne/UML14iEmkAaDfFg==", - "extraneous": true, - "dependencies": { - "graceful-fs": "^4.2.10", - "nested-error-stacks": "^2.1.1", - "p-event": "^5.0.1" - }, - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/cpy": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/cpy/-/cpy-10.1.0.tgz", - "integrity": "sha512-VC2Gs20JcTyeQob6UViBLnyP0bYHkBh6EiKzot9vi2DmeGlFT9Wd7VG3NBrkNx/jYvFBeyDOMMHdHQhbtKLgHQ==", - "extraneous": true, - "dependencies": { - "arrify": "^3.0.0", - "cp-file": "^10.0.0", - "globby": "^13.1.4", - "junk": "^4.0.1", - "micromatch": "^4.0.5", - "nested-error-stacks": "^2.1.1", - "p-filter": "^3.0.0", - "p-map": "^6.0.0" - }, - "engines": { - "node": ">=16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/cpy-cli": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/cpy-cli/-/cpy-cli-5.0.0.tgz", - "integrity": "sha512-fb+DZYbL9KHc0BC4NYqGRrDIJZPXUmjjtqdw4XRRg8iV8dIfghUX/WiL+q4/B/KFTy3sK6jsbUhBaz0/Hxg7IQ==", - "extraneous": true, - "dependencies": { - "cpy": "^10.1.0", - "meow": "^12.0.1" - }, - "bin": { - "cpy": "cli.js" - }, - "engines": { - "node": ">=16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/cpy/node_modules/globby": { - "version": "13.2.2", - "resolved": "https://registry.npmjs.org/globby/-/globby-13.2.2.tgz", - "integrity": "sha512-Y1zNGV+pzQdh7H39l9zgB4PJqjRNqydvdYCDG4HFXM4XuvSaQQlEc91IU1yALL8gUTDomgBAfz3XJdmUS+oo0w==", - "extraneous": true, - "dependencies": { - "dir-glob": "^3.0.1", - "fast-glob": "^3.3.0", - "ignore": "^5.2.4", - "merge2": "^1.4.1", - "slash": "^4.0.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/cpy/node_modules/slash": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-4.0.0.tgz", - "integrity": "sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==", - "extraneous": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/crc-32": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz", - "integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==", - "peer": true, - "bin": { - "crc32": "bin/crc32.njs" - }, - "engines": { - "node": ">=0.8" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/crc32-stream": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/crc32-stream/-/crc32-stream-4.0.3.tgz", - "integrity": "sha512-NT7w2JVU7DFroFdYkeq8cywxrgjPHWkdX1wjpRQXPX5Asews3tA+Ght6lddQO5Mkumffp3X7GEqku3epj2toIw==", - "peer": true, - "dependencies": { - "crc-32": "^1.2.0", - "readable-stream": "^3.4.0" - }, - "engines": { - "node": ">= 10" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/cross-spawn": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", - "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", - "extraneous": true, - "license": "MIT", - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/cycle": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/cycle/-/cycle-1.0.3.tgz", - "integrity": "sha512-TVF6svNzeQCOpjCqsy0/CSy8VgObG3wXusJ73xW2GbG5rGx7lC8zxDSURicsXI2UsGdi2L0QNRCi745/wUDvsA==", - "peer": true, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/dashdash": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", - "integrity": "sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==", - "extraneous": true, - "dependencies": { - "assert-plus": "^1.0.0" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/data-view-buffer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.1.tgz", - "integrity": "sha512-0lht7OugA5x3iJLOWFhWK/5ehONdprk0ISXqVFn/NFrDu+cuc8iADFrGQz5BnRK7LLU3JmkbXSxaqX+/mXYtUA==", - "extraneous": true, - "dependencies": { - "call-bind": "^1.0.6", - "es-errors": "^1.3.0", - "is-data-view": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/data-view-byte-length": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.1.tgz", - "integrity": "sha512-4J7wRJD3ABAzr8wP+OcIcqq2dlUKp4DVflx++hs5h5ZKydWMI6/D/fAot+yh6g2tHh8fLFTvNOaVN357NvSrOQ==", - "extraneous": true, - "dependencies": { - "call-bind": "^1.0.7", - "es-errors": "^1.3.0", - "is-data-view": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/data-view-byte-offset": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.0.tgz", - "integrity": "sha512-t/Ygsytq+R995EJ5PZlD4Cu56sWa8InXySaViRzw9apusqsOO2bQP+SbYzAhR0pFKoB+43lYy8rWban9JSuXnA==", - "extraneous": true, - "dependencies": { - "call-bind": "^1.0.6", - "es-errors": "^1.3.0", - "is-data-view": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/debug": { - "version": "4.3.5", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.5.tgz", - "integrity": "sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==", - "peer": true, - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/@ngageoint/mage.service/node_modules/decamelize": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", - "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", - "extraneous": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/decompress-response": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", - "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", - "optional": true, - "peer": true, - "dependencies": { - "mimic-response": "^3.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/deep-eql": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.4.tgz", - "integrity": "sha512-SUwdGfqdKOwxCPeVYjwSyRpJ7Z+fhpwIAtmCUdZIWZ/YP5R9WAsyuSgpLVDi9bjWoN2LXHNss/dk3urXtdQxGg==", - "extraneous": true, - "license": "MIT", - "dependencies": { - "type-detect": "^4.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/deep-equal": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-2.2.3.tgz", - "integrity": "sha512-ZIwpnevOurS8bpT4192sqAowWM76JDKSHYzMLty3BZGSswgq6pBaH3DhCSW5xVAZICZyKdOBPjwww5wfgT/6PA==", - "extraneous": true, - "license": "MIT", - "dependencies": { - "array-buffer-byte-length": "^1.0.0", - "call-bind": "^1.0.5", - "es-get-iterator": "^1.1.3", - "get-intrinsic": "^1.2.2", - "is-arguments": "^1.1.1", - "is-array-buffer": "^3.0.2", - "is-date-object": "^1.0.5", - "is-regex": "^1.1.4", - "is-shared-array-buffer": "^1.0.2", - "isarray": "^2.0.5", - "object-is": "^1.1.5", - "object-keys": "^1.1.1", - "object.assign": "^4.1.4", - "regexp.prototype.flags": "^1.5.1", - "side-channel": "^1.0.4", - "which-boxed-primitive": "^1.0.2", - "which-collection": "^1.0.1", - "which-typed-array": "^1.1.13" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/deep-extend": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", - "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", - "optional": true, - "peer": true, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/deep-is": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "extraneous": true - }, - "node_modules/@ngageoint/mage.service/node_modules/default-require-extensions": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-2.0.0.tgz", - "integrity": "sha512-B0n2zDIXpzLzKeoEozorDSa1cHc1t0NjmxP0zuAxbizNU2MBqYJJKYXrrFdKuQliojXynrxgd7l4ahfg/+aA5g==", - "extraneous": true, - "dependencies": { - "strip-bom": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/define-data-property": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", - "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", - "peer": true, - "dependencies": { - "es-define-property": "^1.0.0", - "es-errors": "^1.3.0", - "gopd": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/define-properties": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", - "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", - "extraneous": true, - "dependencies": { - "define-data-property": "^1.0.1", - "has-property-descriptors": "^1.0.0", - "object-keys": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", - "peer": true, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/depd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", - "license": "MIT", - "peer": true, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/destroy": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", - "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", - "peer": true, - "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/detect-libc": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.3.tgz", - "integrity": "sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==", - "optional": true, - "peer": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/dezalgo": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.4.tgz", - "integrity": "sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig==", - "peer": true, - "dependencies": { - "asap": "^2.0.0", - "wrappy": "1" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/diff": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-5.2.0.tgz", - "integrity": "sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==", - "extraneous": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.3.1" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/dir-glob": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", - "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", - "extraneous": true, - "dependencies": { - "path-type": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/doctrine": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", - "extraneous": true, - "dependencies": { - "esutils": "^2.0.2" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/doctypes": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/doctypes/-/doctypes-1.1.0.tgz", - "integrity": "sha512-LLBi6pEqS6Do3EKQ3J0NqHWV5hhb78Pi8vvESYwyOy2c31ZEZVdtitdzsQsKb7878PEERhzUk0ftqGhG6Mz+pQ==", - "peer": true - }, - "node_modules/@ngageoint/mage.service/node_modules/drange": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/drange/-/drange-1.1.1.tgz", - "integrity": "sha512-pYxfDYpued//QpnLIm4Avk7rsNtAtQkUES2cwAYSvD/wd2pKD71gN2Ebj3e7klzXwjocvE8c5vx/1fxwpqmSxA==", - "extraneous": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/dunder-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", - "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", - "license": "MIT", - "peer": true, - "dependencies": { - "call-bind-apply-helpers": "^1.0.1", - "es-errors": "^1.3.0", - "gopd": "^1.2.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/duplexer": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz", - "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==", - "peer": true - }, - "node_modules/@ngageoint/mage.service/node_modules/eastasianwidth": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", - "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", - "extraneous": true - }, - "node_modules/@ngageoint/mage.service/node_modules/ecc-jsbn": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", - "integrity": "sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==", - "extraneous": true, - "dependencies": { - "jsbn": "~0.1.0", - "safer-buffer": "^2.1.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/ecc-jsbn/node_modules/jsbn": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", - "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==", - "extraneous": true - }, - "node_modules/@ngageoint/mage.service/node_modules/ecdsa-sig-formatter": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", - "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", - "peer": true, - "dependencies": { - "safe-buffer": "^5.0.1" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/ee-first": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", - "license": "MIT", - "peer": true - }, - "node_modules/@ngageoint/mage.service/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "optional": true, - "peer": true - }, - "node_modules/@ngageoint/mage.service/node_modules/encodeurl": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", - "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", - "license": "MIT", - "peer": true, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/end-of-stream": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", - "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", - "peer": true, - "dependencies": { - "once": "^1.4.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "extraneous": true, - "dependencies": { - "is-arrayish": "^0.2.1" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/es-abstract": { - "version": "1.23.3", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.3.tgz", - "integrity": "sha512-e+HfNH61Bj1X9/jLc5v1owaLYuHdeHHSQlkhCBiTK8rBvKaULl/beGMxwrMXjpYrv4pz22BlY570vVePA2ho4A==", - "extraneous": true, - "dependencies": { - "array-buffer-byte-length": "^1.0.1", - "arraybuffer.prototype.slice": "^1.0.3", - "available-typed-arrays": "^1.0.7", - "call-bind": "^1.0.7", - "data-view-buffer": "^1.0.1", - "data-view-byte-length": "^1.0.1", - "data-view-byte-offset": "^1.0.0", - "es-define-property": "^1.0.0", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.0.0", - "es-set-tostringtag": "^2.0.3", - "es-to-primitive": "^1.2.1", - "function.prototype.name": "^1.1.6", - "get-intrinsic": "^1.2.4", - "get-symbol-description": "^1.0.2", - "globalthis": "^1.0.3", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.2", - "has-proto": "^1.0.3", - "has-symbols": "^1.0.3", - "hasown": "^2.0.2", - "internal-slot": "^1.0.7", - "is-array-buffer": "^3.0.4", - "is-callable": "^1.2.7", - "is-data-view": "^1.0.1", - "is-negative-zero": "^2.0.3", - "is-regex": "^1.1.4", - "is-shared-array-buffer": "^1.0.3", - "is-string": "^1.0.7", - "is-typed-array": "^1.1.13", - "is-weakref": "^1.0.2", - "object-inspect": "^1.13.1", - "object-keys": "^1.1.1", - "object.assign": "^4.1.5", - "regexp.prototype.flags": "^1.5.2", - "safe-array-concat": "^1.1.2", - "safe-regex-test": "^1.0.3", - "string.prototype.trim": "^1.2.9", - "string.prototype.trimend": "^1.0.8", - "string.prototype.trimstart": "^1.0.8", - "typed-array-buffer": "^1.0.2", - "typed-array-byte-length": "^1.0.1", - "typed-array-byte-offset": "^1.0.2", - "typed-array-length": "^1.0.6", - "unbox-primitive": "^1.0.2", - "which-typed-array": "^1.1.15" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/es-define-property": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", - "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", - "license": "MIT", - "peer": true, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/es-errors": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", - "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", - "peer": true, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/es-get-iterator": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/es-get-iterator/-/es-get-iterator-1.1.3.tgz", - "integrity": "sha512-sPZmqHBe6JIiTfN5q2pEi//TwxmAFHwj/XEuYjTuse78i8KxaqMTTzxPoFKuzRpDpTJ+0NAbpfenkmH2rePtuw==", - "extraneous": true, - "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.3", - "has-symbols": "^1.0.3", - "is-arguments": "^1.1.1", - "is-map": "^2.0.2", - "is-set": "^2.0.2", - "is-string": "^1.0.7", - "isarray": "^2.0.5", - "stop-iteration-iterator": "^1.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/es-object-atoms": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", - "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", - "license": "MIT", - "peer": true, - "dependencies": { - "es-errors": "^1.3.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/es-set-tostringtag": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.3.tgz", - "integrity": "sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ==", - "extraneous": true, - "dependencies": { - "get-intrinsic": "^1.2.4", - "has-tostringtag": "^1.0.2", - "hasown": "^2.0.1" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/es-to-primitive": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", - "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", - "extraneous": true, - "dependencies": { - "is-callable": "^1.1.4", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/es6-error": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", - "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==", - "extraneous": true - }, - "node_modules/@ngageoint/mage.service/node_modules/escalade": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz", - "integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==", - "extraneous": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", - "peer": true - }, - "node_modules/@ngageoint/mage.service/node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "extraneous": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/eslint": { - "version": "8.57.1", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.1.tgz", - "integrity": "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==", - "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", - "extraneous": true, - "license": "MIT", - "dependencies": { - "@eslint-community/eslint-utils": "^4.2.0", - "@eslint-community/regexpp": "^4.6.1", - "@eslint/eslintrc": "^2.1.4", - "@eslint/js": "8.57.1", - "@humanwhocodes/config-array": "^0.13.0", - "@humanwhocodes/module-importer": "^1.0.1", - "@nodelib/fs.walk": "^1.2.8", - "@ungap/structured-clone": "^1.2.0", - "ajv": "^6.12.4", - "chalk": "^4.0.0", - "cross-spawn": "^7.0.2", - "debug": "^4.3.2", - "doctrine": "^3.0.0", - "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.2.2", - "eslint-visitor-keys": "^3.4.3", - "espree": "^9.6.1", - "esquery": "^1.4.2", - "esutils": "^2.0.2", - "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^6.0.1", - "find-up": "^5.0.0", - "glob-parent": "^6.0.2", - "globals": "^13.19.0", - "graphemer": "^1.4.0", - "ignore": "^5.2.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "is-path-inside": "^3.0.3", - "js-yaml": "^4.1.0", - "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.4.1", - "lodash.merge": "^4.6.2", - "minimatch": "^3.1.2", - "natural-compare": "^1.4.0", - "optionator": "^0.9.3", - "strip-ansi": "^6.0.1", - "text-table": "^0.2.0" - }, - "bin": { - "eslint": "bin/eslint.js" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/eslint-config-prettier": { - "version": "6.15.0", - "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-6.15.0.tgz", - "integrity": "sha512-a1+kOYLR8wMGustcgAjdydMsQ2A/2ipRPwRKUmfYaSxc9ZPcrku080Ctl6zrZzZNs/U82MjSv+qKREkoq3bJaw==", - "extraneous": true, - "license": "MIT", - "dependencies": { - "get-stdin": "^6.0.0" - }, - "bin": { - "eslint-config-prettier-check": "bin/cli.js" - }, - "peerDependencies": { - "eslint": ">=3.14.1" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/eslint-plugin-prettier": { - "version": "3.4.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-3.4.1.tgz", - "integrity": "sha512-htg25EUYUeIhKHXjOinK4BgCcDwtLHjqaxCDsMy5nbnUMkKFvIhMVCp+5GFUXQ4Nr8lBsPqtGAqBenbpFqAA2g==", - "extraneous": true, - "license": "MIT", - "dependencies": { - "prettier-linter-helpers": "^1.0.0" - }, - "engines": { - "node": ">=6.0.0" - }, - "peerDependencies": { - "eslint": ">=5.0.0", - "prettier": ">=1.13.0" - }, - "peerDependenciesMeta": { - "eslint-config-prettier": { - "optional": true - } - } - }, - "node_modules/@ngageoint/mage.service/node_modules/eslint-scope": { - "version": "7.2.2", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", - "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", - "extraneous": true, - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/eslint-visitor-keys": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", - "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", - "extraneous": true, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/eslint/node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "extraneous": true - }, - "node_modules/@ngageoint/mage.service/node_modules/eslint/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "extraneous": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/eslint/node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "extraneous": true, - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/eslint/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "extraneous": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/espree": { - "version": "9.6.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", - "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", - "extraneous": true, - "dependencies": { - "acorn": "^8.9.0", - "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^3.4.1" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "license": "BSD-2-Clause", - "peer": true, - "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/esquery": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", - "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", - "extraneous": true, - "dependencies": { - "estraverse": "^5.1.0" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", - "extraneous": true, - "dependencies": { - "estraverse": "^5.2.0" - }, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "extraneous": true, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "extraneous": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/etag": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", - "license": "MIT", - "peer": true, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/event-stream": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/event-stream/-/event-stream-4.0.1.tgz", - "integrity": "sha512-qACXdu/9VHPBzcyhdOWR5/IahhGMf0roTeZJfzz077GwylcDd90yOHLouhmv7GJ5XzPi6ekaQWd8AvPP2nOvpA==", - "peer": true, - "dependencies": { - "duplexer": "^0.1.1", - "from": "^0.1.7", - "map-stream": "0.0.7", - "pause-stream": "^0.0.11", - "split": "^1.0.1", - "stream-combiner": "^0.2.2", - "through": "^2.3.8" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/expand-template": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", - "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==", - "optional": true, - "peer": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/express": { - "version": "4.21.2", - "resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz", - "integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==", - "license": "MIT", - "peer": true, - "dependencies": { - "accepts": "~1.3.8", - "array-flatten": "1.1.1", - "body-parser": "1.20.3", - "content-disposition": "0.5.4", - "content-type": "~1.0.4", - "cookie": "0.7.1", - "cookie-signature": "1.0.6", - "debug": "2.6.9", - "depd": "2.0.0", - "encodeurl": "~2.0.0", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "finalhandler": "1.3.1", - "fresh": "0.5.2", - "http-errors": "2.0.0", - "merge-descriptors": "1.0.3", - "methods": "~1.1.2", - "on-finished": "2.4.1", - "parseurl": "~1.3.3", - "path-to-regexp": "0.1.12", - "proxy-addr": "~2.0.7", - "qs": "6.13.0", - "range-parser": "~1.2.1", - "safe-buffer": "5.2.1", - "send": "0.19.0", - "serve-static": "1.16.2", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "type-is": "~1.6.18", - "utils-merge": "1.0.1", - "vary": "~1.1.2" - }, - "engines": { - "node": ">= 0.10.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/express-session": { - "version": "1.18.1", - "resolved": "https://registry.npmjs.org/express-session/-/express-session-1.18.1.tgz", - "integrity": "sha512-a5mtTqEaZvBCL9A9aqkrtfz+3SMDhOVUnjafjo+s7A9Txkq+SVX2DLvSp1Zrv4uCXa3lMSK3viWnh9Gg07PBUA==", - "license": "MIT", - "peer": true, - "dependencies": { - "cookie": "0.7.2", - "cookie-signature": "1.0.7", - "debug": "2.6.9", - "depd": "~2.0.0", - "on-headers": "~1.0.2", - "parseurl": "~1.3.3", - "safe-buffer": "5.2.1", - "uid-safe": "~2.1.5" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/express-session/node_modules/cookie": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", - "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", - "license": "MIT", - "peer": true, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/express-session/node_modules/cookie-signature": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.7.tgz", - "integrity": "sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA==", - "license": "MIT", - "peer": true - }, - "node_modules/@ngageoint/mage.service/node_modules/express-session/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "peer": true, - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/express-session/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "peer": true - }, - "node_modules/@ngageoint/mage.service/node_modules/express/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "license": "MIT", - "peer": true, - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/express/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "license": "MIT", - "peer": true - }, - "node_modules/@ngageoint/mage.service/node_modules/extend": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", - "extraneous": true - }, - "node_modules/@ngageoint/mage.service/node_modules/external-editor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", - "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", - "optional": true, - "peer": true, - "dependencies": { - "chardet": "^0.7.0", - "iconv-lite": "^0.4.24", - "tmp": "^0.0.33" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/external-editor/node_modules/iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "optional": true, - "peer": true, - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/extsprintf": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", - "integrity": "sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==", - "engines": [ - "node >=0.6.0" - ], - "peer": true - }, - "node_modules/@ngageoint/mage.service/node_modules/eyes": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/eyes/-/eyes-0.1.8.tgz", - "integrity": "sha512-GipyPsXO1anza0AOZdy69Im7hGFCNB7Y/NGjDlZGJ3GJJLtwNSb2vrzYrTYJRrRloVx7pl+bhUaTB8yiccPvFQ==", - "peer": true, - "engines": { - "node": "> 0.1.90" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "extraneous": true - }, - "node_modules/@ngageoint/mage.service/node_modules/fast-diff": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz", - "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==", - "extraneous": true - }, - "node_modules/@ngageoint/mage.service/node_modules/fast-fifo": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/fast-fifo/-/fast-fifo-1.3.2.tgz", - "integrity": "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==", - "extraneous": true, - "license": "MIT" - }, - "node_modules/@ngageoint/mage.service/node_modules/fast-glob": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", - "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", - "extraneous": true, - "dependencies": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.4" - }, - "engines": { - "node": ">=8.6.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/fast-glob/node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "extraneous": true, - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "extraneous": true - }, - "node_modules/@ngageoint/mage.service/node_modules/fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", - "extraneous": true - }, - "node_modules/@ngageoint/mage.service/node_modules/fast-safe-stringify": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz", - "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==", - "peer": true - }, - "node_modules/@ngageoint/mage.service/node_modules/fast-xml-parser": { - "version": "4.2.5", - "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.2.5.tgz", - "integrity": "sha512-B9/wizE4WngqQftFPmdaMYlXoJlJOYxGQOanC77fq9k8+Z0v5dDSVh+3glErdIROP//s/jgb7ZuxKfB8nVyo0g==", - "funding": [ - { - "type": "paypal", - "url": "https://paypal.me/naturalintelligence" - }, - { - "type": "github", - "url": "https://github.com/sponsors/NaturalIntelligence" - } - ], - "optional": true, - "peer": true, - "dependencies": { - "strnum": "^1.0.5" - }, - "bin": { - "fxparser": "src/cli/cli.js" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/fastq": { - "version": "1.17.1", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", - "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", - "extraneous": true, - "dependencies": { - "reusify": "^1.0.4" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/figures": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", - "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", - "optional": true, - "peer": true, - "dependencies": { - "escape-string-regexp": "^1.0.5" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/figures/node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "optional": true, - "peer": true, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/file-entry-cache": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", - "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", - "extraneous": true, - "dependencies": { - "flat-cache": "^3.0.4" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/file-type": { - "version": "16.5.4", - "resolved": "https://registry.npmjs.org/file-type/-/file-type-16.5.4.tgz", - "integrity": "sha512-/yFHK0aGjFEgDJjEKP0pWCplsPFPhwyfwevf/pVxiN0tmE4L9LmwWxWukdJSHdoCli4VgQLehjJtwQBnqmsKcw==", - "peer": true, - "dependencies": { - "readable-web-to-node-stream": "^3.0.0", - "strtok3": "^6.2.4", - "token-types": "^4.1.1" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sindresorhus/file-type?sponsor=1" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/file-uri-to-path": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", - "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", - "optional": true, - "peer": true - }, - "node_modules/@ngageoint/mage.service/node_modules/fill-keys": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/fill-keys/-/fill-keys-1.0.2.tgz", - "integrity": "sha512-tcgI872xXjwFF4xgQmLxi76GnwJG3g/3isB1l4/G5Z4zrbddGpBjqZCO9oEAcB5wX0Hj/5iQB3toxfO7in1hHA==", - "extraneous": true, - "dependencies": { - "is-object": "~1.0.1", - "merge-descriptors": "~1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/fill-range": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", - "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", - "extraneous": true, - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/finalhandler": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz", - "integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==", - "license": "MIT", - "peer": true, - "dependencies": { - "debug": "2.6.9", - "encodeurl": "~2.0.0", - "escape-html": "~1.0.3", - "on-finished": "2.4.1", - "parseurl": "~1.3.3", - "statuses": "2.0.1", - "unpipe": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/finalhandler/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "license": "MIT", - "peer": true, - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/finalhandler/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "license": "MIT", - "peer": true - }, - "node_modules/@ngageoint/mage.service/node_modules/find-cache-dir": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz", - "integrity": "sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==", - "extraneous": true, - "license": "MIT", - "dependencies": { - "commondir": "^1.0.1", - "make-dir": "^3.0.2", - "pkg-dir": "^4.1.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/avajs/find-cache-dir?sponsor=1" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "extraneous": true, - "dependencies": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/flat": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", - "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", - "extraneous": true, - "bin": { - "flat": "cli.js" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/flat-cache": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", - "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", - "extraneous": true, - "dependencies": { - "flatted": "^3.2.9", - "keyv": "^4.5.3", - "rimraf": "^3.0.2" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/flat-cache/node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "deprecated": "Rimraf versions prior to v4 are no longer supported", - "extraneous": true, - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/flatted": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz", - "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==", - "extraneous": true - }, - "node_modules/@ngageoint/mage.service/node_modules/follow-redirects": { - "version": "1.15.9", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", - "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", - "extraneous": true, - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/RubenVerborgh" - } - ], - "license": "MIT", - "engines": { - "node": ">=4.0" - }, - "peerDependenciesMeta": { - "debug": { - "optional": true - } - } - }, - "node_modules/@ngageoint/mage.service/node_modules/for-each": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", - "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", - "extraneous": true, - "dependencies": { - "is-callable": "^1.1.3" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/foreachasync": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/foreachasync/-/foreachasync-3.0.0.tgz", - "integrity": "sha512-J+ler7Ta54FwwNcx6wQRDhTIbNeyDcARMkOcguEqnEdtm0jKvN3Li3PDAb2Du3ubJYEWfYL83XMROXdsXAXycw==", - "peer": true - }, - "node_modules/@ngageoint/mage.service/node_modules/foreground-child": { - "version": "1.5.6", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-1.5.6.tgz", - "integrity": "sha512-3TOY+4TKV0Ml83PXJQY+JFQaHNV38lzQDIzzXYg1kWdBLenGgoZhAs0CKgzI31vi2pWEpQMq/Yi4bpKwCPkw7g==", - "extraneous": true, - "dependencies": { - "cross-spawn": "^4", - "signal-exit": "^3.0.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/foreground-child/node_modules/cross-spawn": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-4.0.2.tgz", - "integrity": "sha512-yAXz/pA1tD8Gtg2S98Ekf/sewp3Lcp3YoFKJ4Hkp5h5yLWnKVTDU0kwjKJ8NDCYcfTLfyGkzTikst+jWypT1iA==", - "extraneous": true, - "dependencies": { - "lru-cache": "^4.0.1", - "which": "^1.2.9" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/foreground-child/node_modules/lru-cache": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", - "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", - "extraneous": true, - "dependencies": { - "pseudomap": "^1.0.2", - "yallist": "^2.1.2" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/foreground-child/node_modules/which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", - "extraneous": true, - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "which": "bin/which" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/forever-agent": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", - "integrity": "sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==", - "extraneous": true, - "engines": { - "node": "*" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/form-data": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", - "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", - "peer": true, - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/formidable": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/formidable/-/formidable-2.1.2.tgz", - "integrity": "sha512-CM3GuJ57US06mlpQ47YcunuUZ9jpm8Vx+P2CGt2j7HpgkKZO/DJYQ0Bobim8G6PFQmK5lOqOOdUXboU+h73A4g==", - "peer": true, - "dependencies": { - "dezalgo": "^1.0.4", - "hexoid": "^1.0.0", - "once": "^1.4.0", - "qs": "^6.11.0" - }, - "funding": { - "url": "https://ko-fi.com/tunnckoCore/commissions" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/forwarded": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", - "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", - "peer": true, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/fresh": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", - "license": "MIT", - "peer": true, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/from": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/from/-/from-0.1.7.tgz", - "integrity": "sha512-twe20eF1OxVxp/ML/kq2p1uc6KvFK/+vs8WjEbeKmV2He22MKm7YF2ANIt+EOqhJ5L3K/SuuPhk0hWQDjOM23g==", - "peer": true - }, - "node_modules/@ngageoint/mage.service/node_modules/fs-constants": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", - "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", - "peer": true - }, - "node_modules/@ngageoint/mage.service/node_modules/fs-extra": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-4.0.3.tgz", - "integrity": "sha512-q6rbdDd1o2mAnQreO7YADIxf/Whx4AHBiRf6d+/cVT8h44ss+lHgxf1FemcqDnQt9X3ct4McHr+JMGlYSsK7Cg==", - "peer": true, - "dependencies": { - "graceful-fs": "^4.1.2", - "jsonfile": "^4.0.0", - "universalify": "^0.1.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "peer": true - }, - "node_modules/@ngageoint/mage.service/node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "extraneous": true, - "hasInstallScript": true, - "license": "MIT", - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/function-bind": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "peer": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/function.prototype.name": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.6.tgz", - "integrity": "sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==", - "extraneous": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "functions-have-names": "^1.2.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/functions-have-names": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", - "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", - "extraneous": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/geojson": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/geojson/-/geojson-0.5.0.tgz", - "integrity": "sha512-/Bx5lEn+qRF4TfQ5aLu6NH+UKtvIv7Lhc487y/c8BdludrCTpiWf9wyI0RTyqg49MFefIAvFDuEi5Dfd/zgNxQ==", - "peer": true, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/geojson-rbush": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/geojson-rbush/-/geojson-rbush-3.2.0.tgz", - "integrity": "sha512-oVltQTXolxvsz1sZnutlSuLDEcQAKYC/uXt9zDzJJ6bu0W+baTI8LZBaTup5afzibEH4N3jlq2p+a152wlBJ7w==", - "peer": true, - "dependencies": { - "@turf/bbox": "*", - "@turf/helpers": "6.x", - "@turf/meta": "6.x", - "@types/geojson": "7946.0.8", - "rbush": "^3.0.1" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/geojson-rbush/node_modules/@types/geojson": { - "version": "7946.0.8", - "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.8.tgz", - "integrity": "sha512-1rkryxURpr6aWP7R786/UQOkJ3PcpQiWkAXBmdWc7ryFWqN6a4xfK7BtjXvFBKO9LjQ+MWQSWxYeZX1OApnArA==", - "peer": true - }, - "node_modules/@ngageoint/mage.service/node_modules/geojson-stream": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/geojson-stream/-/geojson-stream-0.1.0.tgz", - "integrity": "sha512-svSg5fFXPaTiqzEBGXScA+nISaeC9rLvku2PH+wM5LToATUw2bLIrvls43ymnT9Xnp51nBPVyK9m4Af40KpJ7w==", - "peer": true, - "dependencies": { - "JSONStream": "^1.0.0", - "through": "^2.3.4" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/geojson-validation": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/geojson-validation/-/geojson-validation-1.0.2.tgz", - "integrity": "sha512-K5jrJ4wFvORn2pRKeg181LL0QPYuEKn2KHPvfH1m2QtFlAXFLKdseqt0XwBM3ELOY7kNM1fglRQ6ZwUQZ5S00A==", - "peer": true, - "bin": { - "gjv": "bin/gjv" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "extraneous": true, - "engines": { - "node": "6.* || 8.* || >= 10.*" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/get-func-name": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.2.tgz", - "integrity": "sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==", - "extraneous": true, - "engines": { - "node": "*" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/get-intrinsic": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", - "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", - "license": "MIT", - "peer": true, - "dependencies": { - "call-bind-apply-helpers": "^1.0.2", - "es-define-property": "^1.0.1", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.1.1", - "function-bind": "^1.1.2", - "get-proto": "^1.0.1", - "gopd": "^1.2.0", - "has-symbols": "^1.1.0", - "hasown": "^2.0.2", - "math-intrinsics": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/get-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", - "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", - "license": "MIT", - "peer": true, - "dependencies": { - "dunder-proto": "^1.0.1", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/get-stdin": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-6.0.0.tgz", - "integrity": "sha512-jp4tHawyV7+fkkSKyvjuLZswblUtz+SQKzSWnBbii16BuZksJlU1wuBYXY75r+duh/llF1ur6oNwi+2ZzjKZ7g==", - "extraneous": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/get-symbol-description": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.2.tgz", - "integrity": "sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg==", - "extraneous": true, - "dependencies": { - "call-bind": "^1.0.5", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.4" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/getpass": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", - "integrity": "sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==", - "extraneous": true, - "dependencies": { - "assert-plus": "^1.0.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/github-from-package": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", - "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==", - "optional": true, - "peer": true - }, - "node_modules/@ngageoint/mage.service/node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "deprecated": "Glob versions prior to v9 are no longer supported", - "peer": true, - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "extraneous": true, - "dependencies": { - "is-glob": "^4.0.3" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/glob/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "peer": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/glob/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "peer": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/globals": { - "version": "13.24.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", - "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", - "extraneous": true, - "dependencies": { - "type-fest": "^0.20.2" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/globalthis": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", - "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", - "extraneous": true, - "dependencies": { - "define-properties": "^1.2.1", - "gopd": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/globby": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", - "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", - "extraneous": true, - "license": "MIT", - "dependencies": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.2.9", - "ignore": "^5.2.0", - "merge2": "^1.4.1", - "slash": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/gopd": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", - "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", - "license": "MIT", - "peer": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/graceful-fs": { - "version": "4.2.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", - "peer": true - }, - "node_modules/@ngageoint/mage.service/node_modules/graphemer": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", - "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", - "extraneous": true - }, - "node_modules/@ngageoint/mage.service/node_modules/har-schema": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", - "integrity": "sha512-Oqluz6zhGX8cyRaTQlFMPw80bSJVG2x/cFb8ZPhUILGgHka9SsokCCOQgpveePerqidZOrT14ipqfJb7ILcW5Q==", - "extraneous": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/har-validator": { - "version": "5.1.5", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz", - "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", - "deprecated": "this library is no longer supported", - "extraneous": true, - "dependencies": { - "ajv": "^6.12.3", - "har-schema": "^2.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/has-bigints": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", - "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", - "extraneous": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/has-color": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/has-color/-/has-color-0.1.7.tgz", - "integrity": "sha512-kaNz5OTAYYmt646Hkqw50/qyxP2vFnTVu5AQ1Zmk22Kk5+4Qx6BpO8+u7IKsML5fOsFk0ZT0AcCJNYwcvaLBvw==", - "peer": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "optional": true, - "peer": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/has-property-descriptors": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", - "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", - "peer": true, - "dependencies": { - "es-define-property": "^1.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/has-proto": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz", - "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==", - "extraneous": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/has-symbols": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", - "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", - "license": "MIT", - "peer": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/has-tostringtag": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", - "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", - "peer": true, - "dependencies": { - "has-symbols": "^1.0.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/hasha": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/hasha/-/hasha-3.0.0.tgz", - "integrity": "sha512-w0Kz8lJFBoyaurBiNrIvxPqr/gJ6fOfSkpAPOepN3oECqGJag37xPbOv57izi/KP8auHgNYxn5fXtAb+1LsJ6w==", - "extraneous": true, - "dependencies": { - "is-stream": "^1.0.1" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/hasown": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", - "peer": true, - "dependencies": { - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/he": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", - "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", - "extraneous": true, - "bin": { - "he": "bin/he" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/hexoid": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/hexoid/-/hexoid-1.0.0.tgz", - "integrity": "sha512-QFLV0taWQOZtvIRIAdBChesmogZrtuXvVWsFHZTk2SU+anspqZ2vMnoLg7IE1+Uk16N19APic1BuF8bC8c2m5g==", - "peer": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/hosted-git-info": { - "version": "2.8.9", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", - "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", - "extraneous": true - }, - "node_modules/@ngageoint/mage.service/node_modules/html-escaper": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", - "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", - "extraneous": true - }, - "node_modules/@ngageoint/mage.service/node_modules/http-errors": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", - "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", - "license": "MIT", - "peer": true, - "dependencies": { - "depd": "2.0.0", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "toidentifier": "1.0.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/http-signature": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", - "integrity": "sha512-CAbnr6Rz4CYQkLYUtSNXxQPUH2gK8f3iWexVlsnMeD+GjlsQ0Xsy1cOX+mN3dtxYomRy21CiOzU8Uhw6OwncEQ==", - "extraneous": true, - "dependencies": { - "assert-plus": "^1.0.0", - "jsprim": "^1.2.2", - "sshpk": "^1.7.0" - }, - "engines": { - "node": ">=0.8", - "npm": ">=1.3.7" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/https-proxy-agent": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", - "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", - "extraneous": true, - "license": "MIT", - "dependencies": { - "agent-base": "^7.1.2", - "debug": "4" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/iconv-lite": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", - "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", - "extraneous": true, - "license": "MIT", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/ieee754": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "peer": true - }, - "node_modules/@ngageoint/mage.service/node_modules/ignore": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", - "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==", - "extraneous": true, - "engines": { - "node": ">= 4" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/image-size": { - "version": "0.8.3", - "resolved": "https://registry.npmjs.org/image-size/-/image-size-0.8.3.tgz", - "integrity": "sha512-SMtq1AJ+aqHB45c3FsB4ERK0UCiA2d3H1uq8s+8T0Pf8A3W4teyBQyaFaktH6xvZqh+npwlKU7i4fJo0r7TYTg==", - "peer": true, - "dependencies": { - "queue": "6.0.1" - }, - "bin": { - "image-size": "bin/image-size.js" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/immediate": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", - "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==", - "extraneous": true - }, - "node_modules/@ngageoint/mage.service/node_modules/import-fresh": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", - "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", - "extraneous": true, - "dependencies": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", - "extraneous": true, - "engines": { - "node": ">=0.8.19" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/indent-string": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-5.0.0.tgz", - "integrity": "sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg==", - "extraneous": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", - "peer": true, - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "peer": true - }, - "node_modules/@ngageoint/mage.service/node_modules/ini": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", - "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", - "optional": true, - "peer": true - }, - "node_modules/@ngageoint/mage.service/node_modules/inquirer": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.0.0.tgz", - "integrity": "sha512-ON8pEJPPCdyjxj+cxsYRe6XfCJepTxANdNnTebsTuQgXpRyZRRT9t4dJwjRubgmvn20CLSEnozRUayXyM9VTXA==", - "optional": true, - "peer": true, - "dependencies": { - "ansi-escapes": "^4.2.1", - "chalk": "^4.1.0", - "cli-cursor": "^3.1.0", - "cli-width": "^3.0.0", - "external-editor": "^3.0.3", - "figures": "^3.0.0", - "lodash": "^4.17.21", - "mute-stream": "0.0.8", - "run-async": "^2.4.0", - "rxjs": "^6.6.6", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0", - "through": "^2.3.6" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/internal-slot": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.7.tgz", - "integrity": "sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g==", - "extraneous": true, - "dependencies": { - "es-errors": "^1.3.0", - "hasown": "^2.0.0", - "side-channel": "^1.0.4" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/ip-address": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-9.0.5.tgz", - "integrity": "sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==", - "peer": true, - "dependencies": { - "jsbn": "1.1.0", - "sprintf-js": "^1.1.3" - }, - "engines": { - "node": ">= 12" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/ip-address/node_modules/sprintf-js": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz", - "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==", - "peer": true - }, - "node_modules/@ngageoint/mage.service/node_modules/ipaddr.js": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", - "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", - "peer": true, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/is-arguments": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", - "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==", - "extraneous": true, - "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/is-array-buffer": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.4.tgz", - "integrity": "sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw==", - "extraneous": true, - "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.2.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", - "extraneous": true - }, - "node_modules/@ngageoint/mage.service/node_modules/is-bigint": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", - "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", - "extraneous": true, - "dependencies": { - "has-bigints": "^1.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "extraneous": true, - "license": "MIT", - "dependencies": { - "binary-extensions": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/is-boolean-object": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", - "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", - "extraneous": true, - "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/is-callable": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", - "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", - "extraneous": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/is-core-module": { - "version": "2.14.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.14.0.tgz", - "integrity": "sha512-a5dFJih5ZLYlRtDc0dZWP7RiKr6xIKzmn/oAYCDvdLThadVgyJwlaoQPmRtMSpz+rk0OGAgIu+TcM9HUF0fk1A==", - "peer": true, - "dependencies": { - "hasown": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/is-data-view": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.1.tgz", - "integrity": "sha512-AHkaJrsUVW6wq6JS8y3JnM/GJF/9cf+k20+iDzlSaJrinEo5+7vRiteOSwBhHRiAyQATN1AmY4hwzxJKPmYf+w==", - "extraneous": true, - "dependencies": { - "is-typed-array": "^1.1.13" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/is-date-object": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", - "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", - "extraneous": true, - "dependencies": { - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/is-expression": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/is-expression/-/is-expression-4.0.0.tgz", - "integrity": "sha512-zMIXX63sxzG3XrkHkrAPvm/OVZVSCPNkwMHU8oTX7/U3AL78I0QXCEICXUM13BIa8TYGZ68PiTKfQz3yaTNr4A==", - "peer": true, - "dependencies": { - "acorn": "^7.1.1", - "object-assign": "^4.1.1" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/is-expression/node_modules/acorn": { - "version": "7.4.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", - "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", - "peer": true, - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "extraneous": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "optional": true, - "peer": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "extraneous": true, - "dependencies": { - "is-extglob": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/is-map": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz", - "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==", - "extraneous": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/is-negative-zero": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz", - "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==", - "extraneous": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "extraneous": true, - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/is-number-object": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", - "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", - "extraneous": true, - "dependencies": { - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/is-object": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-object/-/is-object-1.0.2.tgz", - "integrity": "sha512-2rRIahhZr2UWb45fIOuvZGpFtz0TyOZLf32KxBbSoUCeZR495zCKlWUKKUByk3geS2eAs7ZAABt0Y/Rx0GiQGA==", - "extraneous": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/is-path-inside": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", - "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", - "extraneous": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/is-plain-obj": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", - "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", - "extraneous": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/is-promise": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.2.2.tgz", - "integrity": "sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ==", - "peer": true - }, - "node_modules/@ngageoint/mage.service/node_modules/is-regex": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", - "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", - "peer": true, - "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/is-set": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz", - "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==", - "extraneous": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/is-shared-array-buffer": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.3.tgz", - "integrity": "sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg==", - "extraneous": true, - "dependencies": { - "call-bind": "^1.0.7" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/is-stream": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", - "integrity": "sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==", - "extraneous": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/is-string": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", - "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", - "extraneous": true, - "dependencies": { - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/is-symbol": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", - "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", - "extraneous": true, - "dependencies": { - "has-symbols": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/is-typed-array": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.13.tgz", - "integrity": "sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==", - "extraneous": true, - "dependencies": { - "which-typed-array": "^1.1.14" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==", - "extraneous": true - }, - "node_modules/@ngageoint/mage.service/node_modules/is-unicode-supported": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", - "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", - "extraneous": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/is-weakmap": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz", - "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==", - "extraneous": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/is-weakref": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", - "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", - "extraneous": true, - "dependencies": { - "call-bind": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/is-weakset": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.3.tgz", - "integrity": "sha512-LvIm3/KWzS9oRFHugab7d+M/GcBXuXX5xZkzPmN+NxihdQlZUQ4dWuSV1xR/sq6upL1TJEDrfBgRepHFdBtSNQ==", - "extraneous": true, - "dependencies": { - "call-bind": "^1.0.7", - "get-intrinsic": "^1.2.4" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/isarray": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", - "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", - "extraneous": true - }, - "node_modules/@ngageoint/mage.service/node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "extraneous": true - }, - "node_modules/@ngageoint/mage.service/node_modules/isstream": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", - "integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==", - "peer": true - }, - "node_modules/@ngageoint/mage.service/node_modules/istanbul-lib-coverage": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.5.tgz", - "integrity": "sha512-8aXznuEPCJvGnMSRft4udDRDtb1V3pkQkMMI5LI+6HuQz5oQ4J2UFn1H82raA3qJtyOLkkwVqICBQkjnGtn5mA==", - "extraneous": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/istanbul-lib-hook": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/istanbul-lib-hook/-/istanbul-lib-hook-2.0.7.tgz", - "integrity": "sha512-vrRztU9VRRFDyC+aklfLoeXyNdTfga2EI3udDGn4cZ6fpSXpHLV9X6CHvfoMCPtggg8zvDDmC4b9xfu0z6/llA==", - "extraneous": true, - "dependencies": { - "append-transform": "^1.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/istanbul-lib-instrument": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-3.3.0.tgz", - "integrity": "sha512-5nnIN4vo5xQZHdXno/YDXJ0G+I3dAm4XgzfSVTPLQpj/zAV2dV6Juy0yaf10/zrJOJeHoN3fraFe+XRq2bFVZA==", - "extraneous": true, - "dependencies": { - "@babel/generator": "^7.4.0", - "@babel/parser": "^7.4.3", - "@babel/template": "^7.4.0", - "@babel/traverse": "^7.4.3", - "@babel/types": "^7.4.0", - "istanbul-lib-coverage": "^2.0.5", - "semver": "^6.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/istanbul-lib-instrument/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "extraneous": true, - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/istanbul-lib-report": { - "version": "2.0.8", - "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-2.0.8.tgz", - "integrity": "sha512-fHBeG573EIihhAblwgxrSenp0Dby6tJMFR/HvlerBsrCTD5bkUuoNtn3gVh29ZCS824cGGBPn7Sg7cNk+2xUsQ==", - "extraneous": true, - "dependencies": { - "istanbul-lib-coverage": "^2.0.5", - "make-dir": "^2.1.0", - "supports-color": "^6.1.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/istanbul-lib-report/node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "extraneous": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/istanbul-lib-report/node_modules/make-dir": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", - "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", - "extraneous": true, - "dependencies": { - "pify": "^4.0.1", - "semver": "^5.6.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/istanbul-lib-report/node_modules/pify": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", - "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", - "extraneous": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/istanbul-lib-report/node_modules/semver": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", - "extraneous": true, - "bin": { - "semver": "bin/semver" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/istanbul-lib-report/node_modules/supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "extraneous": true, - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/istanbul-lib-source-maps": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-3.0.6.tgz", - "integrity": "sha512-R47KzMtDJH6X4/YW9XTx+jrLnZnscW4VpNN+1PViSYTejLVPWv7oov+Duf8YQSPyVRUvueQqz1TcsC6mooZTXw==", - "extraneous": true, - "dependencies": { - "debug": "^4.1.1", - "istanbul-lib-coverage": "^2.0.5", - "make-dir": "^2.1.0", - "rimraf": "^2.6.3", - "source-map": "^0.6.1" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/istanbul-lib-source-maps/node_modules/make-dir": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", - "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", - "extraneous": true, - "dependencies": { - "pify": "^4.0.1", - "semver": "^5.6.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/istanbul-lib-source-maps/node_modules/pify": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", - "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", - "extraneous": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/istanbul-lib-source-maps/node_modules/rimraf": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", - "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", - "deprecated": "Rimraf versions prior to v4 are no longer supported", - "extraneous": true, - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/istanbul-lib-source-maps/node_modules/semver": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", - "extraneous": true, - "bin": { - "semver": "bin/semver" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/istanbul-reports": { - "version": "2.2.7", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-2.2.7.tgz", - "integrity": "sha512-uu1F/L1o5Y6LzPVSVZXNOoD/KXpJue9aeLRd0sM9uMXfZvzomB0WxVamWb5ue8kA2vVWEmW7EG+A5n3f1kqHKg==", - "extraneous": true, - "dependencies": { - "html-escaper": "^2.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/jackspeak": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.0.tgz", - "integrity": "sha512-JVYhQnN59LVPFCEcVa2C3CrEKYacvjRfqIQl+h8oi91aLYQVWRYbxjPcv1bUiUy/kLmQaANrYfNMCO3kuEDHfw==", - "extraneous": true, - "dependencies": { - "@isaacs/cliui": "^8.0.2" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - }, - "optionalDependencies": { - "@pkgjs/parseargs": "^0.11.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/js-stringify": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/js-stringify/-/js-stringify-1.0.2.tgz", - "integrity": "sha512-rtS5ATOo2Q5k1G+DADISilDA6lv79zIiwFd6CcjuIxGKLFm5C+RLImRscVap9k55i+MOZwgliw+NejvkLuGD5g==", - "peer": true - }, - "node_modules/@ngageoint/mage.service/node_modules/js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "extraneous": true - }, - "node_modules/@ngageoint/mage.service/node_modules/js-yaml": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.0.0.tgz", - "integrity": "sha512-pqon0s+4ScYUvX30wxQi3PogGFAlUyH0awepWvwkj4jD4v+ova3RiYw8bmA6x2rDrEaj8i/oWKoRxpVNW+Re8Q==", - "license": "MIT", - "peer": true, - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/js-yaml/node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "license": "Python-2.0", - "peer": true - }, - "node_modules/@ngageoint/mage.service/node_modules/jsbn": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-1.1.0.tgz", - "integrity": "sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==", - "peer": true - }, - "node_modules/@ngageoint/mage.service/node_modules/jsesc": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", - "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", - "extraneous": true, - "bin": { - "jsesc": "bin/jsesc" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/json-buffer": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", - "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", - "extraneous": true - }, - "node_modules/@ngageoint/mage.service/node_modules/json-parse-better-errors": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", - "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", - "extraneous": true - }, - "node_modules/@ngageoint/mage.service/node_modules/json-schema": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", - "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==", - "extraneous": true - }, - "node_modules/@ngageoint/mage.service/node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "extraneous": true - }, - "node_modules/@ngageoint/mage.service/node_modules/json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", - "extraneous": true - }, - "node_modules/@ngageoint/mage.service/node_modules/json-stringify-safe": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==", - "extraneous": true - }, - "node_modules/@ngageoint/mage.service/node_modules/json2csv": { - "version": "4.5.4", - "resolved": "https://registry.npmjs.org/json2csv/-/json2csv-4.5.4.tgz", - "integrity": "sha512-YxBhY4Lmn8IvVZ36nqg5omxneLy9JlorkqW1j/EDCeqvmi+CQ4uM+wsvXlcIqvGDewIPXMC/O/oF8DX9EH5aoA==", - "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.", - "license": "MIT", - "peer": true, - "dependencies": { - "commander": "^2.15.1", - "jsonparse": "^1.3.1", - "lodash.get": "^4.4.2" - }, - "bin": { - "json2csv": "bin/json2csv.js" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/json2csv/node_modules/commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", - "peer": true - }, - "node_modules/@ngageoint/mage.service/node_modules/jsonfile": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", - "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", - "peer": true, - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/jsonparse": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", - "integrity": "sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==", - "engines": [ - "node >= 0.2.0" - ], - "peer": true - }, - "node_modules/@ngageoint/mage.service/node_modules/JSONStream": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz", - "integrity": "sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==", - "peer": true, - "dependencies": { - "jsonparse": "^1.2.0", - "through": ">=2.2.7 <3" - }, - "bin": { - "JSONStream": "bin.js" - }, - "engines": { - "node": "*" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/jsonwebtoken": { - "version": "9.0.2", - "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz", - "integrity": "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==", - "peer": true, - "dependencies": { - "jws": "^3.2.2", - "lodash.includes": "^4.3.0", - "lodash.isboolean": "^3.0.3", - "lodash.isinteger": "^4.0.4", - "lodash.isnumber": "^3.0.3", - "lodash.isplainobject": "^4.0.6", - "lodash.isstring": "^4.0.1", - "lodash.once": "^4.0.0", - "ms": "^2.1.1", - "semver": "^7.5.4" - }, - "engines": { - "node": ">=12", - "npm": ">=6" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/jsprim": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.2.tgz", - "integrity": "sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==", - "extraneous": true, - "dependencies": { - "assert-plus": "1.0.0", - "extsprintf": "1.3.0", - "json-schema": "0.4.0", - "verror": "1.10.0" - }, - "engines": { - "node": ">=0.6.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/jsprim/node_modules/verror": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", - "integrity": "sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==", - "engines": [ - "node >=0.6.0" - ], - "extraneous": true, - "dependencies": { - "assert-plus": "^1.0.0", - "core-util-is": "1.0.2", - "extsprintf": "^1.2.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/jstransformer": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/jstransformer/-/jstransformer-1.0.0.tgz", - "integrity": "sha512-C9YK3Rf8q6VAPDCCU9fnqo3mAfOH6vUGnMcP4AQAYIEpWtfGLpwOTmZ+igtdK5y+VvI2n3CyYSzy4Qh34eq24A==", - "peer": true, - "dependencies": { - "is-promise": "^2.0.0", - "promise": "^7.0.1" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/jszip": { - "version": "3.10.1", - "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz", - "integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==", - "extraneous": true, - "license": "(MIT OR GPL-3.0-or-later)", - "dependencies": { - "lie": "~3.3.0", - "pako": "~1.0.2", - "readable-stream": "~2.3.6", - "setimmediate": "^1.0.5" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/jszip/node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", - "extraneous": true - }, - "node_modules/@ngageoint/mage.service/node_modules/jszip/node_modules/readable-stream": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", - "extraneous": true, - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/jszip/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "extraneous": true - }, - "node_modules/@ngageoint/mage.service/node_modules/jszip/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "extraneous": true, - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/junk": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/junk/-/junk-4.0.1.tgz", - "integrity": "sha512-Qush0uP+G8ZScpGMZvHUiRfI0YBWuB3gVBYlI0v0vvOJt5FLicco+IkP0a50LqTTQhmts/m6tP5SWE+USyIvcQ==", - "extraneous": true, - "engines": { - "node": ">=12.20" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/just-extend": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/just-extend/-/just-extend-4.2.1.tgz", - "integrity": "sha512-g3UB796vUFIY90VIv/WX3L2c8CS2MdWUww3CNrYmqza1Fg0DURc2K/O4YrnklBdQarSJ/y8JnJYDGc+1iumQjg==", - "extraneous": true - }, - "node_modules/@ngageoint/mage.service/node_modules/jwa": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", - "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", - "peer": true, - "dependencies": { - "buffer-equal-constant-time": "1.0.1", - "ecdsa-sig-formatter": "1.0.11", - "safe-buffer": "^5.0.1" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/jws": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", - "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", - "peer": true, - "dependencies": { - "jwa": "^1.4.1", - "safe-buffer": "^5.0.1" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/kareem": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/kareem/-/kareem-2.5.1.tgz", - "integrity": "sha512-7jFxRVm+jD+rkq3kY0iZDJfsO2/t4BBPeEb2qKn2lR/9KhuksYk5hxzfRYWMPV8P/x2d0kHD306YyWLzjjH+uA==", - "peer": true, - "engines": { - "node": ">=12.0.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/keyv": { - "version": "4.5.4", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", - "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", - "extraneous": true, - "dependencies": { - "json-buffer": "3.0.1" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/lazystream": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.1.tgz", - "integrity": "sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==", - "peer": true, - "dependencies": { - "readable-stream": "^2.0.5" - }, - "engines": { - "node": ">= 0.6.3" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/lazystream/node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", - "peer": true - }, - "node_modules/@ngageoint/mage.service/node_modules/lazystream/node_modules/readable-stream": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", - "peer": true, - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/lazystream/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "peer": true - }, - "node_modules/@ngageoint/mage.service/node_modules/lazystream/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "peer": true, - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/lcov-parse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/lcov-parse/-/lcov-parse-1.0.0.tgz", - "integrity": "sha512-aprLII/vPzuQvYZnDRU78Fns9I2Ag3gi4Ipga/hxnVMCZC8DnR2nI7XBqrPoywGfxqIx/DgarGvDJZAD3YBTgQ==", - "extraneous": true, - "license": "BSD-3-Clause", - "bin": { - "lcov-parse": "bin/cli.js" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/ldap-filter": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/ldap-filter/-/ldap-filter-0.3.3.tgz", - "integrity": "sha512-/tFkx5WIn4HuO+6w9lsfxq4FN3O+fDZeO9Mek8dCD8rTUpqzRa766BOBO7BcGkn3X86m5+cBm1/2S/Shzz7gMg==", - "peer": true, - "dependencies": { - "assert-plus": "^1.0.0" - }, - "engines": { - "node": ">=0.8" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/ldapauth-fork": { - "version": "5.0.5", - "resolved": "https://registry.npmjs.org/ldapauth-fork/-/ldapauth-fork-5.0.5.tgz", - "integrity": "sha512-LWUk76+V4AOZbny/3HIPQtGPWZyA3SW2tRhsWIBi9imP22WJktKLHV1ofd8Jo/wY7Ve6vAT7FCI5mEn3blZTjw==", - "peer": true, - "dependencies": { - "@types/ldapjs": "^2.2.2", - "bcryptjs": "^2.4.0", - "ldapjs": "^2.2.1", - "lru-cache": "^7.10.1" - }, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/ldapjs": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/ldapjs/-/ldapjs-2.3.3.tgz", - "integrity": "sha512-75QiiLJV/PQqtpH+HGls44dXweviFwQ6SiIK27EqzKQ5jU/7UFrl2E5nLdQ3IYRBzJ/AVFJI66u0MZ0uofKYwg==", - "deprecated": "This package has been decomissioned. See https://github.com/ldapjs/node-ldapjs/blob/8ffd0bc9c149088a10ec4c1ec6a18450f76ad05d/README.md", - "peer": true, - "dependencies": { - "abstract-logging": "^2.0.0", - "asn1": "^0.2.4", - "assert-plus": "^1.0.0", - "backoff": "^2.5.0", - "ldap-filter": "^0.3.3", - "once": "^1.4.0", - "vasync": "^2.2.0", - "verror": "^1.8.1" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/levn": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", - "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", - "extraneous": true, - "dependencies": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/lie": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz", - "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==", - "extraneous": true, - "dependencies": { - "immediate": "~3.0.5" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/load-json-file": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", - "integrity": "sha512-Kx8hMakjX03tiGTLAIdJ+lL0htKnXjEZN6hk/tozf/WOuYGdZBJrZ+rCJRbVCugsjB3jMLn9746NsQIf5VjBMw==", - "extraneous": true, - "dependencies": { - "graceful-fs": "^4.1.2", - "parse-json": "^4.0.0", - "pify": "^3.0.0", - "strip-bom": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", - "extraneous": true, - "dependencies": { - "p-locate": "^5.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "peer": true - }, - "node_modules/@ngageoint/mage.service/node_modules/lodash.defaults": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", - "integrity": "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==", - "peer": true - }, - "node_modules/@ngageoint/mage.service/node_modules/lodash.difference": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.difference/-/lodash.difference-4.5.0.tgz", - "integrity": "sha512-dS2j+W26TQ7taQBGN8Lbbq04ssV3emRw4NY58WErlTO29pIqS0HmoT5aJ9+TUQ1N3G+JOZSji4eugsWwGp9yPA==", - "peer": true - }, - "node_modules/@ngageoint/mage.service/node_modules/lodash.flatten": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz", - "integrity": "sha512-C5N2Z3DgnnKr0LOpv/hKCgKdb7ZZwafIrsesve6lmzvZIRZRGaZ/l6Q8+2W7NaT+ZwO3fFlSCzCzrDCFdJfZ4g==", - "peer": true - }, - "node_modules/@ngageoint/mage.service/node_modules/lodash.flattendeep": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz", - "integrity": "sha512-uHaJFihxmJcEX3kT4I23ABqKKalJ/zDrDg0lsFtc1h+3uw49SIJ5beyhx5ExVRti3AvKoOJngIj7xz3oylPdWQ==", - "extraneous": true - }, - "node_modules/@ngageoint/mage.service/node_modules/lodash.get": { - "version": "4.4.2", - "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", - "integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==", - "peer": true - }, - "node_modules/@ngageoint/mage.service/node_modules/lodash.includes": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", - "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==", - "peer": true - }, - "node_modules/@ngageoint/mage.service/node_modules/lodash.isboolean": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", - "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==", - "peer": true - }, - "node_modules/@ngageoint/mage.service/node_modules/lodash.isinteger": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", - "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==", - "peer": true - }, - "node_modules/@ngageoint/mage.service/node_modules/lodash.isnumber": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", - "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==", - "peer": true - }, - "node_modules/@ngageoint/mage.service/node_modules/lodash.isplainobject": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", - "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", - "peer": true - }, - "node_modules/@ngageoint/mage.service/node_modules/lodash.isstring": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", - "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==", - "peer": true - }, - "node_modules/@ngageoint/mage.service/node_modules/lodash.merge": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "extraneous": true - }, - "node_modules/@ngageoint/mage.service/node_modules/lodash.once": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", - "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==", - "peer": true - }, - "node_modules/@ngageoint/mage.service/node_modules/lodash.union": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/lodash.union/-/lodash.union-4.6.0.tgz", - "integrity": "sha512-c4pB2CdGrGdjMKYLA+XiRDO7Y0PRQbm/Gzg8qMj+QH+pFVAoTp5sBpO0odL3FjoPCGjK96p6qsP+yQoiLoOBcw==", - "peer": true - }, - "node_modules/@ngageoint/mage.service/node_modules/log-driver": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/log-driver/-/log-driver-1.2.7.tgz", - "integrity": "sha512-U7KCmLdqsGHBLeWqYlFA0V0Sl6P08EE1ZrmA9cxjUE0WVqT9qnyVDPz1kzpFEP0jdJuFnasWIfSd7fsaNXkpbg==", - "extraneous": true, - "engines": { - "node": ">=0.8.6" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/log-symbols": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", - "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", - "extraneous": true, - "dependencies": { - "chalk": "^4.1.0", - "is-unicode-supported": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/lolex": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/lolex/-/lolex-4.2.0.tgz", - "integrity": "sha512-gKO5uExCXvSm6zbF562EvM+rd1kQDnB9AZBbiQVzf1ZmdDpxUSvpnAaVOP83N/31mRK8Ml8/VE8DMvsAZQ+7wg==", - "extraneous": true - }, - "node_modules/@ngageoint/mage.service/node_modules/loupe": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.7.tgz", - "integrity": "sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==", - "extraneous": true, - "dependencies": { - "get-func-name": "^2.0.1" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/lru-cache": { - "version": "7.18.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", - "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", - "peer": true, - "engines": { - "node": ">=12" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/make-dir": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", - "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", - "extraneous": true, - "license": "MIT", - "dependencies": { - "semver": "^6.0.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/make-dir/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "extraneous": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/map-stream": { - "version": "0.0.7", - "resolved": "https://registry.npmjs.org/map-stream/-/map-stream-0.0.7.tgz", - "integrity": "sha512-C0X0KQmGm3N2ftbTGBhSyuydQ+vV1LC3f3zPvT3RXHXNZrvfPZcoXp/N5DOa8vedX/rTMm2CjTtivFg2STJMRQ==", - "peer": true - }, - "node_modules/@ngageoint/mage.service/node_modules/math-intrinsics": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", - "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", - "license": "MIT", - "peer": true, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/media-typer": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", - "peer": true, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/memory-pager": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz", - "integrity": "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==", - "optional": true, - "peer": true - }, - "node_modules/@ngageoint/mage.service/node_modules/memorystream": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz", - "integrity": "sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw==", - "extraneous": true, - "engines": { - "node": ">= 0.10.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/meow": { - "version": "12.1.1", - "resolved": "https://registry.npmjs.org/meow/-/meow-12.1.1.tgz", - "integrity": "sha512-BhXM0Au22RwUneMPwSCnyhTOizdWoIEPU9sp0Aqa1PnDMR5Wv2FGXYDjuzJEIX+Eo2Rb8xuYe5jrnm5QowQFkw==", - "extraneous": true, - "engines": { - "node": ">=16.10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/merge-descriptors": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", - "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", - "license": "MIT", - "peer": true, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/merge-source-map": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/merge-source-map/-/merge-source-map-1.1.0.tgz", - "integrity": "sha512-Qkcp7P2ygktpMPh2mCQZaf3jhN6D3Z/qVZHSdWvQ+2Ef5HgRAPBO57A77+ENm0CPx2+1Ce/MYKi3ymqdfuqibw==", - "extraneous": true, - "dependencies": { - "source-map": "^0.6.1" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "extraneous": true, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/methods": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", - "peer": true, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/mgrs": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/mgrs/-/mgrs-1.0.0.tgz", - "integrity": "sha512-awNbTOqCxK1DBGjalK3xqWIstBZgN6fxsMSiXLs9/spqWkF2pAhb2rrYCFSsr1/tT7PhcDGjZndG8SWYn0byYA==", - "peer": true - }, - "node_modules/@ngageoint/mage.service/node_modules/micromatch": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.7.tgz", - "integrity": "sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q==", - "extraneous": true, - "dependencies": { - "braces": "^3.0.3", - "picomatch": "^2.3.1" - }, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", - "license": "MIT", - "peer": true, - "bin": { - "mime": "cli.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "peer": true, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "peer": true, - "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "optional": true, - "peer": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/mimic-response": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", - "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", - "optional": true, - "peer": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/minimatch": { - "version": "9.0.3", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", - "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", - "extraneous": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/minimist": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", - "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", - "peer": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/minipass": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", - "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", - "extraneous": true, - "engines": { - "node": ">=16 || 14 >=14.17" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/mkdirp": { - "version": "0.5.6", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", - "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", - "peer": true, - "dependencies": { - "minimist": "^1.2.6" - }, - "bin": { - "mkdirp": "bin/cmd.js" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/mkdirp-classic": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", - "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", - "optional": true, - "peer": true - }, - "node_modules/@ngageoint/mage.service/node_modules/mocha": { - "version": "10.8.2", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.8.2.tgz", - "integrity": "sha512-VZlYo/WE8t1tstuRmqgeyBgCbJc/lEdopaa+axcKzTBJ+UIdlAB9XnmvTCAH4pwR4ElNInaedhEBmZD8iCSVEg==", - "extraneous": true, - "license": "MIT", - "dependencies": { - "ansi-colors": "^4.1.3", - "browser-stdout": "^1.3.1", - "chokidar": "^3.5.3", - "debug": "^4.3.5", - "diff": "^5.2.0", - "escape-string-regexp": "^4.0.0", - "find-up": "^5.0.0", - "glob": "^8.1.0", - "he": "^1.2.0", - "js-yaml": "^4.1.0", - "log-symbols": "^4.1.0", - "minimatch": "^5.1.6", - "ms": "^2.1.3", - "serialize-javascript": "^6.0.2", - "strip-json-comments": "^3.1.1", - "supports-color": "^8.1.1", - "workerpool": "^6.5.1", - "yargs": "^16.2.0", - "yargs-parser": "^20.2.9", - "yargs-unparser": "^2.0.0" - }, - "bin": { - "_mocha": "bin/_mocha", - "mocha": "bin/mocha.js" - }, - "engines": { - "node": ">= 14.0.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/mocha/node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "extraneous": true - }, - "node_modules/@ngageoint/mage.service/node_modules/mocha/node_modules/glob": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", - "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", - "deprecated": "Glob versions prior to v9 are no longer supported", - "extraneous": true, - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^5.0.1", - "once": "^1.3.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/mocha/node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "extraneous": true, - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/mocha/node_modules/minimatch": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", - "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", - "extraneous": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/mocha/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "extraneous": true - }, - "node_modules/@ngageoint/mage.service/node_modules/mocha/node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "extraneous": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/mock-fs": { - "version": "4.14.0", - "resolved": "https://registry.npmjs.org/mock-fs/-/mock-fs-4.14.0.tgz", - "integrity": "sha512-qYvlv/exQ4+svI3UOvPUpLDF0OMX5euvUH0Ny4N5QyRyhNdgAgUrVH3iUINSzEPLvx0kbo/Bp28GJKIqvE7URw==", - "extraneous": true, - "license": "MIT" - }, - "node_modules/@ngageoint/mage.service/node_modules/module-not-found-error": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/module-not-found-error/-/module-not-found-error-1.0.1.tgz", - "integrity": "sha512-pEk4ECWQXV6z2zjhRZUongnLJNUeGQJ3w6OQ5ctGwD+i5o93qjRQUk2Rt6VdNeu3sEP0AB4LcfvdebpxBRVr4g==", - "extraneous": true - }, - "node_modules/@ngageoint/mage.service/node_modules/moment": { - "version": "2.30.1", - "resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz", - "integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==", - "license": "MIT", - "peer": true, - "engines": { - "node": "*" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/mongodb": { - "version": "4.17.2", - "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-4.17.2.tgz", - "integrity": "sha512-mLV7SEiov2LHleRJPMPrK2PMyhXFZt2UQLC4VD4pnth3jMjYKHhtqfwwkkvS/NXuo/Fp3vbhaNcXrIDaLRb9Tg==", - "peer": true, - "dependencies": { - "bson": "^4.7.2", - "mongodb-connection-string-url": "^2.6.0", - "socks": "^2.7.1" - }, - "engines": { - "node": ">=12.9.0" - }, - "optionalDependencies": { - "@aws-sdk/credential-providers": "^3.186.0", - "@mongodb-js/saslprep": "^1.1.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/mongodb-connection-string-url": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-2.6.0.tgz", - "integrity": "sha512-WvTZlI9ab0QYtTYnuMLgobULWhokRjtC7db9LtcVfJ+Hsnyr5eo6ZtNAt3Ly24XZScGMelOcGtm7lSn0332tPQ==", - "peer": true, - "dependencies": { - "@types/whatwg-url": "^8.2.1", - "whatwg-url": "^11.0.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/mongodb-memory-server": { - "version": "9.5.0", - "resolved": "https://registry.npmjs.org/mongodb-memory-server/-/mongodb-memory-server-9.5.0.tgz", - "integrity": "sha512-In3zRT40cLlVtpy7FK6b96Lby6JBAdXj8Kf9YrH4p1Aa2X4ptojq7SmiRR3x47Lo0/UCXXIwhJpkdbYY8kRZAw==", - "extraneous": true, - "hasInstallScript": true, - "license": "MIT", - "dependencies": { - "mongodb-memory-server-core": "9.5.0", - "tslib": "^2.6.3" - }, - "engines": { - "node": ">=14.20.1" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/mongodb-memory-server-core": { - "version": "9.5.0", - "resolved": "https://registry.npmjs.org/mongodb-memory-server-core/-/mongodb-memory-server-core-9.5.0.tgz", - "integrity": "sha512-Jb/V80JeYAKWaF4bPFme7SmTR6ew1PWgkpPUepLDfRraeN49i1cruxICeA4zz4T33W/o31N+zazP8wI8ebf7yw==", - "extraneous": true, - "license": "MIT", - "dependencies": { - "async-mutex": "^0.4.1", - "camelcase": "^6.3.0", - "debug": "^4.3.7", - "find-cache-dir": "^3.3.2", - "follow-redirects": "^1.15.9", - "https-proxy-agent": "^7.0.5", - "mongodb": "^5.9.2", - "new-find-package-json": "^2.0.0", - "semver": "^7.6.3", - "tar-stream": "^3.1.7", - "tslib": "^2.6.3", - "yauzl": "^3.1.3" - }, - "engines": { - "node": ">=14.20.1" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/mongodb-memory-server-core/node_modules/bson": { - "version": "5.5.1", - "resolved": "https://registry.npmjs.org/bson/-/bson-5.5.1.tgz", - "integrity": "sha512-ix0EwukN2EpC0SRWIj/7B5+A6uQMQy6KMREI9qQqvgpkV2frH63T0UDVd1SYedL6dNCmDBYB3QtXi4ISk9YT+g==", - "extraneous": true, - "license": "Apache-2.0", - "engines": { - "node": ">=14.20.1" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/mongodb-memory-server-core/node_modules/debug": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", - "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", - "extraneous": true, - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/@ngageoint/mage.service/node_modules/mongodb-memory-server-core/node_modules/mongodb": { - "version": "5.9.2", - "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-5.9.2.tgz", - "integrity": "sha512-H60HecKO4Bc+7dhOv4sJlgvenK4fQNqqUIlXxZYQNbfEWSALGAwGoyJd/0Qwk4TttFXUOHJ2ZJQe/52ScaUwtQ==", - "extraneous": true, - "license": "Apache-2.0", - "dependencies": { - "bson": "^5.5.0", - "mongodb-connection-string-url": "^2.6.0", - "socks": "^2.7.1" - }, - "engines": { - "node": ">=14.20.1" - }, - "optionalDependencies": { - "@mongodb-js/saslprep": "^1.1.0" - }, - "peerDependencies": { - "@aws-sdk/credential-providers": "^3.188.0", - "@mongodb-js/zstd": "^1.0.0", - "kerberos": "^1.0.0 || ^2.0.0", - "mongodb-client-encryption": ">=2.3.0 <3", - "snappy": "^7.2.2" - }, - "peerDependenciesMeta": { - "@aws-sdk/credential-providers": { - "optional": true - }, - "@mongodb-js/zstd": { - "optional": true - }, - "kerberos": { - "optional": true - }, - "mongodb-client-encryption": { - "optional": true - }, - "snappy": { - "optional": true - } - } - }, - "node_modules/@ngageoint/mage.service/node_modules/mongodb-memory-server-core/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "extraneous": true, - "license": "MIT" - }, - "node_modules/@ngageoint/mage.service/node_modules/mongodb-memory-server-core/node_modules/tar-stream": { - "version": "3.1.7", - "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.1.7.tgz", - "integrity": "sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ==", - "extraneous": true, - "license": "MIT", - "dependencies": { - "b4a": "^1.6.4", - "fast-fifo": "^1.2.0", - "streamx": "^2.15.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/mongoose": { - "version": "6.13.8", - "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-6.13.8.tgz", - "integrity": "sha512-JHKco/533CyVrqCbyQsnqMpLn8ZCiKrPDTd2mvo2W7ygIvhygWjX2wj+RPjn6upZZgw0jC6U51RD7kUsyK8NBg==", - "license": "MIT", - "peer": true, - "dependencies": { - "bson": "^4.7.2", - "kareem": "2.5.1", - "mongodb": "4.17.2", - "mpath": "0.9.0", - "mquery": "4.0.3", - "ms": "2.1.3", - "sift": "16.0.1" - }, - "engines": { - "node": ">=12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/mongoose" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/mongoose/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "peer": true - }, - "node_modules/@ngageoint/mage.service/node_modules/mpath": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/mpath/-/mpath-0.9.0.tgz", - "integrity": "sha512-ikJRQTk8hw5DEoFVxHG1Gn9T/xcjtdnOKIU1JTmGjZZlg9LST2mBLmcX3/ICIbgJydT2GOc15RnNy5mHmzfSew==", - "peer": true, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/mquery": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/mquery/-/mquery-4.0.3.tgz", - "integrity": "sha512-J5heI+P08I6VJ2Ky3+33IpCdAvlYGTSUjwTPxkAr8i8EoduPMBX2OY/wa3IKZIQl7MU4SbFk8ndgSKyB/cl1zA==", - "peer": true, - "dependencies": { - "debug": "4.x" - }, - "engines": { - "node": ">=12.0.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "peer": true - }, - "node_modules/@ngageoint/mage.service/node_modules/multer": { - "version": "1.4.5-lts.1", - "resolved": "https://registry.npmjs.org/multer/-/multer-1.4.5-lts.1.tgz", - "integrity": "sha512-ywPWvcDMeH+z9gQq5qYHCCy+ethsk4goepZ45GLD63fOu0YcNecQxi64nDs3qluZB+murG3/D4dJ7+dGctcCQQ==", - "peer": true, - "dependencies": { - "append-field": "^1.0.0", - "busboy": "^1.0.0", - "concat-stream": "^1.5.2", - "mkdirp": "^0.5.4", - "object-assign": "^4.1.1", - "type-is": "^1.6.4", - "xtend": "^4.0.0" - }, - "engines": { - "node": ">= 6.0.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/mute-stream": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", - "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", - "optional": true, - "peer": true - }, - "node_modules/@ngageoint/mage.service/node_modules/napi-build-utils": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-1.0.2.tgz", - "integrity": "sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg==", - "optional": true, - "peer": true - }, - "node_modules/@ngageoint/mage.service/node_modules/natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", - "extraneous": true - }, - "node_modules/@ngageoint/mage.service/node_modules/negotiator": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", - "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", - "peer": true, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/nested-error-stacks": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/nested-error-stacks/-/nested-error-stacks-2.1.1.tgz", - "integrity": "sha512-9iN1ka/9zmX1ZvLV9ewJYEk9h7RyRRtqdK0woXcqohu8EWIerfPUjYJPg0ULy0UqP7cslmdGc8xKDJcojlKiaw==", - "extraneous": true - }, - "node_modules/@ngageoint/mage.service/node_modules/new-find-package-json": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/new-find-package-json/-/new-find-package-json-2.0.0.tgz", - "integrity": "sha512-lDcBsjBSMlj3LXH2v/FW3txlh2pYTjmbOXPYJD93HI5EwuLzI11tdHSIpUMmfq/IOsldj4Ps8M8flhm+pCK4Ew==", - "extraneous": true, - "license": "MIT", - "dependencies": { - "debug": "^4.3.4" - }, - "engines": { - "node": ">=12.22.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/nice-try": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", - "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", - "extraneous": true - }, - "node_modules/@ngageoint/mage.service/node_modules/nise": { - "version": "1.5.3", - "resolved": "https://registry.npmjs.org/nise/-/nise-1.5.3.tgz", - "integrity": "sha512-Ymbac/94xeIrMf59REBPOv0thr+CJVFMhrlAkW/gjCIE58BGQdCj0x7KRCb3yz+Ga2Rz3E9XXSvUyyxqqhjQAQ==", - "extraneous": true, - "dependencies": { - "@sinonjs/formatio": "^3.2.1", - "@sinonjs/text-encoding": "^0.7.1", - "just-extend": "^4.0.2", - "lolex": "^5.0.1", - "path-to-regexp": "^1.7.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/nise/node_modules/isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==", - "extraneous": true - }, - "node_modules/@ngageoint/mage.service/node_modules/nise/node_modules/lolex": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/lolex/-/lolex-5.1.2.tgz", - "integrity": "sha512-h4hmjAvHTmd+25JSwrtTIuwbKdwg5NzZVRMLn9saij4SZaepCrTCxPr35H/3bjwfMJtN+t3CX8672UIkglz28A==", - "extraneous": true, - "dependencies": { - "@sinonjs/commons": "^1.7.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/nise/node_modules/path-to-regexp": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.8.0.tgz", - "integrity": "sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA==", - "extraneous": true, - "dependencies": { - "isarray": "0.0.1" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/nock": { - "version": "11.9.1", - "resolved": "https://registry.npmjs.org/nock/-/nock-11.9.1.tgz", - "integrity": "sha512-U5wPctaY4/ar2JJ5Jg4wJxlbBfayxgKbiAeGh+a1kk6Pwnc2ZEuKviLyDSG6t0uXl56q7AALIxoM6FJrBSsVXA==", - "extraneous": true, - "license": "MIT", - "dependencies": { - "debug": "^4.1.0", - "json-stringify-safe": "^5.0.1", - "lodash": "^4.17.13", - "mkdirp": "^0.5.0", - "propagate": "^2.0.0" - }, - "engines": { - "node": ">= 8.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/node-abi": { - "version": "3.65.0", - "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.65.0.tgz", - "integrity": "sha512-ThjYBfoDNr08AWx6hGaRbfPwxKV9kVzAzOzlLKbk2CuqXE2xnCh+cbAGnwM3t8Lq4v9rUB7VfondlkBckcJrVA==", - "optional": true, - "peer": true, - "dependencies": { - "semver": "^7.3.5" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/node-fetch": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", - "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", - "license": "MIT", - "peer": true, - "dependencies": { - "whatwg-url": "^5.0.0" - }, - "engines": { - "node": "4.x || >=6.0.0" - }, - "peerDependencies": { - "encoding": "^0.1.0" - }, - "peerDependenciesMeta": { - "encoding": { - "optional": true - } - } - }, - "node_modules/@ngageoint/mage.service/node_modules/node-fetch/node_modules/tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", - "license": "MIT", - "peer": true - }, - "node_modules/@ngageoint/mage.service/node_modules/node-fetch/node_modules/webidl-conversions": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", - "license": "BSD-2-Clause", - "peer": true - }, - "node_modules/@ngageoint/mage.service/node_modules/node-fetch/node_modules/whatwg-url": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", - "license": "MIT", - "peer": true, - "dependencies": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/nomnom": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/nomnom/-/nomnom-1.8.1.tgz", - "integrity": "sha512-5s0JxqhDx9/rksG2BTMVN1enjWSvPidpoSgViZU4ZXULyTe+7jxcCRLB6f42Z0l1xYJpleCBtSyY6Lwg3uu5CQ==", - "deprecated": "Package no longer supported. Contact support@npmjs.com for more info.", - "peer": true, - "dependencies": { - "chalk": "~0.4.0", - "underscore": "~1.6.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/nomnom/node_modules/ansi-styles": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-1.0.0.tgz", - "integrity": "sha512-3iF4FIKdxaVYT3JqQuY3Wat/T2t7TRbbQ94Fu50ZUCbLy4TFbTzr90NOHQodQkNqmeEGCw8WbeP78WNi6SKYUA==", - "peer": true, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/nomnom/node_modules/chalk": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-0.4.0.tgz", - "integrity": "sha512-sQfYDlfv2DGVtjdoQqxS0cEZDroyG8h6TamA6rvxwlrU5BaSLDx9xhatBYl2pxZ7gmpNaPFVwBtdGdu5rQ+tYQ==", - "peer": true, - "dependencies": { - "ansi-styles": "~1.0.0", - "has-color": "~0.1.0", - "strip-ansi": "~0.1.0" - }, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/nomnom/node_modules/strip-ansi": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-0.1.1.tgz", - "integrity": "sha512-behete+3uqxecWlDAm5lmskaSaISA+ThQ4oNNBDTBJt0x2ppR6IPqfZNuj6BLaLJ/Sji4TPZlcRyOis8wXQTLg==", - "peer": true, - "bin": { - "strip-ansi": "cli.js" - }, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/nomnom/node_modules/underscore": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.6.0.tgz", - "integrity": "sha512-z4o1fvKUojIWh9XuaVLUDdf86RQiq13AC1dmHbTpoyuu+bquHms76v16CjycCbec87J7z0k//SiQVk0sMdFmpQ==", - "peer": true - }, - "node_modules/@ngageoint/mage.service/node_modules/normalize-package-data": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", - "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", - "extraneous": true, - "dependencies": { - "hosted-git-info": "^2.1.4", - "resolve": "^1.10.0", - "semver": "2 || 3 || 4 || 5", - "validate-npm-package-license": "^3.0.1" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/normalize-package-data/node_modules/semver": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", - "extraneous": true, - "bin": { - "semver": "bin/semver" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "peer": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/npm-run-all": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/npm-run-all/-/npm-run-all-4.1.5.tgz", - "integrity": "sha512-Oo82gJDAVcaMdi3nuoKFavkIHBRVqQ1qvMb+9LHk/cF4P6B2m8aP04hGf7oL6wZ9BuGwX1onlLhpuoofSyoQDQ==", - "extraneous": true, - "dependencies": { - "ansi-styles": "^3.2.1", - "chalk": "^2.4.1", - "cross-spawn": "^6.0.5", - "memorystream": "^0.3.1", - "minimatch": "^3.0.4", - "pidtree": "^0.3.0", - "read-pkg": "^3.0.0", - "shell-quote": "^1.6.1", - "string.prototype.padend": "^3.0.0" - }, - "bin": { - "npm-run-all": "bin/npm-run-all/index.js", - "run-p": "bin/run-p/index.js", - "run-s": "bin/run-s/index.js" - }, - "engines": { - "node": ">= 4" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/npm-run-all/node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "extraneous": true, - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/npm-run-all/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "extraneous": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/npm-run-all/node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "extraneous": true, - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/npm-run-all/node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "extraneous": true, - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/npm-run-all/node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "extraneous": true - }, - "node_modules/@ngageoint/mage.service/node_modules/npm-run-all/node_modules/cross-spawn": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", - "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", - "extraneous": true, - "dependencies": { - "nice-try": "^1.0.4", - "path-key": "^2.0.1", - "semver": "^5.5.0", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - }, - "engines": { - "node": ">=4.8" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/npm-run-all/node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "extraneous": true, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/npm-run-all/node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "extraneous": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/npm-run-all/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "extraneous": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/npm-run-all/node_modules/path-key": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", - "integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==", - "extraneous": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/npm-run-all/node_modules/semver": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", - "extraneous": true, - "bin": { - "semver": "bin/semver" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/npm-run-all/node_modules/shebang-command": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", - "integrity": "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==", - "extraneous": true, - "dependencies": { - "shebang-regex": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/npm-run-all/node_modules/shebang-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", - "integrity": "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==", - "extraneous": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/npm-run-all/node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "extraneous": true, - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/npm-run-all/node_modules/which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", - "extraneous": true, - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "which": "bin/which" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/nyc": { - "version": "14.1.1", - "resolved": "https://registry.npmjs.org/nyc/-/nyc-14.1.1.tgz", - "integrity": "sha512-OI0vm6ZGUnoGZv/tLdZ2esSVzDwUC88SNs+6JoSOMVxA+gKMB8Tk7jBwgemLx4O40lhhvZCVw1C+OYLOBOPXWw==", - "extraneous": true, - "dependencies": { - "archy": "^1.0.0", - "caching-transform": "^3.0.2", - "convert-source-map": "^1.6.0", - "cp-file": "^6.2.0", - "find-cache-dir": "^2.1.0", - "find-up": "^3.0.0", - "foreground-child": "^1.5.6", - "glob": "^7.1.3", - "istanbul-lib-coverage": "^2.0.5", - "istanbul-lib-hook": "^2.0.7", - "istanbul-lib-instrument": "^3.3.0", - "istanbul-lib-report": "^2.0.8", - "istanbul-lib-source-maps": "^3.0.6", - "istanbul-reports": "^2.2.4", - "js-yaml": "^3.13.1", - "make-dir": "^2.1.0", - "merge-source-map": "^1.1.0", - "resolve-from": "^4.0.0", - "rimraf": "^2.6.3", - "signal-exit": "^3.0.2", - "spawn-wrap": "^1.4.2", - "test-exclude": "^5.2.3", - "uuid": "^3.3.2", - "yargs": "^13.2.2", - "yargs-parser": "^13.0.0" - }, - "bin": { - "nyc": "bin/nyc.js" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/nyc/node_modules/ansi-regex": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", - "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", - "extraneous": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/nyc/node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "extraneous": true, - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/nyc/node_modules/camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "extraneous": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/nyc/node_modules/cliui": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", - "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", - "extraneous": true, - "dependencies": { - "string-width": "^3.1.0", - "strip-ansi": "^5.2.0", - "wrap-ansi": "^5.1.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/nyc/node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "extraneous": true, - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/nyc/node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "extraneous": true - }, - "node_modules/@ngageoint/mage.service/node_modules/nyc/node_modules/cp-file": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/cp-file/-/cp-file-6.2.0.tgz", - "integrity": "sha512-fmvV4caBnofhPe8kOcitBwSn2f39QLjnAnGq3gO9dfd75mUytzKNZB1hde6QHunW2Rt+OwuBOMc3i1tNElbszA==", - "extraneous": true, - "dependencies": { - "graceful-fs": "^4.1.2", - "make-dir": "^2.0.0", - "nested-error-stacks": "^2.0.0", - "pify": "^4.0.1", - "safe-buffer": "^5.0.1" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/nyc/node_modules/decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", - "extraneous": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/nyc/node_modules/emoji-regex": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", - "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", - "extraneous": true - }, - "node_modules/@ngageoint/mage.service/node_modules/nyc/node_modules/find-cache-dir": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-2.1.0.tgz", - "integrity": "sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ==", - "extraneous": true, - "dependencies": { - "commondir": "^1.0.1", - "make-dir": "^2.0.0", - "pkg-dir": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/nyc/node_modules/find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "extraneous": true, - "dependencies": { - "locate-path": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/nyc/node_modules/is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==", - "extraneous": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/nyc/node_modules/js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", - "extraneous": true, - "dependencies": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/nyc/node_modules/locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", - "extraneous": true, - "dependencies": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/nyc/node_modules/make-dir": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", - "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", - "extraneous": true, - "dependencies": { - "pify": "^4.0.1", - "semver": "^5.6.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/nyc/node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "extraneous": true, - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/nyc/node_modules/p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", - "extraneous": true, - "dependencies": { - "p-limit": "^2.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/nyc/node_modules/path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", - "extraneous": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/nyc/node_modules/pify": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", - "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", - "extraneous": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/nyc/node_modules/pkg-dir": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz", - "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", - "extraneous": true, - "dependencies": { - "find-up": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/nyc/node_modules/rimraf": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", - "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", - "deprecated": "Rimraf versions prior to v4 are no longer supported", - "extraneous": true, - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/nyc/node_modules/semver": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", - "extraneous": true, - "bin": { - "semver": "bin/semver" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/nyc/node_modules/string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", - "extraneous": true, - "dependencies": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/nyc/node_modules/strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "extraneous": true, - "dependencies": { - "ansi-regex": "^4.1.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/nyc/node_modules/uuid": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", - "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", - "deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.", - "extraneous": true, - "bin": { - "uuid": "bin/uuid" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/nyc/node_modules/wrap-ansi": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", - "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", - "extraneous": true, - "dependencies": { - "ansi-styles": "^3.2.0", - "string-width": "^3.0.0", - "strip-ansi": "^5.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/nyc/node_modules/y18n": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", - "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", - "extraneous": true - }, - "node_modules/@ngageoint/mage.service/node_modules/nyc/node_modules/yargs": { - "version": "13.3.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz", - "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==", - "extraneous": true, - "dependencies": { - "cliui": "^5.0.0", - "find-up": "^3.0.0", - "get-caller-file": "^2.0.1", - "require-directory": "^2.1.1", - "require-main-filename": "^2.0.0", - "set-blocking": "^2.0.0", - "string-width": "^3.0.0", - "which-module": "^2.0.0", - "y18n": "^4.0.0", - "yargs-parser": "^13.1.2" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/nyc/node_modules/yargs-parser": { - "version": "13.1.2", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz", - "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==", - "extraneous": true, - "dependencies": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/oauth": { - "version": "0.10.2", - "resolved": "https://registry.npmjs.org/oauth/-/oauth-0.10.2.tgz", - "integrity": "sha512-JtFnB+8nxDEXgNyniwz573xxbKSOu3R8D40xQKqcjwJ2CDkYqUDI53o6IuzDJBx60Z8VKCm271+t8iFjakrl8Q==", - "license": "MIT", - "peer": true - }, - "node_modules/@ngageoint/mage.service/node_modules/oauth-sign": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", - "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", - "extraneous": true, - "engines": { - "node": "*" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", - "peer": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/object-inspect": { - "version": "1.13.4", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", - "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", - "license": "MIT", - "peer": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/object-is": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.6.tgz", - "integrity": "sha512-F8cZ+KfGlSGi09lJT7/Nd6KJZ9ygtvYC0/UYYLI9nmQKLMnydpB9yvbv9K1uSkEu7FU9vYPmVwLg328tX+ot3Q==", - "extraneous": true, - "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", - "extraneous": true, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/object.assign": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.5.tgz", - "integrity": "sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==", - "extraneous": true, - "dependencies": { - "call-bind": "^1.0.5", - "define-properties": "^1.2.1", - "has-symbols": "^1.0.3", - "object-keys": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/on-finished": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", - "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", - "license": "MIT", - "peer": true, - "dependencies": { - "ee-first": "1.1.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/on-headers": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", - "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", - "peer": true, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "peer": true, - "dependencies": { - "wrappy": "1" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/onetime": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", - "optional": true, - "peer": true, - "dependencies": { - "mimic-fn": "^2.1.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/openapi-enforcer": { - "version": "1.23.0", - "resolved": "https://registry.npmjs.org/openapi-enforcer/-/openapi-enforcer-1.23.0.tgz", - "integrity": "sha512-Ja6kvNQ28jvCHpotNZkB129/dBg2IslMDV56aUSl1Dcs+bcd8lGJTq1NatGYsspGpURXvQnFA/q2K8V3AQoO3Q==", - "extraneous": true, - "license": "Apache-2.0", - "dependencies": { - "js-yaml": "^4.1.0", - "randexp": "^0.5.3" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/openapi-enforcer/node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "extraneous": true, - "license": "Python-2.0" - }, - "node_modules/@ngageoint/mage.service/node_modules/openapi-enforcer/node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "extraneous": true, - "license": "MIT", - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/opentype.js": { - "version": "0.7.3", - "resolved": "https://registry.npmjs.org/opentype.js/-/opentype.js-0.7.3.tgz", - "integrity": "sha512-Veui5vl2bLonFJ/SjX/WRWJT3SncgiZNnKUyahmXCc2sa1xXW15u3R/3TN5+JFiP7RsjK5ER4HA5eWaEmV9deA==", - "peer": true, - "dependencies": { - "tiny-inflate": "^1.0.2" - }, - "bin": { - "ot": "bin/ot" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/optionator": { - "version": "0.9.4", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", - "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", - "extraneous": true, - "dependencies": { - "deep-is": "^0.1.3", - "fast-levenshtein": "^2.0.6", - "levn": "^0.4.1", - "prelude-ls": "^1.2.1", - "type-check": "^0.4.0", - "word-wrap": "^1.2.5" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/os-homedir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", - "integrity": "sha512-B5JU3cabzk8c67mRRd3ECmROafjYMXbuzlwtqdM8IbS8ktlTix8aFGb2bAGKrSRIlnfKwovGUUr72JUPyOb6kQ==", - "extraneous": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/os-tmpdir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", - "optional": true, - "peer": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/p-event": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/p-event/-/p-event-5.0.1.tgz", - "integrity": "sha512-dd589iCQ7m1L0bmC5NLlVYfy3TbBEsMUfWx9PyAgPeIcFZ/E2yaTZ4Rz4MiBmmJShviiftHVXOqfnfzJ6kyMrQ==", - "extraneous": true, - "dependencies": { - "p-timeout": "^5.0.2" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/p-filter": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-filter/-/p-filter-3.0.0.tgz", - "integrity": "sha512-QtoWLjXAW++uTX67HZQz1dbTpqBfiidsB6VtQUC9iR85S120+s0T5sO6s+B5MLzFcZkrEd/DGMmCjR+f2Qpxwg==", - "extraneous": true, - "dependencies": { - "p-map": "^5.1.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/p-filter/node_modules/p-map": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-5.5.0.tgz", - "integrity": "sha512-VFqfGDHlx87K66yZrNdI4YGtD70IRyd+zSvgks6mzHPRNkoKy+9EKP4SFC77/vTTQYmRmti7dvqC+m5jBrBAcg==", - "extraneous": true, - "dependencies": { - "aggregate-error": "^4.0.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "extraneous": true, - "dependencies": { - "yocto-queue": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", - "extraneous": true, - "dependencies": { - "p-limit": "^3.0.2" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/p-map": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-6.0.0.tgz", - "integrity": "sha512-T8BatKGY+k5rU+Q/GTYgrEf2r4xRMevAN5mtXc2aPc4rS1j3s+vWTaO2Wag94neXuCAUAs8cxBL9EeB5EA6diw==", - "extraneous": true, - "engines": { - "node": ">=16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/p-timeout": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-5.1.0.tgz", - "integrity": "sha512-auFDyzzzGZZZdHz3BtET9VEz0SE/uMEAx7uWfGPucfzEwwe/xH0iVeZibQmANYE/hp9T2+UUZT5m+BKyrDp3Ew==", - "extraneous": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "extraneous": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/package-hash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/package-hash/-/package-hash-3.0.0.tgz", - "integrity": "sha512-lOtmukMDVvtkL84rJHI7dpTYq+0rli8N2wlnqUcBuDWCfVhRUfOmnR9SsoHFMLpACvEV60dX7rd0rFaYDZI+FA==", - "extraneous": true, - "dependencies": { - "graceful-fs": "^4.1.15", - "hasha": "^3.0.0", - "lodash.flattendeep": "^4.4.0", - "release-zalgo": "^1.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/package-json-from-dist": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.0.tgz", - "integrity": "sha512-dATvCeZN/8wQsGywez1mzHtTlP22H8OEfPrVMLNr4/eGa+ijtLn/6M5f0dY8UKNrC2O9UCU6SSoG3qRKnt7STw==", - "extraneous": true - }, - "node_modules/@ngageoint/mage.service/node_modules/pako": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", - "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", - "extraneous": true - }, - "node_modules/@ngageoint/mage.service/node_modules/parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "extraneous": true, - "dependencies": { - "callsites": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/parse-json": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==", - "extraneous": true, - "dependencies": { - "error-ex": "^1.3.1", - "json-parse-better-errors": "^1.0.1" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/parseurl": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", - "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", - "peer": true, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/passport": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/passport/-/passport-0.7.0.tgz", - "integrity": "sha512-cPLl+qZpSc+ireUvt+IzqbED1cHHkDoVYMo30jbJIdOOjQ1MQYZBPiNvmi8UM6lJuOpTPXJGZQk0DtC4y61MYQ==", - "license": "MIT", - "peer": true, - "dependencies": { - "passport-strategy": "1.x.x", - "pause": "0.0.1", - "utils-merge": "^1.0.1" - }, - "engines": { - "node": ">= 0.4.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/jaredhanson" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/passport-anonymous": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/passport-anonymous/-/passport-anonymous-1.0.1.tgz", - "integrity": "sha512-Mk2dls97nLTzHpsWCYQ54IVGucWaiWSHHr3+IhWYAebg4dRgRQIfyoeYrixoxB2z2z4+EM7p9yjC+a3yMB5z5A==", - "peer": true, - "dependencies": { - "passport-strategy": "1.x.x" - }, - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/passport-http-bearer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/passport-http-bearer/-/passport-http-bearer-1.0.1.tgz", - "integrity": "sha512-SELQM+dOTuMigr9yu8Wo4Fm3ciFfkMq5h/ZQ8ffi4ELgZrX1xh9PlglqZdcUZ1upzJD/whVyt+YWF62s3U6Ipw==", - "peer": true, - "dependencies": { - "passport-strategy": "1.x.x" - }, - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/passport-ldapauth": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/passport-ldapauth/-/passport-ldapauth-3.0.1.tgz", - "integrity": "sha512-TRRx3BHi8GC8MfCT9wmghjde/EGeKjll7zqHRRfGRxXbLcaDce2OftbQrFG7/AWaeFhR6zpZHtBQ/IkINdLVjQ==", - "peer": true, - "dependencies": { - "ldapauth-fork": "^5.0.1", - "passport-strategy": "^1.0.0" - }, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/passport-local": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/passport-local/-/passport-local-1.0.0.tgz", - "integrity": "sha512-9wCE6qKznvf9mQYYbgJ3sVOHmCWoUNMVFoZzNoznmISbhnNNPhN9xfY3sLmScHMetEJeoY7CXwfhCe7argfQow==", - "peer": true, - "dependencies": { - "passport-strategy": "1.x.x" - }, - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/passport-oauth2": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/passport-oauth2/-/passport-oauth2-1.8.0.tgz", - "integrity": "sha512-cjsQbOrXIDE4P8nNb3FQRCCmJJ/utnFKEz2NX209f7KOHPoX18gF7gBzBbLLsj2/je4KrgiwLLGjf0lm9rtTBA==", - "license": "MIT", - "peer": true, - "dependencies": { - "base64url": "3.x.x", - "oauth": "0.10.x", - "passport-strategy": "1.x.x", - "uid2": "0.0.x", - "utils-merge": "1.x.x" - }, - "engines": { - "node": ">= 0.4.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/jaredhanson" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/passport-openidconnect": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/passport-openidconnect/-/passport-openidconnect-0.1.2.tgz", - "integrity": "sha512-JX3rTyW+KFZ/E9OF/IpXJPbyLO9vGzcmXB5FgSP2jfL3LGKJPdV7zUE8rWeKeeI/iueQggOeFa3onrCmhxXZTg==", - "license": "MIT", - "peer": true, - "dependencies": { - "oauth": "0.10.x", - "passport-strategy": "1.x.x" - }, - "engines": { - "node": ">= 0.6.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/jaredhanson" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/passport-strategy": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/passport-strategy/-/passport-strategy-1.0.0.tgz", - "integrity": "sha512-CB97UUvDKJde2V0KDWWB3lyf6PC3FaZP7YxZ2G8OAtn9p4HI9j9JLP9qjOGZFvyl8uwNT8qM+hGnz/n16NI7oA==", - "peer": true, - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "extraneous": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "peer": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "extraneous": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "peer": true - }, - "node_modules/@ngageoint/mage.service/node_modules/path-scurry": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", - "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", - "extraneous": true, - "dependencies": { - "lru-cache": "^10.2.0", - "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" - }, - "engines": { - "node": ">=16 || 14 >=14.18" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/path-scurry/node_modules/lru-cache": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.3.0.tgz", - "integrity": "sha512-CQl19J/g+Hbjbv4Y3mFNNXFEL/5t/KCg8POCuUqd4rMKjGG+j1ybER83hxV58zL+dFI1PTkt3GNFSHRt+d8qEQ==", - "extraneous": true, - "engines": { - "node": "14 || >=16.14" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/path-to-regexp": { - "version": "0.1.12", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz", - "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==", - "license": "MIT", - "peer": true - }, - "node_modules/@ngageoint/mage.service/node_modules/path-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", - "extraneous": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/pathval": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", - "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", - "extraneous": true, - "engines": { - "node": "*" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/pause": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/pause/-/pause-0.0.1.tgz", - "integrity": "sha512-KG8UEiEVkR3wGEb4m5yZkVCzigAD+cVEJck2CzYZO37ZGJfctvVptVO192MwrtPhzONn6go8ylnOdMhKqi4nfg==", - "peer": true - }, - "node_modules/@ngageoint/mage.service/node_modules/pause-stream": { - "version": "0.0.11", - "resolved": "https://registry.npmjs.org/pause-stream/-/pause-stream-0.0.11.tgz", - "integrity": "sha512-e3FBlXLmN/D1S+zHzanP4E/4Z60oFAa3O051qt1pxa7DEJWKAyil6upYVXCWadEnuoqa4Pkc9oUx9zsxYeRv8A==", - "peer": true, - "dependencies": { - "through": "~2.3" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/peek-readable": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/peek-readable/-/peek-readable-4.1.0.tgz", - "integrity": "sha512-ZI3LnwUv5nOGbQzD9c2iDG6toheuXSZP5esSHBjopsXH4dg19soufvpUGA3uohi5anFtGb2lhAVdHzH6R/Evvg==", - "peer": true, - "engines": { - "node": ">=8" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/Borewit" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/pend": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", - "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==", - "extraneous": true, - "license": "MIT" - }, - "node_modules/@ngageoint/mage.service/node_modules/performance-now": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", - "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==", - "extraneous": true - }, - "node_modules/@ngageoint/mage.service/node_modules/picocolors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz", - "integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==", - "extraneous": true - }, - "node_modules/@ngageoint/mage.service/node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "extraneous": true, - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/pidtree": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.3.1.tgz", - "integrity": "sha512-qQbW94hLHEqCg7nhby4yRC7G2+jYHY4Rguc2bjw7Uug4GIJuu1tvf2uHaZv5Q8zdt+WKJ6qK1FOI6amaWUo5FA==", - "extraneous": true, - "bin": { - "pidtree": "bin/pidtree.js" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/pify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", - "extraneous": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/pkg-dir": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", - "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", - "extraneous": true, - "license": "MIT", - "dependencies": { - "find-up": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/pkg-dir/node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "extraneous": true, - "license": "MIT", - "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/pkg-dir/node_modules/locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "extraneous": true, - "license": "MIT", - "dependencies": { - "p-locate": "^4.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/pkg-dir/node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "extraneous": true, - "license": "MIT", - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/pkg-dir/node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "extraneous": true, - "license": "MIT", - "dependencies": { - "p-limit": "^2.2.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/pkginfo": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/pkginfo/-/pkginfo-0.3.1.tgz", - "integrity": "sha512-yO5feByMzAp96LtP58wvPKSbaKAi/1C4kV9XpTctr6EepnP6F33RBNOiVrdz9BrPA98U2BMFsTNHo44TWcbQ2A==", - "peer": true, - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/polygon-clipping": { - "version": "0.15.7", - "resolved": "https://registry.npmjs.org/polygon-clipping/-/polygon-clipping-0.15.7.tgz", - "integrity": "sha512-nhfdr83ECBg6xtqOAJab1tbksbBAOMUltN60bU+llHVOL0e5Onm1WpAXXWXVB39L8AJFssoIhEVuy/S90MmotA==", - "peer": true, - "dependencies": { - "robust-predicates": "^3.0.2", - "splaytree": "^3.1.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/ports": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/ports/-/ports-1.1.0.tgz", - "integrity": "sha512-XmS7dspHnkTXZC75NkG0ti2hLj8aSyg1Izp87/2cWT7QhTo1vdtWsQ4ldp4BEQ/EXqy0s4yTATJUZ3t9RGZVpg==", - "peer": true - }, - "node_modules/@ngageoint/mage.service/node_modules/possible-typed-array-names": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz", - "integrity": "sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==", - "extraneous": true, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/prebuild-install": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.2.tgz", - "integrity": "sha512-UnNke3IQb6sgarcZIDU3gbMeTp/9SSU1DAIkil7PrqG1vZlBtY5msYccSKSHDqa3hNg436IXK+SNImReuA1wEQ==", - "optional": true, - "peer": true, - "dependencies": { - "detect-libc": "^2.0.0", - "expand-template": "^2.0.3", - "github-from-package": "0.0.0", - "minimist": "^1.2.3", - "mkdirp-classic": "^0.5.3", - "napi-build-utils": "^1.0.1", - "node-abi": "^3.3.0", - "pump": "^3.0.0", - "rc": "^1.2.7", - "simple-get": "^4.0.0", - "tar-fs": "^2.0.0", - "tunnel-agent": "^0.6.0" - }, - "bin": { - "prebuild-install": "bin.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/precond": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/precond/-/precond-0.2.3.tgz", - "integrity": "sha512-QCYG84SgGyGzqJ/vlMsxeXd/pgL/I94ixdNFyh1PusWmTCyVfPJjZ1K1jvHtsbfnXQs2TSkEP2fR7QiMZAnKFQ==", - "peer": true, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/prelude-ls": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", - "extraneous": true, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/prettier": { - "version": "1.19.1", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-1.19.1.tgz", - "integrity": "sha512-s7PoyDv/II1ObgQunCbB9PdLmUcBZcnWOcxDh7O0N/UwDEsHyqkW+Qh28jW+mVuCdx7gLB0BotYI1Y6uI9iyew==", - "extraneous": true, - "bin": { - "prettier": "bin-prettier.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/prettier-linter-helpers": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", - "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", - "extraneous": true, - "dependencies": { - "fast-diff": "^1.1.2" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/process-nextick-args": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", - "peer": true - }, - "node_modules/@ngageoint/mage.service/node_modules/proj4": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/proj4/-/proj4-2.8.0.tgz", - "integrity": "sha512-baC+YcD4xsSqJ+CpCZljj2gcQDhlKb+J+Zjv/2KSBwWNjk4M0pafgQsE+mWurd84tflMIsP+7j7mtIpFDHzQfQ==", - "peer": true, - "dependencies": { - "mgrs": "1.0.0", - "wkt-parser": "^1.3.1" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/promise": { - "version": "7.3.1", - "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz", - "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==", - "peer": true, - "dependencies": { - "asap": "~2.0.3" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/propagate": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/propagate/-/propagate-2.0.1.tgz", - "integrity": "sha512-vGrhOavPSTz4QVNuBNdcNXePNdNMaO1xj9yBeH1ScQPjk/rhg9sSlCXPhMkFuaNNW/syTvYqsnbIJxMBfRbbag==", - "extraneous": true, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/proxy-addr": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", - "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", - "peer": true, - "dependencies": { - "forwarded": "0.2.0", - "ipaddr.js": "1.9.1" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/proxy-from-env": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", - "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", - "extraneous": true - }, - "node_modules/@ngageoint/mage.service/node_modules/proxyquire": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/proxyquire/-/proxyquire-2.1.3.tgz", - "integrity": "sha512-BQWfCqYM+QINd+yawJz23tbBM40VIGXOdDw3X344KcclI/gtBbdWF6SlQ4nK/bYhF9d27KYug9WzljHC6B9Ysg==", - "extraneous": true, - "dependencies": { - "fill-keys": "^1.0.2", - "module-not-found-error": "^1.0.1", - "resolve": "^1.11.1" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/pseudomap": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", - "integrity": "sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ==", - "extraneous": true - }, - "node_modules/@ngageoint/mage.service/node_modules/psl": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", - "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==", - "extraneous": true - }, - "node_modules/@ngageoint/mage.service/node_modules/pug": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/pug/-/pug-3.0.3.tgz", - "integrity": "sha512-uBi6kmc9f3SZ3PXxqcHiUZLmIXgfgWooKWXcwSGwQd2Zi5Rb0bT14+8CJjJgI8AB+nndLaNgHGrcc6bPIB665g==", - "license": "MIT", - "peer": true, - "dependencies": { - "pug-code-gen": "^3.0.3", - "pug-filters": "^4.0.0", - "pug-lexer": "^5.0.1", - "pug-linker": "^4.0.0", - "pug-load": "^3.0.0", - "pug-parser": "^6.0.0", - "pug-runtime": "^3.0.1", - "pug-strip-comments": "^2.0.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/pug-attrs": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pug-attrs/-/pug-attrs-3.0.0.tgz", - "integrity": "sha512-azINV9dUtzPMFQktvTXciNAfAuVh/L/JCl0vtPCwvOA21uZrC08K/UnmrL+SXGEVc1FwzjW62+xw5S/uaLj6cA==", - "peer": true, - "dependencies": { - "constantinople": "^4.0.1", - "js-stringify": "^1.0.2", - "pug-runtime": "^3.0.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/pug-code-gen": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/pug-code-gen/-/pug-code-gen-3.0.3.tgz", - "integrity": "sha512-cYQg0JW0w32Ux+XTeZnBEeuWrAY7/HNE6TWnhiHGnnRYlCgyAUPoyh9KzCMa9WhcJlJ1AtQqpEYHc+vbCzA+Aw==", - "peer": true, - "dependencies": { - "constantinople": "^4.0.1", - "doctypes": "^1.1.0", - "js-stringify": "^1.0.2", - "pug-attrs": "^3.0.0", - "pug-error": "^2.1.0", - "pug-runtime": "^3.0.1", - "void-elements": "^3.1.0", - "with": "^7.0.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/pug-error": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/pug-error/-/pug-error-2.1.0.tgz", - "integrity": "sha512-lv7sU9e5Jk8IeUheHata6/UThZ7RK2jnaaNztxfPYUY+VxZyk/ePVaNZ/vwmH8WqGvDz3LrNYt/+gA55NDg6Pg==", - "peer": true - }, - "node_modules/@ngageoint/mage.service/node_modules/pug-filters": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/pug-filters/-/pug-filters-4.0.0.tgz", - "integrity": "sha512-yeNFtq5Yxmfz0f9z2rMXGw/8/4i1cCFecw/Q7+D0V2DdtII5UvqE12VaZ2AY7ri6o5RNXiweGH79OCq+2RQU4A==", - "peer": true, - "dependencies": { - "constantinople": "^4.0.1", - "jstransformer": "1.0.0", - "pug-error": "^2.0.0", - "pug-walk": "^2.0.0", - "resolve": "^1.15.1" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/pug-lexer": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/pug-lexer/-/pug-lexer-5.0.1.tgz", - "integrity": "sha512-0I6C62+keXlZPZkOJeVam9aBLVP2EnbeDw3An+k0/QlqdwH6rv8284nko14Na7c0TtqtogfWXcRoFE4O4Ff20w==", - "peer": true, - "dependencies": { - "character-parser": "^2.2.0", - "is-expression": "^4.0.0", - "pug-error": "^2.0.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/pug-linker": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/pug-linker/-/pug-linker-4.0.0.tgz", - "integrity": "sha512-gjD1yzp0yxbQqnzBAdlhbgoJL5qIFJw78juN1NpTLt/mfPJ5VgC4BvkoD3G23qKzJtIIXBbcCt6FioLSFLOHdw==", - "peer": true, - "dependencies": { - "pug-error": "^2.0.0", - "pug-walk": "^2.0.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/pug-load": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pug-load/-/pug-load-3.0.0.tgz", - "integrity": "sha512-OCjTEnhLWZBvS4zni/WUMjH2YSUosnsmjGBB1An7CsKQarYSWQ0GCVyd4eQPMFJqZ8w9xgs01QdiZXKVjk92EQ==", - "peer": true, - "dependencies": { - "object-assign": "^4.1.1", - "pug-walk": "^2.0.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/pug-parser": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/pug-parser/-/pug-parser-6.0.0.tgz", - "integrity": "sha512-ukiYM/9cH6Cml+AOl5kETtM9NR3WulyVP2y4HOU45DyMim1IeP/OOiyEWRr6qk5I5klpsBnbuHpwKmTx6WURnw==", - "peer": true, - "dependencies": { - "pug-error": "^2.0.0", - "token-stream": "1.0.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/pug-runtime": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/pug-runtime/-/pug-runtime-3.0.1.tgz", - "integrity": "sha512-L50zbvrQ35TkpHwv0G6aLSuueDRwc/97XdY8kL3tOT0FmhgG7UypU3VztfV/LATAvmUfYi4wNxSajhSAeNN+Kg==", - "peer": true - }, - "node_modules/@ngageoint/mage.service/node_modules/pug-strip-comments": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/pug-strip-comments/-/pug-strip-comments-2.0.0.tgz", - "integrity": "sha512-zo8DsDpH7eTkPHCXFeAk1xZXJbyoTfdPlNR0bK7rpOMuhBYb0f5qUVCO1xlsitYd3w5FQTK7zpNVKb3rZoUrrQ==", - "peer": true, - "dependencies": { - "pug-error": "^2.0.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/pug-walk": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/pug-walk/-/pug-walk-2.0.0.tgz", - "integrity": "sha512-yYELe9Q5q9IQhuvqsZNwA5hfPkMJ8u92bQLIMcsMxf/VADjNtEYptU+inlufAFYcWdHlwNfZOEnOOQrZrcyJCQ==", - "peer": true - }, - "node_modules/@ngageoint/mage.service/node_modules/pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", - "optional": true, - "peer": true, - "dependencies": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/punycode": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", - "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", - "peer": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/qs": { - "version": "6.13.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", - "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", - "license": "BSD-3-Clause", - "peer": true, - "dependencies": { - "side-channel": "^1.0.6" - }, - "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/queue": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/queue/-/queue-6.0.1.tgz", - "integrity": "sha512-AJBQabRCCNr9ANq8v77RJEv73DPbn55cdTb+Giq4X0AVnNVZvMHlYp7XlQiN+1npCZj1DuSmaA2hYVUUDgxFDg==", - "peer": true, - "dependencies": { - "inherits": "~2.0.3" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/queue-microtask": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "extraneous": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/@ngageoint/mage.service/node_modules/quickselect": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/quickselect/-/quickselect-2.0.0.tgz", - "integrity": "sha512-RKJ22hX8mHe3Y6wH/N3wCM6BWtjaxIyyUIkpHOvfFnxdI4yD4tBXEBKSbriGujF6jnSVkJrffuo6vxACiSSxIw==", - "peer": true - }, - "node_modules/@ngageoint/mage.service/node_modules/randexp": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/randexp/-/randexp-0.5.3.tgz", - "integrity": "sha512-U+5l2KrcMNOUPYvazA3h5ekF80FHTUG+87SEAmHZmolh1M+i/WyTCxVzmi+tidIa1tM4BSe8g2Y/D3loWDjj+w==", - "extraneous": true, - "dependencies": { - "drange": "^1.0.2", - "ret": "^0.2.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/random-bytes": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/random-bytes/-/random-bytes-1.0.0.tgz", - "integrity": "sha512-iv7LhNVO047HzYR3InF6pUcUsPQiHTM1Qal51DcGSuZFBil1aBBWG5eHPNek7bvILMaYJ/8RU1e8w1AMdHmLQQ==", - "peer": true, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/randombytes": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", - "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", - "extraneous": true, - "license": "MIT", - "dependencies": { - "safe-buffer": "^5.1.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/range-parser": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", - "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", - "license": "MIT", - "peer": true, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/raw-body": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", - "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", - "license": "MIT", - "peer": true, - "dependencies": { - "bytes": "3.1.2", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "unpipe": "1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/raw-body/node_modules/iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "license": "MIT", - "peer": true, - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/rbush": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/rbush/-/rbush-3.0.1.tgz", - "integrity": "sha512-XRaVO0YecOpEuIvbhbpTrZgoiI6xBlz6hnlr6EHhd+0x9ase6EmeN+hdwwUaJvLcsFFQ8iWVF1GAK1yB0BWi0w==", - "peer": true, - "dependencies": { - "quickselect": "^2.0.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/rc": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", - "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", - "optional": true, - "peer": true, - "dependencies": { - "deep-extend": "^0.6.0", - "ini": "~1.3.0", - "minimist": "^1.2.0", - "strip-json-comments": "~2.0.1" - }, - "bin": { - "rc": "cli.js" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/rc/node_modules/strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", - "optional": true, - "peer": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/read-pkg": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", - "integrity": "sha512-BLq/cCO9two+lBgiTYNqD6GdtK8s4NpaWrl6/rCO9w0TUS8oJl7cmToOZfRYllKTISY6nt1U7jQ53brmKqY6BA==", - "extraneous": true, - "dependencies": { - "load-json-file": "^4.0.0", - "normalize-package-data": "^2.3.2", - "path-type": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/read-pkg-up": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-4.0.0.tgz", - "integrity": "sha512-6etQSH7nJGsK0RbG/2TeDzZFa8shjQ1um+SwQQ5cwKy0dhSXdOncEhb1CPpvQG4h7FyOV6EB6YlV0yJvZQNAkA==", - "extraneous": true, - "dependencies": { - "find-up": "^3.0.0", - "read-pkg": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/read-pkg-up/node_modules/find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "extraneous": true, - "dependencies": { - "locate-path": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/read-pkg-up/node_modules/locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", - "extraneous": true, - "dependencies": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/read-pkg-up/node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "extraneous": true, - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/read-pkg-up/node_modules/p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", - "extraneous": true, - "dependencies": { - "p-limit": "^2.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/read-pkg-up/node_modules/path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", - "extraneous": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/read-pkg/node_modules/path-type": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", - "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", - "extraneous": true, - "dependencies": { - "pify": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "peer": true, - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/readable-web-to-node-stream": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/readable-web-to-node-stream/-/readable-web-to-node-stream-3.0.2.tgz", - "integrity": "sha512-ePeK6cc1EcKLEhJFt/AebMCLL+GgSKhuygrZ/GLaKZYEecIgIECf4UaUuaByiGtzckwR4ain9VzUh95T1exYGw==", - "peer": true, - "dependencies": { - "readable-stream": "^3.6.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/Borewit" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/readdir-glob": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/readdir-glob/-/readdir-glob-1.1.3.tgz", - "integrity": "sha512-v05I2k7xN8zXvPD9N+z/uhXPaj0sUFCe2rcWZIpBsqxfP7xXFQ0tipAd/wjj1YxWyWtUS5IDJpOG82JKt2EAVA==", - "peer": true, - "dependencies": { - "minimatch": "^5.1.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/readdir-glob/node_modules/minimatch": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", - "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", - "peer": true, - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "extraneous": true, - "license": "MIT", - "dependencies": { - "picomatch": "^2.2.1" - }, - "engines": { - "node": ">=8.10.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/regexp.prototype.flags": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.2.tgz", - "integrity": "sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw==", - "extraneous": true, - "dependencies": { - "call-bind": "^1.0.6", - "define-properties": "^1.2.1", - "es-errors": "^1.3.0", - "set-function-name": "^2.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/release-zalgo": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/release-zalgo/-/release-zalgo-1.0.0.tgz", - "integrity": "sha512-gUAyHVHPPC5wdqX/LG4LWtRYtgjxyX78oanFNTMMyFEfOqdC54s3eE82imuWKbOeqYht2CrNf64Qb8vgmmtZGA==", - "extraneous": true, - "dependencies": { - "es6-error": "^4.0.1" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/reproject": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/reproject/-/reproject-1.2.5.tgz", - "integrity": "sha512-cTH78fi1uuv5gzW/GVepO4LbCvOUhO0X2BEyyvrKkYb4KPRmDPs7cZnIxemHPUIch/CoSI8MPLmXRHZFSHjbKw==", - "peer": true, - "dependencies": { - "concat-stream": "^2.0.0", - "event-stream": "^4.0.0", - "geojson-stream": "0.1.0", - "minimist": "^1.2.0", - "proj4": "^2.4.4" - }, - "bin": { - "reproject": "cli.js" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/reproject/node_modules/concat-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-2.0.0.tgz", - "integrity": "sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==", - "engines": [ - "node >= 6.0" - ], - "peer": true, - "dependencies": { - "buffer-from": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^3.0.2", - "typedarray": "^0.0.6" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/request": { - "version": "2.88.2", - "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", - "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", - "deprecated": "request has been deprecated, see https://github.com/request/request/issues/3142", - "extraneous": true, - "dependencies": { - "aws-sign2": "~0.7.0", - "aws4": "^1.8.0", - "caseless": "~0.12.0", - "combined-stream": "~1.0.6", - "extend": "~3.0.2", - "forever-agent": "~0.6.1", - "form-data": "~2.3.2", - "har-validator": "~5.1.3", - "http-signature": "~1.2.0", - "is-typedarray": "~1.0.0", - "isstream": "~0.1.2", - "json-stringify-safe": "~5.0.1", - "mime-types": "~2.1.19", - "oauth-sign": "~0.9.0", - "performance-now": "^2.1.0", - "qs": "~6.5.2", - "safe-buffer": "^5.1.2", - "tough-cookie": "~2.5.0", - "tunnel-agent": "^0.6.0", - "uuid": "^3.3.2" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/request/node_modules/form-data": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", - "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", - "extraneous": true, - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.6", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 0.12" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/request/node_modules/qs": { - "version": "6.5.3", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.3.tgz", - "integrity": "sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==", - "extraneous": true, - "engines": { - "node": ">=0.6" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/request/node_modules/uuid": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", - "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", - "deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.", - "extraneous": true, - "bin": { - "uuid": "bin/uuid" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", - "extraneous": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/require-main-filename": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", - "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", - "extraneous": true - }, - "node_modules/@ngageoint/mage.service/node_modules/resolve": { - "version": "1.22.8", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", - "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", - "peer": true, - "dependencies": { - "is-core-module": "^2.13.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "extraneous": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/restore-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", - "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", - "optional": true, - "peer": true, - "dependencies": { - "onetime": "^5.1.0", - "signal-exit": "^3.0.2" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/ret": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/ret/-/ret-0.2.2.tgz", - "integrity": "sha512-M0b3YWQs7R3Z917WRQy1HHA7Ba7D8hvZg6UE5mLykJxQVE2ju0IXbGlaHPPlkY+WN7wFP+wUMXmBFA0aV6vYGQ==", - "extraneous": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/reusify": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", - "extraneous": true, - "engines": { - "iojs": ">=1.0.0", - "node": ">=0.10.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/rfc5646": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/rfc5646/-/rfc5646-3.0.0.tgz", - "integrity": "sha512-yFQuMO3ANS0jhQLB3b4NJGQ13RTm6tA9ZRf70nRSmOKchaT88pzh4fBdhcXEOptYdGw22vNqH7IRD/7pjDILZQ==", - "peer": true - }, - "node_modules/@ngageoint/mage.service/node_modules/rimraf": { - "version": "5.0.10", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-5.0.10.tgz", - "integrity": "sha512-l0OE8wL34P4nJH/H2ffoaniAokM2qSmrtXHmlpvYr5AVVX8msAyW0l8NVJFDxlSK4u3Uh/f41cQheDVdnYijwQ==", - "extraneous": true, - "license": "ISC", - "dependencies": { - "glob": "^10.3.7" - }, - "bin": { - "rimraf": "dist/esm/bin.mjs" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/rimraf/node_modules/foreground-child": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.2.1.tgz", - "integrity": "sha512-PXUUyLqrR2XCWICfv6ukppP96sdFwWbNEnfEMt7jNsISjMsvaLNinAHNDYyvkyU+SZG2BTSbT5NjG+vZslfGTA==", - "extraneous": true, - "dependencies": { - "cross-spawn": "^7.0.0", - "signal-exit": "^4.0.1" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/rimraf/node_modules/glob": { - "version": "10.4.2", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.2.tgz", - "integrity": "sha512-GwMlUF6PkPo3Gk21UxkCohOv0PLcIXVtKyLlpEI28R/cO/4eNOdmLk3CMW1wROV/WR/EsZOWAfBbBOqYvs88/w==", - "extraneous": true, - "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^3.1.2", - "minimatch": "^9.0.4", - "minipass": "^7.1.2", - "package-json-from-dist": "^1.0.0", - "path-scurry": "^1.11.1" - }, - "bin": { - "glob": "dist/esm/bin.mjs" - }, - "engines": { - "node": ">=16 || 14 >=14.18" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/rimraf/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", - "extraneous": true, - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/rimraf/node_modules/signal-exit": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", - "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", - "extraneous": true, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/robust-predicates": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/robust-predicates/-/robust-predicates-3.0.2.tgz", - "integrity": "sha512-IXgzBWvWQwE6PrDI05OvmXUIruQTcoMDzRsOd5CDvHCVLcLHMTSYvOK5Cm46kWqlV3yAbuSpBZdJ5oP5OUoStg==", - "peer": true - }, - "node_modules/@ngageoint/mage.service/node_modules/rtree-sql.js": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/rtree-sql.js/-/rtree-sql.js-1.7.0.tgz", - "integrity": "sha512-hue7klBnBrM6pvUPSnXNNgA0yN9FIjmhb8QveVY9q5h/3b//Cd8jaafUE0Ty89fuiamaLEHRw8eQq8mTn6Df5Q==", - "peer": true - }, - "node_modules/@ngageoint/mage.service/node_modules/run-async": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", - "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==", - "optional": true, - "peer": true, - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/run-parallel": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "extraneous": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "dependencies": { - "queue-microtask": "^1.2.2" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/rxjs": { - "version": "6.6.7", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", - "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", - "optional": true, - "peer": true, - "dependencies": { - "tslib": "^1.9.0" - }, - "engines": { - "npm": ">=2.0.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/rxjs/node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "optional": true, - "peer": true - }, - "node_modules/@ngageoint/mage.service/node_modules/safe-array-concat": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.2.tgz", - "integrity": "sha512-vj6RsCsWBCf19jIeHEfkRMw8DPiBb+DMXklQ/1SGDHOMlHdPUkZXFQ2YdplS23zESTijAcurb1aSgJA3AgMu1Q==", - "extraneous": true, - "dependencies": { - "call-bind": "^1.0.7", - "get-intrinsic": "^1.2.4", - "has-symbols": "^1.0.3", - "isarray": "^2.0.5" - }, - "engines": { - "node": ">=0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "peer": true - }, - "node_modules/@ngageoint/mage.service/node_modules/safe-regex-test": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.3.tgz", - "integrity": "sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw==", - "extraneous": true, - "dependencies": { - "call-bind": "^1.0.6", - "es-errors": "^1.3.0", - "is-regex": "^1.1.4" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "peer": true - }, - "node_modules/@ngageoint/mage.service/node_modules/sax": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.4.1.tgz", - "integrity": "sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==", - "peer": true - }, - "node_modules/@ngageoint/mage.service/node_modules/semver": { - "version": "7.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", - "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", - "license": "ISC", - "peer": true, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/send": { - "version": "0.19.0", - "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz", - "integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==", - "license": "MIT", - "peer": true, - "dependencies": { - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "fresh": "0.5.2", - "http-errors": "2.0.0", - "mime": "1.6.0", - "ms": "2.1.3", - "on-finished": "2.4.1", - "range-parser": "~1.2.1", - "statuses": "2.0.1" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/send/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "license": "MIT", - "peer": true, - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/send/node_modules/debug/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "license": "MIT", - "peer": true - }, - "node_modules/@ngageoint/mage.service/node_modules/send/node_modules/encodeurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", - "license": "MIT", - "peer": true, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/send/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "license": "MIT", - "peer": true - }, - "node_modules/@ngageoint/mage.service/node_modules/serialize-javascript": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", - "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", - "extraneous": true, - "license": "BSD-3-Clause", - "dependencies": { - "randombytes": "^2.1.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/serve-static": { - "version": "1.16.2", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz", - "integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==", - "license": "MIT", - "peer": true, - "dependencies": { - "encodeurl": "~2.0.0", - "escape-html": "~1.0.3", - "parseurl": "~1.3.3", - "send": "0.19.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/set-blocking": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", - "extraneous": true - }, - "node_modules/@ngageoint/mage.service/node_modules/set-function-length": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", - "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", - "peer": true, - "dependencies": { - "define-data-property": "^1.1.4", - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.4", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/set-function-name": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz", - "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", - "extraneous": true, - "dependencies": { - "define-data-property": "^1.1.4", - "es-errors": "^1.3.0", - "functions-have-names": "^1.2.3", - "has-property-descriptors": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/setimmediate": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", - "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==", - "extraneous": true, - "license": "MIT" - }, - "node_modules/@ngageoint/mage.service/node_modules/setprototypeof": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", - "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", - "license": "ISC", - "peer": true - }, - "node_modules/@ngageoint/mage.service/node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "extraneous": true, - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "extraneous": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/shell-quote": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.1.tgz", - "integrity": "sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA==", - "extraneous": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/side-channel": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", - "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", - "license": "MIT", - "peer": true, - "dependencies": { - "es-errors": "^1.3.0", - "object-inspect": "^1.13.3", - "side-channel-list": "^1.0.0", - "side-channel-map": "^1.0.1", - "side-channel-weakmap": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/side-channel-list": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", - "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", - "license": "MIT", - "peer": true, - "dependencies": { - "es-errors": "^1.3.0", - "object-inspect": "^1.13.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/side-channel-map": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", - "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", - "license": "MIT", - "peer": true, - "dependencies": { - "call-bound": "^1.0.2", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.5", - "object-inspect": "^1.13.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/side-channel-weakmap": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", - "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", - "license": "MIT", - "peer": true, - "dependencies": { - "call-bound": "^1.0.2", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.5", - "object-inspect": "^1.13.3", - "side-channel-map": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/sift": { - "version": "16.0.1", - "resolved": "https://registry.npmjs.org/sift/-/sift-16.0.1.tgz", - "integrity": "sha512-Wv6BjQ5zbhW7VFefWusVP33T/EM0vYikCaQ2qR8yULbsilAT8/wQaXvuQ3ptGLpoKx+lihJE3y2UTgKDyyNHZQ==", - "peer": true - }, - "node_modules/@ngageoint/mage.service/node_modules/signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "optional": true, - "peer": true - }, - "node_modules/@ngageoint/mage.service/node_modules/simple-concat": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", - "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "optional": true, - "peer": true - }, - "node_modules/@ngageoint/mage.service/node_modules/simple-get": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz", - "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "optional": true, - "peer": true, - "dependencies": { - "decompress-response": "^6.0.0", - "once": "^1.3.1", - "simple-concat": "^1.0.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/simplify-js": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/simplify-js/-/simplify-js-1.2.4.tgz", - "integrity": "sha512-vITfSlwt7h/oyrU42R83mtzFpwYk3+mkH9bOHqq/Qw6n8rtR7aE3NZQ5fbcyCUVVmuMJR6ynsAhOfK2qoah8Jg==", - "peer": true - }, - "node_modules/@ngageoint/mage.service/node_modules/sinon": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/sinon/-/sinon-7.5.0.tgz", - "integrity": "sha512-AoD0oJWerp0/rY9czP/D6hDTTUYGpObhZjMpd7Cl/A6+j0xBE+ayL/ldfggkBXUs0IkvIiM1ljM8+WkOc5k78Q==", - "deprecated": "16.1.1", - "extraneous": true, - "dependencies": { - "@sinonjs/commons": "^1.4.0", - "@sinonjs/formatio": "^3.2.1", - "@sinonjs/samsam": "^3.3.3", - "diff": "^3.5.0", - "lolex": "^4.2.0", - "nise": "^1.5.2", - "supports-color": "^5.5.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/sinon-chai": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/sinon-chai/-/sinon-chai-3.7.0.tgz", - "integrity": "sha512-mf5NURdUaSdnatJx3uhoBOrY9dtL19fiOtAdT1Azxg3+lNJFiuN0uzaU3xX1LeAfL17kHQhTAJgpsfhbMJMY2g==", - "extraneous": true, - "license": "(BSD-2-Clause OR WTFPL)", - "peerDependencies": { - "chai": "^4.0.0", - "sinon": ">=4.0.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/sinon-mongoose": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/sinon-mongoose/-/sinon-mongoose-2.3.0.tgz", - "integrity": "sha512-d0rrL53wuDDs91GMCFAvQam64IpdVfkaxA4cGLTZfw1d5tTg6+F/D7F080d1n3d1gSHJBZLUf9pGpijC/x7xKQ==", - "extraneous": true, - "peerDependencies": { - "sinon": "5 - 7" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/sinon/node_modules/diff": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", - "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", - "extraneous": true, - "engines": { - "node": ">=0.3.1" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/sinon/node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "extraneous": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/sinon/node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "extraneous": true, - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "extraneous": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/smart-buffer": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", - "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", - "peer": true, - "engines": { - "node": ">= 6.0.0", - "npm": ">= 3.0.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/socks": { - "version": "2.8.3", - "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.3.tgz", - "integrity": "sha512-l5x7VUUWbjVFbafGLxPWkYsHIhEvmF85tbIeFZWc8ZPtoMyybuEhL7Jye/ooC4/d48FgOjSJXgsF/AJPYCW8Zw==", - "peer": true, - "dependencies": { - "ip-address": "^9.0.5", - "smart-buffer": "^4.2.0" - }, - "engines": { - "node": ">= 10.0.0", - "npm": ">= 3.0.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "extraneous": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/sparse-bitfield": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz", - "integrity": "sha512-kvzhi7vqKTfkh0PZU+2D2PIllw2ymqJKujUcyPMd9Y75Nv4nPbGJZXNhxsgdQab2BmlDct1YnfQCguEvHr7VsQ==", - "optional": true, - "peer": true, - "dependencies": { - "memory-pager": "^1.0.2" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/spawn-wrap": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/spawn-wrap/-/spawn-wrap-1.4.3.tgz", - "integrity": "sha512-IgB8md0QW/+tWqcavuFgKYR/qIRvJkRLPJDFaoXtLLUaVcCDK0+HeFTkmQHj3eprcYhc+gOl0aEA1w7qZlYezw==", - "extraneous": true, - "dependencies": { - "foreground-child": "^1.5.6", - "mkdirp": "^0.5.0", - "os-homedir": "^1.0.1", - "rimraf": "^2.6.2", - "signal-exit": "^3.0.2", - "which": "^1.3.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/spawn-wrap/node_modules/rimraf": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", - "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", - "deprecated": "Rimraf versions prior to v4 are no longer supported", - "extraneous": true, - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/spawn-wrap/node_modules/which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", - "extraneous": true, - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "which": "bin/which" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/spdx-correct": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", - "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==", - "extraneous": true, - "dependencies": { - "spdx-expression-parse": "^3.0.0", - "spdx-license-ids": "^3.0.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/spdx-exceptions": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz", - "integrity": "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==", - "extraneous": true - }, - "node_modules/@ngageoint/mage.service/node_modules/spdx-expression-parse": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", - "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", - "extraneous": true, - "dependencies": { - "spdx-exceptions": "^2.1.0", - "spdx-license-ids": "^3.0.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/spdx-license-ids": { - "version": "3.0.18", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.18.tgz", - "integrity": "sha512-xxRs31BqRYHwiMzudOrpSiHtZ8i/GeionCBDSilhYRj+9gIcI8wCZTlXZKu9vZIVqViP3dcp9qE5G6AlIaD+TQ==", - "extraneous": true - }, - "node_modules/@ngageoint/mage.service/node_modules/splaytree": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/splaytree/-/splaytree-3.1.2.tgz", - "integrity": "sha512-4OM2BJgC5UzrhVnnJA4BkHKGtjXNzzUfpQjCO8I05xYPsfS/VuQDwjCGGMi8rYQilHEV4j8NBqTFbls/PZEE7A==", - "peer": true - }, - "node_modules/@ngageoint/mage.service/node_modules/split": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/split/-/split-1.0.1.tgz", - "integrity": "sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg==", - "peer": true, - "dependencies": { - "through": "2" - }, - "engines": { - "node": "*" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", - "peer": true - }, - "node_modules/@ngageoint/mage.service/node_modules/sshpk": { - "version": "1.18.0", - "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.18.0.tgz", - "integrity": "sha512-2p2KJZTSqQ/I3+HX42EpYOa2l3f8Erv8MWKsy2I9uf4wA7yFIkXRffYdsx86y6z4vHtV8u7g+pPlr8/4ouAxsQ==", - "extraneous": true, - "dependencies": { - "asn1": "~0.2.3", - "assert-plus": "^1.0.0", - "bcrypt-pbkdf": "^1.0.0", - "dashdash": "^1.12.0", - "ecc-jsbn": "~0.1.1", - "getpass": "^0.1.1", - "jsbn": "~0.1.0", - "safer-buffer": "^2.0.2", - "tweetnacl": "~0.14.0" - }, - "bin": { - "sshpk-conv": "bin/sshpk-conv", - "sshpk-sign": "bin/sshpk-sign", - "sshpk-verify": "bin/sshpk-verify" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/sshpk/node_modules/jsbn": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", - "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==", - "extraneous": true - }, - "node_modules/@ngageoint/mage.service/node_modules/stack-trace": { - "version": "0.0.10", - "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", - "integrity": "sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==", - "peer": true, - "engines": { - "node": "*" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/statuses": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", - "license": "MIT", - "peer": true, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/stop-iteration-iterator": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.0.0.tgz", - "integrity": "sha512-iCGQj+0l0HOdZ2AEeBADlsRC+vsnDsZsbdSiH1yNSjcfKM7fdpCMfqAL/dwF5BLiw/XhRft/Wax6zQbhq2BcjQ==", - "extraneous": true, - "dependencies": { - "internal-slot": "^1.0.4" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/stream-combiner": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/stream-combiner/-/stream-combiner-0.2.2.tgz", - "integrity": "sha512-6yHMqgLYDzQDcAkL+tjJDC5nSNuNIx0vZtRZeiPh7Saef7VHX9H5Ijn9l2VIol2zaNYlYEX6KyuT/237A58qEQ==", - "peer": true, - "dependencies": { - "duplexer": "~0.1.1", - "through": "~2.3.4" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/streamsearch": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", - "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==", - "peer": true, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/streamx": { - "version": "2.22.0", - "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.22.0.tgz", - "integrity": "sha512-sLh1evHOzBy/iWRiR6d1zRcLao4gGZr3C1kzNz4fopCOKJb6xD9ub8Mpi9Mr1R6id5o43S+d93fI48UC5uM9aw==", - "extraneous": true, - "license": "MIT", - "dependencies": { - "fast-fifo": "^1.3.2", - "text-decoder": "^1.1.0" - }, - "optionalDependencies": { - "bare-events": "^2.2.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "peer": true, - "dependencies": { - "safe-buffer": "~5.2.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "optional": true, - "peer": true, - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/string-width-cjs": { - "name": "string-width", - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "extraneous": true, - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/string.prototype.padend": { - "version": "3.1.6", - "resolved": "https://registry.npmjs.org/string.prototype.padend/-/string.prototype.padend-3.1.6.tgz", - "integrity": "sha512-XZpspuSB7vJWhvJc9DLSlrXl1mcA2BdoY5jjnS135ydXqLoqhs96JjDtCkjJEQHvfqZIp9hBuBMgI589peyx9Q==", - "extraneous": true, - "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.2", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/string.prototype.trim": { - "version": "1.2.9", - "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.9.tgz", - "integrity": "sha512-klHuCNxiMZ8MlsOihJhJEBJAiMVqU3Z2nEXWfWnIqjN0gEFS9J9+IxKozWWtQGcgoa1WUZzLjKPTr4ZHNFTFxw==", - "extraneous": true, - "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.0", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/string.prototype.trimend": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.8.tgz", - "integrity": "sha512-p73uL5VCHCO2BZZ6krwwQE3kCzM7NKmis8S//xEC6fQonchbum4eP6kR4DLEjQFO3Wnj3Fuo8NM0kOSjVdHjZQ==", - "extraneous": true, - "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-object-atoms": "^1.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/string.prototype.trimstart": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz", - "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==", - "extraneous": true, - "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "optional": true, - "peer": true, - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/strip-ansi-cjs": { - "name": "strip-ansi", - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "extraneous": true, - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", - "extraneous": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "extraneous": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/strnum": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/strnum/-/strnum-1.0.5.tgz", - "integrity": "sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==", - "optional": true, - "peer": true - }, - "node_modules/@ngageoint/mage.service/node_modules/strtok3": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/strtok3/-/strtok3-6.3.0.tgz", - "integrity": "sha512-fZtbhtvI9I48xDSywd/somNqgUHl2L2cstmXCCif0itOf96jeW18MBSyrLuNicYQVkvpOxkZtkzujiTJ9LW5Jw==", - "peer": true, - "dependencies": { - "@tokenizer/token": "^0.3.0", - "peek-readable": "^4.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/Borewit" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/superagent": { - "version": "8.1.2", - "resolved": "https://registry.npmjs.org/superagent/-/superagent-8.1.2.tgz", - "integrity": "sha512-6WTxW1EB6yCxV5VFOIPQruWGHqc3yI7hEmZK6h+pyk69Lk/Ut7rLUY6W/ONF2MjBuGjvmMiIpsrVJ2vjrHlslA==", - "deprecated": "Please upgrade to v9.0.0+ as we have fixed a public vulnerability with formidable dependency. Note that v9.0.0+ requires Node.js v14.18.0+. See https://github.com/ladjs/superagent/pull/1800 for insight. This project is supported and maintained by the team at Forward Email @ https://forwardemail.net", - "license": "MIT", - "peer": true, - "dependencies": { - "component-emitter": "^1.3.0", - "cookiejar": "^2.1.4", - "debug": "^4.3.4", - "fast-safe-stringify": "^2.1.1", - "form-data": "^4.0.0", - "formidable": "^2.1.2", - "methods": "^1.1.2", - "mime": "2.6.0", - "qs": "^6.11.0", - "semver": "^7.3.8" - }, - "engines": { - "node": ">=6.4.0 <13 || >=14" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/superagent/node_modules/mime": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", - "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", - "peer": true, - "bin": { - "mime": "cli.js" - }, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/supertest": { - "version": "6.3.4", - "resolved": "https://registry.npmjs.org/supertest/-/supertest-6.3.4.tgz", - "integrity": "sha512-erY3HFDG0dPnhw4U+udPfrzXa4xhSG+n4rxfRuZWCUvjFWwKl+OxWf/7zk50s84/fAAs7vf5QAb9uRa0cCykxw==", - "extraneous": true, - "license": "MIT", - "dependencies": { - "methods": "^1.1.2", - "superagent": "^8.1.2" - }, - "engines": { - "node": ">=6.4.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "optional": true, - "peer": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/supports-preserve-symlinks-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "peer": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/svg-captcha": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/svg-captcha/-/svg-captcha-1.4.0.tgz", - "integrity": "sha512-/fkkhavXPE57zRRCjNqAP3txRCSncpMx3NnNZL7iEoyAtYwUjPhJxW6FQTQPG5UPEmCrbFoXS10C3YdJlW7PDg==", - "peer": true, - "dependencies": { - "opentype.js": "^0.7.3" - }, - "engines": { - "node": ">=4.x" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/tar-fs": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.1.tgz", - "integrity": "sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==", - "optional": true, - "peer": true, - "dependencies": { - "chownr": "^1.1.1", - "mkdirp-classic": "^0.5.2", - "pump": "^3.0.0", - "tar-stream": "^2.1.4" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/tar-stream": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", - "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", - "peer": true, - "dependencies": { - "bl": "^4.0.3", - "end-of-stream": "^1.4.1", - "fs-constants": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^3.1.1" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/test-exclude": { - "version": "5.2.3", - "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-5.2.3.tgz", - "integrity": "sha512-M+oxtseCFO3EDtAaGH7iiej3CBkzXqFMbzqYAACdzKui4eZA+pq3tZEwChvOdNfa7xxy8BfbmgJSIr43cC/+2g==", - "extraneous": true, - "dependencies": { - "glob": "^7.1.3", - "minimatch": "^3.0.4", - "read-pkg-up": "^4.0.0", - "require-main-filename": "^2.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/test-exclude/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "extraneous": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/test-exclude/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "extraneous": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/text-decoder": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/text-decoder/-/text-decoder-1.2.3.tgz", - "integrity": "sha512-3/o9z3X0X0fTupwsYvR03pJ/DjWuqqrfwBgTQzdWDiQSm9KitAyz/9WqsT2JQW7KV2m+bC2ol/zqpW37NHxLaA==", - "extraneous": true, - "license": "Apache-2.0", - "dependencies": { - "b4a": "^1.6.4" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", - "extraneous": true - }, - "node_modules/@ngageoint/mage.service/node_modules/through": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", - "peer": true - }, - "node_modules/@ngageoint/mage.service/node_modules/tiny-inflate": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/tiny-inflate/-/tiny-inflate-1.0.3.tgz", - "integrity": "sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw==", - "peer": true - }, - "node_modules/@ngageoint/mage.service/node_modules/tmp": { - "version": "0.0.33", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", - "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", - "optional": true, - "peer": true, - "dependencies": { - "os-tmpdir": "~1.0.2" - }, - "engines": { - "node": ">=0.6.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/to-fast-properties": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", - "peer": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "extraneous": true, - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/toidentifier": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", - "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", - "license": "MIT", - "peer": true, - "engines": { - "node": ">=0.6" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/token-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/token-stream/-/token-stream-1.0.0.tgz", - "integrity": "sha512-VSsyNPPW74RpHwR8Fc21uubwHY7wMDeJLys2IX5zJNih+OnAnaifKHo+1LHT7DAdloQ7apeaaWg8l7qnf/TnEg==", - "peer": true - }, - "node_modules/@ngageoint/mage.service/node_modules/token-types": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/token-types/-/token-types-4.2.1.tgz", - "integrity": "sha512-6udB24Q737UD/SDsKAHI9FCRP7Bqc9D/MQUV02ORQg5iskjtLJlZJNdN4kKtcdtwCeWIwIHDGaUsTsCCAa8sFQ==", - "peer": true, - "dependencies": { - "@tokenizer/token": "^0.3.0", - "ieee754": "^1.2.1" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/Borewit" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/tough-cookie": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", - "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", - "extraneous": true, - "dependencies": { - "psl": "^1.1.28", - "punycode": "^2.1.1" - }, - "engines": { - "node": ">=0.8" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/tr46": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-3.0.0.tgz", - "integrity": "sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA==", - "peer": true, - "dependencies": { - "punycode": "^2.1.1" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/ts-api-utils": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.3.0.tgz", - "integrity": "sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==", - "extraneous": true, - "engines": { - "node": ">=16" - }, - "peerDependencies": { - "typescript": ">=4.2.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/tslib": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD", - "peer": true - }, - "node_modules/@ngageoint/mage.service/node_modules/tunnel-agent": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", - "optional": true, - "peer": true, - "dependencies": { - "safe-buffer": "^5.0.1" - }, - "engines": { - "node": "*" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/tweetnacl": { - "version": "0.14.5", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", - "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==", - "extraneous": true - }, - "node_modules/@ngageoint/mage.service/node_modules/type-check": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", - "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", - "extraneous": true, - "dependencies": { - "prelude-ls": "^1.2.1" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/type-detect": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", - "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", - "extraneous": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "extraneous": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/type-is": { - "version": "1.6.18", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", - "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", - "peer": true, - "dependencies": { - "media-typer": "0.3.0", - "mime-types": "~2.1.24" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/typed-array-buffer": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.2.tgz", - "integrity": "sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ==", - "extraneous": true, - "dependencies": { - "call-bind": "^1.0.7", - "es-errors": "^1.3.0", - "is-typed-array": "^1.1.13" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/typed-array-byte-length": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.1.tgz", - "integrity": "sha512-3iMJ9q0ao7WE9tWcaYKIptkNBuOIcZCCT0d4MRvuuH88fEoEH62IuQe0OtraD3ebQEoTRk8XCBoknUNc1Y67pw==", - "extraneous": true, - "dependencies": { - "call-bind": "^1.0.7", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-proto": "^1.0.3", - "is-typed-array": "^1.1.13" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/typed-array-byte-offset": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.2.tgz", - "integrity": "sha512-Ous0vodHa56FviZucS2E63zkgtgrACj7omjwd/8lTEMEPFFyjfixMZ1ZXenpgCFBBt4EC1J2XsyVS2gkG0eTFA==", - "extraneous": true, - "dependencies": { - "available-typed-arrays": "^1.0.7", - "call-bind": "^1.0.7", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-proto": "^1.0.3", - "is-typed-array": "^1.1.13" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/typed-array-length": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.6.tgz", - "integrity": "sha512-/OxDN6OtAk5KBpGb28T+HZc2M+ADtvRxXrKKbUwtsLgdoxgX13hyy7ek6bFRl5+aBs2yZzB0c4CnQfAtVypW/g==", - "extraneous": true, - "dependencies": { - "call-bind": "^1.0.7", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-proto": "^1.0.3", - "is-typed-array": "^1.1.13", - "possible-typed-array-names": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/typedarray": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", - "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==", - "peer": true - }, - "node_modules/@ngageoint/mage.service/node_modules/typescript": { - "version": "4.9.5", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", - "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", - "extraneous": true, - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=4.2.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/uid-safe": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/uid-safe/-/uid-safe-2.1.5.tgz", - "integrity": "sha512-KPHm4VL5dDXKz01UuEd88Df+KzynaohSL9fBh096KWAxSKZQDI2uBrVqtvRM4rwrIrRRKsdLNML/lnaaVSRioA==", - "peer": true, - "dependencies": { - "random-bytes": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/uid2": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/uid2/-/uid2-0.0.4.tgz", - "integrity": "sha512-IevTus0SbGwQzYh3+fRsAMTVVPOoIVufzacXcHPmdlle1jUpq7BRL+mw3dgeLanvGZdwwbWhRV6XrcFNdBmjWA==", - "peer": true - }, - "node_modules/@ngageoint/mage.service/node_modules/unbox-primitive": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", - "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", - "extraneous": true, - "dependencies": { - "call-bind": "^1.0.2", - "has-bigints": "^1.0.2", - "has-symbols": "^1.0.3", - "which-boxed-primitive": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/underscore": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.12.1.tgz", - "integrity": "sha512-hEQt0+ZLDVUMhebKxL4x1BTtDY7bavVofhZ9KZ4aI26X9SRaE+Y3m83XUL1UP2jn8ynjndwCCpEHdUG+9pP1Tw==", - "license": "MIT", - "peer": true - }, - "node_modules/@ngageoint/mage.service/node_modules/undici-types": { - "version": "5.26.5", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", - "license": "MIT", - "peer": true - }, - "node_modules/@ngageoint/mage.service/node_modules/uniqid": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/uniqid/-/uniqid-5.4.0.tgz", - "integrity": "sha512-38JRbJ4Fj94VmnC7G/J/5n5SC7Ab46OM5iNtSstB/ko3l1b5g7ALt4qzHFgGciFkyiRNtDXtLNb+VsxtMSE77A==", - "license": "MIT", - "peer": true - }, - "node_modules/@ngageoint/mage.service/node_modules/universalify": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", - "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", - "peer": true, - "engines": { - "node": ">= 4.0.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/unpipe": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", - "peer": true, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "extraneous": true, - "dependencies": { - "punycode": "^2.1.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", - "peer": true - }, - "node_modules/@ngageoint/mage.service/node_modules/utils-merge": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", - "peer": true, - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/uuid": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", - "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", - "funding": [ - "https://github.com/sponsors/broofa", - "https://github.com/sponsors/ctavan" - ], - "optional": true, - "peer": true, - "bin": { - "uuid": "dist/bin/uuid" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/validate-npm-package-license": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", - "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", - "extraneous": true, - "dependencies": { - "spdx-correct": "^3.0.0", - "spdx-expression-parse": "^3.0.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/vary": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", - "peer": true, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/vasync": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/vasync/-/vasync-2.2.1.tgz", - "integrity": "sha512-Hq72JaTpcTFdWiNA4Y22Amej2GH3BFmBaKPPlDZ4/oC8HNn2ISHLkFrJU4Ds8R3jcUi7oo5Y9jcMHKjES+N9wQ==", - "engines": [ - "node >=0.6.0" - ], - "peer": true, - "dependencies": { - "verror": "1.10.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/vasync/node_modules/verror": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", - "integrity": "sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==", - "engines": [ - "node >=0.6.0" - ], - "peer": true, - "dependencies": { - "assert-plus": "^1.0.0", - "core-util-is": "1.0.2", - "extsprintf": "^1.2.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/verror": { - "version": "1.10.1", - "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.1.tgz", - "integrity": "sha512-veufcmxri4e3XSrT0xwfUR7kguIkaxBeosDg00yDWhk49wdwkSUrvvsm7nc75e1PUyvIeZj6nS8VQRYz2/S4Xg==", - "peer": true, - "dependencies": { - "assert-plus": "^1.0.0", - "core-util-is": "1.0.2", - "extsprintf": "^1.2.0" - }, - "engines": { - "node": ">=0.6.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/void-elements": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-3.1.0.tgz", - "integrity": "sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w==", - "peer": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/walk": { - "version": "2.3.15", - "resolved": "https://registry.npmjs.org/walk/-/walk-2.3.15.tgz", - "integrity": "sha512-4eRTBZljBfIISK1Vnt69Gvr2w/wc3U6Vtrw7qiN5iqYJPH7LElcYh/iU4XWhdCy2dZqv1ToMyYlybDylfG/5Vg==", - "license": "(MIT OR Apache-2.0)", - "peer": true, - "dependencies": { - "foreachasync": "^3.0.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/webidl-conversions": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", - "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", - "peer": true, - "engines": { - "node": ">=12" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/webworkify": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/webworkify/-/webworkify-1.5.0.tgz", - "integrity": "sha512-AMcUeyXAhbACL8S2hqqdqOLqvJ8ylmIbNwUIqQujRSouf4+eUFaXbG6F1Rbu+srlJMmxQWsiU7mOJi0nMBfM1g==", - "peer": true - }, - "node_modules/@ngageoint/mage.service/node_modules/whatwg-url": { - "version": "11.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-11.0.0.tgz", - "integrity": "sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ==", - "peer": true, - "dependencies": { - "tr46": "^3.0.0", - "webidl-conversions": "^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "extraneous": true, - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/which-boxed-primitive": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", - "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", - "extraneous": true, - "dependencies": { - "is-bigint": "^1.0.1", - "is-boolean-object": "^1.1.0", - "is-number-object": "^1.0.4", - "is-string": "^1.0.5", - "is-symbol": "^1.0.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/which-collection": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz", - "integrity": "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==", - "extraneous": true, - "dependencies": { - "is-map": "^2.0.3", - "is-set": "^2.0.3", - "is-weakmap": "^2.0.2", - "is-weakset": "^2.0.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/which-module": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.1.tgz", - "integrity": "sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==", - "extraneous": true - }, - "node_modules/@ngageoint/mage.service/node_modules/which-typed-array": { - "version": "1.1.15", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.15.tgz", - "integrity": "sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==", - "extraneous": true, - "dependencies": { - "available-typed-arrays": "^1.0.7", - "call-bind": "^1.0.7", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-tostringtag": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/winston": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/winston/-/winston-1.1.2.tgz", - "integrity": "sha512-rl9hA8se2gjdYI6nP1f+kjjSCFCZrObIJB/eXOcMdzWxxcYp7exyc5Bs248fwLT+wHA/+aK0VtBlPHL8qO0T0w==", - "license": "MIT", - "peer": true, - "dependencies": { - "async": "~1.0.0", - "colors": "1.0.x", - "cycle": "1.0.x", - "eyes": "0.1.x", - "isstream": "0.1.x", - "pkginfo": "0.3.x", - "stack-trace": "0.0.x" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/winston/node_modules/async": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/async/-/async-1.0.0.tgz", - "integrity": "sha512-5mO7DX4CbJzp9zjaFXusQQ4tzKJARjNB1Ih1pVBi8wkbmXy/xzIDgEMXxWePLzt2OdFwaxfneIlT1nCiXubrPQ==", - "license": "MIT", - "peer": true - }, - "node_modules/@ngageoint/mage.service/node_modules/with": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/with/-/with-7.0.2.tgz", - "integrity": "sha512-RNGKj82nUPg3g5ygxkQl0R937xLyho1J24ItRCBTr/m1YnZkzJy1hUiHUJrc/VlsDQzsCnInEGSg3bci0Lmd4w==", - "peer": true, - "dependencies": { - "@babel/parser": "^7.9.6", - "@babel/types": "^7.9.6", - "assert-never": "^1.2.1", - "babel-walk": "3.0.0-canary-5" - }, - "engines": { - "node": ">= 10.0.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/wkt-parser": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/wkt-parser/-/wkt-parser-1.3.3.tgz", - "integrity": "sha512-ZnV3yH8/k58ZPACOXeiHaMuXIiaTk1t0hSUVisbO0t4RjA5wPpUytcxeyiN2h+LZRrmuHIh/1UlrR9e7DHDvTw==", - "peer": true - }, - "node_modules/@ngageoint/mage.service/node_modules/wkx": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/wkx/-/wkx-0.5.0.tgz", - "integrity": "sha512-Xng/d4Ichh8uN4l0FToV/258EjMGU9MGcA0HV2d9B/ZpZB3lqQm7nkOdZdm5GhKtLLhAE7PiVQwN4eN+2YJJUg==", - "license": "MIT", - "peer": true, - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/wms-capabilities": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/wms-capabilities/-/wms-capabilities-0.6.0.tgz", - "integrity": "sha512-yTbtesuSyKkHhw1TUX4VklXEkbQByc+hyo7TWq1sGjBA6tQ+XN32w546TPDTzROuwFTE/Dcq5F5FN4aA9nesaQ==", - "license": "BSD-2-Clause", - "peer": true, - "bin": { - "wmscapabilities": "bin/wmscapabilities" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/word-wrap": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", - "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", - "extraneous": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/workerpool": { - "version": "6.5.1", - "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.5.1.tgz", - "integrity": "sha512-Fs4dNYcsdpYSAfVxhnl1L5zTksjvOJxtC5hzMNl+1t9B8hTJTdKDyZ5ju7ztgPy+ft9tBFXoOlDNiOT9WUXZlA==", - "extraneous": true, - "license": "Apache-2.0" - }, - "node_modules/@ngageoint/mage.service/node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "extraneous": true, - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/wrap-ansi-cjs": { - "name": "wrap-ansi", - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "extraneous": true, - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "peer": true - }, - "node_modules/@ngageoint/mage.service/node_modules/write-file-atomic": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-2.4.3.tgz", - "integrity": "sha512-GaETH5wwsX+GcnzhPgKcKjJ6M2Cq3/iZp1WyY/X1CSqrW+jVNM9Y7D8EC2sM4ZG/V8wZlSniJnCKWPmBYAucRQ==", - "extraneous": true, - "dependencies": { - "graceful-fs": "^4.1.11", - "imurmurhash": "^0.1.4", - "signal-exit": "^3.0.2" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/xml-crypto": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/xml-crypto/-/xml-crypto-3.2.0.tgz", - "integrity": "sha512-qVurBUOQrmvlgmZqIVBqmb06TD2a/PpEUfFPgD7BuBfjmoH4zgkqaWSIJrnymlCvM2GGt9x+XtJFA+ttoAufqg==", - "peer": true, - "dependencies": { - "@xmldom/xmldom": "^0.8.8", - "xpath": "0.0.32" - }, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/xml-crypto/node_modules/@xmldom/xmldom": { - "version": "0.8.10", - "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.10.tgz", - "integrity": "sha512-2WALfTl4xo2SkGCYRt6rDTFfk9R1czmBvUQy12gK2KuRKIpWEhcbbzy8EZXtz/jkRqHX8bFEc6FC1HjX4TUWYw==", - "license": "MIT", - "peer": true, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/xml-crypto/node_modules/xpath": { - "version": "0.0.32", - "resolved": "https://registry.npmjs.org/xpath/-/xpath-0.0.32.tgz", - "integrity": "sha512-rxMJhSIoiO8vXcWvSifKqhvV96GjiD5wYb8/QHdoRyQvraTpp4IEv944nhGausZZ3u7dhQXteZuZbaqfpB7uYw==", - "peer": true, - "engines": { - "node": ">=0.6.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/xml-encryption": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/xml-encryption/-/xml-encryption-3.0.2.tgz", - "integrity": "sha512-VxYXPvsWB01/aqVLd6ZMPWZ+qaj0aIdF+cStrVJMcFj3iymwZeI0ABzB3VqMYv48DkSpRhnrXqTUkR34j+UDyg==", - "peer": true, - "dependencies": { - "@xmldom/xmldom": "^0.8.5", - "escape-html": "^1.0.3", - "xpath": "0.0.32" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/xml-encryption/node_modules/@xmldom/xmldom": { - "version": "0.8.10", - "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.10.tgz", - "integrity": "sha512-2WALfTl4xo2SkGCYRt6rDTFfk9R1czmBvUQy12gK2KuRKIpWEhcbbzy8EZXtz/jkRqHX8bFEc6FC1HjX4TUWYw==", - "license": "MIT", - "peer": true, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/xml-encryption/node_modules/xpath": { - "version": "0.0.32", - "resolved": "https://registry.npmjs.org/xpath/-/xpath-0.0.32.tgz", - "integrity": "sha512-rxMJhSIoiO8vXcWvSifKqhvV96GjiD5wYb8/QHdoRyQvraTpp4IEv944nhGausZZ3u7dhQXteZuZbaqfpB7uYw==", - "peer": true, - "engines": { - "node": ">=0.6.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/xml2js": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.5.0.tgz", - "integrity": "sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA==", - "peer": true, - "dependencies": { - "sax": ">=0.6.0", - "xmlbuilder": "~11.0.0" - }, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/xml2js/node_modules/xmlbuilder": { - "version": "11.0.1", - "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", - "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==", - "peer": true, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/xmlbuilder": { - "version": "15.1.1", - "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-15.1.1.tgz", - "integrity": "sha512-yMqGBqtXyeN1e3TGYvgNgDVZ3j84W4cwkOXQswghol6APgZWaff9lnbvN7MHYJOiXsvGPXtjTYJEiC9J2wv9Eg==", - "peer": true, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/xmlbuilder2": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/xmlbuilder2/-/xmlbuilder2-3.1.1.tgz", - "integrity": "sha512-WCSfbfZnQDdLQLiMdGUQpMxxckeQ4oZNMNhLVkcekTu7xhD4tuUDyAPoY8CwXvBYE6LwBHd6QW2WZXlOWr1vCw==", - "license": "MIT", - "peer": true, - "dependencies": { - "@oozcitak/dom": "1.15.10", - "@oozcitak/infra": "1.0.8", - "@oozcitak/util": "8.3.8", - "js-yaml": "3.14.1" - }, - "engines": { - "node": ">=12.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/xmlbuilder2/node_modules/js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", - "license": "MIT", - "peer": true, - "dependencies": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/xpath": { - "version": "0.0.34", - "resolved": "https://registry.npmjs.org/xpath/-/xpath-0.0.34.tgz", - "integrity": "sha512-FxF6+rkr1rNSQrhUNYrAFJpRXNzlDoMxeXN5qI84939ylEv3qqPFKa85Oxr6tDaJKqwW6KKyo2v26TSv3k6LeA==", - "license": "MIT", - "peer": true, - "engines": { - "node": ">=0.6.0" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/xtend": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", - "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", - "peer": true, - "engines": { - "node": ">=0.4" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "extraneous": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/yallist": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", - "integrity": "sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==", - "extraneous": true - }, - "node_modules/@ngageoint/mage.service/node_modules/yaml": { - "version": "1.10.2", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", - "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", - "license": "ISC", - "peer": true, - "engines": { - "node": ">= 6" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/yargs": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", - "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", - "extraneous": true, - "dependencies": { - "cliui": "^7.0.2", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.0", - "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/yargs-parser": { - "version": "20.2.9", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", - "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", - "extraneous": true, - "license": "ISC", - "engines": { - "node": ">=10" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/yargs-unparser": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", - "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", - "extraneous": true, - "dependencies": { - "camelcase": "^6.0.0", - "decamelize": "^4.0.0", - "flat": "^5.0.2", - "is-plain-obj": "^2.1.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/yauzl": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-3.2.0.tgz", - "integrity": "sha512-Ow9nuGZE+qp1u4JIPvg+uCiUr7xGQWdff7JQSk5VGYTAZMDe2q8lxJ10ygv10qmSj031Ty/6FNJpLO4o1Sgc+w==", - "extraneous": true, - "license": "MIT", - "dependencies": { - "buffer-crc32": "~0.2.3", - "pend": "~1.2.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/yocto-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", - "extraneous": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/zip-stream": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/zip-stream/-/zip-stream-4.1.1.tgz", - "integrity": "sha512-9qv4rlDiopXg4E69k+vMHjNN63YFMe9sZMrdlvKnCjlCRWeCBswPPMPUfx+ipsAWq1LXHe70RcbaHdJJpS6hyQ==", - "peer": true, - "dependencies": { - "archiver-utils": "^3.0.4", - "compress-commons": "^4.1.2", - "readable-stream": "^3.6.0" - }, - "engines": { - "node": ">= 10" - } - }, - "node_modules/@ngageoint/mage.service/node_modules/zip-stream/node_modules/archiver-utils": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-3.0.4.tgz", - "integrity": "sha512-KVgf4XQVrTjhyWmx6cte4RxonPLR9onExufI1jhvw/MQ4BB6IsZD5gT8Lq+u/+pRkWna/6JoHpiQioaqFP5Rzw==", - "peer": true, - "dependencies": { - "glob": "^7.2.3", - "graceful-fs": "^4.2.0", - "lazystream": "^1.0.0", - "lodash.defaults": "^4.2.0", - "lodash.difference": "^4.5.0", - "lodash.flatten": "^4.4.0", - "lodash.isplainobject": "^4.0.6", - "lodash.union": "^4.6.0", - "normalize-path": "^3.0.0", - "readable-stream": "^3.6.0" - }, - "engines": { - "node": ">= 10" - } - }, - "node_modules/@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@pkgjs/parseargs": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", - "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", - "dev": true, - "license": "MIT", - "optional": true, - "engines": { - "node": ">=14" - } + "node_modules/@tokenizer/token": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@tokenizer/token/-/token-0.3.0.tgz", + "integrity": "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==", + "license": "MIT" }, - "node_modules/@pkgr/core": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.1.2.tgz", - "integrity": "sha512-fdDH1LSGfZdTH2sxdpVMw31BanV28K/Gry0cVFxaNP77neJSkd82mM8ErPNYs9e+0O7SdHBLTDzDgwUuy18RnQ==", + "node_modules/@tsconfig/node10": { + "version": "1.0.13", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.13.tgz", + "integrity": "sha512-gcLdvR9HO1ZJBypsOGqaP6TFEzb6vIta0KSTLt9NAQ6pXQO3cRgSVyCN6pzYqI9DlJgY71XKO0dpDhCf08b3pg==", "dev": true, - "license": "MIT", - "engines": { - "node": "^12.20.0 || ^14.18.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/unts" - } + "license": "MIT" }, - "node_modules/@sinclair/typebox": { - "version": "0.34.49", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.49.tgz", - "integrity": "sha512-brySQQs7Jtn0joV8Xh9ZV/hZb9Ozb0pmazDIASBkYKCjXrXU3mpcFahmK/z4YDhGkQvP9mWJbVyahdtU5wQA+A==", + "node_modules/@tsconfig/node12": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", "dev": true, "license": "MIT" }, - "node_modules/@sinonjs/commons": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", - "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", + "node_modules/@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "type-detect": "4.0.8" - } + "license": "MIT" }, - "node_modules/@sinonjs/fake-timers": { - "version": "15.4.0", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-15.4.0.tgz", - "integrity": "sha512-DsG+8/LscQIQg68J6Ef3dv10u6nVyetYn923s3/sus5eaGfTo1of5WMZSLf0UJc9KDuKPilPH0UDJCjvNbDNCA==", + "node_modules/@tsconfig/node16": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", + "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "@sinonjs/commons": "^3.0.1" - } - }, - "node_modules/@terraformer/arcgis": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@terraformer/arcgis/-/arcgis-2.1.2.tgz", - "integrity": "sha512-IvdfqehcNAUtKU1OFMKwPT8EvdKlVFZ7q7ZKzkIF8XzYZIVsZLuXuOS1UBdRh5u/o+X5Gax7jiZhD8U/4TV+Jw==", - "license": "MIT", - "dependencies": { - "@terraformer/common": "^2.1.2" - } - }, - "node_modules/@terraformer/common": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@terraformer/common/-/common-2.1.2.tgz", - "integrity": "sha512-cwPdTFzIpekZhZRrgDEkqLKNPoqbyCBQHiemaovnGIeUx0Pl336MY/eCxzJ5zXkrQLVo9zPalq/vYW5HnyKevQ==", "license": "MIT" }, "node_modules/@tybys/wasm-util": { - "version": "0.10.2", - "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.2.tgz", - "integrity": "sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg==", + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.3.tgz", + "integrity": "sha512-F3fo1MYrRJYL3zER0OUOmkutjr1Vp23m7OsSgp7nq4SP6OqX6C/56XFIPAl5bt3zaBRjmW7SGz3u/6LwFpYcOg==", "dev": true, "license": "MIT", "optional": true, @@ -14946,9 +1831,9 @@ } }, "node_modules/@types/express-serve-static-core": { - "version": "4.19.8", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.8.tgz", - "integrity": "sha512-02S5fmqeoKzVZCHPZid4b8JH2eM5HzQLZWN2FohQEy/0eXTq8VXZfSN6Pcr3F6N9R/vNrj7cpgbhjie6m/1tCA==", + "version": "4.19.9", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.9.tgz", + "integrity": "sha512-QP2ESEe/ImWY0HDwNAnK9PvEffUyhLTnWkk7KXzHfyeWAnlrDe1fN77bXl6ia8KT3wPlmA7t9/VPRpnf4Ex9sg==", "dev": true, "license": "MIT", "dependencies": { @@ -15017,14 +1902,21 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/mime-types": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/mime-types/-/mime-types-3.0.1.tgz", + "integrity": "sha512-xRMsfuQbnRq1Ef+C+RKaENOxXX87Ygl38W1vDfPHRku02TgQr+Qd8iivLtAMcR0KF5/29xlnFihkTlbqFrGOVQ==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/node": { - "version": "25.9.3", - "resolved": "https://registry.npmjs.org/@types/node/-/node-25.9.3.tgz", - "integrity": "sha512-603BddQMv3pUcr4U2dhujk83N2tTDVr/34wII2B6bJy6g+8WD6yUb11jszNs0gdi4PesVWl7ABt8nYMVpnLUcg==", + "version": "26.3.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-26.3.0.tgz", + "integrity": "sha512-L3fgrnchriRC2ExBflb8j4uZZURHZfQsmQeyVzhjcHW4kkwVyo8/0h1B2MVzMTrYUJYu6G7EWs14hW/L9putqw==", "dev": true, "license": "MIT", "dependencies": { - "undici-types": ">=7.24.0 <7.24.7" + "undici-types": "~8.3.0" } }, "node_modules/@types/qs": { @@ -15069,6 +1961,33 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/terraformer__wkt": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/terraformer__wkt/-/terraformer__wkt-2.0.3.tgz", + "integrity": "sha512-60CGvi30kMIKl2QERrE6LD5iPm4lutZ1M/mqBY4wrn6H/QlZQa/5CN1e6trZ6ZtSRHLbHLwG+egt/nAIDbPG0A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/geojson": "*" + } + }, + "node_modules/@types/webidl-conversions": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/@types/webidl-conversions/-/webidl-conversions-7.0.3.tgz", + "integrity": "sha512-CiJJvcRtIgzadHCYXw7dqEnMNRjhGZlYK05Mj9OyktqV8uVT8fD2BFOB7S1uwBE3Kj2Z+4UyPmFw/Ixgw/LAlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/whatwg-url": { + "version": "11.0.5", + "resolved": "https://registry.npmjs.org/@types/whatwg-url/-/whatwg-url-11.0.5.tgz", + "integrity": "sha512-coYR071JRaHa+xoEvvYqvnIHaVqaYrLPbsufM9BF63HkwI5Lgmy2QR8Q5K/lYDYo5AK82wOvSOS0UsLTpTG7uQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/webidl-conversions": "*" + } + }, "node_modules/@types/yargs": { "version": "17.0.35", "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.35.tgz", @@ -15225,9 +2144,9 @@ } }, "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.1.tgz", - "integrity": "sha512-WR1cURNjuvBLMZBMbqM0UoE+WAfdUcEV1ccD8PVBVOI+Z3ND4+SZbN8RsfT2bMuG1qwz5RFvPukSZm5fF2D5eA==", + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.4.tgz", + "integrity": "sha512-hGfVzPxthbf3+2yjg/RBs60cB0FhqBS/zvdV/4wn4/BmN0bNMMHPc4V/BbFieqf1TKAGGAHnY4eSjajCl0f2Xg==", "dev": true, "license": "MIT", "dependencies": { @@ -15293,9 +2212,9 @@ } }, "node_modules/@ungap/structured-clone": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.1.tgz", - "integrity": "sha512-mUFwbeTqrVgDQxFveS+df2yfap6iuP20NAKAsBt5jDEoOTDew+zwLAOilHCeQJOVSvmgCX4ogqIrA0mnyr08yQ==", + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.4.tgz", + "integrity": "sha512-JL+CF0GeLHyPWI0rXu7UnxgiuOm9UQWzadi0OYOJNhNO2q6EZElpwlgXkNkfU1PzANDHq3YcwKVZprdvS+BrbQ==", "dev": true, "license": "ISC" }, @@ -15612,6 +2531,18 @@ "win32" ] }, + "node_modules/abort-controller": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "license": "MIT", + "dependencies": { + "event-target-shim": "^5.0.0" + }, + "engines": { + "node": ">=6.5" + } + }, "node_modules/accepts": { "version": "1.3.8", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", @@ -15626,10 +2557,33 @@ "node": ">= 0.6" } }, + "node_modules/accepts/node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/accepts/node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "peer": true, + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/acorn": { - "version": "8.17.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.17.0.tgz", - "integrity": "sha512-xRQbDb9BnwDafYNn6Vwl839DYVjqXYb1XVGtWAZ1kcDc6iwAL4hg3B1dZlRiuENFeO2H53gFG3in621AdERVAg==", + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.18.0.tgz", + "integrity": "sha512-lGq+9yr1/GuAWaVYIHRjvvySG5/4VfKIvC8EWxStPdcDh/Ka7FG3twP6v4d5BkravUilhIAsG4Qj83t02LWUPQ==", "dev": true, "license": "MIT", "bin": { @@ -15649,6 +2603,19 @@ "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, + "node_modules/acorn-walk": { + "version": "8.3.5", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.5.tgz", + "integrity": "sha512-HEHNfbars9v4pgpW6SO1KSPkfoS0xVOM/9UzkJltjlsHZmJasxg8aXkuZa7SMf8vKGIBhpUsPluQSqhJFCqebw==", + "dev": true, + "license": "MIT", + "dependencies": { + "acorn": "^8.11.0" + }, + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/ajv": { "version": "6.15.0", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.15.0.tgz", @@ -15683,9 +2650,9 @@ } }, "node_modules/ansi-regex": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", - "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.3.0.tgz", + "integrity": "sha512-WpDfL7NO6j7tH88IDBNVdUJxDh9nmCteAVW9dsep846XdwF4naCBK+/tGLX3KJgcpgMRXCFlTM2hKGoK9FsdrQ==", "dev": true, "license": "MIT", "engines": { @@ -15748,6 +2715,13 @@ "node": ">=14" } }, + "node_modules/arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true, + "license": "MIT" + }, "node_modules/argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", @@ -15874,10 +2848,30 @@ "dev": true, "license": "MIT" }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, "node_modules/baseline-browser-mapping": { - "version": "2.10.38", - "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.38.tgz", - "integrity": "sha512-31/02mVB4yuQU6adKk5SlY6m+mxDwUq5KZkyYgnLrrKl7TEm1+3PyDtDBz2kOv/wxZz41GHsvV1A/u6RmiyBvw==", + "version": "2.11.19", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.11.19.tgz", + "integrity": "sha512-Grytf1xOxOEMTGRwx6rLGKkTabd4vMg3VrKdj/7joCmV0qgh4QwMMO6xh34YEXQqirAuUdgQGa5orJQQ+69RBw==", "dev": true, "license": "Apache-2.0", "bin": { @@ -15888,9 +2882,9 @@ } }, "node_modules/body-parser": { - "version": "1.20.5", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.5.tgz", - "integrity": "sha512-3grm+/2tUOvu2cjJkvsIxrv/wVpfXQW4PsQHYm7yk4vfpu7Ekl6nEsYBoJUL6qDwZUx8wUhQ8tR2qz+ad9c9OA==", + "version": "1.20.6", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.6.tgz", + "integrity": "sha512-p5tAzS57i5MV9fZFDj9LeIiTZEufbSe2eDozP+ElheSUq1m74CRq1jI4mYNDdVs9vQztXFLuk/Gd6BWTdwRJ5g==", "license": "MIT", "peer": true, "dependencies": { @@ -15922,27 +2916,6 @@ "ms": "2.0.0" } }, - "node_modules/body-parser/node_modules/http-errors": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", - "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", - "license": "MIT", - "peer": true, - "dependencies": { - "depd": "~2.0.0", - "inherits": "~2.0.4", - "setprototypeof": "~1.2.0", - "statuses": "~2.0.2", - "toidentifier": "~1.0.1" - }, - "engines": { - "node": ">= 0.8" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, "node_modules/body-parser/node_modules/ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", @@ -15950,20 +2923,10 @@ "license": "MIT", "peer": true }, - "node_modules/body-parser/node_modules/statuses": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", - "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", - "license": "MIT", - "peer": true, - "engines": { - "node": ">= 0.8" - } - }, "node_modules/brace-expansion": { - "version": "1.1.15", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.15.tgz", - "integrity": "sha512-EwOCDEex4quD37XhqM3omwtMoJjr//isUZz1JopUNWms+4Z2ViyM/k1YIRePpoVNnQhENnxtFjLaxNHrT7xIUg==", + "version": "1.1.18", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.18.tgz", + "integrity": "sha512-Edep/X9fGqVNmzKBVsDYIOtD+z1tuezV70LBjdCst9Tqu76lsnvRiZ6oTic1n+/BIwX6QDGAO94PN4N2SADvtw==", "dev": true, "license": "MIT", "dependencies": { @@ -15985,9 +2948,9 @@ } }, "node_modules/browserslist": { - "version": "4.28.2", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.2.tgz", - "integrity": "sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg==", + "version": "4.28.8", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.8.tgz", + "integrity": "sha512-V2NpofLblG64mfOtSgDhOJESZEGogzDMBv/q+W6oc4LXWP/q75eOXoOaaOu1EOadB9U4Bwx/e0yzbvwKH8zalA==", "dev": true, "funding": [ { @@ -16005,11 +2968,11 @@ ], "license": "MIT", "dependencies": { - "baseline-browser-mapping": "^2.10.12", - "caniuse-lite": "^1.0.30001782", - "electron-to-chromium": "^1.5.328", - "node-releases": "^2.0.36", - "update-browserslist-db": "^1.2.3" + "baseline-browser-mapping": "^2.11.12", + "caniuse-lite": "^1.0.30001809", + "electron-to-chromium": "^1.5.402", + "node-releases": "^2.0.53", + "update-browserslist-db": "^1.3.0" }, "bin": { "browserslist": "cli.js" @@ -16018,6 +2981,19 @@ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, + "node_modules/bs-logger": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz", + "integrity": "sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-json-stable-stringify": "2.x" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/bser": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", @@ -16028,6 +3004,40 @@ "node-int64": "^0.4.0" } }, + "node_modules/bson": { + "version": "6.10.4", + "resolved": "https://registry.npmjs.org/bson/-/bson-6.10.4.tgz", + "integrity": "sha512-WIsKqkSC0ABoBJuT1LEX+2HEvNmNKKgnTAyd0fL8qzK4SH2i9NXg+t08YtdZp/V9IZ33cxe3iV4yM0qg8lMQng==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=16.20.1" + } + }, + "node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, "node_modules/buffer-from": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", @@ -16096,9 +3106,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001799", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001799.tgz", - "integrity": "sha512-hG1bReV+OUU+MOqK4t/ZWI0tZOyz3rqS9XuhOUz1cIcbwBKjOyJEJuw9ER5JuNyqxNk8u/JUVbGibBOL1yrjFw==", + "version": "1.0.30001810", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001810.tgz", + "integrity": "sha512-TITQPUkaz+aVk5GL6NhOdwk1aEaNTSDPsGFWrTuhKGtjTF70jL/Oht2W4c6rXUe5fu7Ie19VIahAXHIIiWWNeg==", "dev": true, "funding": [ { @@ -16160,9 +3170,9 @@ } }, "node_modules/cjs-module-lexer": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-2.2.0.tgz", - "integrity": "sha512-4bHTS2YuzUvtoLjdy+98ykbNB5jS0+07EvFNXerqZQJ89F7DI6ET7OQo/HJuW6K0aVsKA9hj9/RVb2kQVOrPDQ==", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-2.2.1.tgz", + "integrity": "sha512-Ca8swihM+/4yKecYHY52kgJd300hi2lADU/a1RxNTRe+RJ9jvqQlESpbz9DnG9mowez8qwXHB8qYdIUw9e+F5Q==", "dev": true, "license": "MIT" }, @@ -16342,9 +3352,9 @@ "license": "MIT" }, "node_modules/cookie": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz", - "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==", + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", "license": "MIT", "peer": true, "engines": { @@ -16352,12 +3362,19 @@ } }, "node_modules/cookie-signature": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", - "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.7.tgz", + "integrity": "sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA==", "license": "MIT", "peer": true }, + "node_modules/create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "dev": true, + "license": "MIT" + }, "node_modules/cross-spawn": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", @@ -16472,6 +3489,16 @@ "node": ">=8" } }, + "node_modules/diff": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.4.tgz", + "integrity": "sha512-X07nttJQkwkfKfvTPG/KSnE2OMdcUCao6+eXF3wmnIQRn2aPAHH3VxDbDOdegkd6JbPsXqShpvEOHfAT+nCNwQ==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, "node_modules/dunder-proto": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", @@ -16501,9 +3528,9 @@ "peer": true }, "node_modules/electron-to-chromium": { - "version": "1.5.375", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.375.tgz", - "integrity": "sha512-ZWP5eB4BVPW/ZYo9252hQZHZ5XavtsTgpbhcmMmRwymavC5AsLWQWBPaKMeNd2LW0KGby5HPXvj7+sr4ta5j/Q==", + "version": "1.5.415", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.415.tgz", + "integrity": "sha512-958V+Kbhtgz+SxXeEVKBjrlKRBIDAYvUJfwhjxMZ5S6ut9jAl7l9ZKBkBrvjyjZE36PabLUo2L8kEeV5O4vgJg==", "dev": true, "license": "ISC" }, @@ -16633,6 +3660,7 @@ "version": "9.24.0", "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.24.0.tgz", "integrity": "sha512-eh/jxIEJyZrvbWRe4XuVclLPDYSYYYgLy5zXGGxD6j8zjSAxFEzI2fL/8xNq6O2yKqVt+eF2YhV+hxjV6UKXwQ==", + "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", "dev": true, "license": "MIT", "dependencies": { @@ -16834,6 +3862,24 @@ "node": ">= 0.6" } }, + "node_modules/event-target-shim": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "license": "MIT", + "engines": { + "node": ">=0.8.x" + } + }, "node_modules/execa": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", @@ -17073,6 +4119,23 @@ "node": ">=16.0.0" } }, + "node_modules/file-type": { + "version": "16.5.4", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-16.5.4.tgz", + "integrity": "sha512-/yFHK0aGjFEgDJjEKP0pWCplsPFPhwyfwevf/pVxiN0tmE4L9LmwWxWukdJSHdoCli4VgQLehjJtwQBnqmsKcw==", + "license": "MIT", + "dependencies": { + "readable-web-to-node-stream": "^3.0.0", + "strtok3": "^6.2.4", + "token-types": "^4.1.1" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/file-type?sponsor=1" + } + }, "node_modules/fill-range": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", @@ -17087,18 +4150,18 @@ } }, "node_modules/finalhandler": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz", - "integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.2.tgz", + "integrity": "sha512-aA4RyPcd3badbdABGDuTXCMTtOneUCAYH/gxoYRTZlIJdF0YPWuGqiAsIrhNnnqdXGswYk6dGujem4w80UJFhg==", "license": "MIT", "peer": true, "dependencies": { "debug": "2.6.9", "encodeurl": "~2.0.0", "escape-html": "~1.0.3", - "on-finished": "2.4.1", + "on-finished": "~2.4.1", "parseurl": "~1.3.3", - "statuses": "2.0.1", + "statuses": "~2.0.2", "unpipe": "~1.0.0" }, "engines": { @@ -17154,9 +4217,9 @@ } }, "node_modules/flatted": { - "version": "3.4.2", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz", - "integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==", + "version": "3.4.4", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.4.tgz", + "integrity": "sha512-5+ybhBZANEJxaH3X5evAFatUxLfEHSr7n6kYJ+1Qd0mUqr4eu9gIf6GDbWHf8RJijHrjjO8G+la14SlL2SeS1Q==", "dev": true, "license": "ISC" }, @@ -17193,6 +4256,27 @@ "node": ">= 6" } }, + "node_modules/form-data/node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/form-data/node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/formdata-node": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/formdata-node/-/formdata-node-4.4.1.tgz", @@ -17385,9 +4469,9 @@ } }, "node_modules/glob/node_modules/brace-expansion": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.1.tgz", - "integrity": "sha512-WR1cURNjuvBLMZBMbqM0UoE+WAfdUcEV1ccD8PVBVOI+Z3ND4+SZbN8RsfT2bMuG1qwz5RFvPukSZm5fF2D5eA==", + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.4.tgz", + "integrity": "sha512-hGfVzPxthbf3+2yjg/RBs60cB0FhqBS/zvdV/4wn4/BmN0bNMMHPc4V/BbFieqf1TKAGGAHnY4eSjajCl0f2Xg==", "dev": true, "license": "MIT", "dependencies": { @@ -17449,6 +4533,28 @@ "dev": true, "license": "MIT" }, + "node_modules/handlebars": { + "version": "4.7.9", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.9.tgz", + "integrity": "sha512-4E71E0rpOaQuJR2A3xDZ+GM1HyWYv1clR58tC8emQNeQe3RH7MAzSbat+V0wG78LQBo6m6bzSG/L4pBuCsgnUQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "minimist": "^1.2.5", + "neo-async": "^2.6.2", + "source-map": "^0.6.1", + "wordwrap": "^1.0.0" + }, + "bin": { + "handlebars": "bin/handlebars" + }, + "engines": { + "node": ">=0.4.7" + }, + "optionalDependencies": { + "uglify-js": "^3.1.4" + } + }, "node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -17506,20 +4612,24 @@ "license": "MIT" }, "node_modules/http-errors": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", - "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", + "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", "license": "MIT", "peer": true, "dependencies": { - "depd": "2.0.0", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "toidentifier": "1.0.1" + "depd": "~2.0.0", + "inherits": "~2.0.4", + "setprototypeof": "~1.2.0", + "statuses": "~2.0.2", + "toidentifier": "~1.0.1" }, "engines": { "node": ">= 0.8" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/human-signals": { @@ -17545,6 +4655,26 @@ "node": ">=0.10.0" } }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause" + }, "node_modules/ignore": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", @@ -18414,9 +5544,9 @@ "license": "MIT" }, "node_modules/js-yaml": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.2.0.tgz", - "integrity": "sha512-ePWsvanv0DWuDRsW8dnt+R4jQ31SCRCQ7hhNcPXZPsoBZiemuZNYGf7adZdqX2D86j6rvKp3RpCxVTSb8WQlOw==", + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.1.tgz", + "integrity": "sha512-CY6crGq313MX8GkwvB7tzgp99vjQxY1++5y10/BKN/GUfHqWaOGQMNZkBvqSzsZKWk/ijwHlWzzkLulsGHhjWQ==", "dev": true, "funding": [ { @@ -18507,6 +5637,16 @@ "node": ">=6" } }, + "node_modules/kareem": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/kareem/-/kareem-2.6.3.tgz", + "integrity": "sha512-C3iHfuGUXK2u8/ipq9LfjFfXFxAZMQJJq7vLS45r3D9Y2xQ/m4S8zaR4zMLFWh9AsNPXmcFfUDhTEO8UIC/V6Q==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/keyv": { "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", @@ -18571,6 +5711,13 @@ "dev": true, "license": "MIT" }, + "node_modules/lodash.memoize": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", + "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==", + "dev": true, + "license": "MIT" + }, "node_modules/lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", @@ -18604,6 +5751,13 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true, + "license": "ISC" + }, "node_modules/makeerror": { "version": "1.0.12", "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", @@ -18633,6 +5787,13 @@ "node": ">= 0.6" } }, + "node_modules/memory-pager": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz", + "integrity": "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==", + "dev": true, + "license": "MIT" + }, "node_modules/merge-descriptors": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", @@ -18711,24 +5872,28 @@ } }, "node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", "license": "MIT", "engines": { "node": ">= 0.6" } }, "node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz", + "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==", "license": "MIT", "dependencies": { - "mime-db": "1.52.0" + "mime-db": "^1.54.0" }, "engines": { - "node": ">= 0.6" + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/mimic-fn": { @@ -18754,6 +5919,16 @@ "node": "*" } }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/minipass": { "version": "7.1.3", "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", @@ -18770,6 +5945,123 @@ "integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==", "license": "MIT" }, + "node_modules/mockingoose": { + "version": "2.16.2", + "resolved": "https://registry.npmjs.org/mockingoose/-/mockingoose-2.16.2.tgz", + "integrity": "sha512-6JokLoDoH4yzwnZYLwoynU2lNmbfW7Mcf0d8sF580XI2hqE9Cs5l22iFTjLB51cdxkOvRtjoL84LLVEGmgcwKQ==", + "dev": true, + "license": "The Unlicense", + "engines": { + "node": ">=6.4.0" + }, + "peerDependencies": { + "mongoose": ">=4.9.10" + } + }, + "node_modules/mongodb": { + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.20.0.tgz", + "integrity": "sha512-Tl6MEIU3K4Rq3TSHd+sZQqRBoGlFsOgNrH5ltAcFBV62Re3Fd+FcaVf8uSEQFOJ51SDowDVttBTONMfoYWrWlQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@mongodb-js/saslprep": "^1.3.0", + "bson": "^6.10.4", + "mongodb-connection-string-url": "^3.0.2" + }, + "engines": { + "node": ">=16.20.1" + }, + "peerDependencies": { + "@aws-sdk/credential-providers": "^3.188.0", + "@mongodb-js/zstd": "^1.1.0 || ^2.0.0", + "gcp-metadata": "^5.2.0", + "kerberos": "^2.0.1", + "mongodb-client-encryption": ">=6.0.0 <7", + "snappy": "^7.3.2", + "socks": "^2.7.1" + }, + "peerDependenciesMeta": { + "@aws-sdk/credential-providers": { + "optional": true + }, + "@mongodb-js/zstd": { + "optional": true + }, + "gcp-metadata": { + "optional": true + }, + "kerberos": { + "optional": true + }, + "mongodb-client-encryption": { + "optional": true + }, + "snappy": { + "optional": true + }, + "socks": { + "optional": true + } + } + }, + "node_modules/mongodb-connection-string-url": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-3.0.2.tgz", + "integrity": "sha512-rMO7CGo/9BFwyZABcKAWL8UJwH/Kc2x0g72uhDWzG48URRax5TCIcJ7Rc3RZqffZzO/Gwff/jyKwCU9TN8gehA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/whatwg-url": "^11.0.2", + "whatwg-url": "^14.1.0 || ^13.0.0" + } + }, + "node_modules/mongoose": { + "version": "8.24.1", + "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-8.24.1.tgz", + "integrity": "sha512-UpHBA0l5kHyKJQFjmBaFYQFo5sgz1DK0TRqDkOyBLYbqiIbKKhIvBpHWBXqeo0rgW4kGI1UhhAw+kTQZoj1BdA==", + "dev": true, + "license": "MIT", + "dependencies": { + "bson": "^6.10.4", + "kareem": "2.6.3", + "mongodb": "~6.20.0", + "mpath": "0.9.0", + "mquery": "5.0.0", + "ms": "2.1.3", + "sift": "17.1.3" + }, + "engines": { + "node": ">=16.20.1" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mongoose" + } + }, + "node_modules/mpath": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/mpath/-/mpath-0.9.0.tgz", + "integrity": "sha512-ikJRQTk8hw5DEoFVxHG1Gn9T/xcjtdnOKIU1JTmGjZZlg9LST2mBLmcX3/ICIbgJydT2GOc15RnNy5mHmzfSew==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/mquery": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/mquery/-/mquery-5.0.0.tgz", + "integrity": "sha512-iQMncpmEK8R8ncT8HJGsGc9Dsp8xcgYMVSbs5jgnm1lFHTZqMJTUWTDx1LBO8+mK3tPNZWFLBghQEIOULSTHZg==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "4.x" + }, + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", @@ -18809,6 +6101,13 @@ "node": ">= 0.6" } }, + "node_modules/neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", + "dev": true, + "license": "MIT" + }, "node_modules/nock": { "version": "12.0.3", "resolved": "https://registry.npmjs.org/nock/-/nock-12.0.3.tgz", @@ -18871,9 +6170,9 @@ "license": "MIT" }, "node_modules/node-releases": { - "version": "2.0.48", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.48.tgz", - "integrity": "sha512-1uz8041X6LoI6ZSdZacM9lVY28vuzDlSKitnpbSNK0RfKoIJkX29NBPVEFXhnuSuEOA9Ww0xnPJ+ILWbGAv8DA==", + "version": "2.0.53", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.53.tgz", + "integrity": "sha512-D9UOmYG3UH1V+ENW56t5QXBwJw1YEY18ruVeus89Rw+SyIgjPkCO84bRzO3uNIYosJbNwiabWVn48o3uJLjxFQ==", "dev": true, "license": "MIT", "engines": { @@ -19133,12 +6432,25 @@ "license": "ISC" }, "node_modules/path-to-regexp": { - "version": "0.1.12", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz", - "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==", + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.13.tgz", + "integrity": "sha512-A/AGNMFN3c8bOlvV9RreMdrv7jsmF9XIfDeCd87+I8RNg6s78BhJxMu69NEMHBSJFxKidViTEdruRwEk/WIKqA==", "license": "MIT", "peer": true }, + "node_modules/peek-readable": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/peek-readable/-/peek-readable-4.1.0.tgz", + "integrity": "sha512-ZI3LnwUv5nOGbQzD9c2iDG6toheuXSZP5esSHBjopsXH4dg19soufvpUGA3uohi5anFtGb2lhAVdHzH6R/Evvg==", + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } + }, "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", @@ -19147,9 +6459,9 @@ "license": "ISC" }, "node_modules/picomatch": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", - "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.7.tgz", + "integrity": "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==", "dev": true, "license": "MIT", "engines": { @@ -19277,6 +6589,15 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", + "license": "MIT", + "engines": { + "node": ">= 0.6.0" + } + }, "node_modules/propagate": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/propagate/-/propagate-2.0.1.tgz", @@ -19329,13 +6650,14 @@ "license": "MIT" }, "node_modules/qs": { - "version": "6.15.2", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.2.tgz", - "integrity": "sha512-Rzq0KEyX/w/tEybncDgdkZrJgVUsUMk3xjh3t5bv3S1HTAtg+uOYt72+ZfwiQwKdysThkTBdL/rTi6HDmX9Ddw==", + "version": "6.15.3", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.3.tgz", + "integrity": "sha512-O9gl3zCl5h5blw1KGUzQKhA5oUXSl8rwUIM5o0S3nCXMliSvy5Dzx7/DJcI+SwgICv+IneSZwhBh1oSyEHA71A==", "license": "BSD-3-Clause", "peer": true, "dependencies": { - "side-channel": "^1.1.0" + "es-define-property": "^1.0.1", + "side-channel": "^1.1.1" }, "engines": { "node": ">=0.6" @@ -19391,37 +6713,6 @@ "node": ">= 0.8" } }, - "node_modules/raw-body/node_modules/http-errors": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", - "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", - "license": "MIT", - "peer": true, - "dependencies": { - "depd": "~2.0.0", - "inherits": "~2.0.4", - "setprototypeof": "~1.2.0", - "statuses": "~2.0.2", - "toidentifier": "~1.0.1" - }, - "engines": { - "node": ">= 0.8" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/raw-body/node_modules/statuses": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", - "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", - "license": "MIT", - "peer": true, - "engines": { - "node": ">= 0.8" - } - }, "node_modules/react-is-18": { "name": "react-is", "version": "18.3.1", @@ -19432,12 +6723,44 @@ }, "node_modules/react-is-19": { "name": "react-is", - "version": "19.2.7", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-19.2.7.tgz", - "integrity": "sha512-kZFnouyVv7eP/Phmrlo9FK+zcAdriZJvzxXHF1Sl1P377WSGe2G/JxVolhTrB/jeV47lKImhNUsijjHAAbcl/A==", + "version": "19.2.8", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-19.2.8.tgz", + "integrity": "sha512-s5un28nYxKJw5gvUHyW5PCC28CvBqLu9r3cWgzHT4Vo/5fqqkFcdRYsGcKf50WMPpjjFZS5d76fn3YCo2njKwQ==", "dev": true, "license": "MIT" }, + "node_modules/readable-stream": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz", + "integrity": "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==", + "license": "MIT", + "dependencies": { + "abort-controller": "^3.0.0", + "buffer": "^6.0.3", + "events": "^3.3.0", + "process": "^0.11.10", + "string_decoder": "^1.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/readable-web-to-node-stream": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/readable-web-to-node-stream/-/readable-web-to-node-stream-3.0.4.tgz", + "integrity": "sha512-9nX56alTf5bwXQ3ZDipHJhusu9NTQJ/CVPtb/XHAJCXihZeitfJvIRS4GqQ/mfIoOE3IelHMrpayVrosdHBuLw==", + "license": "MIT", + "dependencies": { + "readable-stream": "^4.7.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } + }, "node_modules/require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", @@ -19534,8 +6857,7 @@ "url": "https://feross.org/support" } ], - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/safer-buffer": { "version": "2.1.2", @@ -19545,9 +6867,9 @@ "peer": true }, "node_modules/semver": { - "version": "7.8.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.4.tgz", - "integrity": "sha512-rUCObTnP32Q08R2uuIrt7r9PlEonuTmtuXYcW6s5kjdlj3xbnwe+21yXptAUYcMAABLkYYTtnmzb3w3EDZfueA==", + "version": "7.8.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", "dev": true, "license": "ISC", "bin": { @@ -19558,25 +6880,25 @@ } }, "node_modules/send": { - "version": "0.19.0", - "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz", - "integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==", + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/send/-/send-0.19.2.tgz", + "integrity": "sha512-VMbMxbDeehAxpOtWJXlcUS5E8iXh6QmN+BkRX1GARS3wRaXEEgzCcB10gTQazO42tpNIya8xIyNx8fll1OFPrg==", "license": "MIT", "peer": true, "dependencies": { "debug": "2.6.9", "depd": "2.0.0", "destroy": "1.2.0", - "encodeurl": "~1.0.2", + "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "etag": "~1.8.1", - "fresh": "0.5.2", - "http-errors": "2.0.0", + "fresh": "~0.5.2", + "http-errors": "~2.0.1", "mime": "1.6.0", "ms": "2.1.3", - "on-finished": "2.4.1", + "on-finished": "~2.4.1", "range-parser": "~1.2.1", - "statuses": "2.0.1" + "statuses": "~2.0.2" }, "engines": { "node": ">= 0.8.0" @@ -19599,27 +6921,17 @@ "license": "MIT", "peer": true }, - "node_modules/send/node_modules/encodeurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", - "license": "MIT", - "peer": true, - "engines": { - "node": ">= 0.8" - } - }, "node_modules/serve-static": { - "version": "1.16.2", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz", - "integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==", + "version": "1.16.3", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.3.tgz", + "integrity": "sha512-x0RTqQel6g5SY7Lg6ZreMmsOzncHFU7nhnRWkKgWuMTu5NN0DR5oruckMqRvacAN9d5w6ARnRBXl9xhDCgfMeA==", "license": "MIT", "peer": true, "dependencies": { "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "parseurl": "~1.3.3", - "send": "0.19.0" + "send": "~0.19.1" }, "engines": { "node": ">= 0.8.0" @@ -19731,6 +7043,13 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/sift": { + "version": "17.1.3", + "resolved": "https://registry.npmjs.org/sift/-/sift-17.1.3.tgz", + "integrity": "sha512-Rtlj66/b0ICeFzYTuNvX/EF1igRbbnGSvEyT79McoZa/DeGhMyC5pWKOEsZKnpkqtSeovd5FL/bjHWC3CIIvCQ==", + "dev": true, + "license": "MIT" + }, "node_modules/signal-exit": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", @@ -19782,6 +7101,16 @@ "source-map": "^0.6.0" } }, + "node_modules/sparse-bitfield": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz", + "integrity": "sha512-kvzhi7vqKTfkh0PZU+2D2PIllw2ymqJKujUcyPMd9Y75Nv4nPbGJZXNhxsgdQab2BmlDct1YnfQCguEvHr7VsQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "memory-pager": "^1.0.2" + } + }, "node_modules/spdx-exceptions": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz", @@ -19838,15 +7167,24 @@ } }, "node_modules/statuses": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", "license": "MIT", "peer": true, "engines": { "node": ">= 0.8" } }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, "node_modules/string-length": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", @@ -20021,6 +7359,23 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/strtok3": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/strtok3/-/strtok3-6.3.0.tgz", + "integrity": "sha512-fZtbhtvI9I48xDSywd/somNqgUHl2L2cstmXCCif0itOf96jeW18MBSyrLuNicYQVkvpOxkZtkzujiTJ9LW5Jw==", + "license": "MIT", + "dependencies": { + "@tokenizer/token": "^0.3.0", + "peek-readable": "^4.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } + }, "node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -20118,6 +7473,36 @@ "node": ">=0.6" } }, + "node_modules/token-types": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/token-types/-/token-types-4.2.1.tgz", + "integrity": "sha512-6udB24Q737UD/SDsKAHI9FCRP7Bqc9D/MQUV02ORQg5iskjtLJlZJNdN4kKtcdtwCeWIwIHDGaUsTsCCAa8sFQ==", + "license": "MIT", + "dependencies": { + "@tokenizer/token": "^0.3.0", + "ieee754": "^1.2.1" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } + }, + "node_modules/tr46": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-5.1.1.tgz", + "integrity": "sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==", + "dev": true, + "license": "MIT", + "dependencies": { + "punycode": "^2.3.1" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/ts-api-utils": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.5.0.tgz", @@ -20131,6 +7516,116 @@ "typescript": ">=4.8.4" } }, + "node_modules/ts-jest": { + "version": "29.4.6", + "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.4.6.tgz", + "integrity": "sha512-fSpWtOO/1AjSNQguk43hb/JCo16oJDnMJf3CdEGNkqsEX3t0KX96xvyX1D7PfLCpVoKu4MfVrqUkFyblYoY4lA==", + "dev": true, + "license": "MIT", + "dependencies": { + "bs-logger": "^0.2.6", + "fast-json-stable-stringify": "^2.1.0", + "handlebars": "^4.7.8", + "json5": "^2.2.3", + "lodash.memoize": "^4.1.2", + "make-error": "^1.3.6", + "semver": "^7.7.3", + "type-fest": "^4.41.0", + "yargs-parser": "^21.1.1" + }, + "bin": { + "ts-jest": "cli.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || ^18.0.0 || >=20.0.0" + }, + "peerDependencies": { + "@babel/core": ">=7.0.0-beta.0 <8", + "@jest/transform": "^29.0.0 || ^30.0.0", + "@jest/types": "^29.0.0 || ^30.0.0", + "babel-jest": "^29.0.0 || ^30.0.0", + "jest": "^29.0.0 || ^30.0.0", + "jest-util": "^29.0.0 || ^30.0.0", + "typescript": ">=4.3 <6" + }, + "peerDependenciesMeta": { + "@babel/core": { + "optional": true + }, + "@jest/transform": { + "optional": true + }, + "@jest/types": { + "optional": true + }, + "babel-jest": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "jest-util": { + "optional": true + } + } + }, + "node_modules/ts-jest/node_modules/type-fest": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz", + "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ts-node": { + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", + "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.1", + "yn": "3.1.1" + }, + "bin": { + "ts-node": "dist/bin.js", + "ts-node-cwd": "dist/bin-cwd.js", + "ts-node-esm": "dist/bin-esm.js", + "ts-node-script": "dist/bin-script.js", + "ts-node-transpile-only": "dist/bin-transpile.js", + "ts-script": "dist/bin-script-deprecated.js" + }, + "peerDependencies": { + "@swc/core": ">=1.2.50", + "@swc/wasm": ">=1.2.50", + "@types/node": "*", + "typescript": ">=2.7" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "@swc/wasm": { + "optional": true + } + } + }, "node_modules/tslib": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", @@ -20187,6 +7682,29 @@ "node": ">= 0.6" } }, + "node_modules/type-is/node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/type-is/node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "peer": true, + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/typescript": { "version": "5.8.2", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.2.tgz", @@ -20224,10 +7742,24 @@ "typescript": ">=4.8.4 <5.9.0" } }, + "node_modules/uglify-js": { + "version": "3.19.3", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.3.tgz", + "integrity": "sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==", + "dev": true, + "license": "BSD-2-Clause", + "optional": true, + "bin": { + "uglifyjs": "bin/uglifyjs" + }, + "engines": { + "node": ">=0.8.0" + } + }, "node_modules/undici-types": { - "version": "7.24.6", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.24.6.tgz", - "integrity": "sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg==", + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-8.3.0.tgz", + "integrity": "sha512-j375ScV60dom+YkPFIfTLcOiPxkN/buHz5GobjLhixFuANaNs3C9l4GmrWqejgXWJ7BbJcFYpTEUkS1Ge8bpZQ==", "dev": true, "license": "MIT" }, @@ -20280,9 +7812,9 @@ } }, "node_modules/update-browserslist-db": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", - "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.3.1.tgz", + "integrity": "sha512-ZZ61DsRsOnakl74HAmp3oSN4aXUmEWXf+i/yv0h7tIBfICc3VdrFErQKUUKPgu3AMsTUMbcongALEN4l6GSUrQ==", "dev": true, "funding": [ { @@ -20330,6 +7862,13 @@ "node": ">= 0.4.0" } }, + "node_modules/v8-compile-cache-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", + "dev": true, + "license": "MIT" + }, "node_modules/v8-to-istanbul": { "version": "9.3.0", "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz", @@ -20374,6 +7913,30 @@ "node": ">= 14" } }, + "node_modules/webidl-conversions": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", + "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + } + }, + "node_modules/whatwg-url": { + "version": "14.2.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.2.0.tgz", + "integrity": "sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==", + "dev": true, + "license": "MIT", + "dependencies": { + "tr46": "^5.1.0", + "webidl-conversions": "^7.0.0" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -20400,6 +7963,13 @@ "node": ">=0.10.0" } }, + "node_modules/wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==", + "dev": true, + "license": "MIT" + }, "node_modules/wrap-ansi": { "version": "8.1.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", @@ -20534,9 +8104,9 @@ "license": "ISC" }, "node_modules/yargs": { - "version": "17.7.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "version": "17.7.3", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.3.tgz", + "integrity": "sha512-GZtjxm/J/4TSxuL3FNYjCmLktBTnIw/rVmKSIyKeYAZpmJB2ig9VauCC5xsa82GNKVKDAqpOn3KVzNt0zmrU0g==", "dev": true, "license": "MIT", "dependencies": { @@ -20607,6 +8177,16 @@ "node": ">=8" } }, + "node_modules/yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", diff --git a/plugins/arcgis/service/package.json b/plugins/arcgis/service/package.json index 7bd699a1d..abd79a060 100644 --- a/plugins/arcgis/service/package.json +++ b/plugins/arcgis/service/package.json @@ -1,6 +1,6 @@ { "name": "@ngageoint/mage.arcgis.service", - "version": "1.0.0-beta.3", + "version": "1.0.0-beta.4", "description": "A mage service plugin that synchronizes mage observations to a configured ArcGIS feature layer.", "main": "lib/index.js", "files": [ @@ -14,8 +14,8 @@ "scripts": { "build:tsc": "tsc -b src", "build": "tsc -b src", - "test:run": "jest lib", - "test": "tsc -b src && jest lib", + "test:run": "jest", + "test": "tsc -b src && jest", "clean": "rm -rf lib" }, "repository": { @@ -39,23 +39,34 @@ "homepage": "https://github.com/ngageoint/mage-server#readme", "dependencies": { "@esri/arcgis-rest-feature-service": "4.1.0", + "@esri/arcgis-rest-portal": "4.3.0", "@esri/arcgis-rest-request": "4.3.0", "@terraformer/arcgis": "2.1.2", - "form-data": "4.0.6" + "@terraformer/wkt": "2.2.1", + "file-type": "16.5.4", + "form-data": "4.0.6", + "mime-types": "3.0.2" }, "peerDependencies": { "@ngageoint/mage.service": "^6.5.8 || ^6.6.0-beta", "express": "4.22.2" }, "devDependencies": { + "@ngageoint/mage.service": "file:../../../service", "@eslint/js": "9.24.0", "@types/express": "4.17.21", "@types/geojson": "7946.0.16", "@types/jest": "^30.0.0", + "@types/mime-types": "3.0.1", + "@types/terraformer__wkt": "2.0.3", "eslint": "9.24.0", "eslint-plugin-jsdoc": "50.6.9", "jest": "^30.2.0", + "mockingoose": "2.16.2", + "mongoose": "8.24.1", "nock": "12.0.3", + "ts-jest": "29.4.6", + "ts-node": "10.9.2", "typescript": "5.8.2", "typescript-eslint": "8.29.1" } diff --git a/plugins/arcgis/service/src/ArcGISService.ts b/plugins/arcgis/service/src/ArcGISService.ts index 42573ff92..57b359cc2 100644 --- a/plugins/arcgis/service/src/ArcGISService.ts +++ b/plugins/arcgis/service/src/ArcGISService.ts @@ -2,6 +2,7 @@ import { ArcGISIdentityManager } from '@esri/arcgis-rest-request' import { FeatureServiceConfig } from './types/ArcGISConfig' import { PluginStateRepository } from '@ngageoint/mage.service/lib/plugins.api' import { ArcGISPluginConfig } from './types/ArcGISPluginConfig' +import { decryptAndDeserialize, serializeAndEncrypt } from './CredentialEncryption' /** * Interface for managing ArcGIS identity and authentication. @@ -24,10 +25,12 @@ export interface ArcGISIdentityService { /** * Creates a new ArcGIS identity service. * @param {PluginStateRepository} stateRepo The plugin state repository. + * @param {Console} console used to log messages. * @returns {ArcGISIdentityService} The ArcGIS identity service. */ export function createArcGISIdentityService( - stateRepo: PluginStateRepository + stateRepo: PluginStateRepository, + console: Console ): ArcGISIdentityService { const identityManagerCache: Map> = new Map(); @@ -35,13 +38,17 @@ export function createArcGISIdentityService( async signin(featureService: FeatureServiceConfig): Promise { const cached = await identityManagerCache.get(featureService.url); if (!cached) { - const identityManager = ArcGISIdentityManager.deserialize(featureService.identityManager); - const promise = identityManager.getUser().then(() => identityManager); + const identityManager = decryptAndDeserialize(featureService.identityManager, console); + const promise = identityManager.getToken(featureService.url) + .then(() => identityManager) + .catch(err => { + identityManagerCache.delete(featureService.url); + throw err; + }); identityManagerCache.set(featureService.url, promise); return promise; - } else { - return cached; } + return cached!; }, async updateIndentityManagers() { @@ -50,9 +57,9 @@ export function createArcGISIdentityService( const persistedIdentityManager = await persistedIdentityManagerPromise; const featureService: FeatureServiceConfig | undefined = config?.featureServices.find((service: FeatureServiceConfig) => service.url === url); if (featureService && config) { - const identityManager = ArcGISIdentityManager.deserialize(featureService.identityManager); + const identityManager = decryptAndDeserialize(featureService.identityManager, console); if (identityManager.token !== persistedIdentityManager.token || identityManager.refreshToken !== persistedIdentityManager.refreshToken) { - featureService.identityManager = persistedIdentityManager.serialize(); + featureService.identityManager = serializeAndEncrypt(persistedIdentityManager, console); await stateRepo.patch(config as never); } } diff --git a/plugins/arcgis/service/src/CredentialEncryption.spec.ts b/plugins/arcgis/service/src/CredentialEncryption.spec.ts new file mode 100644 index 000000000..b711618e7 --- /dev/null +++ b/plugins/arcgis/service/src/CredentialEncryption.spec.ts @@ -0,0 +1,87 @@ +import fs from 'fs'; +import path from 'path'; +import { ArcGISIdentityManager } from '@esri/arcgis-rest-request'; +import { serializeAndEncrypt, decryptAndDeserialize, _resetKeyCacheForTests } from './CredentialEncryption'; + +/** + * Test suite for the serialization and encryption of ArcGISIdentityManager instances. + */ + +const silentConsole = { log: jest.fn(), debug: jest.fn(), info: jest.fn(), warn: jest.fn(), error: jest.fn() } as unknown as Console; + +const fakeIdentityManager = (username: string): ArcGISIdentityManager => ({ + username, + serialize: () => JSON.stringify({ username }) +} as unknown as ArcGISIdentityManager); + +let tempSecurityDir: string; +const originalEnvKey = process.env.MAGE_ARCGIS_ENCRYPTION_KEY; +const originalSecurityDir = process.env.MAGE_SECURITY_DIR; + +beforeEach(() => { + tempSecurityDir = fs.mkdtempSync('/tmp/arcgisCredentialEncryptionTest'); + process.env.MAGE_SECURITY_DIR = tempSecurityDir; + delete process.env.MAGE_ARCGIS_ENCRYPTION_KEY; + _resetKeyCacheForTests(); +}); + +afterEach(() => { + fs.rmSync(tempSecurityDir, { recursive: true }); + process.env.MAGE_ARCGIS_ENCRYPTION_KEY = originalEnvKey; + process.env.MAGE_SECURITY_DIR = originalSecurityDir; + _resetKeyCacheForTests(); +}); + +describe('serializeAndEncrypt / decryptAndDeserialize', () => { + it('round-trips an identity manager, auto-generating a key file under MAGE_SECURITY_DIR', () => { + const encrypted = serializeAndEncrypt(fakeIdentityManager('jdoe'), silentConsole); + + expect(encrypted).not.toContain('jdoe'); + expect(encrypted.startsWith('v1:')).toBe(true); + + const keyFile = path.join(tempSecurityDir, 'arcgis', 'credential-key'); + expect(fs.existsSync(keyFile)).toBe(true); + + const decrypted = decryptAndDeserialize(encrypted, silentConsole); + expect(decrypted.username).toEqual('jdoe'); + }); + + it('reuses an already-persisted key file rather than generating a new one', () => { + const encrypted = serializeAndEncrypt(fakeIdentityManager('jdoe'), silentConsole); + const keyFile = path.join(tempSecurityDir, 'arcgis', 'credential-key'); + const keyBytesAfterFirstUse = fs.readFileSync(keyFile); + + // simulate a fresh process picking the key back up from disk + _resetKeyCacheForTests(); + + const decrypted = decryptAndDeserialize(encrypted, silentConsole); + expect(decrypted.username).toEqual('jdoe'); + expect(fs.readFileSync(keyFile)).toEqual(keyBytesAfterFirstUse); + }); + + it('prefers MAGE_ARCGIS_ENCRYPTION_KEY over the key file when set', () => { + process.env.MAGE_ARCGIS_ENCRYPTION_KEY = 'admin passphrase'; + const encrypted = serializeAndEncrypt(fakeIdentityManager('jdoe'), silentConsole); + + // no key file should have been created since the env var took priority + const keyFile = path.join(tempSecurityDir, 'arcgis', 'credential-key'); + expect(fs.existsSync(keyFile)).toBe(false); + + // a fresh cache with the same env var can still decrypt it + _resetKeyCacheForTests(); + const decrypted = decryptAndDeserialize(encrypted, silentConsole); + expect(decrypted.username).toEqual('jdoe'); + }); + + it('still deserializes a legacy plaintext value persisted before encryption was added', () => { + const legacyPlaintext = JSON.stringify({ username: 'legacy-user' }); + const decrypted = decryptAndDeserialize(legacyPlaintext, silentConsole); + + expect(decrypted.username).toEqual('legacy-user'); + }); + + it('throws a clear error for an unrecognized stored format', () => { + expect(() => decryptAndDeserialize('not-json-and-not-our-format', silentConsole)) + .toThrow('Unrecognized stored ArcGIS credential format'); + }); +}); diff --git a/plugins/arcgis/service/src/CredentialEncryption.ts b/plugins/arcgis/service/src/CredentialEncryption.ts new file mode 100644 index 000000000..9cc21d7ca --- /dev/null +++ b/plugins/arcgis/service/src/CredentialEncryption.ts @@ -0,0 +1,77 @@ +import { ArcGISIdentityManager } from '@esri/arcgis-rest-request' +import { randomBytes, scryptSync, createCipheriv, createDecipheriv } from 'crypto' +import * as fs from 'fs' +import * as path from 'path' + +const ALGORITHM = 'aes-256-gcm' +let cachedKey: Buffer | undefined + +/** + * Resolves the AES key used to encrypt/decrypt persisted ArcGIS credentials + * @param console used to log messages + * @returns the 32-byte AES-256 key + */ +function resolveEncryptionKey(console: Console): Buffer { + if (cachedKey) { + return cachedKey + } + const envKey = process.env.MAGE_ARCGIS_ENCRYPTION_KEY + if (envKey) { + cachedKey = scryptSync(envKey, 'mage-arcgis-credential-store', 32) + return cachedKey + } + const securityDir = process.env.MAGE_SECURITY_DIR || '/var/lib/mage/security' + const keyFile = path.join(securityDir, 'arcgis', 'credential-key') + if (fs.existsSync(keyFile)) { + cachedKey = fs.readFileSync(keyFile) + } else { + console.log(`Generating a new ArcGIS credential encryption key at ${keyFile}`) + fs.mkdirSync(path.dirname(keyFile), { recursive: true }) + cachedKey = randomBytes(32) + fs.writeFileSync(keyFile, cachedKey, { mode: 0o600 }) + } + return cachedKey +} + +/** + * Serializes an ArcGISIdentityManager and encrypts it for storage in the plugin's persisted config + * @param identityManager the identity to serialize and encrypt + * @param console used to log messages + * @returns the encrypted value, formatted as `v1:::` (each base64-encoded) + */ +export function serializeAndEncrypt(identityManager: ArcGISIdentityManager, console: Console): string { + const serialized = identityManager.serialize() + const key = resolveEncryptionKey(console) + const iv = randomBytes(12) + const cipher = createCipheriv(ALGORITHM, key, iv) + const ciphertext = Buffer.concat([cipher.update(serialized, 'utf8'), cipher.final()]) + const authTag = cipher.getAuthTag() + return ['v1', iv.toString('base64'), authTag.toString('base64'), ciphertext.toString('base64')].join(':') +} + +/** + * Decrypts a value produced by serializeAndEncrypt and reconstructs the identity manager. + * Transparently handles values persisted before encryption was added + * @param stored the persisted `FeatureServiceConfig.identityManager` value + * @param console used to log messages + * @returns the reconstructed identity manager + * @throws if `stored` is neither legacy JSON nor a recognized encrypted format + */ +export function decryptAndDeserialize(stored: string, console: Console): ArcGISIdentityManager { + if (stored.trim().startsWith('{')) { + return ArcGISIdentityManager.deserialize(stored) + } + const [version, ivB64, authTagB64, ciphertextB64] = stored.split(':') + if (version !== 'v1' || !ivB64 || !authTagB64 || !ciphertextB64) { + throw new Error(`Unrecognized stored ArcGIS credential format: ${version}`) + } + const key = resolveEncryptionKey(console) + const decipher = createDecipheriv(ALGORITHM, key, Buffer.from(ivB64, 'base64')) + decipher.setAuthTag(Buffer.from(authTagB64, 'base64')) + const plaintext = Buffer.concat([decipher.update(Buffer.from(ciphertextB64, 'base64')), decipher.final()]).toString('utf8') + return ArcGISIdentityManager.deserialize(plaintext) +} + +export function _resetKeyCacheForTests(): void { + cachedKey = undefined +} diff --git a/plugins/arcgis/service/src/DrawingInfoBuilder.spec.ts b/plugins/arcgis/service/src/DrawingInfoBuilder.spec.ts new file mode 100644 index 000000000..563c405ae --- /dev/null +++ b/plugins/arcgis/service/src/DrawingInfoBuilder.spec.ts @@ -0,0 +1,204 @@ +// make a temporary directory to hold icon files (needs to be done before any MAGE imports) +import fs from "fs"; +let tempdir:string = fs.mkdtempSync('/tmp/drawingInfoTest'); +process.env.MAGE_ICON_DIR = tempdir; + +// import the mock module before anything that imports mongoose +const mockingoose = require("mockingoose"); + +import { DrawingInfoBuilder } from "./DrawingInfoBuilder"; +import { ArcGISPluginConfig, defaultArcGISPluginConfig } from "./types/ArcGISPluginConfig"; +import { Acl, MageEvent } from '@ngageoint/mage.service/lib/entities/events/entities.events'; +import { Form, FormField, FormFieldType } from "@ngageoint/mage.service/lib/entities/events/entities.events.forms"; +import { LineStyle } from "@ngageoint/mage.service/lib/entities/entities.global"; +import { Model as IconModel } from '@ngageoint/mage.service/lib/models/icon'; +import { Query } from 'mongoose'; + +// clean up icon directory after all tests +afterAll(() => { + if (tempdir) { + fs.rmSync(tempdir, { recursive: true }); + } +}) + +describe('feature service drawing info builder', () => { + + let simpleStyle: LineStyle = {fill: '#FACADE'} as LineStyle; + let emptyACL: Acl = {} as Acl; + let simpleEvent = new MageEvent({ + id: 999, name: 'test event', layerIds: [], feedIds: [], forms: [], style: simpleStyle, acl: emptyACL + }); + let simpleForm = { + id: 1, name: 'simple form', description: 'simple form description', + default: false, color: '#BEADED', archived: false, userFields: [], + fields: [{ + id: 1, archived: false, name: 'name', title: 'Name', type: FormFieldType.Text, required: false, value: null + } as FormField] + } as Form; + let secondSimpleForm = { + id: 2, name: 'simple form 2', description: 'simple form 2 description', + default: false, color: '#DABBED', archived: false, userFields: [], + fields: [{ + id: 1, archived: false, name: 'subject', title: 'Subject', type: FormFieldType.Text, required: false, value: null + } as FormField] + } as Form; + let eventWithSimpleForm = new MageEvent({ + id: 4321, name: 'test event with simple form', layerIds: [], feedIds: [], + forms: [simpleForm], style: simpleStyle, acl: emptyACL + }); + let eventWithTwoForms = new MageEvent({ + id: 789, name: 'test event with 2 forms', layerIds: [], feedIds: [], + forms: [simpleForm, secondSimpleForm], style: simpleStyle, acl: emptyACL + }); + let config: ArcGISPluginConfig = {} as ArcGISPluginConfig; + Object.assign(config, + { + ...defaultArcGISPluginConfig, + eventIdField: 'test_event_id' + } + ); + + beforeEach(() => { mockingoose.resetAll() }) + + test('produces a simple value renderer', async () => { + + let events: MageEvent[] = [simpleEvent]; + + fs.mkdirSync(`${tempdir}/999`); + fs.writeFileSync(`${tempdir}/999/icon.svg`, ''); + + // mock the icon data that would otherwise have been loaded from MongoDB + const iconFindMock = (query:Query) => { + const findQuery = query.getQuery(); + if (findQuery['eventId'] != null) { + return [{eventId: simpleEvent.id, relativePath: `${simpleEvent.id}/icon.svg`}]; + } + return []; + } + const iconFindOneMock = (query:Query) => { + return {eventId: simpleEvent.id, relativePath: `${simpleEvent.id}/icon.svg`}; + } + + mockingoose(IconModel).toReturn(iconFindMock, 'find'); + mockingoose(IconModel).toReturn(iconFindOneMock, 'findOne'); + + const drawingInfo = await new DrawingInfoBuilder(console, config) + .events(events) + .build(); + + expect(drawingInfo).not.toBeNull(); + expect(drawingInfo).toHaveProperty("renderer"); + const renderer = drawingInfo?.renderer; + expect(renderer).toHaveProperty("type") + expect(renderer?.type).toEqual("uniqueValue") + expect(renderer?.field1).toEqual("icon_symbol"); + expect(renderer?.uniqueValueInfos).toHaveLength(1); + const valueInfos = renderer?.uniqueValueInfos; + expect(valueInfos[0]).toHaveProperty("value", '999/icon.svg'); + expect(renderer).toHaveProperty("defaultSymbol"); + expect(renderer?.defaultSymbol?.type).toEqual("esriPMS"); + expect(atob(renderer?.defaultSymbol?.imageData)) + .toEqual(''); + }); + + test('produces a proper renderer for an event with a form', async () => { + + let events: MageEvent[] = [eventWithSimpleForm]; + + const testEventId = eventWithSimpleForm.id; + + fs.mkdirSync(`${tempdir}/${testEventId}/1`, { recursive: true }); + fs.writeFileSync(`${tempdir}/${testEventId}/icon.svg`, 'event default'); + fs.writeFileSync(`${tempdir}/${testEventId}/1/icon.svg`, 'form default'); + + // mock the icon data that would otherwise have been loaded from MongoDB + const iconFindOneMock = (query:Query) => { + const findQuery = query.getQuery(); + if (findQuery['eventId'] != null && findQuery['formId'] != null) { + return {eventId: testEventId, relativePath: `${testEventId}/1/icon.svg`, formId: 1} + } + if (findQuery['eventId'] != null) { + return {eventId: testEventId, relativePath: `${testEventId}/icon.svg`} + } + return null; + } + const iconFindMock = (query:Query) => { + const findQuery = query.getQuery(); + if (findQuery['eventId'] != null) { + return [ + {eventId: testEventId, relativePath: `${testEventId}/1/icon.svg`, formId: 1}, + {eventId: testEventId, relativePath: `${testEventId}/icon.svg`, formId: null}, + ] + } + return []; + } + mockingoose(IconModel).toReturn(iconFindOneMock, 'findOne'); + mockingoose(IconModel).toReturn(iconFindMock, 'find'); + + const drawingInfo = await new DrawingInfoBuilder(console, config) + .events(events) + .build(); + const renderer = drawingInfo?.renderer; + expect(renderer?.uniqueValueInfos).toHaveLength(2); + expect(renderer).toHaveProperty("defaultSymbol"); + expect(renderer?.defaultSymbol?.type).toEqual("esriPMS"); + expect(atob(renderer?.defaultSymbol?.imageData)) + .toEqual('event default'); + const valueInfos: { [key: string]: any }[] = renderer?.uniqueValueInfos; + const values = valueInfos.map(vi => vi.value); + expect(values).toContain(`${testEventId}/icon.svg`); + expect(values).toContain(`${testEventId}/1/icon.svg`); + }); + + test('produces a proper renderer for an event with two forms', async () => { + + let events: MageEvent[] = [eventWithTwoForms]; + + const testEventId = eventWithTwoForms.id; + + fs.mkdirSync(`${tempdir}/${testEventId}/${events[0].forms[0].id}`, { recursive: true }); + fs.mkdirSync(`${tempdir}/${testEventId}/${events[0].forms[1].id}`, { recursive: true }); + fs.writeFileSync(`${tempdir}/${testEventId}/icon.svg`, 'event default'); + fs.writeFileSync(`${tempdir}/${testEventId}/${events[0].forms[0].id}/icon.svg`, 'form 1 default'); + fs.writeFileSync(`${tempdir}/${testEventId}/${events[0].forms[1].id}/icon.svg`, 'form 2 default'); + + // mock the icon data that would otherwise have been loaded from MongoDB + const iconFindOneMock = (query:Query) => { + const findQuery = query.getQuery(); + if (findQuery['eventId'] != null && findQuery['formId'] != null) { + return { + eventId: testEventId, + relativePath: `${testEventId}/${findQuery['formId']}/icon.svg`, + formId: findQuery['formId']}; + } + if (findQuery['eventId'] != null) { + return {eventId: testEventId, relativePath: `${testEventId}/icon.svg`} + } + return null; + } + const iconFindMock = (query:Query) => { + const findQuery = query.getQuery(); + if (findQuery['eventId'] != null) { + return [ + {eventId: testEventId, relativePath: `${testEventId}/${events[0].forms[0].id}/icon.svg`, formId: events[0].forms[0].id}, + {eventId: testEventId, relativePath: `${testEventId}/${events[0].forms[1].id}/icon.svg`, formId: events[0].forms[1].id}, + {eventId: testEventId, relativePath: `${testEventId}/icon.svg`, formId: null}, + ] + } + return []; + } + mockingoose(IconModel).toReturn(iconFindOneMock, 'findOne'); + mockingoose(IconModel).toReturn(iconFindMock, 'find'); + + const drawingInfo = await new DrawingInfoBuilder(console, config) + .events(events) + .build(); + const renderer = drawingInfo?.renderer; + expect(renderer?.uniqueValueInfos).toHaveLength(3); + const valueInfos: { [key: string]: any }[] = renderer?.uniqueValueInfos; + const values = valueInfos.map(vi => vi.value); + expect(values).toContain(`${testEventId}/icon.svg`); + expect(values).toContain(`${testEventId}/1/icon.svg`); + expect(values).toContain(`${testEventId}/2/icon.svg`); + }); +}); diff --git a/plugins/arcgis/service/src/DrawingInfoBuilder.ts b/plugins/arcgis/service/src/DrawingInfoBuilder.ts new file mode 100644 index 000000000..d7693e49d --- /dev/null +++ b/plugins/arcgis/service/src/DrawingInfoBuilder.ts @@ -0,0 +1,373 @@ +import { promisify } from 'node:util' +import { readFile as readFileAsync } from 'node:fs/promises' +import { ArcGISPluginConfig } from './types/ArcGISPluginConfig'; +import { MageEvent } from '@ngageoint/mage.service/lib/entities/events/entities.events'; +import { LineStyle } from '@ngageoint/mage.service/lib/entities/entities.global'; +import api from '@ngageoint/mage.service/lib/api'; +import { fromBuffer } from 'file-type'; +import mimetypes from 'mime-types'; +import { IconDocumentResolved } from "@ngageoint/mage.service/lib/api/icon"; + +/** + * DrawingInfoBuilder + * ================== + * + * This class is responsible for building an ArcGIS feature layer `drawingInfo` object + * containing a "unique value" renderer configured to render the proper symbology + * for the MAGE events that sync to the feature layer. + * + * At a high level, the logic is: + * - if no events are syncing, no drawingInfo is built + * - if one event is syncing, that event's default style becomes the defaultSymbol + * - if more than one event is syncing, the event ID field becomes one + * of the value fields that dictate the unique value info (and part of the `valueExpression`) + * - all icons for the syncing events are determined, producing value info objects where the unique value is + * based on (event + icon) or just (icon) + * + * ArcGIS rendering behavior has been found to be inconsistent with respect to mapping feature attribute + * values to unique value info definitions. Therefore, the builder constructs the renderer with both + * ways of achieving this mapping, to cover as many ArcGIS versions and behaviors as possible: + * - using `field` and `field2` properties, in conjunction with the `fieldDelimiter`. + * - using a `valueExpression` that computes the unique value for each feature + */ +export class DrawingInfoBuilder { + + private static readonly UNIQUE_VALUE_FIELD_DELIMITER: string = '||'; + private static readonly SYMBOL_SIZE = 24; + + private _events: MageEvent[] = []; + private _console: Console; + private _config: ArcGISPluginConfig; + + private readonly _defaultSymbol = { + type: 'esriSMS', + style: 'esriSMSCircle', + color: [200, 100, 100, 255], + size: DrawingInfoBuilder.SYMBOL_SIZE, + angle: 0, + xoffset: 0, + yoffset: 0 + }; + + /** + * Constructor. + * @param {Console} console Used to log to the console. + * @param {ArcGISPluginConfig} config plugin configuration + */ + constructor(console:Console, config:ArcGISPluginConfig) { + this._console = console; + this._config = config; + } + + /** + * set the MAGE events for which to build a drawing info object + * @param events + */ + events(events: MageEvent[]): DrawingInfoBuilder { + this._events.push(...events); + return this; + } + + /** + convert a hexadecimal color string (#RRGGBB[AA]) to an array of components ([r, g, b, [a]]) + */ + private hexColorToArray(hexColor:string|undefined): number[] { + if (hexColor) { + const elements = hexColor.match(/^#([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})?/i); + if (elements) { + return [ + parseInt(elements[1], 16), + parseInt(elements[2], 16), + parseInt(elements[3], 16), + elements[4] ? parseInt(elements[4], 16) : 255 + ]; + } + } + return [10, 10, 10, 255]; + } + + /** + * Create a symbol object to include in a renderer definition + * @param lineStyle style/color definitions for the symbol + * @private + * @returns symbol object + */ + private buildSimpleMarkerSymbol(lineStyle:LineStyle): { [key:string]:any } { + return { + type: 'esriSMS', + style: 'esriSMSTriangle', + color: this.hexColorToArray(lineStyle.fill), + size: DrawingInfoBuilder.SYMBOL_SIZE, + angle: 0, + xoffset: 0, + yoffset: 0, + outline: { + color: this.hexColorToArray(lineStyle.stroke), + width: lineStyle.strokeWidth || null + } + }; + } + + /** + * Create a unique value renderer info object for a unique value, containing a simple marker symbol + * @param uniqueValue field value associated with the renderer style + * @param label human readable label to go with the unique value + * @param lineStyle style/color definitions for the symbol + * @private + * @returns unique value info object + */ + private buildSimpleMarkerInfo(uniqueValue:string, label:string, lineStyle:LineStyle): { [key:string]:any } { + return { + value: uniqueValue, + label: label, + description: "", + symbol: this.buildSimpleMarkerSymbol(lineStyle) + }; + } + + /** + * Create a picture/icon marker symbol object to include in a renderer definition + * @param iconData encoded icon data + * @param iconDataContentType media type of encoded icon data + * @private + */ + private buildMarkerIconSymbol(iconData:string, iconDataContentType?:string):{ [key:string]:any } { + return { + type: 'esriPMS', + imageData: iconData, + contentType: iconDataContentType, + width: DrawingInfoBuilder.SYMBOL_SIZE, + height: DrawingInfoBuilder.SYMBOL_SIZE, + angle: 0, + xoffset: 0, + yoffset: 0 + }; + } + + /** + * Create a unique value renderer info object for a unique value, containing an icon marker symbol + * @param uniqueValue field value associated with the renderer style + * @param label human readable label to go with the unique value + * @param symbol icon symbol definition + * @private + * @returns unique value info object + */ + private buildIconValueInfo(uniqueValue:string, label:string, symbol:{ [key:string]:any }): { [key:string]:any } { + return { + value: uniqueValue, + label: label, + description: "", + symbol: symbol + }; + } + + /** + * Create a uniqueValue renderer info object for the given event, form, and field values + * @param {IconDocumentResolved} icon the icon for which to build the renderer value info + * @param {MageEvent} event MAGE event definition + * @param {string} uniqueValuePrefix prefix to include with the combined field value string + * @private + * @returns unique value renderer info object + */ + private async buildValueInfo(icon: IconDocumentResolved, + event: MageEvent, + uniqueValuePrefix: string): Promise<{ [key:string]:any } | null> { + const uniqueValue:string = uniqueValuePrefix + icon.relativePath; + const symbol = await this.loadIconSymbol(icon); + if (symbol) { + return this.buildIconValueInfo(uniqueValue, `Event ${event.id}, ${icon.relativePath}`, symbol); + } + return null; + } + + /** + * Given an icon document, try to load the associated icon data and build an ArcGIS symbol definition for it + * @param {IconDocumentResolved} icon the icon + * @private + */ + private async loadIconSymbol(icon: IconDocumentResolved): Promise<{ [key: string]: any } | null> { + try { + const data = await readFileAsync(icon.path) + const fileTypeResult = await fromBuffer(data) + let mediaType: string | undefined = fileTypeResult?.mime; + if (!mediaType) { + const mimeType = mimetypes.lookup(icon.path); + if (mimeType !== false) { + mediaType = mimeType; + } + } + return this.buildMarkerIconSymbol(data.toString('base64'), mediaType) + } + catch (err) { + this._console.error(`error reading observation icon at path: ${icon.path}`) + throw err + } + } + + /** + * Given an event and possibly form and associated variant fields, try to load the associated icon and + * build an ArcGIS symbol definition for it + * @param event + * @private + */ + private async buildEventIconSymbol(event:MageEvent): Promise<{ [key: string]: any } | null> { + const iconContext = new api.Icon(event.id) + const getIconAsync = promisify(iconContext.getIcon.bind(iconContext)) + let icon: IconDocumentResolved | undefined + try { + icon = await getIconAsync() + } + catch (err) { + this._console.error(`error determining observation icon for event: ${event.name}`, err) + throw err + } + if (!icon) { + this._console.debug(`no icon found for event: ${event.name}`) + return null + } + try { + const data = await readFileAsync(icon.path) + const fileTypeResult = await fromBuffer(data) + let mediaType: string | undefined = fileTypeResult?.mime + if (!mediaType) { + const mimeType = mimetypes.lookup(icon.path) + if (mimeType !== false) { + mediaType = mimeType + } + } + this._console.debug(`found icon for event: ${event.name} — ${icon.path}`) + return this.buildMarkerIconSymbol(data.toString('base64'), mediaType) + } + catch (err) { + this._console.error(`error reading observation icon for event: ${event.name}`, err) + throw err + } + } + + /** + * Build an ArcGIS unique value renderer value expression to produce the + * correct "unique value" for combinations of event ID and icon field. + * + * @param {number[]} eventIds IDs of events + * @private + */ + private buildValueExpression(eventIds: number[]): string { + const valueFields: string[] = []; + if (this._config.eventIdField !== undefined && eventIds.length > 1) { + valueFields.push(this._config.eventIdField); + } + valueFields.push(this._config.iconSymbolField); + return valueFields.map(f => "$feature[\"" + f + "\"]").join(" + '" + DrawingInfoBuilder.UNIQUE_VALUE_FIELD_DELIMITER + "' + "); + } + + /** + * build a drawingInfo object for the given set of MAGE events + * @returns drawingInfo object + */ + async build(): Promise<{ [key: string]: any } | null> { + if (this._events.length == 0) { + this._console.debug("Not creating drawing info for layer without any associated MAGE events"); + return null; + } + + if (this._events.length > 1 && this._config.eventIdField === undefined) { + this._console.debug("Cannot create drawing info for layer with multiple MAGE events without an event ID field configured"); + return null; + } + + const uniqueValueInfos: { [key: string]: any }[] = []; + const renderer: { [key:string]: any } = { + type: "uniqueValue", + field1: null, + field2: null, + field3: null, + fieldDelimiter: DrawingInfoBuilder.UNIQUE_VALUE_FIELD_DELIMITER, + uniqueValueInfos + }; + const drawingInfo: object = { renderer: renderer }; + + const numEvents = this._events.length; + if (numEvents == 1) { + const firstEvent = this._events[0]; + const eventIconSymbol = await this.buildEventIconSymbol(firstEvent); + if (eventIconSymbol) { + renderer.defaultSymbol = eventIconSymbol + } + else { + renderer.defaultSymbol = this.buildSimpleMarkerSymbol(firstEvent.style); + } + renderer.field1 = this._config.iconSymbolField; + } + else { + renderer.field1 = this._config.eventIdField; + renderer.field2 = this._config.iconSymbolField; + renderer.defaultSymbol = this._defaultSymbol; + } + + await Promise.all(this._events.map(async (event) => { + let uniqueValuePrefix: string = ''; + if (numEvents > 1 && this._config.eventIdField !== undefined) { + uniqueValuePrefix = `${event.id}${renderer.fieldDelimiter}`; + } + + if (numEvents > 1) { + // make a "default" style for observations in the event without an icon symbol defined + const valueInfo = await this.buildEventValueInfo(event); + uniqueValueInfos.push(valueInfo); + } + + try { + const icons: IconDocumentResolved[] = await this.loadEventIcons(event); + await Promise.all(icons.map(async (icon) => { + const valueInfo = await this.buildValueInfo(icon, event, uniqueValuePrefix); + if (valueInfo) { + uniqueValueInfos.push(valueInfo); + } + })); + } catch (err) { + this._console.error(`error loading icons for event ${event.id} - ${event.name}`, err) + } + })); + + renderer.valueExpression = this.buildValueExpression(this._events.map((event) => event.id)); + + return drawingInfo; + } + + /** + * Load all icons for a given event + * @param {MageEvent} event the event for which to load icons + * @private + */ + private loadEventIcons(event: MageEvent): Promise { + return new Promise((resolve, reject) => { + new api.Icon(event.id).getIcons((err, icons) => { + if (err) { + reject(err); + return; + } + if (icons != null) { + resolve(icons); + return; + } + resolve([]); + }); + }); + } + + /** + * Build the "default" value info object for the event itself, without icon field value + * @param {MageEvent} event MAGE event + * @private + */ + private async buildEventValueInfo(event: MageEvent): Promise<{ [key:string]:any }> { + const uniqueValue = + event.id.toString() + DrawingInfoBuilder.UNIQUE_VALUE_FIELD_DELIMITER; + + const eventIconSymbol = await this.buildEventIconSymbol(event); + if (eventIconSymbol) { + return this.buildIconValueInfo(uniqueValue, `Event ${event.id}`, eventIconSymbol); + } + return this.buildSimpleMarkerInfo(uniqueValue, `Event ${event.id}`, event.style); + } +} diff --git a/plugins/arcgis/service/src/EventDeletionHandler.ts b/plugins/arcgis/service/src/EventDeletionHandler.ts index faa52fdfa..d9ba749d5 100644 --- a/plugins/arcgis/service/src/EventDeletionHandler.ts +++ b/plugins/arcgis/service/src/EventDeletionHandler.ts @@ -11,7 +11,7 @@ export class EventDeletionHandler { /** * The current set of event ids. */ - private _currentEventIds: Map; + private _currentEventIds: Map; /** * Used to log messages. @@ -29,7 +29,7 @@ export class EventDeletionHandler { * @param {ArcGISPluginConfig} config The plugin configuration. */ constructor(console: Console, config: ArcGISPluginConfig) { - this._currentEventIds = new Map(); + this._currentEventIds = new Map(); this._console = console; this._config = config; } @@ -47,41 +47,43 @@ export class EventDeletionHandler { checkForEventDeletion(activeEvents: MageEventAttrs[], layerProcessors: FeatureLayerProcessor[], firstRun: boolean) { if (firstRun) { for (const activeEvent of activeEvents) { - this._currentEventIds.set(activeEvent.id, activeEvent.name); + this._currentEventIds.set(activeEvent.id.toString(), activeEvent.name); } for (const layerProcessor of layerProcessors) { const response: (result: QueryObjectResult) => void = (result) => { this.figureOutAllEventsOnArc(layerProcessor, result); }; if (this._config.eventIdField == null) { - layerProcessor.featureQuerier.queryObservations(response, [this._config.observationIdField], false); + void layerProcessor.featureQuerier.queryObservations(response, [this._config.observationIdField], false); } else { - layerProcessor.featureQuerier.queryDistinct(response, this._config.eventIdField); + void layerProcessor.featureQuerier.queryDistinct(response, this._config.eventIdField); } } } else { - this._console.log('Checking for event deletions the previous known events are:'); - const deletedEvents = new Map(); - this._currentEventIds.forEach((eventName: string, eventId: number) => { - this._console.log(eventName + ' with id ' + eventId); + this._console.log('Checking for event deletions'); + const deletedEvents = new Map(); + this._currentEventIds.forEach((eventName: string, eventId: string) => { deletedEvents.set(eventId, eventName); }); - this._console.log('Active events are:'); for (const activeEvent of activeEvents) { - this._console.log(activeEvent.name + ' with id ' + activeEvent.id); - deletedEvents.delete(activeEvent.id); + deletedEvents.delete(activeEvent.id.toString()); } - deletedEvents.forEach((eventName: string, eventId: number) => { - this._console.log('Event named ' + eventName + ' was deleted removing observations from arc layers'); - for (const layerProcessor of layerProcessors) { - layerProcessor.sender.sendDeleteEvent(eventId); - } - this._currentEventIds.delete(eventId); - }); + if (deletedEvents.size > 0) { + this._console.debug('Removing observations from ArcGIS layers for deleted events: ' + + [...deletedEvents.keys()].join(",")); + deletedEvents.forEach((eventName: string, eventId: string) => { + for (const layerProcessor of layerProcessors) { + void layerProcessor.sender.sendDeleteEvent(eventId); + } + this._currentEventIds.delete(eventId); + }); + } else { + this._console.debug('No deleted events found'); + } for (const activeEvent of activeEvents) { - this._currentEventIds.set(activeEvent.id, activeEvent.name); + this._currentEventIds.set(activeEvent.id.toString(), activeEvent.name); } } } @@ -96,30 +98,30 @@ export class EventDeletionHandler { this._console.log('ArcGIS investigating all events for feature layer ' + layerProcessor.layerInfo.url); if (result.features != null) { - const arcEventIds = new Set(); + const arcEventIds = new Set(); for (const feature of result.features) { if (this._config.eventIdField == null) { const value = feature.attributes[this._config.observationIdField] as string; const splitIds = value.split(this._config.idSeparator) if (splitIds.length === 2) { - const eventId = parseInt(splitIds[1]) - if (!isNaN(eventId)) { - arcEventIds.add(eventId); - } + const eventId = splitIds[1]; + arcEventIds.add(eventId); } } else { - const value = feature.attributes[this._config.eventIdField] as number; - arcEventIds.add(value); + const value = feature.attributes[this._config.eventIdField]; + if (value != null) { + arcEventIds.add(String(value)); + } } } - this._currentEventIds.forEach((eventName: string, eventId: number) => { + this._currentEventIds.forEach((eventName: string, eventId: string) => { arcEventIds.delete(eventId); }); for (const arcEventId of arcEventIds) { - layerProcessor.sender.sendDeleteEvent(arcEventId); + void layerProcessor.sender.sendDeleteEvent(arcEventId); } } } diff --git a/plugins/arcgis/service/src/EventTransform.spec.ts b/plugins/arcgis/service/src/EventTransform.spec.ts index c9db481d6..e5165da78 100644 --- a/plugins/arcgis/service/src/EventTransform.spec.ts +++ b/plugins/arcgis/service/src/EventTransform.spec.ts @@ -1,4 +1,49 @@ +import { EventTransform } from './EventTransform'; +import { ArcGISPluginConfig, defaultArcGISPluginConfig } from "./types/ArcGISPluginConfig"; +import { LineStyle } from "@ngageoint/mage.service/lib/entities/entities.global"; +import { Acl, MageEvent } from "@ngageoint/mage.service/lib/entities/events/entities.events"; +import { Form, FormField, FormFieldType } from "@ngageoint/mage.service/lib/entities/events/entities.events.forms"; + describe('event transform', () => { - it('has a test', () => { }); -}); \ No newline at end of file + const simpleStyle: LineStyle = { fill: '#FACADE' } as LineStyle; + const emptyACL: Acl = {} as Acl; + + const simpleForm = { + id: 1, name: 'simple form', description: 'simple form description', + default: false, color: '#BEADED', archived: false, userFields: [], + fields: [{ + id: 1, archived: false, name: 'location', title: 'Location', type: FormFieldType.Geometry, required: false, value: null + } as FormField] + } as Form; + + const eventWithForm = new MageEvent({ + id: 4321, name: 'test event with simple form', layerIds: [], feedIds: [], + forms: [simpleForm], style: simpleStyle, acl: emptyACL + }); + + const config: ArcGISPluginConfig = { ...defaultArcGISPluginConfig } as ArcGISPluginConfig; + + it('names an attribute deterministically from the form id and field name by default', () => { + const transform = new EventTransform(config, eventWithForm); + const formFields = transform.get(1); + expect(formFields).not.toBeUndefined(); + expect(formFields?.getField('location')).toEqual('form1_location'); + }); + + it('uses a configured field attribute override when present', () => { + const overrideConfig: ArcGISPluginConfig = { + ...defaultArcGISPluginConfig, + fieldAttributes: { + 'test event with simple form': { + 'simple form': { + 'Location': 'custom_location_attribute' + } + } + } + } as ArcGISPluginConfig; + const transform = new EventTransform(overrideConfig, eventWithForm); + const formFields = transform.get(1); + expect(formFields?.getField('location')).toEqual('custom_location_attribute'); + }); +}); diff --git a/plugins/arcgis/service/src/EventTransform.ts b/plugins/arcgis/service/src/EventTransform.ts index fd729f783..925d99f90 100644 --- a/plugins/arcgis/service/src/EventTransform.ts +++ b/plugins/arcgis/service/src/EventTransform.ts @@ -1,4 +1,4 @@ -import { ArcGISPluginConfig } from "./types/ArcGISPluginConfig" +import { ArcGISPluginConfig, FieldAttributesFormConfig } from "./types/ArcGISPluginConfig" import { MageEvent } from '@ngageoint/mage.service/lib/entities/events/entities.events' import { Form, FormId } from '@ngageoint/mage.service/lib/entities/events/entities.events.forms' @@ -33,78 +33,49 @@ export class EventTransform { */ private initialize(config: ArcGISPluginConfig) { - const allFields = new Set(); - - allFields.add(config.observationIdField); - if (config.eventIdField != null) { - allFields.add(config.eventIdField); - } - if (config.eventNameField != null) { - allFields.add(config.eventNameField); - } - if (config.userIdField != null) { - allFields.add(config.userIdField); - } - if (config.usernameField != null) { - allFields.add(config.usernameField); - } - if (config.userDisplayNameField != null) { - allFields.add(config.userDisplayNameField); - } - if (config.deviceIdField != null) { - allFields.add(config.deviceIdField); - } - if (config.createdAtField != null) { - allFields.add(config.createdAtField); - } - if (config.lastModifiedField != null) { - allFields.add(config.lastModifiedField); - } - if (config.geometryType != null) { - allFields.add(config.geometryType); - } - if (this.mageEvent != null) { - const formAttributes = this.configValue(config.fieldAttributes, this.mageEvent.name, this.mageEvent.id); + let formAttributes: FieldAttributesFormConfig | null = null; + if (config.fieldAttributes) { + formAttributes = + config.fieldAttributes[this.mageEvent.name] || config.fieldAttributes[this.mageEvent.id]; + } // Initialize active form active fields for (const form of this.mageEvent.activeForms) { - this.initializeFields(form, allFields, formAttributes); + this.initializeFields(form, formAttributes); } // Initialize active form archived fields - this.initializeArchivedFields(allFields, formAttributes); + this.initializeArchivedFields(this.mageEvent.activeForms, formAttributes); // Initialize archived form fields for (const form of this.mageEvent.archivedForms) { - this.initializeFields(form, allFields, formAttributes); + this.initializeFields(form, formAttributes); } - } - } /** * Initialize the form fields. * @param {Form} form The form. - * @param {Set} allFields Used to build and track all event fields. - * @param {any} formAttributes Form attributes override mappings + * @param {FieldAttributesFormConfig | null} formAttributes Form attributes override mappings */ - private initializeFields(form: Form, allFields: Set, formAttributes: any) { + private initializeFields(form: Form, formAttributes: FieldAttributesFormConfig | null) { const fields = new FormFields(form); - const fieldAttributes = this.configValue(formAttributes, fields.name, fields.id); + let fieldAttributes: { [fieldTitle: string]: string } | null = null; + if (formAttributes) { + fieldAttributes = formAttributes[form.name] || formAttributes[form.id]; + } for (const field of form.fields) { - - let attribute = field.title; + let attribute = field.name; if (form.archived || !field.archived) { - attribute = this.initializeField(attribute, fields.name, allFields, fieldAttributes); + attribute = this.initializeField(field.name, field.title, fields.id, fieldAttributes); } - - fields.set(field.title, attribute, field.archived); + fields.setField(field.name, attribute, field.archived); } this.formFields.set(form.id, fields); @@ -112,70 +83,56 @@ export class EventTransform { /** * Initialize the archived form fields. - * @param {Set} allFields Used to build and track all event fields. - * @param {any} formAttributes Form attributes override mappings + * @param {Form[]} forms MAGE event form definitions + * @param {FieldAttributesFormConfig | null} formAttributes Form attributes override mappings */ - private initializeArchivedFields(allFields: Set, formAttributes: any) { - - for (const fields of this.formFields.values()) { + private initializeArchivedFields(forms: Form[], formAttributes: FieldAttributesFormConfig | null) { - const fieldAttributes = this.configValue(formAttributes, fields.name, fields.id) + for (const formFields of this.formFields.values()) { - for (const field of fields.archivedFields) { - const attribute = this.initializeField(field, fields.name, allFields, fieldAttributes) - fields.set(field, attribute) + let fieldAttributes: { [fieldTitle: string]: string } | null = null; + if (formAttributes) { + fieldAttributes = formAttributes[formFields.name] || formAttributes[formFields.id]; } - } - - } - - /** - * Retrieve a config value by name or id. - * @param {any} config The configuration. - * @param {string} name Configuration name. - * @param {number} id Configuration id. - * @returns {any} configuration value - */ - private configValue(config: any, name: string, id: number): any { - let value = null; - if (config != null) { - value = config[name] - if (!value) { - value = config[id] + for (const fieldName of formFields.archivedFields) { + let fieldTitle = fieldName; + const form = forms.find((form) => form.id === formFields.id); + if (form) { + const formField = form.fields.find((field) => field.name === fieldName); + if (formField) { + fieldTitle = formField.title; + } + } + const attribute = this.initializeField(fieldName, fieldTitle, formFields.id, fieldAttributes); + formFields.setField(fieldName, attribute); } } - return value; } /** * Initialize the form field. - * @param {string} field The field. - * @param {string} formName The form name. - * @param {Set} allFields Used to build and track all event fields. - * @param {any} fieldAttributes Field attributes override mappings + * @param {string} fieldName The form field name. + * @param {string} fieldTitle The form field title. + * @param {number} formId The form ID. + * @param {{ [fieldTitle: string]: string } | null} fieldAttributes Field attributes override mappings * @returns {string} attribute name */ - private initializeField(field: string, formName: string, allFields: Set, fieldAttributes: any): string { + private initializeField(fieldName: string, + fieldTitle: string, + formId: number, + fieldAttributes: { [fieldTitle: string]: string } | null): string { let attribute = null; if (fieldAttributes != null) { - attribute = fieldAttributes[field]; + attribute = fieldAttributes[fieldTitle]; } if (attribute == null) { - - attribute = field; - - if (allFields.has(attribute)) { - attribute = formName + '_' + attribute; - } - + attribute = `form${formId}_${fieldName}`; } - allFields.add(attribute); - return attribute; } @@ -187,11 +144,10 @@ export class EventTransform { get(id: number): FormFields | undefined { return this.formFields.get(id); } - } /** - * Mapping between form field titles and arc attributes. + * Mapping between form field names and ArcGIS attributes. */ export class FormFields { @@ -211,12 +167,12 @@ export class FormFields { archived: boolean; /** - * Form field mapping between titles and arc atrribute fields. + * Form field mapping between form field names and ArcGIS attribute names. */ fields: Map = new Map(); /** - * Archived form fields + * Archived form field names */ archivedFields: Set = new Set(); @@ -231,25 +187,25 @@ export class FormFields { } /** - * Set the form field title to an arc attribute. - * @param {string} title The form field title. - * @param {string} attribute The arc attribute. + * Set the form field name to an ArcGIS attribute. + * @param {string} name The form field name. + * @param {string} attribute The ArcGIS attribute name. * @param {boolean} [archived] Archived field flag. */ - set(title: string, attribute: string, archived?: boolean) { - this.fields.set(title, attribute); + setField(name: string, attribute: string, archived?: boolean) { + this.fields.set(name, attribute); if (archived) { - this.archivedFields.add(title); + this.archivedFields.add(name); } } /** - * Get the arc attribute for the form field title. - * @param {string} title The form field title. + * Get the ArcGIS attribute for the form field name. + * @param {string} name The form field name. * @returns {string | undefined} The arc attribute. */ - get(title: string): string | undefined { - return this.fields.get(title); + getField(name: string): string | undefined { + return this.fields.get(name); } /** diff --git a/plugins/arcgis/service/src/FeatureLayerProcessor.ts b/plugins/arcgis/service/src/FeatureLayerProcessor.ts index dd7060a3a..ad6de3afa 100644 --- a/plugins/arcgis/service/src/FeatureLayerProcessor.ts +++ b/plugins/arcgis/service/src/FeatureLayerProcessor.ts @@ -5,6 +5,7 @@ import { LayerInfo } from "./LayerInfo"; import { ObservationBins } from "./ObservationBins"; import { ObservationsSender } from "./ObservationsSender"; import { ArcGISIdentityManager } from "@esri/arcgis-rest-request"; +import { QueryObjectResult } from "./types/QueryObjectResult"; /** * Processes new, updated, and deleted observations and sends the changes to a specific arc feature layer. */ @@ -47,11 +48,9 @@ export class FeatureLayerProcessor { private _console: Console; - private _addedObs: Set = new Set(); - /** * Creates a new instance of FeatureLayerProcessor. - * @param {LayerInfo} layerInfo - Information about the arc feature layer this class sends observations to. + * @param {LayerInfo} layerInfo - Information about the ArcGIS feature layer this object sends observations to. * @param {ArcGISPluginConfig} config - Contains certain parameters that can be configured. * @param {ArcGISIdentityManager} identityManager - ArcGIS identity manager for authentication. * @param {Console} console - Used to log messages to the console. @@ -77,11 +76,12 @@ export class FeatureLayerProcessor { * Checks to see if there are any updates that need to be sent to the feature layer. */ async processPendingUpdates() { + const existingObsIds = await this.existingObservationIds(); + const newAndUpdates = new ObservationBins(); for (let i = 0; i < this._pendingNewAndUpdates.adds.count(); i++) { - if (!this._addedObs.has(this._pendingNewAndUpdates.adds.observations[i].id)) { + if (!existingObsIds.has(this._pendingNewAndUpdates.adds.observations[i].id)) { newAndUpdates.adds.add(this._pendingNewAndUpdates.adds.observations[i]); - this._addedObs.add(this._pendingNewAndUpdates.adds.observations[i].id); } } for (let i = 0; i < this._pendingNewAndUpdates.updates.count(); i++) { @@ -108,13 +108,13 @@ export class FeatureLayerProcessor { const bins = new ObservationBins(); + const existingObsIds = await this.existingObservationIds(); + for (const arcObservation of arcObjectsForLayer.observations) { - // TODO: Would probably want a better way to determine which observations need to be updated in arcgis - if (arcObjectsForLayer.firstRun || arcObservation.lastModified !== arcObservation.createdAt) { + if (existingObsIds.has(arcObservation.id)) { bins.updates.add(arcObservation); - } else if (!this._addedObs.has(arcObservation.id)) { + } else { bins.adds.add(arcObservation); - this._addedObs.add(arcObservation.id); } } @@ -123,14 +123,13 @@ export class FeatureLayerProcessor { this.featureQuerier.queryObservation(arcObservation.id, (result) => { this._existenceQueryCounts--; if (result.features != null && result.features.length > 0) { - this._addedObs.add(arcObservation.id); const arcAttributes = result.features[0].attributes; - let lastEdited = null; - if (this._config.lastEditedDateField != null) { - lastEdited = Number(arcAttributes[this._config.lastEditedDateField]); + let featureLastModified = null; + if (this._config.lastModifiedField != null) { + featureLastModified = Number(arcAttributes[this._config.lastModifiedField]); } - if (!lastEdited || lastEdited < arcObservation.lastModified) { + if (!featureLastModified || featureLastModified < arcObservation.lastModified) { const objectIdField = result.objectIdFieldName; const updateAttributes = arcObservation.object.attributes; @@ -162,9 +161,8 @@ export class FeatureLayerProcessor { await this.send(bins); for (const arcObservation of observations.deletions) { - if (this.layerInfo.geometryType === arcObservation.esriGeometryType && this._addedObs.has(arcObservation.id)) { + if (this.layerInfo.geometryType === arcObservation.esriGeometryType && existingObsIds.has(arcObservation.id)) { await this.sender.sendDelete(arcObservation.id); - this._addedObs.delete(arcObservation.id); } } } @@ -183,4 +181,17 @@ export class FeatureLayerProcessor { } await Promise.all(promises); } + + /** + * Query for all existing observation IDs in the feature layer + */ + private async existingObservationIds(): Promise> { + const ids = new Set(); + await this.featureQuerier.queryDistinct((result: QueryObjectResult) => { + result.features.forEach(feature => { + ids.add(feature.attributes[this._config.observationIdField] as string); + }); + }, this._config.observationIdField); + return ids; + } } \ No newline at end of file diff --git a/plugins/arcgis/service/src/FeatureQuerier.ts b/plugins/arcgis/service/src/FeatureQuerier.ts index b577a3b33..8a19ec348 100644 --- a/plugins/arcgis/service/src/FeatureQuerier.ts +++ b/plugins/arcgis/service/src/FeatureQuerier.ts @@ -1,8 +1,9 @@ import { ArcGISPluginConfig } from "./types/ArcGISPluginConfig"; import { LayerInfo } from "./LayerInfo"; import { QueryObjectResult } from "./types/QueryObjectResult"; -import { ArcGISIdentityManager } from "@esri/arcgis-rest-request"; -import { queryFeatures } from '@esri/arcgis-rest-feature-service'; +import { ArcGISIdentityManager, ArcGISRequestError } from "@esri/arcgis-rest-request"; +import { queryFeatures, IQueryFeaturesOptions } from '@esri/arcgis-rest-feature-service'; +import { MageEventId } from "@ngageoint/mage.service/lib/entities/events/entities.events"; /** * Performs various queries on observations for a specific arc feature layer. @@ -48,55 +49,134 @@ export class FeatureQuerier { /** * Queries for an observation by id. * @param {string} observationId - The id of the observation to query for on the arc feature layer. - * @param {function(QueryObjectResult): void} response - Callback function called with the query result. + * @param {function(QueryObjectResult): void} handleResponse - Callback function called with the query result. * @param {string[]} [fields] - Optional array of field names to query. If not provided, all fields are queried. * @param {boolean} [geometry] - Optional flag to include geometry in the query. Defaults to true. */ - async queryObservation(observationId: string, response: (result: QueryObjectResult) => void, fields?: string[], geometry?: boolean) { + async queryObservation(observationId: string, handleResponse: (result: QueryObjectResult) => void, fields?: string[], geometry?: boolean) { const where = !this._config.eventIdField ? `${this._config.observationIdField} LIKE '${observationId}${this._config.idSeparator}%'` : `${this._config.observationIdField} = '${observationId}'`; this._console.info('ArcGIS query observation: ' + this._url.toString() + where); - await queryFeatures({ - url: this._url.toString(), - authentication: this._identityManager, - where, - returnGeometry: geometry, - outFields: fields?.length ? fields : '*' - }).then((queryResponse) => response(queryResponse as QueryObjectResult)).catch((error) => this._console.error('Error in FeatureQuerier.queryObservation :: ' + error)); + try { + const response = await queryFeatures({ + url: this._url.toString(), + authentication: this._identityManager, + where, + returnGeometry: geometry, + outFields: fields?.length ? fields : '*' + }); + handleResponse(response as QueryObjectResult); + } catch (error) { + this._console.error('Error in FeatureQuerier.queryObservation :: ' + error); + if (error instanceof ArcGISRequestError) { + this._console.error(` message: ${error.response?.error?.message || ""}, details: ${error?.response?.error?.details || ""}`); + } + } } /** * Queries all observations. - * @param {function(QueryObjectResult): void} response - Callback function called with the query result. + * @param {function(QueryObjectResult): void} handleResponse - Callback function called with the query result. * @param {string[]} [fields] - Optional array of field names to query. If not provided, all fields are queried. * @param {boolean} [geometry] - Optional flag to include geometry in the query. Defaults to true. */ - async queryObservations(response: (result: QueryObjectResult) => void, fields?: string[], geometry?: boolean) { + async queryObservations(handleResponse: (result: QueryObjectResult) => void, fields?: string[], geometry?: boolean) { this._console.info('ArcGIS query observation: ' + this._url.toString()); - await queryFeatures({ - url: this._url.toString(), - authentication: this._identityManager, - where: `${this._config.observationIdField} IS NOT NULL`, - returnGeometry: geometry, - outFields: fields?.length ? fields : '*' - }).then((queryResponse) => response(queryResponse as QueryObjectResult)).catch((error) => this._console.error('Error in FeatureQuerier.queryObservations :: ' + error)); + try { + const response = await queryFeatures({ + url: this._url.toString(), + authentication: this._identityManager, + where: `${this._config.observationIdField} IS NOT NULL`, + returnGeometry: geometry, + outFields: fields?.length ? fields : '*' + }); + handleResponse(response as QueryObjectResult); + } catch (error) { + this._console.error('Error in FeatureQuerier.queryObservations :: ' + error); + if (error instanceof ArcGISRequestError) { + this._console.error(` message: ${error.response?.error?.message || ""}, details: ${error?.response?.error?.details || ""}`); + } + } + } + + /** + * Queries for the MAGE observation ids (and their arc object ids) of all features on this layer + * belonging to the given event. + * @param {MageEventId} eventId - The MAGE event id to find synced observations for. + * @param {function(PushedFeatureId[]): void} handleResponse - Callback function called with the resolved MAGE observation ids and their arc object ids. + */ + async queryObservationsForEvent(eventId: MageEventId, handleResponse: (results: PushedFeatureId[]) => void) { + const where = this._config.eventIdField + ? `${this._config.eventIdField} = '${eventId}'` + : `${this._config.observationIdField} LIKE '%${this._config.idSeparator}${eventId}'`; + this._console.info(`ArcGIS query observations for event ${eventId}: ${this._url.toString()}`); + try { + const response = await queryFeatures({ + url: this._url.toString(), + authentication: this._identityManager, + where, + returnGeometry: false, + outFields: '*' + }) as QueryObjectResult; + const results = response.features + .map(feature => ({ + observationId: this.extractObservationId(feature.attributes[this._config.observationIdField] as string), + objectId: feature.attributes[response.objectIdFieldName] as number + })) + .filter((result): result is PushedFeatureId => !!result.observationId); + handleResponse(results); + } catch (error) { + this._console.error(`Error in FeatureQuerier.queryObservationsForEvent :: ` + error); + if (error instanceof ArcGISRequestError) { + this._console.error(` message: ${error.response?.error?.message || ""}, details: ${error?.response?.error?.details || ""}`); + } + } + } + + // when eventIdField is configured, observationIdField holds the plain MAGE observation id; otherwise + // the id and event id are concatenated (observationId + idSeparator + eventId), so recover just the id + private extractObservationId(rawValue: string): string | undefined { + if (!rawValue) { + return undefined; + } + if (this._config.eventIdField) { + return rawValue; + } + return rawValue.split(this._config.idSeparator)[0]; } /** * Queries for distinct non-null values in a specified field. - * @param {function(QueryObjectResult): void} response - Callback function called with the query result. + * @param {function(QueryObjectResult): void} handleResponse - Callback function called with the query result. * @param {string} field - The field name to query for distinct values. */ - async queryDistinct(response: (result: QueryObjectResult) => void, field: string) { - this._console.info('ArcGIS query observation: ' + this._url.toString()); - await queryFeatures({ - url: this._url.toString(), - authentication: this._identityManager, - where: `${field} IS NOT NULL`, - returnGeometry: false, - outFields: field ? [field] : '*', - returnDistinctValues: true - }).then((queryResponse) => response(queryResponse as QueryObjectResult)).catch((error) => this._console.error('Error in FeatureQuerier.queryDistinct :: ' + error)); + async queryDistinct(handleResponse: (result: QueryObjectResult) => void, field: string) { + this._console.info(`ArcGIS query distinct observations, field: ${field}, URL: ${this._url.toString()}`); + try { + const query = { + url: this._url.toString(), + authentication: this._identityManager, + where: `${field} IS NOT NULL`, + returnGeometry: false, + outFields: field ? [field] : '*', + returnDistinctValues: true + } as IQueryFeaturesOptions; + const response = await queryFeatures(query); + handleResponse(response as QueryObjectResult); + } catch (error) { + this._console.error('Error in FeatureQuerier.queryDistinct :: ' + error); + if (error instanceof ArcGISRequestError) { + this._console.error(` message: ${error.response?.error?.message || ""}, details: ${error?.response?.error?.details || ""}`); + } + } } +} + +/** + * A MAGE observation id and its corresponding arc object id, as found on a feature layer. + */ +export interface PushedFeatureId { + observationId: string; + objectId: number; } \ No newline at end of file diff --git a/plugins/arcgis/service/src/FeatureServiceAdmin.ts b/plugins/arcgis/service/src/FeatureServiceAdmin.ts index 1f24e7297..e45c24a9b 100644 --- a/plugins/arcgis/service/src/FeatureServiceAdmin.ts +++ b/plugins/arcgis/service/src/FeatureServiceAdmin.ts @@ -1,6 +1,6 @@ import { ArcGISPluginConfig } from "./types/ArcGISPluginConfig"; import { FeatureServiceConfig, FeatureLayerConfig } from "./types/ArcGISConfig"; -import { MageEvent, MageEventId, MageEventRepository } from '@ngageoint/mage.service/lib/entities/events/entities.events'; +import { MageEvent, MageEventAttrs, MageEventRepository } from '@ngageoint/mage.service/lib/entities/events/entities.events'; import { Layer, Field } from "./types/AddLayersRequest"; import { Form, FormField, FormFieldType, FormId } from '@ngageoint/mage.service/lib/entities/events/entities.events.forms'; import { ObservationsTransformer } from "./ObservationsTransformer"; @@ -8,6 +8,9 @@ import { LayerInfoResult, LayerField } from "./types/LayerInfoResult"; import FormData from 'form-data'; import { request } from '@esri/arcgis-rest-request'; import { ArcGISIdentityService } from "./ArcGISService"; +import { ILayerDefinition } from "@esri/arcgis-rest-feature-service"; +import { DrawingInfoBuilder } from "./DrawingInfoBuilder"; +import { EventTransform } from "./EventTransform"; /** * Administers hosted feature services such as layer creation and updates. @@ -16,17 +19,23 @@ export class FeatureServiceAdmin { private _config: ArcGISPluginConfig; private _identityService: ArcGISIdentityService; private _console: Console; + private _verbose: boolean; + + /** string field length to hold WKT-encoded form fields with locations/geometries */ + private static readonly GEOMETRY_STRING_FIELD_LENGTH: number = 2 ** 31 - 1; /** * Constructor. * @param {ArcGISPluginConfig} config The plugins configuration. * @param {ArcGISIdentityService} identityService The identity service. * @param {Console} console Used to log to the console. + * @param {boolean} verbose whether to perform verbose logging */ - constructor(config: ArcGISPluginConfig, identityService: ArcGISIdentityService, console: Console) { + constructor(config: ArcGISPluginConfig, identityService: ArcGISIdentityService, console: Console, verbose: boolean) { this._config = config; this._identityService = identityService; this._console = console; + this._verbose = verbose; } /** @@ -83,7 +92,6 @@ export class FeatureServiceAdmin { async updateLayer(service: FeatureServiceConfig, featureLayer: FeatureLayerConfig, layerInfo: LayerInfoResult, eventRepo: MageEventRepository): Promise { this._console.info('FeatureServiceAdmin updateLayer()'); const events = await this.layerEvents(featureLayer, eventRepo); - const promises = []; const eventFields = this.fields(events); @@ -106,7 +114,7 @@ export class FeatureServiceAdmin { } if (addFields.length > 0) { - promises.push(this.addFields(service, layerInfo.id, addFields)); + await this.addFields(service, layerInfo.id, addFields); } const eventFieldSet = new Set(); @@ -118,7 +126,13 @@ export class FeatureServiceAdmin { const remainingFields = []; for (const field of layerFields) { if (field.editable && !eventFieldSet.has(field.name)) { - deleteFields.push(field); + if (layerInfo.displayField != null && field.name === layerInfo.displayField) { + this._console.warn(`FeatureServiceAdmin updateLayer() not deleting field '${field.name}' ` + + `because it is set as the layer's display field`); + remainingFields.push(field); + } else { + deleteFields.push(field); + } } else { remainingFields.push(field); } @@ -126,41 +140,30 @@ export class FeatureServiceAdmin { if (deleteFields.length > 0) { layerInfo.fields = remainingFields; - promises.push(this.deleteFields(service, layerInfo.id, deleteFields)); + await this.deleteFields(service, layerInfo.id, deleteFields); } - await Promise.all(promises); + + await this.updateDrawingInfo(service, layerInfo.id, events); + return eventFields; } /** - * Get the Mage layer events + * Get the MAGE events for a feature layer * @param {FeatureLayerConfig} layer feature layer * @param {MageEventRepository} eventRepo event repository * @returns {Promise} Mage layer events */ private async layerEvents(layer: FeatureLayerConfig, eventRepo: MageEventRepository): Promise { - const layerEventIds: Set = new Set(); - if (layer.eventIds != null) { - for (const layerEventId of layer.eventIds) { - layerEventIds.add(layerEventId); - } - } - - let mageEvents; - if (layerEventIds.size > 0) { - mageEvents = await eventRepo.findAll(); - } else { - mageEvents = await eventRepo.findActiveEvents(); - } - const events: MageEvent[] = []; - for (const mageEvent of mageEvents) { - if (layerEventIds.size === 0 || layerEventIds.has(mageEvent.id)) { - const event = await eventRepo.findById(mageEvent.id); - if (event != null) { - events.push(event); - } - } + if (layer.eventIds) { + const results = await eventRepo.findAllByIds(layer.eventIds); + events.push( + ...layer.eventIds + .map(id => results[id]) + .filter((eventAttrs): eventAttrs is MageEventAttrs => eventAttrs != null) + .map(eventAttrs => new MageEvent(eventAttrs)) + ) } return events; @@ -218,13 +221,9 @@ export class FeatureServiceAdmin { if (this._config.geometryType != null) { fields.push(this.createTextField(this._config.geometryType)); } + fields.push(this.createTextField(this._config.iconSymbolField)); - const fieldNames = new Set(); - for (const field of fields) { - fieldNames.add(field.name); - } - - this.eventsFields(events, fields, fieldNames); + fields.push(...this.eventsFields(events)); return fields; } @@ -284,64 +283,70 @@ export class FeatureServiceAdmin { } /** - * Build fields from the layer events + * Build ArcGIS fields from the layer events * @param {MageEvent[]} events layer events - * @param {Field[]} fields created fields - * @param {Set} fieldNames set of all field names + * @returns {Field[]} ArcGIS fields */ - private eventsFields(events: MageEvent[], fields: Field[], fieldNames: Set) { + private eventsFields(events: MageEvent[]): Field[] { const forms = new Set(); + const newFields: Field[] = []; for (const event of events) { - this.eventFields(event, forms, fields, fieldNames); + newFields.push(...this.eventFields(event, forms)); } + return newFields; } /** - * Build fields from the layer event + * Build ArcGIS fields from the layer event * @param {MageEvent} event layer event * @param {Set} forms set of processed forms - * @param {Field[]} fields created fields - * @param {Set} fieldNames set of all field names + * @returns {Field[]} ArcGIS fields */ - private eventFields(event: MageEvent, forms: Set, fields: Field[], fieldNames: Set) { + private eventFields(event: MageEvent, forms: Set): Field[] { + const transform = new EventTransform(this._config, event); + const newFields: Field[] = []; for (const form of event.activeForms) { if (!forms.has(form.id)) { forms.add(form.id); for (const formField of form.fields) { if (!formField.archived) { - this.createFormField(form, formField, fields, fieldNames); + const newField = this.createFormField(form, formField, transform); + if (newField != null) { + newFields.push(newField); + } } } } } + return newFields; } /** - * Build a field from the form field + * Build an ArcGIS field from the form field * @param {Form} form form * @param {FormField} formField form field - * @param {Field[]} fields created fields - * @param {Set} fieldNames set of all field names + * @param {EventTransform} transform transformer object for event + * @returns {Field | null} ArcGIS field */ - private createFormField(form: Form, formField: FormField, fields: Field[], fieldNames: Set) { + private createFormField(form: Form, formField: FormField, transform: EventTransform): Field | null { const field = this.initField(formField.type); if (field != null) { - const sanitizedName = ObservationsTransformer.replaceSpaces(formField.title); - const sanitizedFormName = ObservationsTransformer.replaceSpaces(form.name); - const name = `${sanitizedFormName}_${sanitizedName}`.toLowerCase(); - - fieldNames.add(name); - - field.name = name; - field.alias = field.name; + field.name = formField.name; + const formTransform = transform.get(form.id); + if (formTransform != null) { + const fieldName = formTransform.getField(formField.name); + if (fieldName != null) { + field.name = fieldName; + } + } + field.alias = formField.title; field.editable = true; field.defaultValue = formField.value; - - fields.push(field); } + return field; } /** @@ -374,8 +379,7 @@ export class FeatureServiceAdmin { break; case FormFieldType.DateTime: field.type = 'esriFieldTypeDate'; - field.sqlType = 'sqlTypeOther'; - field.length = 10; + field.sqlType = 'sqlTypeTimestamp'; break; case FormFieldType.Numeric: if (integer) { @@ -389,6 +393,10 @@ export class FeatureServiceAdmin { } break; case FormFieldType.Geometry: + field.type = 'esriFieldTypeString'; + field.sqlType = 'sqlTypeNVarchar'; + field.length = FeatureServiceAdmin.GEOMETRY_STRING_FIELD_LENGTH; + break; case FormFieldType.Attachment: case FormFieldType.Hidden: default: @@ -497,4 +505,40 @@ export class FeatureServiceAdmin { return url; } + + /** + * Update the drawingInfo within the feature layer definition to properly render features for the given events + * @param {FeatureServiceConfig} service feature service + * @param {number} featureLayer feature layer number + * @param {MageEvent[]} events MAGE events applicable to the layer + */ + private async updateDrawingInfo(service: FeatureServiceConfig, featureLayer: number, events: MageEvent[]): Promise { + const builder = new DrawingInfoBuilder(this._console, this._config) + .events(events); + const drawingInfo = await builder.build(); + if (!drawingInfo) { + return; + } + const definitionUpdate: ILayerDefinition = { drawingInfo: drawingInfo } as ILayerDefinition; + + const url = this.adminUrl(service) + featureLayer.toString() + '/updateDefinition'; + + this._console.info('ArcGIS feature layer updateDefinition with drawingInfo, URL ' + url); + if (this._verbose) { + this._console.debug(' drawingInfo:\n' + JSON.stringify(drawingInfo, null, 2)); + } + + try { + const identityManager = await this._identityService.signin(service); + await request(url, { + authentication: identityManager, + params: { + updateDefinition: definitionUpdate, + f: "json" + } + }).catch((error) => this._console.error('Error in updateDrawingInfo: ' + error)); + } catch (error) { + this._console.error('FeatureServiceAdmin updateDrawingInfo() error ' + error); + } + } } \ No newline at end of file diff --git a/plugins/arcgis/service/src/FeatureServiceConfigStore.spec.ts b/plugins/arcgis/service/src/FeatureServiceConfigStore.spec.ts new file mode 100644 index 000000000..0ed0b2369 --- /dev/null +++ b/plugins/arcgis/service/src/FeatureServiceConfigStore.spec.ts @@ -0,0 +1,165 @@ +import fs from 'fs'; +import { ArcGISIdentityManager } from '@esri/arcgis-rest-request'; +import { ArcGISIdentityService } from './ArcGISService'; +import { ArcGISPluginConfig, defaultArcGISPluginConfig } from './types/ArcGISPluginConfig'; +import { FeatureServiceConfig } from './types/ArcGISConfig'; +import { buildFeatureServicesForSave, commitFeatureService, IncomingFeatureServiceConfig } from './FeatureServiceConfigStore'; +import { decryptAndDeserialize, _resetKeyCacheForTests } from './CredentialEncryption'; + +/** + * Tests for saving feature services to the config + */ + +jest.mock('@esri/arcgis-rest-request', () => { + const actual = jest.requireActual('@esri/arcgis-rest-request'); + return { ...actual, request: jest.fn() }; +}); + +const silentConsole = { log: jest.fn(), debug: jest.fn(), info: jest.fn(), warn: jest.fn(), error: jest.fn() } as unknown as Console; +const emptyConfig = (): ArcGISPluginConfig => ({ ...defaultArcGISPluginConfig, featureServices: [] } as ArcGISPluginConfig); +const fakeIdentityManager = (username: string): ArcGISIdentityManager => ({ + username, + serialize: () => JSON.stringify({ username }) +} as unknown as ArcGISIdentityManager); + +// commitFeatureService encrypts the identityManager before persisting it. keep that isolated +// from any real MAGE_SECURITY_DIR on the machine running these tests +let tempSecurityDir: string; +beforeEach(() => { + tempSecurityDir = fs.mkdtempSync('/tmp/featureServiceConfigStoreTest'); + process.env.MAGE_SECURITY_DIR = tempSecurityDir; + _resetKeyCacheForTests(); +}); +afterEach(() => { + fs.rmSync(tempSecurityDir, { recursive: true }); + _resetKeyCacheForTests(); +}); + +describe('commitFeatureService', () => { + it('adds a newly-selected feature service to the config with no layers yet, and persists it', async () => { + const config = emptyConfig(); + const identityService: ArcGISIdentityService = { + signin: jest.fn().mockResolvedValue(fakeIdentityManager('jdoe')), + updateIndentityManagers: jest.fn() + }; + const patchConfig = jest.fn().mockResolvedValue(config); + const sanitized = await commitFeatureService( + patchConfig, identityService, config, 'https://host/arcgis/rest/services/My/FeatureServer', + 'https://host/arcgis/sharing/rest', fakeIdentityManager('jdoe'), silentConsole + ); + + expect(config.featureServices).toHaveLength(1); + expect(config.featureServices[0]).toMatchObject({ + url: 'https://host/arcgis/rest/services/My/FeatureServer', + portalUrl: 'https://host/arcgis/sharing/rest', + layers: [] + }); + expect(patchConfig).toHaveBeenCalledWith(config); + expect(sanitized.authenticated).toBe(true); + // never leak the serialized identity manager back to the client + expect((sanitized as any).identityManager).toBeUndefined(); + }); + + it('updates the identityManager and portalUrl of an already-selected feature service, without touching its saved layers', async () => { + const existing: FeatureServiceConfig = { + url: 'https://host/arcgis/rest/services/My/FeatureServer', + portalUrl: 'https://host/old-portal/sharing/rest', + identityManager: JSON.stringify({ username: 'old-user' }), + layers: [{ layer: 'Incidents', eventIds: [42] }] + }; + const config: ArcGISPluginConfig = { ...defaultArcGISPluginConfig, featureServices: [existing] } as ArcGISPluginConfig; + const identityService: ArcGISIdentityService = { + signin: jest.fn().mockResolvedValue(fakeIdentityManager('jdoe')), + updateIndentityManagers: jest.fn() + }; + const patchConfig = jest.fn().mockResolvedValue(config); + + await commitFeatureService( + patchConfig, identityService, config, existing.url, + 'https://host/new-portal/sharing/rest', fakeIdentityManager('jdoe'), silentConsole + ); + + expect(config.featureServices).toHaveLength(1); + expect(config.featureServices[0].portalUrl).toEqual('https://host/new-portal/sharing/rest'); + // the identityManager is encrypted before being persisted, not stored as plaintext JSON + expect(config.featureServices[0].identityManager.startsWith('v1:')).toBe(true); + expect(decryptAndDeserialize(config.featureServices[0].identityManager, silentConsole).username).toEqual('jdoe'); + expect(config.featureServices[0].layers).toEqual([{ layer: 'Incidents', eventIds: [42] }]); + }); +}); + +describe('buildFeatureServicesForSave', () => { + it('converts a selected layers event names to event ids', () => { + const updated: IncomingFeatureServiceConfig[] = [{ + url: 'https://host/arcgis/rest/services/My/FeatureServer', + portalUrl: 'https://host/arcgis/sharing/rest', + layers: [{ layer: 'Incidents', events: ['Training Event'] }] + }]; + const eventNameToIdMap = new Map([['Training Event', 42]]); + const result = buildFeatureServicesForSave(updated, [], eventNameToIdMap); + expect(result).toHaveLength(1); + expect(result[0].layers).toEqual([{ layer: 'Incidents', events: ['Training Event'], eventIds: [42] }]); + }); + + it('drops event names that no longer correspond to a known event', () => { + const updated: IncomingFeatureServiceConfig[] = [{ + url: 'https://host/arcgis/rest/services/My/FeatureServer', + layers: [{ layer: 'Incidents', events: ['Deleted Event'] }] + }]; + const result = buildFeatureServicesForSave(updated, [], new Map()); + expect(result[0].layers[0].eventIds).toEqual([]); + }); + + it('carries forward the identityManager already persisted for a matching existing service', () => { + const existing: FeatureServiceConfig[] = [{ + url: 'https://host/arcgis/rest/services/My/FeatureServer', + identityManager: 'serialized-identity', + layers: [] + }]; + const updated: IncomingFeatureServiceConfig[] = [{ + url: 'https://host/arcgis/rest/services/My/FeatureServer', + layers: [{ layer: 'Incidents', events: [] }] + }]; + const result = buildFeatureServicesForSave(updated, existing, new Map()); + expect(result[0].identityManager).toEqual('serialized-identity'); + }); + + it('leaves identityManager empty for a service with no existing match', () => { + const updated: IncomingFeatureServiceConfig[] = [{ + url: 'https://host/arcgis/rest/services/New/FeatureServer', + layers: [] + }]; + const result = buildFeatureServicesForSave(updated, [], new Map()); + expect(result[0].identityManager).toEqual(''); + }); + + it('handles multiple services and layers, each resolving independently', () => { + const existing: FeatureServiceConfig[] = [{ + url: 'https://host/arcgis/rest/services/A/FeatureServer', + identityManager: 'identity-a', + layers: [] + }]; + const updated: IncomingFeatureServiceConfig[] = [ + { + url: 'https://host/arcgis/rest/services/A/FeatureServer', + layers: [ + { layer: 'Layer1', events: ['Event One'] }, + { layer: 'Layer2', events: ['Event One', 'Event Two'] } + ] + }, + { + url: 'https://host/arcgis/rest/services/B/FeatureServer', + layers: [{ layer: 'Layer3', events: [] }] + } + ]; + const eventNameToIdMap = new Map([['Event One', 1], ['Event Two', 2]]); + const result = buildFeatureServicesForSave(updated, existing, eventNameToIdMap); + + expect(result).toHaveLength(2); + expect(result[0].identityManager).toEqual('identity-a'); + expect(result[0].layers[0].eventIds).toEqual([1]); + expect(result[0].layers[1].eventIds).toEqual([1, 2]); + expect(result[1].identityManager).toEqual(''); + expect(result[1].layers[0].eventIds).toEqual([]); + }); +}); diff --git a/plugins/arcgis/service/src/FeatureServiceConfigStore.ts b/plugins/arcgis/service/src/FeatureServiceConfigStore.ts new file mode 100644 index 000000000..1de18e35c --- /dev/null +++ b/plugins/arcgis/service/src/FeatureServiceConfigStore.ts @@ -0,0 +1,108 @@ +import { ArcGISIdentityManager, ArcGISRequestError } from "@esri/arcgis-rest-request"; +import { MageEventId } from '@ngageoint/mage.service/lib/entities/events/entities.events'; +import { FeatureServiceConfig, FeatureLayerConfig } from './types/ArcGISConfig'; +import { ArcGISPluginConfig } from './types/ArcGISPluginConfig'; +import { ArcGISIdentityService } from './ArcGISService'; +import { checkEditPrivilege } from './PortalDiscovery'; +import { serializeAndEncrypt } from './CredentialEncryption'; + +export const sanitizeFeatureService = async ( + config: FeatureServiceConfig, + identityService: ArcGISIdentityService, + console: Console +): Promise> => { + let authenticated = false; + let mayLackEditPrivilege: boolean | undefined; + try { + const identityManager = await identityService.signin(config); + authenticated = true; + if (config.portalUrl) { + mayLackEditPrivilege = await checkEditPrivilege(config.portalUrl, identityManager, console); + } + } catch (error) { + console.error('Error in sanitizeFeatureService'); + if (error instanceof ArcGISRequestError) { + console.error(` message: ${error.response?.error?.message || ""}, details: ${error.response?.error?.details || ""}`); + } + } + + const { identityManager, ...sanitized } = config; + return { ...sanitized, authenticated, mayLackEditPrivilege }; +}; + +/** + * Add or update a feature service in the plugin configuration and return the sanitized result. + * @param patchConfig persists the updated configuration (typically ObservationProcessor.patchConfig) + * @param identityService the identity service, used to verify the feature service is reachable + * @param config the current plugin configuration + * @param url the feature service url + * @param portalUrl the ArcGIS portal API url, if any + * @param identityManager authenticated identity to associate with the feature service + * @param console used to log messages + */ +export const commitFeatureService = async ( + patchConfig: (config: ArcGISPluginConfig) => Promise, + identityService: ArcGISIdentityService, + config: ArcGISPluginConfig, + url: string, + portalUrl: string | undefined, + identityManager: ArcGISIdentityManager, + console: Console +) => { + const existingService = config.featureServices.find(service => service.url === url); + let service: FeatureServiceConfig; + if (existingService) { + existingService.identityManager = serializeAndEncrypt(identityManager, console); + if (portalUrl) { + existingService.portalUrl = portalUrl; + } + service = existingService; + } else { + service = { url, portalUrl, layers: [], identityManager: serializeAndEncrypt(identityManager, console) }; + config.featureServices.push(service); + } + await patchConfig(config); + return sanitizeFeatureService(service, identityService, console); +}; + +export type IncomingFeatureServiceConfig = { + url: string + portalUrl?: string + layers: Array<{ layer: string | number, geometryType?: string, events?: string[] }> +} + +/** + * Builds the FeatureServiceConfig[] to persist from what the client submitted when saving event/layer + * selections: translates each layer's event *names* to the numeric event ids MAGE uses internally, and + * carries forward the identityManager already persisted for a matching existing service (the client + * never sends it back, since selecting layers doesn't involve re-authenticating). + * @param updatedServices the feature services as submitted by the client + * @param existingFeatureServices the currently persisted feature services, for identityManager continuity + * @param eventNameToIdMap a lookup of current MAGE event name -> id + */ +export const buildFeatureServicesForSave = ( + updatedServices: IncomingFeatureServiceConfig[], + existingFeatureServices: FeatureServiceConfig[], + eventNameToIdMap: Map +): FeatureServiceConfig[] => { + return updatedServices.map((updateService) => { + const existingService = existingFeatureServices.find(featureService => featureService.url === updateService.url); + + const layers: FeatureLayerConfig[] = updateService.layers.map((layer) => { + const eventNames = layer.events || []; + const eventIds = eventNames + .map(eventName => eventNameToIdMap.get(eventName)) + .filter((id): id is MageEventId => id !== undefined); + + return { ...layer, eventIds }; + }); + + return { + url: updateService.url, + portalUrl: updateService.portalUrl, + layers, + // the client never sends this back - carry forward whatever was already persisted + identityManager: existingService?.identityManager || '' + }; + }); +}; diff --git a/plugins/arcgis/service/src/LayerInfo.ts b/plugins/arcgis/service/src/LayerInfo.ts index e5dc07357..b58e835cf 100644 --- a/plugins/arcgis/service/src/LayerInfo.ts +++ b/plugins/arcgis/service/src/LayerInfo.ts @@ -70,8 +70,8 @@ export class LayerInfo { * @param {MageEventId} eventId The event. * @returns {boolean} true if enabled */ - hasEvent(eventId: MageEventId) { - return this.events.size === 0 || this.events.has(eventId) + hasEvent(eventId: MageEventId): boolean { + return this.events.has(eventId) } } \ No newline at end of file diff --git a/plugins/arcgis/service/src/ObservationProcessor.ts b/plugins/arcgis/service/src/ObservationProcessor.ts index 4338a0b94..ad6f305a2 100644 --- a/plugins/arcgis/service/src/ObservationProcessor.ts +++ b/plugins/arcgis/service/src/ObservationProcessor.ts @@ -1,7 +1,8 @@ import { PagingParameters } from '@ngageoint/mage.service/lib/entities/entities.global'; import { MageEventId } from "@ngageoint/mage.service/lib/entities/events/entities.events"; +import { FormId } from '@ngageoint/mage.service/lib/entities/events/entities.events.forms'; import { MageEventRepository } from '@ngageoint/mage.service/lib/entities/events/entities.events'; -import { EventScopedObservationRepository, ObservationRepositoryForEvent } from '@ngageoint/mage.service/lib/entities/observations/entities.observations'; +import { EventScopedObservationRepository, ObservationAttrs, ObservationRepositoryForEvent } from '@ngageoint/mage.service/lib/entities/observations/entities.observations'; import { UserRepository } from '@ngageoint/mage.service/lib/entities/users/entities.users'; import { ArcGISPluginConfig, defaultArcGISPluginConfig } from './types/ArcGISPluginConfig'; import { ObservationsTransformer } from './ObservationsTransformer' @@ -18,6 +19,10 @@ import { FeatureServiceConfig } from "./types/ArcGISConfig" import { PluginStateRepository } from '@ngageoint/mage.service/lib/plugins.api' import { FeatureServiceAdmin } from './FeatureServiceAdmin'; import { ArcGISIdentityService } from './ArcGISService'; +import { ArcGISRequestError } from "@esri/arcgis-rest-request"; +import { attachmentFileName } from './ObservationsSender'; + +const VERBOSE_DEBUG = false; /** * Class that wakes up at a certain configured interval and processes any new observations that can be @@ -137,7 +142,7 @@ export class ObservationProcessor { public async safeGetConfig(): Promise { const state = await this._stateRepo.get(); if (!state) return await this._stateRepo.put(defaultArcGISPluginConfig as never); - return await this._stateRepo.get().then((state) => state ? state : this._stateRepo.put(defaultArcGISPluginConfig as never)); + return { ...defaultArcGISPluginConfig, ...(state as ArcGISPluginConfig) }; } /** @@ -192,6 +197,124 @@ export class ObservationProcessor { return config; } + /** + * Finds MAGE observations belonging to the given event that also already exist as features on any + * ArcGIS feature layer configured to sync that event, i.e. observations already pushed to ArcGIS. + * @param {MageEventId} eventId The MAGE event id to find pushed observations for. + * @param {PagingParameters} paging Which page of the (newest-modified-first) results to return. + * @returns {Promise} The requested page of observations found on both MAGE and the ArcGIS layer(s), and the total count across all pages. + */ + public async getPushedObservations(eventId: MageEventId, paging: PagingParameters): Promise { + const layerProcessors = this._layerProcessors.filter(layerProcessor => layerProcessor.layerInfo.hasEvent(eventId)); + + const arcFeaturesByObservationId = new Map(); + await Promise.all(layerProcessors.map(layerProcessor => + layerProcessor.featureQuerier.queryObservationsForEvent(eventId, (results) => { + results.forEach(({ observationId, objectId }) => arcFeaturesByObservationId.set(observationId, { objectId, layerProcessor })); + }) + )); + + if (arcFeaturesByObservationId.size === 0) { + return { items: [], totalCount: 0, pageIndex: paging.pageIndex, pageSize: paging.pageSize }; + } + + const obsRepo = await this._obsRepos(eventId); + const mageEvent = await this._eventRepo.findById(eventId); + const fieldTitlesByFormId = new Map>(); + for (const form of mageEvent?.forms || []) { + fieldTitlesByFormId.set(form.id, new Map(form.fields.map(field => [field.name, field.title]))); + } + + const pushedObservations: { observation: ObservationAttrs, objectId: number, layerProcessor: FeatureLayerProcessor, pushed: PushedObservation }[] = []; + for (const [observationId, { objectId, layerProcessor }] of arcFeaturesByObservationId) { + const observation = await obsRepo.findById(observationId); + if (observation) { + const position = this.observationPosition(observation); + pushedObservations.push({ + observation, + objectId, + layerProcessor, + pushed: { + id: observation.id, + createdAt: observation.createdAt.toISOString(), + lastModified: observation.lastModified.toISOString(), + status: this.isArchived(observation) ? 'archived' : 'sent', + fields: this.extractFormFields(observation, fieldTitlesByFormId), + latitude: position?.[1], + longitude: position?.[0] + } + }); + } + } + + pushedObservations.sort((a, b) => b.pushed.lastModified.localeCompare(a.pushed.lastModified)); + const start = paging.pageIndex * paging.pageSize; + const pageItems = pushedObservations.slice(start, start + paging.pageSize); + + if (mageEvent) { + const config = await this.safeGetConfig(); + const eventTransform = new EventTransform(config, mageEvent); + await Promise.all(pageItems.map(item => this.attachAttachmentStatus(item, eventTransform))); + } + + const items = pageItems.map(item => item.pushed); + return { items, totalCount: pushedObservations.length, pageIndex: paging.pageIndex, pageSize: paging.pageSize }; + } + + /** + * For a single pushed observation, determines whether each of its attachments actually made it + * to the arc feature - an observation can succeed while one or more of its attachments silently + * fail to write + * @param item The pushed observation to check for attachment status + * @param eventTransform The event transform for the observation's event + * @returns {Promise} Resolves when the attachment status has been attached to the pushed observation + */ + private async attachAttachmentStatus( + item: { observation: ObservationAttrs, objectId: number, layerProcessor: FeatureLayerProcessor, pushed: PushedObservation }, + eventTransform: EventTransform + ): Promise { + if (!item.observation.attachments?.length) { + return; + } + const arcObservation = await this._transformer.transform(item.observation, eventTransform, null); + if (!arcObservation.attachments?.length) { + return; + } + const expectedNames = arcObservation.attachments.map(attachment => attachmentFileName(attachment)); + const actualInfos = await item.layerProcessor.sender.getAttachmentInfos(item.objectId); + const actualNames = new Set((actualInfos || []).map(info => info.name)); + item.pushed.attachments = expectedNames.map(name => ({ name, sent: actualNames.has(name) })); + } + + private isArchived(observation: ObservationAttrs): boolean { + return observation.states.length > 0 && observation.states[0].name.startsWith('archive'); + } + + // returns the observation's [longitude, latitude] if its geometry is a Point, undefined otherwise + private observationPosition(observation: ObservationAttrs): [number, number] | undefined { + if (observation.geometry.type === 'Point') { + const [longitude, latitude] = observation.geometry.coordinates; + return [longitude, latitude]; + } + return undefined; + } + + /** + * strips the form/entry bookkeeping keys (id, formId) off each form entry, and translates each + * field's internal name (e.g. 'field0') to its human-readable form field title, for display in + * the push status hover + */ + private extractFormFields(observation: ObservationAttrs, fieldTitlesByFormId: Map>): Record[] { + return observation.properties.forms.map(({ id, formId, ...fields }) => { + const fieldTitles = fieldTitlesByFormId.get(formId); + const titledFields: Record = {}; + for (const [name, value] of Object.entries(fields)) { + titledFields[fieldTitles?.get(name) || name] = value; + } + return titledFields; + }); + } + /** * Starts the processor. */ @@ -231,10 +354,16 @@ export class ObservationProcessor { if (featureLayer.geometryType != null) { // TODO The featureLayerConfig should contain the layer id featureLayerConfig.layer = featureLayer.id; - const admin = new FeatureServiceAdmin(config, this._identityService, this._console) const eventIds = featureLayerConfig.eventIds || [] - const layerFields = await admin.updateLayer(service, featureLayerConfig, layerInfo, this._eventRepo) - const info = new LayerInfo(url, eventIds, { ...layerInfo, fields: layerFields } as LayerInfoResult); + // don't touch the layer's schema (addFields/deleteFields/drawingInfo) until at least + // one event is actually configured to sync to it + let layerInfoResult = layerInfo as LayerInfoResult; + if (eventIds.length > 0) { + const admin = new FeatureServiceAdmin(config, this._identityService, this._console, VERBOSE_DEBUG) + const layerFields = await admin.updateLayer(service, featureLayerConfig, layerInfo, this._eventRepo) + layerInfoResult = { ...layerInfo, fields: layerFields } as LayerInfoResult; + } + const info = new LayerInfo(url, eventIds, layerInfoResult); const layerProcessor = new FeatureLayerProcessor(info, config, identityManager, this._console); this._layerProcessors.push(layerProcessor); } @@ -242,6 +371,17 @@ export class ObservationProcessor { } } catch (err) { this._console.error(`Error getting feature service layers for ${service.url}:`, err); + if (err instanceof Error) { + let requestError: ArcGISRequestError | null = null; + if ('cause' in err && err.cause instanceof ArcGISRequestError) { + requestError = err.cause; + } else if (err instanceof ArcGISRequestError) { + requestError = err; + } + if (requestError) { + this._console.error(` message: ${requestError.response?.error?.message || ""}, details: ${requestError.response?.error?.details || ""}`); + } + } } })()); } @@ -262,12 +402,13 @@ export class ObservationProcessor { } await Promise.all(pendingPromises); this._console.info('ArcGIS plugin processing new observations...'); - const enabledEvents = (await this._eventRepo.findActiveEvents()).filter(event => + const activeEvents = await this._eventRepo.findActiveEvents(); + const enabledEvents = activeEvents.filter(event => this._layerProcessors.some(layerProcessor => layerProcessor.layerInfo.hasEvent(event.id) ) ); - this._eventDeletionHandler.checkForEventDeletion(enabledEvents, this._layerProcessors, this._firstRun); + this._eventDeletionHandler.checkForEventDeletion(activeEvents, this._layerProcessors, this._firstRun); const eventsToProcessors = this._organizer.organize(enabledEvents, this._layerProcessors); const nextQueryTime = Date.now(); const promises = []; @@ -351,17 +492,24 @@ export class ObservationProcessor { const eventTransform = new EventTransform(config, mageEvent); const arcObjects = new ArcObjects(); this._geometryChangeHandler.checkForGeometryChange(observations, arcObjects, layerProcessors, this._firstRun); + + const syncAfter = mageEvent ? config.syncAfterByEventId?.[mageEvent.id] : undefined; + const syncAfterTime = syncAfter ? new Date(syncAfter).getTime() : undefined; + for (const observation of observations) { // TODO: Should archived observations be removed after a certain time? Also this uses 'startsWith' because not all deleted observations use 'archived' which is a bug if (observation.states.length > 0 && observation.states[0].name.startsWith('archive')) { const arcObservation = this._transformer.createObservation(observation); arcObjects.deletions.push(arcObservation); } else { + if (syncAfterTime !== undefined && observation.properties.timestamp.getTime() < syncAfterTime) { + continue; + } let user = null; if (observation.userId != null) { user = await this._userRepo.findById(observation.userId); } - const arcObservation = this._transformer.transform(observation, eventTransform, user); + const arcObservation = await this._transformer.transform(observation, eventTransform, user); arcObjects.add(arcObservation); } } @@ -377,4 +525,44 @@ export class ObservationProcessor { return newNumberLeft; } +} + +/** + * A MAGE observation's ArcGIS push status. + * - 'sent': found on the ArcGIS layer and still active in MAGE. + * - 'archived': found on the ArcGIS layer, but has since been archived (deleted) in MAGE. + */ +export type PushStatus = 'sent' | 'archived' + +/** + * A MAGE observation that has already been synced to an ArcGIS feature layer. + */ +export interface PushedObservation { + id: string + createdAt: string + lastModified: string + status: PushStatus + fields: Record[] + // undefined if the observation's geometry isn't a Point (e.g. a line/polygon) + latitude?: number + longitude?: number + attachments?: PushedAttachment[] +} + +/** + * Whether a single MAGE attachment made it to the arc feature layer along with its observation. + */ +export interface PushedAttachment { + name: string + sent: boolean +} + +/** + * A page of pushed observations, newest-modified-first, plus the total count across all pages. + */ +export interface PushedObservationsPage { + items: PushedObservation[] + totalCount: number + pageIndex: number + pageSize: number } \ No newline at end of file diff --git a/plugins/arcgis/service/src/ObservationsSender.spec.ts b/plugins/arcgis/service/src/ObservationsSender.spec.ts new file mode 100644 index 000000000..7c801823d --- /dev/null +++ b/plugins/arcgis/service/src/ObservationsSender.spec.ts @@ -0,0 +1,125 @@ +const mockingoose = require("mockingoose"); + +jest.mock('@esri/arcgis-rest-request', () => { + const actual = jest.requireActual('@esri/arcgis-rest-request'); + return { ...actual, request: jest.fn() }; +}); + +import { request, ArcGISIdentityManager } from '@esri/arcgis-rest-request'; +import { ObservationsSender } from './ObservationsSender'; +import { ObservationsTransformer } from './ObservationsTransformer'; +import { EventTransform } from './EventTransform'; +import { ArcObjects } from './ArcObjects'; +import { LayerInfo } from './LayerInfo'; +import { ArcGISPluginConfig, defaultArcGISPluginConfig } from './types/ArcGISPluginConfig'; +import { Attachment, ObservationAttrs } from '@ngageoint/mage.service/lib/entities/observations/entities.observations'; +import { Acl, MageEvent } from '@ngageoint/mage.service/lib/entities/events/entities.events'; +import { Form, FormField, FormFieldType } from '@ngageoint/mage.service/lib/entities/events/entities.events.forms'; +import { Model as IconModel } from '@ngageoint/mage.service/lib/models/icon'; + +const silentConsole = { log: jest.fn(), debug: jest.fn(), info: jest.fn(), warn: jest.fn(), error: jest.fn() } as unknown as Console; +const mockedRequest = request as jest.Mock; +const fakeIdentityManager = { username: 'jdoe' } as unknown as ArcGISIdentityManager; + +const simpleStyle = { fill: '#caf9fa' } as any; +const emptyACL: Acl = {} as Acl; +const observationForm = { + id: 1, name: 'incident report', description: 'incident report form', + default: false, color: '#BEADED', archived: false, userFields: [], + fields: [ + { id: 1, archived: false, name: 'title', title: 'Title', type: FormFieldType.Text, required: false, value: null } as FormField, + { id: 2, archived: false, name: 'description', title: 'Description', type: FormFieldType.TextArea, required: false, value: null } as FormField, + { id: 3, archived: false, name: 'population', title: 'Population', type: FormFieldType.Numeric, required: false, value: null } as FormField + ] +} as Form; +const eventWithForm = new MageEvent({ + id: 4321, name: 'test event', layerIds: [], feedIds: [], + forms: [observationForm], style: simpleStyle, acl: emptyACL +}); + +const config: ArcGISPluginConfig = { ...defaultArcGISPluginConfig } as ArcGISPluginConfig; + +// builds a MAGE observation with the title/description/population form filled out +const buildObservation = (index: number): ObservationAttrs => ({ + id: `observation-${index}`, + eventId: 4321, + createdAt: new Date(), + lastModified: new Date(), + geometry: { type: 'Point', coordinates: [-1.391412 + index * 0.001, 50.885025] }, + attachments: [] as Attachment[], + properties: { + forms: [{ + id: `entry-${index}`, + formId: 1, + title: `Incident ${index}`, + description: `Description for incident ${index}`, + population: index * 100 + }] + } +} as unknown as ObservationAttrs); + +describe('ObservationsSender.sendAdds', () => { + beforeEach(() => { + mockedRequest.mockReset(); + mockingoose(IconModel).toReturn(null, 'findOne'); + }); + + it('batches 10 observations into a single addFeatures request to the fake ArcGIS server', async () => { + const transformer = new ObservationsTransformer(config, silentConsole); + const eventTransform = new EventTransform(config, eventWithForm); + + const arcObjects = new ArcObjects(); + for (let i = 1; i <= 10; i++) { + const arcObservation = await transformer.transform(buildObservation(i), eventTransform, null); + arcObjects.add(arcObservation); + } + expect(arcObjects.count()).toEqual(10); + + mockedRequest.mockResolvedValue({ + addResults: arcObjects.observations.map((_, i) => ({ objectId: i + 1, success: true })) + }); + + const layerInfo = new LayerInfo('https://fake-arcgis.example.com/arcgis/rest/services/Incidents/FeatureServer/0', [4321], { id: 0, geometryType: 'esriGeometryPoint', fields: [] }); + const sender = new ObservationsSender(layerInfo, config, fakeIdentityManager, silentConsole); + + await sender.sendAdds(arcObjects); + + // 10 observations should be sent as a single batched request + expect(mockedRequest).toHaveBeenCalledTimes(1); + const [url, options] = mockedRequest.mock.calls[0]; + expect(url).toEqual('https://fake-arcgis.example.com/arcgis/rest/services/Incidents/FeatureServer/0/addFeatures'); + expect(options.authentication).toBe(fakeIdentityManager); + + const sentFeatures = options.params.features; + expect(sentFeatures).toHaveLength(10); + + const first = sentFeatures[0]; + expect(first.attributes.form1_title).toEqual('Incident 1'); + expect(first.attributes.form1_description).toEqual('Description for incident 1'); + expect(first.attributes.form1_population).toEqual(100); + expect(first.geometry).toMatchObject({ x: -1.391412 + 0.001, y: 50.885025 }); + + const tenth = sentFeatures[9]; + expect(tenth.attributes.form1_title).toEqual('Incident 10'); + expect(tenth.attributes.form1_population).toEqual(1000); + }); + + it('logs a per-feature failure from the fake server without throwing', async () => { + const transformer = new ObservationsTransformer(config, silentConsole); + const eventTransform = new EventTransform(config, eventWithForm); + + const arcObjects = new ArcObjects(); + arcObjects.add(await transformer.transform(buildObservation(1), eventTransform, null)); + + mockedRequest.mockResolvedValue({ + addResults: [{ success: false, error: { code: 403, description: 'not authorized' } }] + }); + + const layerInfo = new LayerInfo('https://fake-arcgis.example.com/arcgis/rest/services/Incidents/FeatureServer/0', [4321], { id: 0, geometryType: 'esriGeometryPoint', fields: [] }); + const sender = new ObservationsSender(layerInfo, config, fakeIdentityManager, silentConsole); + + await expect(sender.sendAdds(arcObjects)).resolves.not.toThrow(); + + expect(silentConsole.error).toHaveBeenCalledWith(expect.stringContaining('403')); + }); +}); diff --git a/plugins/arcgis/service/src/ObservationsSender.ts b/plugins/arcgis/service/src/ObservationsSender.ts index 923be99cc..1d30119d2 100644 --- a/plugins/arcgis/service/src/ObservationsSender.ts +++ b/plugins/arcgis/service/src/ObservationsSender.ts @@ -3,12 +3,22 @@ import { ArcObjects } from './ArcObjects'; import { ArcObservation, ArcAttachment } from './types/ArcObservation'; import { LayerInfo } from "./LayerInfo"; import { EditResult } from './types/EditResult'; -import { AttachmentInfosResult, AttachmentInfo } from './types/AttachmentInfosResult'; +import { AttachmentInfo } from './types/AttachmentInfosResult'; import environment from '@ngageoint/mage.service/lib/environment/env' import fs from 'fs' import path from 'path' -import { ArcGISIdentityManager, IFeature, request } from "@esri/arcgis-rest-request" -import { addFeatures, updateFeatures, deleteFeatures, getAttachments, updateAttachment, addAttachment, deleteAttachments } from "@esri/arcgis-rest-feature-service"; +import { ArcGISIdentityManager, ArcGISRequestError, IFeature } from "@esri/arcgis-rest-request" +import { + addAttachment, + addFeatures, + deleteAttachments, + deleteFeatures, + getAttachments, + updateAttachment, + updateFeatures, + IAddAttachmentOptions, + IEditFeatureResult +} from "@esri/arcgis-rest-feature-service"; /** * Class that transforms observations into a json string that can then be sent to an arcgis server. @@ -58,14 +68,23 @@ export class ObservationsSender { * @param {ArcObjects} observations The observations to convert. */ async sendAdds(observations: ArcObjects) { - this._console.info('ArcGIS addFeatures'); - - await addFeatures({ - url: this._url, - authentication: this._identityManager, - features: observations.objects as IFeature[] - }).then(this.responseHandler(observations, false)) - .catch((error) => this._console.error('Error in ObservationSender.sendAdds :: ' + error)); + this._console.info(`ArcGIS addFeatures - ${observations.objects.length} observations`); + + try { + const response = await addFeatures({ + url: this._url, + authentication: this._identityManager, + features: observations.objects as IFeature[] + }); + this.responseHandler(observations, false)(response); + } catch (error) { + this._console.error('Error in ObservationSender.sendAdds :: ' + error) + if (error instanceof ArcGISRequestError) { + const message = error.response?.error?.message || ""; + const details = error?.response?.error?.details || ""; + this._console.error(` message: ${message}, details: ${details}${editFailureHint(message, String(details))}`); + } + } } /** @@ -74,14 +93,23 @@ export class ObservationsSender { * @param {ArcObjects} observations The observations to convert. */ async sendUpdates(observations: ArcObjects) { - this._console.info('ArcGIS updateFeatures'); - - await updateFeatures({ - url: this._url, - authentication: this._identityManager, - features: observations.objects as IFeature[] - }).then(this.responseHandler(observations, true)) - .catch((error) => this._console.error('Error in ObservationSender.sendUpdates :: ' + error)); + this._console.info(`ArcGIS updateFeatures - ${observations.objects.length} observations`); + + try { + const response = await updateFeatures({ + url: this._url, + authentication: this._identityManager, + features: observations.objects as IFeature[] + }); + this.responseHandler(observations, true)(response); + } catch (error) { + this._console.error('Error in ObservationSender.sendUpdates :: ' + error) + if (error instanceof ArcGISRequestError) { + const message = error.response?.error?.message || ""; + const details = error?.response?.error?.details || ""; + this._console.error(` message: ${message}, details: ${details}${editFailureHint(message, String(details))}`); + } + } } /** @@ -91,29 +119,55 @@ export class ObservationsSender { async sendDelete(id: string) { this._console.info('ArcGIS deleteFeatures id: ' + id) - await deleteFeatures({ - url: this._url, - authentication: this._identityManager, - where: `${this._config.observationIdField} LIKE '%${id}%'`, - objectIds: [] - }).catch((error) => this._console.error('Error in ObservationSender.sendDelete :: ' + error)); + try { + const response = await deleteFeatures({ + url: this._url, + authentication: this._identityManager, + where: `${this._config.observationIdField} LIKE '%${id}%'`, + objectIds: [] + }); + const results = response.deleteResults; + for (const result of results) { + if (!result.success) { + this._console.error(`Error deleting feature(s) for observation! code: ${result.error?.code}, description: ${result.error?.description}`); + } + } + } catch (error) { + this._console.error('Error in ObservationSender.sendDelete :: ' + error) + if (error instanceof ArcGISRequestError) { + this._console.error(` message: ${error.response?.error?.message || ""}, details: ${error?.response?.error?.details || ""}`); + } + } } /** * Deletes all observations that are apart of a specified event. - * @param {number} id The event id. + * @param {string} id The event id. */ - async sendDeleteEvent(id: number) { - this._console.info('ArcGIS deleteFeatures by event ' + this._config.observationIdField + ': ' + id); - - await deleteFeatures({ - url: this._url, - authentication: this._identityManager, - where: this._config.eventIdField - ? `${this._config.eventIdField} = ${id}` - : `${this._config.observationIdField} LIKE '%${this._config.idSeparator + id}%'`, - objectIds: [] - }).catch((error) => this._console.error('Error in ObservationSender.sendDeleteEvent :: ' + error)); + async sendDeleteEvent(id: string) { + this._console.info('ArcGIS deleteFeatures by event ' + this._config.eventIdField + ': ' + id); + + try { + const response = await deleteFeatures({ + url: this._url, + authentication: this._identityManager, + where: this._config.eventIdField + ? `${this._config.eventIdField} = '${id}'` + : `${this._config.observationIdField} LIKE '%${this._config.idSeparator + id}%'`, + objectIds: [] + }); + const results: IEditFeatureResult[] = response.deleteResults; + for (const result of results) { + if (!result.success) { + this._console.error(`Error deleting features for event! code: ${result.error?.code}, description: ${result.error?.description}`); + } + } + } catch (error) { + this._console.error('Error in ObservationSender.sendDeleteEvent :: ' + error) + if (error instanceof ArcGISRequestError) { + this._console.error(` message: ${error.response?.error?.message || ""}, details: ${error?.response?.error?.details || ""}`); + } + } } /** @@ -124,9 +178,8 @@ export class ObservationsSender { */ private responseHandler(observations: ArcObjects, update: boolean): (chunk: { addResults?: EditResult[], updateResults?: EditResult[] }) => void { const console = this._console; - return (chunk: { addResults?: EditResult[], updateResults?: EditResult[] }) => { - console.log('ArcGIS ' + (update ? 'Update' : 'Add') + ' Response: ' + JSON.stringify(chunk)); - const response = chunk; + return (response: { addResults?: EditResult[], updateResults?: EditResult[] }) => { + console.log('ArcGIS ' + (update ? 'Update' : 'Add') + ' Response: ' + JSON.stringify(response)); const results = response[update ? 'updateResults' : 'addResults'] as EditResult[]; if (results != null) { const obs = observations.observations; @@ -139,13 +192,14 @@ export class ObservationsSender { if (objectId != null) { console.log((update ? 'Update' : 'Add') + ' Features Observation id: ' + observation.id + ', Object id: ' + objectId); if (update) { - this.queryAndUpdateAttachments(observation, objectId); + void this.queryAndUpdateAttachments(observation, objectId); } else { this.sendAttachments(observation, objectId); } } } else if (result.error != null) { - console.error('ArcGIS Error. Code: ' + result.error.code + ', Description: ' + result.error.description); + const description = result.error.description || ''; + console.error('ArcGIS Error. Code: ' + result.error.code + ', Description: ' + description + editFailureHint(description, String(result.error.code))); } } } @@ -160,7 +214,7 @@ export class ObservationsSender { private sendAttachments(observation: ArcObservation, objectId: number) { if (observation.attachments != null) { for (const attachment of observation.attachments) { - this.sendAttachment(attachment, objectId); + void this.sendAttachment(attachment, objectId); } } } @@ -170,16 +224,31 @@ export class ObservationsSender { * @param {ArcObservation} observation The observation. * @param {number} objectId The arc object id of the observation. */ - private queryAndUpdateAttachments(observation: ArcObservation, objectId: number) { - // Query for existing attachments - getAttachments({ - url: this._url, - authentication: this._identityManager, - featureId: objectId - }).then((response) => { - const result = response as AttachmentInfosResult; - this.updateAttachments(observation, objectId, result.attachmentInfos); - }).catch((error) => this._console.error(error)); + private async queryAndUpdateAttachments(observation: ArcObservation, objectId: number) { + const attachmentInfos = await this.getAttachmentInfos(objectId); + await this.updateAttachments(observation, objectId, attachmentInfos); + } + + /** + * Queries the arc feature layer for the attachments currently present on a feature. + * @param {number} objectId The arc object id of the observation. + * @returns {Promise} The arc attachment infos, or an empty array if the query fails. + */ + async getAttachmentInfos(objectId: number): Promise { + try { + const response = await getAttachments({ + url: this._url, + authentication: this._identityManager, + featureId: objectId + }); + return response.attachmentInfos ?? []; + } catch (error) { + this._console.error("Error querying ArcGIS attachments: " + error) + if (error instanceof ArcGISRequestError) { + this._console.error(` message: ${error.response?.error?.message || ""}, details: ${error?.response?.error?.details || ""}`); + } + return []; + } } /** @@ -188,7 +257,7 @@ export class ObservationsSender { * @param {number} objectId The arc object id of the observation. * @param {AttachmentInfo[]} attachmentInfos The arc attachment infos. */ - private updateAttachments(observation: ArcObservation, objectId: number, attachmentInfos: AttachmentInfo[]) { + private async updateAttachments(observation: ArcObservation, objectId: number, attachmentInfos: AttachmentInfo[]) { // Build a mapping between existing arc attachment names and the attachment infos const nameAttachments = new Map(); if (attachmentInfos != null) { @@ -201,7 +270,7 @@ export class ObservationsSender { if (observation.attachments != null) { for (const attachment of observation.attachments) { - const fileName = this.attachmentFileName(attachment); + const fileName = attachmentFileName(attachment); const existingAttachment = nameAttachments.get(fileName); if (existingAttachment != null) { @@ -209,11 +278,11 @@ export class ObservationsSender { // Update the existing attachment if the file sizes do not match or last modified date updated if (attachment.size != existingAttachment.size || attachment.lastModified + this._config.attachmentModifiedTolerance >= observation.lastModified) { - this.updateAttachment(attachment, objectId, existingAttachment.id); + await this.updateAttachment(attachment, objectId, existingAttachment.id); } } else { // Add the new attachment on the updated observation - this.sendAttachment(attachment, objectId); + await this.sendAttachment(attachment, objectId); } } @@ -221,7 +290,7 @@ export class ObservationsSender { // Delete arc attachments that are no longer on the observation if (nameAttachments.size > 0) { - this.deleteAttachments(objectId, Array.from(nameAttachments.values())); + await this.deleteAttachments(objectId, Array.from(nameAttachments.values())); } } @@ -235,18 +304,32 @@ export class ObservationsSender { if (attachment.contentLocator) { const file = path.join(this._attachmentDirectory, attachment.contentLocator!); - const fileName = this.attachmentFileName(attachment); - this._console.info('ArcGIS ' + request + ' file ' + fileName + ' from ' + file); + const fileName = attachmentFileName(attachment); const readStream = await fs.openAsBlob(file); - const attachmentFile = new File([readStream], fileName); + const attachmentFile = new File([readStream], fileName, { type: attachment.mediaType }); + + this._console.info('ArcGIS sending file ' + fileName + ' from ' + file + ' for ' + objectId + ', ' + attachmentFile.size + ' bytes'); - addAttachment({ + const o = { url: this._url, authentication: this._identityManager, featureId: objectId, attachment: attachmentFile - }).catch((error) => this._console.error(error)); + } as IAddAttachmentOptions; + try { + const response: { addAttachmentResult: IEditFeatureResult; } = await addAttachment(o); + const result = response.addAttachmentResult; + if (!result.success) { + this._console.error(`Error sending attachment! code: ${result.error?.code}, description: ${result.error?.description}`); + } + } + catch (error) { + this._console.error("Error sending attachment! " + error); + if (error instanceof ArcGISRequestError) { + this._console.error(` message: ${error.response?.error?.message || ""}, details: ${error?.response?.error?.details || ""}`); + } + } } } @@ -260,19 +343,31 @@ export class ObservationsSender { if (attachment.contentLocator) { const file = path.join(this._attachmentDirectory, attachment.contentLocator!); - const fileName = this.attachmentFileName(attachment); - this._console.info('ArcGIS ' + request + ' file ' + fileName + ' from ' + file); + const fileName = attachmentFileName(attachment); const readStream = await fs.openAsBlob(file); - const attachmentFile = new File([readStream], fileName); - - updateAttachment({ - url: this._url, - authentication: this._identityManager, - featureId: objectId, - attachmentId, - attachment: attachmentFile - }).catch((error) => this._console.error(error)); + const attachmentFile = new File([readStream], fileName, { type: attachment.mediaType }); + + this._console.info('ArcGIS sending file ' + fileName + ' from ' + file + ' for update to ' + objectId + ', ' + attachmentFile.size + ' bytes'); + + try { + const response = await updateAttachment({ + url: this._url, + authentication: this._identityManager, + featureId: objectId, + attachmentId, + attachment: attachmentFile + }); + const result = response.updateAttachmentResult + if (!result.success) { + this._console.error(`Error updating attachment! code: ${result.error?.code}, description: ${result.error?.description}`); + } + } catch (error) { + this._console.error("Error updating attachment! " + error) + if (error instanceof ArcGISRequestError) { + this._console.error(` details: ${error?.response?.error?.details || ""}`); + } + } } } @@ -281,14 +376,14 @@ export class ObservationsSender { * @param {number} objectId The arc object id of the observation. * @param {AttachmentInfo[]} attachmentInfos The arc attachment infos. */ - private deleteAttachments(objectId: number, attachmentInfos: AttachmentInfo[]) { + private async deleteAttachments(objectId: number, attachmentInfos: AttachmentInfo[]) { const attachmentIds: number[] = []; for (const attachmentInfo of attachmentInfos) { attachmentIds.push(attachmentInfo.id); } - this.deleteAttachmentIds(objectId, attachmentIds); + await this.deleteAttachmentIds(objectId, attachmentIds); } /** @@ -296,31 +391,55 @@ export class ObservationsSender { * @param {number} objectId The arc object id of the observation. * @param {number[]} attachmentIds The arc attachment ids. */ - private deleteAttachmentIds(objectId: number, attachmentIds: number[]) { + private async deleteAttachmentIds(objectId: number, attachmentIds: number[]) { this._console.info('ArcGIS deleteAttachments ' + attachmentIds); - deleteAttachments({ - url: this._url, - authentication: this._identityManager, - featureId: objectId, - attachmentIds - }).catch((error) => this._console.error(error)); + try { + const response = await deleteAttachments({ + url: this._url, + authentication: this._identityManager, + featureId: objectId, + attachmentIds + }); + const results = response.deleteAttachmentResults; + for (const result of results) { + if (!result.success) { + this._console.error(`Error deleting attachments! code: ${result.error?.code}, description: ${result.error?.description}`); + } + } + } catch (error) { + this._console.error("Error deleting attachment ID(s)! " + error) + if (error instanceof ArcGISRequestError) { + this._console.error(` details: ${error?.response?.error?.details || ""}`); + } + } } - /** - * Determine the attachment file name. - * @param {ArcAttachment} attachment The observation attachment. - * @returns {string} attachment file name. - */ - private attachmentFileName(attachment: ArcAttachment): string { - let fileName = attachment.field + "_" + attachment.name; +} - const extensionIndex = attachment.contentLocator.lastIndexOf('.'); - if (extensionIndex != -1) { - fileName += attachment.contentLocator.substring(extensionIndex); - } +/** + * Determine the attachment file name arcgis will store/expect for a MAGE attachment. + * @param {ArcAttachment} attachment The observation attachment. + * @returns {string} attachment file name. + */ +export function attachmentFileName(attachment: ArcAttachment): string { + let fileName = attachment.field + "_" + attachment.name; - return fileName; + const extensionIndex = attachment.contentLocator.lastIndexOf('.'); + if (extensionIndex != -1) { + fileName += attachment.contentLocator.substring(extensionIndex); } -} \ No newline at end of file + return fileName; +} + +const editFailureHint = (message: string, details: string): string => { + const text = `${message} ${details}`.toLowerCase(); + if (/permission|not authorized|unauthorized|access is denied|\b403\b/.test(text)) { + return ' -- this usually means the signed-in ArcGIS user does not have edit permissions on this layer/service'; + } + if (/not supported|invalid request|editing.{0,15}disabled|capabilit/.test(text)) { + return ' -- this usually means the layer/service does not have editing capabilities enabled'; + } + return ''; +}; \ No newline at end of file diff --git a/plugins/arcgis/service/src/ObservationsTransformer.spec.ts b/plugins/arcgis/service/src/ObservationsTransformer.spec.ts new file mode 100644 index 000000000..a03b0cf14 --- /dev/null +++ b/plugins/arcgis/service/src/ObservationsTransformer.spec.ts @@ -0,0 +1,85 @@ +// import the mock module before anything that imports mongoose +const mockingoose = require("mockingoose"); + +import { ObservationsTransformer } from './ObservationsTransformer'; +import { ArcGISPluginConfig, defaultArcGISPluginConfig } from "./types/ArcGISPluginConfig"; +import { Attachment, ObservationAttrs } from "@ngageoint/mage.service/lib/entities/observations/entities.observations"; +import { EventTransform } from "./EventTransform"; +import { LineStyle } from "@ngageoint/mage.service/lib/entities/entities.global"; +import { Acl, MageEvent } from "@ngageoint/mage.service/lib/entities/events/entities.events"; +import { Form, FormField, FormFieldType } from "@ngageoint/mage.service/lib/entities/events/entities.events.forms"; +import { Model as IconModel } from '@ngageoint/mage.service/lib/models/icon'; +import { Polygon } from 'geojson' + +describe('MAGE observation to ArcGIS feature transformer', () => { + + let simpleStyle: LineStyle = {fill: '#FACADE'} as LineStyle; + let emptyACL: Acl = {} as Acl; + let simpleForm = { + id: 1, name: 'simple form', description: 'simple form description', + default: false, color: '#BEADED', archived: false, userFields: [], + fields: [{ + id: 1, archived: false, name: 'other_geometry', title: 'other_geometry', type: FormFieldType.Geometry, required: false, value: null + } as FormField] + } as Form; + let eventWithForm = new MageEvent({ + id: 4321, name: 'test event with simple form', layerIds: [], feedIds: [], + forms: [simpleForm], style: simpleStyle, acl: emptyACL + }); + + let config: ArcGISPluginConfig = {} as ArcGISPluginConfig; + Object.assign(config, + { + ...defaultArcGISPluginConfig, + eventIdField: 'test_event_id' + } + ); + + test('transforms an observation with an event icon', async () => { + + // mock the icon data that would otherwise have been loaded from MongoDB + mockingoose(IconModel).toReturn({eventId: 4321, relativePath: '4321/icon.svg'}, 'findOne'); + + const xformer = new ObservationsTransformer(config, console); + const mageObservation = { + id: 'testing321', + properties: {forms: []}, + eventId: 4321, + geometry: { type: 'Point', coordinates: [ -1.391412, 50.885025 ] }, + attachments: [] as Attachment[] + } as unknown as ObservationAttrs; + + const eventTransform = new EventTransform(config, eventWithForm); + const feature = await xformer.transform(mageObservation, eventTransform, null); + expect(feature.object).not.toBeNull(); + expect(feature.object.attributes).not.toBeNull(); + const attributes = feature.object.attributes; + expect(attributes).toHaveProperty('icon_symbol', '4321/icon.svg'); + }); + + test('transforms an observation with a geometry form attribute', async () => { + + mockingoose(IconModel).toReturn(null, 'findOne'); + + const xformer = new ObservationsTransformer(config, console); + const mageObservation = { + properties: {forms: [{formId: 1}]}, + eventId: 4321, + geometry: { type: 'Point', coordinates: [ -1.391412, 50.885025 ] }, + attachments: [] as Attachment[] + } as unknown as ObservationAttrs; + const positions = [[[0.00000001, 1], [0, 0], [1, 0], [1.00001, 1.00001], [0.00000001, 1]]] + mageObservation.properties.forms[0].id = 'formentry1'; + mageObservation.properties.forms[0].other_geometry = { type: "Polygon", coordinates: positions} as Polygon; + const eventTransform = new EventTransform(config, eventWithForm); + const feature = await xformer.transform(mageObservation, eventTransform, null); + expect(feature.object).not.toBeNull(); + expect(feature.object.attributes).not.toBeNull(); + const attributes = feature.object.attributes; + expect(attributes).toHaveProperty('form1_other_geometry'); + const other_geometry_string = attributes.form1_other_geometry; + expect(other_geometry_string).toContain('POLYGON'); + expect(other_geometry_string).toContain('1.00001 1.00001'); + }); + +}); diff --git a/plugins/arcgis/service/src/ObservationsTransformer.ts b/plugins/arcgis/service/src/ObservationsTransformer.ts index 30c4f3b5f..49cc07ecd 100644 --- a/plugins/arcgis/service/src/ObservationsTransformer.ts +++ b/plugins/arcgis/service/src/ObservationsTransformer.ts @@ -1,12 +1,16 @@ import { ArcGISPluginConfig } from "./types/ArcGISPluginConfig"; import { AttributeDefaultConfig } from "./types/ArcGISConfig"; -import { ObservationAttrs, Attachment } from '@ngageoint/mage.service/lib/entities/observations/entities.observations' +import { ObservationAttrs, Attachment, FormEntry } from '@ngageoint/mage.service/lib/entities/observations/entities.observations' import { User } from '@ngageoint/mage.service/lib/entities/users/entities.users' import { FormFieldType } from '@ngageoint/mage.service/lib/entities/events/entities.events.forms' +import { MageEvent } from '@ngageoint/mage.service/lib/entities/events/entities.events' +import api from '@ngageoint/mage.service/lib/api' import { Geometry, Point, LineString, Polygon } from 'geojson' +import { geojsonToWKT } from '@terraformer/wkt'; import { ArcObservation, ArcAttachment } from './types/ArcObservation' import { ArcGeometry, ArcObject, ArcPoint, ArcPolyline, ArcPolygon } from './types/ArcObject' import { EventTransform } from './EventTransform' +import mimetypes from 'mime-types'; /** * Class that transforms observations into a json string that can then be sent to an arcgis server. @@ -77,7 +81,7 @@ export class ObservationsTransformer { * @param {User | null} user The MAGE user. * @returns {ArcObservation} The ArcObservation of the observation. */ - transform(observation: ObservationAttrs, transform: EventTransform, user: User | null): ArcObservation { + async transform(observation: ObservationAttrs, transform: EventTransform, user: User | null): Promise { this.init(); const arcObject = {} as ArcObject; @@ -91,9 +95,9 @@ export class ObservationsTransformer { arcObject.geometry = arcGeometry; } - let formIds: { [name: string]: number } = {}; + let formIds: Map = new Map(); if (observation.properties != null) { - formIds = this.propertiesToAttributes(observation.properties, transform, arcObject); + formIds = await this.propertiesToAttributes(observation.properties, transform, arcObject); } const arcObservation = this.createObservation(observation); @@ -227,6 +231,52 @@ export class ObservationsTransformer { return arcGeometry; } + /** + * Recursively rounds coordinate precision in a GeoJSON geometry. + * Works with any geometry type including GeometryCollections. + * @param {Geometry} geometry The geometry to round. + * @param {number} precision maximum number of decimal places to keep + * @returns new geometry with rounded coordinates + */ + private roundCoordinates(geometry: Geometry, precision = 6): Geometry { + const factor = Math.pow(10, precision); + const round = (n: number) => Math.round(n * factor) / factor; + + const processCoord = (coord: number[]): number[] => coord.map(round); + const processRing = (ring: number[][]): number[][] => ring.map(processCoord); + + switch (geometry.type) { + case "Point": + return { ...geometry, coordinates: processCoord(geometry.coordinates) }; + + case "LineString": + case "MultiPoint": + return { ...geometry, coordinates: geometry.coordinates.map(processCoord) }; + + case "Polygon": + case "MultiLineString": + return { ...geometry, coordinates: geometry.coordinates.map(processRing) }; + + case "MultiPolygon": + return { ...geometry, coordinates: geometry.coordinates.map(p => p.map(processRing)) }; + + case "GeometryCollection": + return { ...geometry, geometries: geometry.geometries.map(g => this.roundCoordinates(g, precision)) }; + + default: + throw new Error(`Unsupported geometry type: ${(geometry as Geometry).type}`); + } + } + + /** + * Converts an observation geometry to a WKT string. + * @param {Geometry} geometry The observation geometry to convert. + * @returns {string} The WKT-encoded geometry. + */ + private geometryToWKT(geometry: Geometry): string { + return geojsonToWKT(this.roundCoordinates(geometry)); + } + /** * Converts an observation Point to an ArcPoint. * @param {Point} point The observation Point to convert. @@ -266,14 +316,14 @@ export class ObservationsTransformer { * @param {{ [name: string]: unknown }} properties The observation properties to convert. * @param {EventTransform} transform The Event transform. * @param {ArcObject} arcObject The converted ArcObject. - * @returns {{ [name: string]: number }} form ids map + * @returns {Map} mapping from form entry ID to form ID */ - private propertiesToAttributes(properties: { [name: string]: any }, transform: EventTransform, arcObject: ArcObject): { [name: string]: number } { - let formIds: { [name: string]: number } = {}; + private async propertiesToAttributes(properties: { [name: string]: any }, transform: EventTransform, arcObject: ArcObject): Promise> { + let formIds: Map = new Map(); for (const property in properties) { const value = properties[property] if (property === 'forms') { - formIds = this.formsToAttributes(value, transform, arcObject) + formIds = await this.formsToAttributes(value as FormEntry[], transform, arcObject) } else { this.addAttribute(property, value, arcObject); } @@ -283,53 +333,55 @@ export class ObservationsTransformer { /** * Converts and adds observation property forms data to ArcObject attributes. - * @param {[{ [name: string]: unknown }]} forms The observation property forms to convert. + * @param {FormEntry[]} forms The observation property forms to convert. * @param {EventTransform} transform The Event transform. * @param {ArcObject} arcObject The converted ArcObject. - * @returns {{ [name: string]: number }} form ids map + * @returns {Map} mapping from form entry ID to form ID */ - private formsToAttributes(forms: [{ [name: string]: any }], transform: EventTransform, arcObject: ArcObject): { [name: string]: number } { + private async formsToAttributes(forms: FormEntry[], transform: EventTransform, arcObject: ArcObject): Promise> { - const formIds: { [id: string]: number } = {}; - const formIdCount: { [id: number]: number } = {}; + // mapping from form entry ID to form ID + const formIds: Map = new Map(); + // mapping from form ID to number of instances of that form in the observation + const formIdCount: Map = new Map(); const mageEvent = transform.mageEvent; + let firstFormEntry: FormEntry | null = null; + for (let i = 0; i < forms.length; i++) { - const form = forms[i]; - const formId = form['formId']; - const id = form['id']; + const form: FormEntry = forms[i]; + const formId: number = form['formId']; + if (firstFormEntry == null) { + firstFormEntry = form; + } + const formEntryId: string = form['id']; let fields = null; let formCount = 1; - if (formId != null && id != null) { - formIds[id] = formId; - let count = formIdCount[formId]; - if (count == null) { - count = 0; - } + if (formId != null && formEntryId != null) { + formIds.set(formEntryId, formId); + const count: number = formIdCount.get(formId) || 0; formCount = count + 1; - formIdCount[formId] = formCount; + formIdCount.set(formId, formCount); fields = transform.get(formId); } for (const formProperty in form) { - let value = form[formProperty]; + let value: any = form[formProperty]; if (value != null) { if (mageEvent != null && formId != null) { const field = mageEvent.formFieldFor(formProperty, formId); if (field != null && field.type !== FormFieldType.Attachment) { - let title = field.title + let attributeName = formProperty; if (fields != null) { - const fieldTitle = fields.get(title) - if (fieldTitle != null) { - const sanitizedName = ObservationsTransformer.replaceSpaces(fieldTitle) - const sanitizedFormName = ObservationsTransformer.replaceSpaces(fields.name) - title = `${sanitizedFormName}_${sanitizedName}`.toLowerCase() + const fieldAttributeName = fields.getField(formProperty) + if (fieldAttributeName != null) { + attributeName = fieldAttributeName } } if (field.type === FormFieldType.Geometry) { - value = this.geometryToArcGeometry(value) + value = this.geometryToWKT(value as Geometry) } - this.addFormAttribute(title, formCount, value, arcObject); + this.addFormAttribute(attributeName, formCount, value, arcObject); } } else { this.addFormAttribute(formProperty, formCount, value, arcObject); @@ -338,9 +390,53 @@ export class ObservationsTransformer { } } + await this.addIconSymbolAttribute(arcObject, mageEvent, firstFormEntry); + return formIds; } + /** + * Add an attribute to the ArcGIS feature to specify the icon symbol that should be rendered for the feature + * @param {ArcObject} arcObject the converted ArcObject + * @param {MageEvent | null} mageEvent event to which the observation belongs + * @param {FormEntry | null} formEntry the form entry to drive the symbology + */ + private async addIconSymbolAttribute(arcObject: ArcObject, mageEvent: MageEvent | null, formEntry: FormEntry | null): Promise { + let primaryFieldValue: string | undefined = undefined; + let variantFieldValue: string | undefined = undefined; + if (formEntry != null && mageEvent != null) { + const form = mageEvent.forms.find((f) => f.id === formEntry.formId); + if (form != null) { + primaryFieldValue = form.primaryField ? formEntry[form.primaryField] as string : undefined; + variantFieldValue = form.variantField ? formEntry[form.variantField] as string : undefined; + } + } + + let iconPath: string | null = null; + try { + iconPath = await new Promise((resolve, reject) => { + new api.Icon(mageEvent?.id, formEntry?.formId, primaryFieldValue, variantFieldValue) + .getIcon((err, icon) => { + if (err) { + reject(err); + return; + } + if (icon) { + resolve(icon.relativePath); + return; + } + resolve(null); + }); + }); + } catch (err) { + this._console.error(`error determining observation icon for event:${mageEvent?.name}${formEntry ? ", form:" + formEntry.formId : ""}${primaryFieldValue ? ", primary field value:" + primaryFieldValue : ""}${variantFieldValue ? ", variant field value:" + variantFieldValue : ""} — ${err}`); + } + + if (iconPath) { + this.addAttribute(this._config.iconSymbolField, iconPath, arcObject); + } + } + /** * Add an ArcObject attribute for a form field. * @param {string} name The attribute name. @@ -473,11 +569,11 @@ export class ObservationsTransformer { /** * Transform observation attachments. * @param {readonly Attachment[]} attachments The observation attachments. - * @param {{ [name: string]: number }} formIds Form ids map. + * @param {Map} formIds Form ids map. * @param {EventTransform} transform The Event transform. * @returns {ArcAttachment[]} The converted ArcAttachments. */ - private attachments(attachments: readonly Attachment[], formIds: { [name: string]: number }, transform: EventTransform): ArcAttachment[] { + private attachments(attachments: readonly Attachment[], formIds: Map, transform: EventTransform): ArcAttachment[] { const arcAttachments: ArcAttachment[] = []; @@ -489,7 +585,7 @@ export class ObservationsTransformer { let fieldName = attachment.fieldName; if (mageEvent != null) { - const formId = formIds[attachment.observationFormId]; + const formId = formIds.get(attachment.observationFormId); if (formId != null) { const field = mageEvent.formFieldFor(fieldName, formId); if (field != null) { @@ -517,6 +613,14 @@ export class ObservationsTransformer { arcAttachment.name = attachment.id; } arcAttachment.contentLocator = attachment.contentLocator; + if (attachment.contentType != null) { + arcAttachment.mediaType = attachment.contentType; + } else if (attachment.name != null) { + const mimeType = mimetypes.lookup(attachment.name); + if (mimeType !== false) { + arcAttachment.mediaType = mimeType; + } + } arcAttachments.push(arcAttachment); } diff --git a/plugins/arcgis/service/src/PortalDiscovery.spec.ts b/plugins/arcgis/service/src/PortalDiscovery.spec.ts new file mode 100644 index 000000000..efbe03832 --- /dev/null +++ b/plugins/arcgis/service/src/PortalDiscovery.spec.ts @@ -0,0 +1,240 @@ +import { ArcGISRequestError, request } from '@esri/arcgis-rest-request'; +import { searchItems } from '@esri/arcgis-rest-portal'; +import { + portalUrlCandidates, + discoverWithPortalCandidates, + discoverFeatureServices, + checkEditPrivilege, + describeAuthFailure +} from './PortalDiscovery'; + +/** + * Tests for browsing an arcgis portal URL + */ + +jest.mock('@esri/arcgis-rest-request', () => { + const actual = jest.requireActual('@esri/arcgis-rest-request'); + return { ...actual, request: jest.fn() }; +}); +jest.mock('@esri/arcgis-rest-portal', () => ({ searchItems: jest.fn() })); + +const notFoundError = (url: string): ArcGISRequestError => new ArcGISRequestError('Not Found', 'HTTP 404', undefined, url); +const mockedRequest = request as jest.Mock; +const mockedSearchItems = searchItems as jest.Mock; +const silentConsole = { log: jest.fn(), debug: jest.fn(), info: jest.fn(), warn: jest.fn(), error: jest.fn() } as unknown as Console; + +beforeEach(() => { + mockedRequest.mockReset(); + mockedSearchItems.mockReset(); +}); + +describe('portalUrlCandidates', () => { + it('uses the url as-is when it already points at sharing/rest', () => { + expect(portalUrlCandidates('https://my.portal.com/arcgis/sharing/rest')) + .toEqual(['https://my.portal.com/arcgis/sharing/rest']); + }); + + it('appends sharing/rest to a url that already includes a web adaptor path', () => { + expect(portalUrlCandidates('https://my.portal.com/gis')) + .toEqual(['https://my.portal.com/gis/sharing/rest']); + }); + + it('offers the conventional "arcgis" web adaptor path as a second candidate for a bare hostname', () => { + expect(portalUrlCandidates('https://gis.example.com')).toEqual([ + 'https://gis.example.com/sharing/rest', + 'https://gis.example.com/arcgis/sharing/rest' + ]); + }); + + it('strips a feature service path down to the server root before building candidates', () => { + expect(portalUrlCandidates('https://my.portal.com/arcgis/rest/services/MyLayer/FeatureServer')) + .toEqual(['https://my.portal.com/arcgis/sharing/rest']); + }); + + it('returns no candidates for an empty input', () => { + expect(portalUrlCandidates('')).toEqual([]); + expect(portalUrlCandidates(undefined)).toEqual([]); + }); +}); + +describe('discoverWithPortalCandidates', () => { + it('uses the first candidate portal url when it succeeds', async () => { + mockedSearchItems.mockResolvedValue({ results: [], total: 0, start: 1, num: 100 }); + const buildIdentity = jest.fn().mockResolvedValue({ username: 'jdoe' }); + + const result = await discoverWithPortalCandidates( + 'https://my.portal.com/arcgis/sharing/rest', buildIdentity, undefined, undefined, undefined, silentConsole + ); + + expect(result.portalUrl).toEqual('https://my.portal.com/arcgis/sharing/rest'); + expect(buildIdentity).toHaveBeenCalledTimes(1); + expect(buildIdentity).toHaveBeenCalledWith('https://my.portal.com/arcgis/sharing/rest'); + }); + + it('falls back to the backup (arcgis web adaptor) candidate when the first one fails', async () => { + const buildIdentity = jest.fn() + .mockRejectedValueOnce(new Error('not found')) + .mockResolvedValueOnce({ username: 'jdoe' }); + mockedSearchItems.mockResolvedValue({ results: [], total: 0, start: 1, num: 100 }); + + const result = await discoverWithPortalCandidates( + 'https://gis.example.com', buildIdentity, undefined, undefined, undefined, silentConsole + ); + + expect(buildIdentity).toHaveBeenNthCalledWith(1, 'https://gis.example.com/sharing/rest'); + expect(buildIdentity).toHaveBeenNthCalledWith(2, 'https://gis.example.com/arcgis/sharing/rest'); + expect(result.portalUrl).toEqual('https://gis.example.com/arcgis/sharing/rest'); + }); + + it('also retries the backup candidate when identity succeeds but the actual search fails', async () => { + const buildIdentity = jest.fn().mockResolvedValue({ username: 'jdoe' }); + mockedSearchItems + .mockRejectedValueOnce(new Error('search failed against wrong portal')) + .mockResolvedValueOnce({ results: [], total: 0, start: 1, num: 100 }); + + const result = await discoverWithPortalCandidates( + 'https://gis.example.com', buildIdentity, undefined, undefined, undefined, silentConsole + ); + + expect(mockedSearchItems).toHaveBeenCalledTimes(2); + expect(result.portalUrl).toEqual('https://gis.example.com/arcgis/sharing/rest'); + }); + + it('throws (failure to authenticate) when every candidate fails', async () => { + const authError = new Error('invalid credentials'); + const buildIdentity = jest.fn().mockRejectedValue(authError); + + await expect(discoverWithPortalCandidates( + 'https://gis.example.com', buildIdentity, undefined, undefined, undefined, silentConsole + )).rejects.toThrow('invalid credentials'); + expect(buildIdentity).toHaveBeenCalledTimes(2); + }); + + it('throws when no portal url is provided at all', async () => { + const buildIdentity = jest.fn(); + await expect(discoverWithPortalCandidates( + undefined, buildIdentity, undefined, undefined, undefined, silentConsole + )).rejects.toThrow('portalUrl is required'); + expect(buildIdentity).not.toHaveBeenCalled(); + }); + + it('falls back to the backup candidate when the given portal url comes back 404 not found', async () => { + const buildIdentity = jest.fn() + .mockRejectedValueOnce(notFoundError('https://gis.example.com/sharing/rest/generateToken')) + .mockResolvedValueOnce({ username: 'jdoe' }); + mockedSearchItems.mockResolvedValue({ results: [], total: 0, start: 1, num: 100 }); + + const result = await discoverWithPortalCandidates( + 'https://gis.example.com', buildIdentity, undefined, undefined, undefined, silentConsole + ); + + expect(result.portalUrl).toEqual('https://gis.example.com/arcgis/sharing/rest'); + }); + + it('surfaces the 404 (portal url not found) error when every candidate comes back not found', async () => { + const buildIdentity = jest.fn().mockRejectedValue(notFoundError('https://gis.example.com/sharing/rest/generateToken')); + + await expect(discoverWithPortalCandidates( + 'https://gis.example.com', buildIdentity, undefined, undefined, undefined, silentConsole + )).rejects.toMatchObject({ code: 'HTTP 404' }); + expect(buildIdentity).toHaveBeenCalledTimes(2); + }); + + it('reports mayLackEditPrivilege from the successful candidate', async () => { + mockedSearchItems.mockResolvedValue({ results: [], total: 0, start: 1, num: 100 }); + mockedRequest.mockResolvedValue({ username: 'jdoe', privileges: [] }); + const buildIdentity = jest.fn().mockResolvedValue({ username: 'jdoe' }); + + const result = await discoverWithPortalCandidates( + 'https://my.portal.com/arcgis/sharing/rest', buildIdentity, undefined, undefined, undefined, silentConsole + ); + + expect(result.mayLackEditPrivilege).toBe(true); + }); +}); + +describe('discoverFeatureServices', () => { + it('returns the discovered feature services', async () => { + mockedSearchItems.mockResolvedValue({ + results: [{ id: '1', title: 'My Service', url: 'https://host/arcgis/rest/services/My/FeatureServer', owner: 'jdoe' }], + total: 1, + start: 1, + num: 100 + }); + mockedRequest.mockResolvedValue({ capabilities: 'Query,Create,Update,Delete,Editing' }); + + const page = await discoverFeatureServices({ username: 'jdoe' } as any, silentConsole); + + expect(page.total).toEqual(1); + expect(page.services).toHaveLength(1); + expect(page.services[0]).toMatchObject({ id: '1', title: 'My Service', url: 'https://host/arcgis/rest/services/My/FeatureServer' }); + }); + + it('marks a service with no edit capabilities as "Read only"', async () => { + mockedSearchItems.mockResolvedValue({ + results: [{ id: '2', title: 'View Only Service', url: 'https://host/arcgis/rest/services/ViewOnly/FeatureServer', owner: 'jdoe' }], + total: 1, + start: 1, + num: 100 + }); + mockedRequest.mockResolvedValue({ capabilities: 'Query' }); + + const page = await discoverFeatureServices({ username: 'jdoe' } as any, silentConsole); + + expect(page.services[0].permission).toEqual('Read only'); + }); + + it('does not mark a service with edit capabilities as read only', async () => { + mockedSearchItems.mockResolvedValue({ + results: [{ id: '3', title: 'Editable Service', url: 'https://host/arcgis/rest/services/Editable/FeatureServer', owner: 'jdoe' }], + total: 1, + start: 1, + num: 100 + }); + mockedRequest.mockResolvedValue({ capabilities: 'Query,Create,Editing' }); + + const page = await discoverFeatureServices({ username: 'jdoe' } as any, silentConsole); + + expect(page.services[0].permission).toEqual(''); + }); +}); + +describe('checkEditPrivilege', () => { + it('returns false (does not lack privilege) when the user has features:user:edit', async () => { + mockedRequest.mockResolvedValue({ username: 'jdoe', privileges: ['features:user:edit', 'portal:user:shareToPublic'] }); + + const mayLack = await checkEditPrivilege('https://my.portal.com/arcgis/sharing/rest', {} as any, silentConsole); + + expect(mayLack).toBe(false); + }); + + it('returns true (may lack privilege) when the user does not have features:user:edit', async () => { + mockedRequest.mockResolvedValue({ username: 'jdoe', privileges: ['portal:user:shareToPublic'] }); + + const mayLack = await checkEditPrivilege('https://my.portal.com/arcgis/sharing/rest', {} as any, silentConsole); + + expect(mayLack).toBe(true); + }); + + it('returns undefined (unknown) when the privilege check itself fails', async () => { + mockedRequest.mockRejectedValue(new Error('community/self failed')); + + const mayLack = await checkEditPrivilege('https://my.portal.com/arcgis/sharing/rest', {} as any, silentConsole); + + expect(mayLack).toBeUndefined(); + }); +}); + +describe('describeAuthFailure', () => { + it('reports a not-found message for a 404, instead of blaming credentials', () => { + const message = describeAuthFailure('portal', notFoundError('https://gis.example.com/sharing/rest/generateToken')); + + expect(message).toEqual('Could not find an ArcGIS endpoint at the portal URL. Check the URL and try again.'); + }); + + it('reports an invalid-credentials message for a non-404 error', () => { + const message = describeAuthFailure('portal', new Error('Invalid username or password.')); + + expect(message).toEqual('Invalid credentials provided to communicate with portal: Invalid username or password.'); + }); +}); diff --git a/plugins/arcgis/service/src/PortalDiscovery.ts b/plugins/arcgis/service/src/PortalDiscovery.ts new file mode 100644 index 000000000..6b9271ddc --- /dev/null +++ b/plugins/arcgis/service/src/PortalDiscovery.ts @@ -0,0 +1,172 @@ +import { ArcGISIdentityManager, ArcGISRequestError, request } from "@esri/arcgis-rest-request"; +import { searchItems } from "@esri/arcgis-rest-portal"; + +export const describeArcGISError = (err: unknown): string => { + if (err instanceof ArcGISRequestError) { + return `${err.message}${err.response?.error?.message ? ` (${err.response.error.message})` : ''}`; + } + return err instanceof Error ? err.message : String(err); +}; + +// produces a user-facing message for an auth/communication failure against a given ArcGIS endpoint +export const describeAuthFailure = (label: string, err: unknown): string => { + if (err instanceof ArcGISRequestError && String(err.code ?? '').includes('404')) { + return `Could not find an ArcGIS endpoint at the ${label} URL. Check the URL and try again.`; + } + return `Invalid credentials provided to communicate with ${label}: ${describeArcGISError(err)}`; +}; + +// derives an ordered, de-duplicated list of portal "sharing/rest" URLs to try +export const portalUrlCandidates = (input: string | undefined): string[] => { + const trimmed = (input || '').trim().replace(/\/+$/, ''); + if (!trimmed) { + return []; + } + + const sharingMatch = trimmed.match(/^(.*\/sharing\/rest)\b/i); + const serverRoot = sharingMatch + ? sharingMatch[1].replace(/\/sharing\/rest$/i, '') + : trimmed.replace(/\/rest\/services\/.*$/i, '').replace(/\/rest\/?$/i, ''); + + const bareOrigin = /^https?:\/\/[^/]+$/i.test(serverRoot); + const roots = bareOrigin ? [serverRoot, `${serverRoot}/arcgis`] : [serverRoot]; + return Array.from(new Set(roots.map(root => `${root}/sharing/rest`))); +}; + +// tries to sign in with username/password against each candidate portal url derived from the +// user's input, in order, until one succeeds - returns the identity manager along with whichever +// portal url actually worked so the caller can report it back / persist the corrected value +export const signInWithPortalCandidates = async ( + username: string, + password: string, + portalUrlInput: string | undefined, + console: Console +): Promise<{ identityManager: ArcGISIdentityManager, portalUrl: string }> => { + const candidates = portalUrlCandidates(portalUrlInput); + if (candidates.length === 0) { + throw new Error('portalUrl is required'); + } + + let lastErr: unknown; + for (const candidatePortalUrl of candidates) { + try { + const identityManager = await ArcGISIdentityManager.signIn({ username, password, portal: candidatePortalUrl }); + console.log(`Signed in to portal ${candidatePortalUrl} as ${username}`); + return { identityManager, portalUrl: candidatePortalUrl }; + } catch (err) { + console.debug(`Could not sign in to portal ${candidatePortalUrl}: ${describeArcGISError(err)}`); + lastErr = err; + } + } + throw lastErr; +}; + +// checks whether the authenticated user's ArcGIS privileges include features:user:edit, required +// to write observations to feature layers +export const checkEditPrivilege = async (portalUrl: string, identityManager: ArcGISIdentityManager, console: Console): Promise => { + try { + const self = await request(`${portalUrl}/community/self`, { authentication: identityManager }); + const privileges: string[] = self?.privileges ?? []; + console.debug(`ArcGIS user ${self?.username} has privileges: ${privileges.join(', ')}`); + return !privileges.includes('features:user:edit'); + } catch (err) { + console.error(`Could not check ArcGIS user privileges: ${describeArcGISError(err)}`); + return undefined; + } +}; + +export type DiscoveredFeatureService = { + id: string + title: string + url: string + owner: string + // undefined if the service's own definition couldn't be fetched, meaning read-only status is unknown + capabilities?: string + permission: string +} + +export type DiscoveredFeatureServicesPage = { + services: DiscoveredFeatureService[] + total: number + start: number + num: number +} + +/** + * Search a portal for feature services accessible to the given identity, sorted alphabetically by title. + * @param identityManager authenticated identity to search the portal with + * @param console used to log messages + * @param start 1-based index of the first result to return, per the ArcGIS REST paging convention + * @param num number of results to return + * @param titleFilter if provided, restricts results to services whose title starts with this text + * @returns a page of feature services available to that identity, and the total count across all pages + */ +export const discoverFeatureServices = async (identityManager: ArcGISIdentityManager, console: Console, start = 1, num = 20, titleFilter?: string): Promise => { + const sanitizedFilter = titleFilter?.replace(/["*:]/g, '').trim(); + // a trailing-only wildcard (prefix match) is used because leading wildcards are not reliably + // supported by the portal's search index and can cause the filter clause to be silently ignored + const q = sanitizedFilter ? `type:"Feature Service" AND title:${sanitizedFilter}*` : 'type:"Feature Service"'; + const result = await searchItems({ + q, + authentication: identityManager, + sortField: 'title', + sortOrder: 'asc', + start, + num + }); + const services = await Promise.all(result.results + .filter((item): item is typeof item & { url: string } => !!item.url) + .map(async (item) => { + // the portal search result doesn't include capabilities, so fetch each service's own + // definition to find out whether it allows editing before the user selects it + let capabilities: string | undefined; + try { + const response = await request(item.url, { authentication: identityManager }); + capabilities = response.capabilities; + } catch (err) { + console.error(`Could not get capabilities for discovered feature service ${item.url}: ${describeArcGISError(err)}`); + } + + const serviceHasEditCapability = !!capabilities?.split(',').some(c => ['Create', 'Update', 'Delete', 'Editing'].includes(c)); + + return { + id: item.id, + title: item.title, + url: item.url, + owner: item.owner, + capabilities, + permission: serviceHasEditCapability ? '' : 'Read only' + }; + })); + return { services, total: result.total, start, num }; +}; + +// tries a full "authenticate + search" attempt against each candidate portal url in order +export const discoverWithPortalCandidates = async ( + portalUrlInput: string | undefined, + buildIdentity: (candidatePortalUrl: string) => Promise, + start: number | undefined, + num: number | undefined, + filter: string | undefined, + console: Console +): Promise<{ identityManager: ArcGISIdentityManager, portalUrl: string, mayLackEditPrivilege?: boolean, page: DiscoveredFeatureServicesPage }> => { + const candidates = portalUrlCandidates(portalUrlInput); + if (candidates.length === 0) { + throw new Error('portalUrl is required'); + } + + let lastErr: unknown; + for (const candidatePortalUrl of candidates) { + try { + const identityManager = await buildIdentity(candidatePortalUrl); + const page = await discoverFeatureServices(identityManager, console, start, num, filter); + const mayLackEditPrivilege = await checkEditPrivilege(candidatePortalUrl, identityManager, console); + console.log(`Successfully browsed portal ${candidatePortalUrl} as ${identityManager.username}`); + return { identityManager, portalUrl: candidatePortalUrl, mayLackEditPrivilege, page }; + } catch (err) { + console.debug(`Could not browse portal ${candidatePortalUrl}: ${describeArcGISError(err)}`); + lastErr = err; + } + } + throw lastErr; +}; diff --git a/plugins/arcgis/service/src/index.ts b/plugins/arcgis/service/src/index.ts index 0bf8de835..37797300f 100644 --- a/plugins/arcgis/service/src/index.ts +++ b/plugins/arcgis/service/src/index.ts @@ -1,3 +1,4 @@ +import baseLog from '@ngageoint/mage.service/lib/logger'; import { InitPluginHook, PluginStateRepositoryToken } from '@ngageoint/mage.service/lib/plugins.api' import { GetAppRequestContext, WebRoutesHooks } from '@ngageoint/mage.service/lib/plugins.api/plugins.api.web' import { ObservationRepositoryToken } from '@ngageoint/mage.service/lib/plugins.api/plugins.api.observations' @@ -7,25 +8,43 @@ import { SettingPermission } from '@ngageoint/mage.service/lib/entities/authoriz import { MageEventId } from '@ngageoint/mage.service/lib/entities/events/entities.events' import { ObservationProcessor } from './ObservationProcessor' import { ArcGISIdentityManager, request } from "@esri/arcgis-rest-request" -import { FeatureServiceConfig, FeatureLayerConfig } from './types/ArcGISConfig' +import { FeatureServiceConfig } from './types/ArcGISConfig' import { URL } from "node:url" import express from 'express' -import { ArcGISIdentityService, createArcGISIdentityService, getPortalUrl } from './ArcGISService' - -const logPrefix = '[mage.arcgis]'; -const logMethods = ['log', 'debug', 'info', 'warn', 'error'] as const; -const consoleOverrides = logMethods.reduce((overrides, fn) => { +import { createArcGISIdentityService, getPortalUrl } from './ArcGISService' +import { + checkEditPrivilege, + describeArcGISError, + describeAuthFailure, + discoverFeatureServices, + discoverWithPortalCandidates, + signInWithPortalCandidates +} from './PortalDiscovery' +import { buildFeatureServicesForSave, commitFeatureService, sanitizeFeatureService } from './FeatureServiceConfigStore' +import { serializeAndEncrypt } from './CredentialEncryption' + +// scope every log message from this plugin under MAGE's central winston logger +// map console's method names to the child logger's methods explicitly +const arcgisLog = baseLog.child({ component: 'arcgis' }); +const logMethodMap: Record<'log' | 'debug' | 'info' | 'warn' | 'error', (...args: unknown[]) => void> = { + log: (...args) => arcgisLog.info(...args as [string, ...unknown[]]), + debug: (...args) => arcgisLog.debug(...args as [string, ...unknown[]]), + info: (...args) => arcgisLog.info(...args as [string, ...unknown[]]), + warn: (...args) => arcgisLog.warn(...args as [string, ...unknown[]]), + error: (...args) => arcgisLog.error(...args as [string, ...unknown[]]), +}; +const logOverrides = (Object.keys(logMethodMap) as (keyof typeof logMethodMap)[]).reduce((overrides, fn) => { return { ...overrides, [fn]: { writable: false, value: (...args: unknown[]) => { - globalThis.console[fn](new Date().toISOString(), '-', logPrefix, ...args); + logMethodMap[fn](...args); } } } as PropertyDescriptorMap; }, {} as PropertyDescriptorMap); -const console = Object.create(globalThis.console, consoleOverrides) as Console; +const console = Object.create(globalThis.console, logOverrides) as Console; type InjectedServices = { stateRepo: typeof PluginStateRepositoryToken, @@ -36,16 +55,6 @@ type InjectedServices = { const pluginWebRoute = "plugins/@ngageoint/mage.arcgis.service"; -const sanitizeFeatureService = async (config: FeatureServiceConfig, identityService: ArcGISIdentityService): Promise> => { - let authenticated = false; - try { - await identityService.signin(config); - authenticated = true; - } catch (error) { console.error('Error in sanitizeFeatureService :: ', error); } - - const { identityManager, ...sanitized } = config; - return { ...sanitized, authenticated }; -}; /** * The MAGE ArcGIS Plugin finds new MAGE observations and if configured to send the observations @@ -63,17 +72,26 @@ const arcgisPluginHooks: InitPluginHook = { console.info('Intializing ArcGIS plugin...'); const { stateRepo, eventRepo, obsRepoForEvent, userRepo } = services; - const identityService = createArcGISIdentityService(stateRepo); + const identityService = createArcGISIdentityService(stateRepo, console); const processor = new ObservationProcessor(stateRepo, eventRepo, obsRepoForEvent, userRepo, identityService, console); - await processor.start(); + // don't block server startup on the first ArcGIS sync cycle, which can involve + // several rounds of network I/O against the configured ArcGIS server(s) + processor.start().catch((err) => console.error(`Error starting ArcGIS observation processor: ${describeArcGISError(err)}`)); return { webRoutes: { public: () => { const routes = express.Router().use(express.json()); routes.get('/oauth/signin', async (req, res) => { + const discover = req.query.discover === 'true'; const url = req.query.featureServiceUrl as string; - if (!URL.canParse(url)) { + const portalUrl = req.query.portalUrl as string; + + if (discover) { + if (!portalUrl) { + return res.status(404).send('portalUrl is required to discover feature services'); + } + } else if (!URL.canParse(url)) { return res.status(404).send('invalid feature service url'); } @@ -82,23 +100,21 @@ const arcgisPluginHooks: InitPluginHook = { return res.status(404).send('clientId is required'); } - const portalUrl = req.query.portalUrl as string; - const config = await processor.safeGetConfig(); ArcGISIdentityManager.authorize({ clientId, - portal: portalUrl || getPortalUrl(url), + portal: discover ? portalUrl : (portalUrl || getPortalUrl(url)), redirectUri: `${config.baseUrl}/${pluginWebRoute}/oauth/authenticate`, - state: JSON.stringify({ url: url, clientId: clientId, portalUrl: portalUrl }) + state: JSON.stringify({ url, clientId, portalUrl, discover }) }, res); }); routes.get('/oauth/authenticate', async (req, res) => { const code = req.query.code as string; - let state: { url: string, clientId: string, portalUrl?: string }; + let state: { url: string, clientId: string, portalUrl?: string, discover?: boolean }; try { - const { url, clientId, portalUrl } = JSON.parse(req.query.state as string); - state = { url, clientId, portalUrl }; + const { url, clientId, portalUrl, discover } = JSON.parse(req.query.state as string); + state = { url, clientId, portalUrl, discover }; } catch (err) { console.error('error parsing relay state', err); return res.sendStatus(500); @@ -108,19 +124,37 @@ const arcgisPluginHooks: InitPluginHook = { const creds = { clientId: state.clientId, redirectUri: `${config.baseUrl}/${pluginWebRoute}/oauth/authenticate`, - portal: state.portalUrl || getPortalUrl(state.url) + portal: state.discover ? (state.portalUrl as string) : (state.portalUrl || getPortalUrl(state.url)) + }; + const postMessageResponse = (data: unknown) => { + res.send(` + + + + + + `); }; ArcGISIdentityManager.exchangeAuthorizationCode(creds, code).then(async (idManager: ArcGISIdentityManager) => { + if (state.discover) { + const mayLackEditPrivilege = state.portalUrl ? await checkEditPrivilege(state.portalUrl, idManager, console) : undefined; + const page = await discoverFeatureServices(idManager, console); + postMessageResponse({ identityManager: idManager.serialize(), portalUrl: state.portalUrl, mayLackEditPrivilege, ...page }); + return; + } + let service = config.featureServices.find(service => service.url === state.url); if (!service) { service = { url: state.url, portalUrl: state.portalUrl, - identityManager: idManager.serialize(), + identityManager: serializeAndEncrypt(idManager, console), layers: [] }; } else { - service.identityManager = idManager.serialize(); + service.identityManager = serializeAndEncrypt(idManager, console); if (state.portalUrl) { service.portalUrl = state.portalUrl; } @@ -129,16 +163,8 @@ const arcgisPluginHooks: InitPluginHook = { config.featureServices.push(service); await processor.putConfig(config); - const sanitizedService = await sanitizeFeatureService(service, identityService); - res.send(` - - - - - - `); + const sanitizedService = await sanitizeFeatureService(service, identityService, console); + postMessageResponse(sanitizedService); }).catch((error) => res.status(400).json(error)); }); @@ -162,8 +188,25 @@ const arcgisPluginHooks: InitPluginHook = { const config = await processor.safeGetConfig(); const { featureServices, ...remaining } = config; + // the client only understands event names (layer.events), not the ids persisted + // server-side (layer.eventIds) - convert back so previously saved selections survive a reload + const allEvents = await eventRepo.findAll(); + const eventIdToNameMap = new Map(); + allEvents.forEach(event => eventIdToNameMap.set(event.id, event.name)); + const sanitizeFeatureServices = await Promise.all( - featureServices.map(async (service) => await sanitizeFeatureService(service, identityService)) + featureServices.map(async (service) => { + const sanitized = await sanitizeFeatureService(service, identityService, console); + return { + ...sanitized, + layers: sanitized.layers.map(layer => ({ + ...layer, + events: (layer.eventIds || []) + .map(eventId => eventIdToNameMap.get(eventId)) + .filter((name): name is string => name !== undefined) + })) + }; + }) ); res.json({ ...remaining, featureServices: sanitizeFeatureServices }); @@ -174,47 +217,16 @@ const arcgisPluginHooks: InitPluginHook = { const { featureServices: updatedServices, ...updateConfig } = req.body; - // Convert event names to event IDs - // Fetch all events and create a mapping of event names to event IDs + // the client only knows event names, not the numeric ids persisted server-side - + // build a lookup to translate layer.events back to eventIds, and carry forward each + // service's already-persisted identityManager (the client never sends it back) const allEvents = await eventRepo.findAll(); const eventNameToIdMap = new Map(); allEvents.forEach(event => { eventNameToIdMap.set(event.name, event.id); }); - // Process the incoming feature services with eventIds instead of event names - const featureServices: FeatureServiceConfig[] = updatedServices.map((updateService: any) => { - const existingService = config.featureServices.find( - (featureService: FeatureServiceConfig) => featureService.url === updateService.url - ); - - // Process layers - const layers: FeatureLayerConfig[] = updateService.layers.map((layer: any) => { - // Extract event names from the incoming layer data - const eventNames: string[] = layer.events || []; - - // Convert event names to event IDs using the mapping - const eventIds = eventNames - .map(eventName => eventNameToIdMap.get(eventName)) - .filter((id): id is MageEventId => id !== undefined); - - // Construct the FeatureLayerConfig with eventIds - const featureLayerConfig: FeatureLayerConfig = { - ...layer, - eventIds: eventIds - }; - - return featureLayerConfig; - }); - - return { - url: updateService.url, - portalUrl: updateService.portalUrl, - layers: layers, - // Map existing identityManager, client does not send this - identityManager: existingService?.identityManager || '', - }; - }); + const featureServices = buildFeatureServicesForSave(updatedServices, config.featureServices, eventNameToIdMap); await stateRepo.patch({ ...updateConfig, featureServices }); @@ -229,39 +241,119 @@ const arcgisPluginHooks: InitPluginHook = { const config = await processor.safeGetConfig(); const { url, portalUrl, token, username, password } = req.body; if (!URL.canParse(url)) { - return res.send('Invalid feature service url').status(400); - } - - let service: FeatureServiceConfig; - let identityManager: ArcGISIdentityManager; - if (token) { - identityManager = await ArcGISIdentityManager.fromToken({ token }); - service = { url, portalUrl, layers: [], identityManager: identityManager.serialize() }; - } else if (username && password) { - identityManager = await ArcGISIdentityManager.signIn({ - username, - password, - portal: portalUrl || getPortalUrl(url) - }); - service = { url, portalUrl, layers: [], identityManager: identityManager.serialize() }; - } else { - return res.sendStatus(400); + return res.status(400).send('Invalid feature service url'); } try { + let identityManager: ArcGISIdentityManager; + let resolvedPortalUrl = portalUrl; + if (token) { + identityManager = await ArcGISIdentityManager.fromToken({ token }); + } else if (username && password) { + const serverRoot = url.split(/\/rest\/services/i)[0]; + const { owningSystemUrl } = await request(`${serverRoot}/rest/info`); + if (owningSystemUrl || portalUrl) { + const signedIn = await signInWithPortalCandidates(username, password, portalUrl || `${owningSystemUrl}/sharing/rest`, console); + identityManager = signedIn.identityManager; + resolvedPortalUrl = signedIn.portalUrl; + } else { + // stand-alone ArcGIS Server with no portal; sign in to the server's own token service + identityManager = new ArcGISIdentityManager({ username, password, server: serverRoot }); + await identityManager.refreshCredentials(); + } + } else { + return res.sendStatus(400); + } + + const service: FeatureServiceConfig = { url, portalUrl: resolvedPortalUrl, layers: [], identityManager: serializeAndEncrypt(identityManager, console) }; const existingService = config.featureServices.find(service => service.url === url); if (!existingService) { config.featureServices.push(service); } await processor.patchConfig(config); - const sanitized = await sanitizeFeatureService(service, identityService); + const sanitized = await sanitizeFeatureService(service, identityService, console); + return res.send(sanitized); + } catch (err) { + return res.status(400).send(describeAuthFailure('feature service', err)); + } + }); + + routes.post('/featureService/discover', async (req, res) => { + const { portalUrl, token, username, password, identityManager: serializedIdentityManager, start, num, filter } = req.body; + if (!portalUrl) { + return res.status(400).send('portalUrl is required'); + } + + try { + if (serializedIdentityManager) { + const identityManager = ArcGISIdentityManager.deserialize(serializedIdentityManager); + const mayLackEditPrivilege = await checkEditPrivilege(portalUrl, identityManager, console); + const page = await discoverFeatureServices(identityManager, console, start, num, filter); + return res.send({ identityManager: identityManager.serialize(), portalUrl, mayLackEditPrivilege, ...page }); + } + + if (token) { + const result = await discoverWithPortalCandidates( + portalUrl, + (candidatePortalUrl) => ArcGISIdentityManager.fromToken({ token, portal: candidatePortalUrl }), + start, num, filter, console + ); + return res.send({ identityManager: result.identityManager.serialize(), portalUrl: result.portalUrl, mayLackEditPrivilege: result.mayLackEditPrivilege, ...result.page }); + } else if (username && password) { + const result = await discoverWithPortalCandidates( + portalUrl, + (candidatePortalUrl) => ArcGISIdentityManager.signIn({ username, password, portal: candidatePortalUrl }), + start, num, filter, console + ); + return res.send({ identityManager: result.identityManager.serialize(), portalUrl: result.portalUrl, mayLackEditPrivilege: result.mayLackEditPrivilege, ...result.page }); + } else { + return res.sendStatus(400); + } + } catch (err) { + return res.status(400).send(describeAuthFailure('portal', err)); + } + }); + + routes.post('/featureService/confirm', async (req, res) => { + const { url, portalUrl, identityManager: serializedIdentityManager } = req.body; + if (!URL.canParse(url)) { + return res.status(400).send('Invalid feature service url'); + } + if (!serializedIdentityManager) { + return res.sendStatus(400); + } + + try { + const identityManager = ArcGISIdentityManager.deserialize(serializedIdentityManager); + const config = await processor.safeGetConfig(); + const sanitized = await commitFeatureService(processor.patchConfig.bind(processor), identityService, config, url, portalUrl, identityManager, console); return res.send(sanitized); } catch (err) { - return res.send('Invalid credentials provided to communicate with feature service' + err).status(400); + return res.status(400).send(describeAuthFailure('feature service', err)); } }); + routes.delete('/featureService', async (req, res) => { + const url = req.query.featureServiceUrl as string; + const config = await processor.safeGetConfig(); + const featureService = config.featureServices.find(featureService => featureService.url === url); + if (!featureService) { + return res.sendStatus(204); + } + + // only remove services with no layers configured yet, so this can't be used to drop a + // real, already-configured feature service - just the placeholder created by validate/ + // confirm before the user picks layers and saves, e.g. when they close the dialog first + if (featureService.layers.length > 0) { + return res.status(409).send('Feature service has configured layers and cannot be removed this way'); + } + + config.featureServices = config.featureServices.filter(service => service.url !== url); + await processor.patchConfig(config); + return res.sendStatus(204); + }); + routes.get('/featureService/layers', async (req, res) => { const url = req.query.featureServiceUrl as string; const config = await processor.safeGetConfig(); @@ -275,10 +367,64 @@ const arcgisPluginHooks: InitPluginHook = { const response = await request(url, { authentication: identityManager }); - res.send(response.layers); + + // the FeatureServer root response only includes a lightweight summary per layer + // (no capabilities); fetch each layer's own definition to get its actual capabilities, + // falling back to the service-wide capabilities if the layer doesn't report its own + const layers = await Promise.all(response.layers.map(async (layer: { id: number, capabilities?: string }) => { + try { + const layerInfo = await request(`${url}/${layer.id}`, { authentication: identityManager }); + return { ...layer, capabilities: layerInfo.capabilities || response.capabilities }; + } catch (err) { + console.error(`Could not get capabilities for layer ${layer.id}: ${describeArcGISError(err)}`); + return { ...layer, capabilities: response.capabilities }; + } + })); + + res.send(layers); + } catch (err) { + const message = describeArcGISError(err); + console.error(message); + res.status(500).json({ message: 'Could not get ArcGIS layer info', error: message }); + } + }); + + routes.get('/featureService/capabilities', async (req, res) => { + const url = req.query.featureServiceUrl as string; + const config = await processor.safeGetConfig(); + const featureService = config.featureServices.find(featureService => featureService.url === url); + if (!featureService) { + return res.status(400); + } + + try { + const identityManager = await identityService.signin(featureService); + const response = await request(url, { + authentication: identityManager + }); + res.send({ capabilities: response.capabilities }); + } catch (err) { + const message = describeArcGISError(err); + console.error(message); + res.status(500).json({ message: 'Could not get ArcGIS feature service capabilities', error: message }); + } + }); + + routes.get('/pushStatus', async (req, res) => { + const eventId = Number(req.query.eventId); + if (!Number.isFinite(eventId)) { + return res.status(400).send('eventId query param is required'); + } + const pageIndex = Math.max(0, Number(req.query.pageIndex) || 0); + const pageSize = Math.min(200, Math.max(1, Number(req.query.pageSize) || 25)); + + try { + const page = await processor.getPushedObservations(eventId, { pageIndex, pageSize }); + res.send(page); } catch (err) { - console.error(err); - res.status(500).json({ message: 'Could not get ArcGIS layer info', error: err }); + const message = err instanceof Error ? err.message : String(err); + console.error(`Could not get pushed observations for event ${eventId}: ${message}`); + res.status(500).json({ message: 'Could not get pushed observations', error: message }); } }); diff --git a/plugins/arcgis/service/src/types/ArcGISConfig.ts b/plugins/arcgis/service/src/types/ArcGISConfig.ts index ed2f02e3d..3b6fcd409 100644 --- a/plugins/arcgis/service/src/types/ArcGISConfig.ts +++ b/plugins/arcgis/service/src/types/ArcGISConfig.ts @@ -16,7 +16,7 @@ export interface FeatureServiceConfig { portalUrl?: string /** - * Serialized ArcGISIdentityManager + * Serialized ArcGISIdentityManager, encrypted via CredentialEncryption.ts */ identityManager: string diff --git a/plugins/arcgis/service/src/types/ArcGISPluginConfig.ts b/plugins/arcgis/service/src/types/ArcGISPluginConfig.ts index 2936e1a71..55a5ce2b9 100644 --- a/plugins/arcgis/service/src/types/ArcGISPluginConfig.ts +++ b/plugins/arcgis/service/src/types/ArcGISPluginConfig.ts @@ -1,5 +1,8 @@ import { FeatureServiceConfig, AttributeConfig } from "./ArcGISConfig"; +export type FieldAttributesFormConfig = { [formName: string]: { [fieldTitle: string]: string } } +export type FieldAttributes = { [eventName: string]: FieldAttributesFormConfig } + /** * Contains various configuration values used by the plugin. */ @@ -75,11 +78,6 @@ export interface ArcGISPluginConfig { */ eventIdField?: string - /** - * The last edited date field attribute name on the ArcGIS server. - */ - lastEditedDateField?: string - /** * The event name field attribute name. */ @@ -111,7 +109,7 @@ export interface ArcGISPluginConfig { createdAtField?: string /** - * The last modified field attribute name from MAGE observations (may be the same as lastEditedDateField if editable). + * The last modified field attribute name from MAGE observations */ lastModifiedField?: string @@ -120,16 +118,29 @@ export interface ArcGISPluginConfig { */ geometryType?: string + /** + * The field name containing the icon symbol + */ + iconSymbolField: string + /** * Override mappings between event form fields and ArcGIS attributes as: { event: { form: { field: attribute } } } */ - fieldAttributes?: unknown + fieldAttributes?: FieldAttributes /** * The attribute configurations. */ attributes?: { [attribute: string]: AttributeConfig } + /** + * Per-event observation sync filter. Maps a MAGE event id to a datetime + * string; only observations whose properties.timestamp is at or after + * that value are synced to ArcGIS for that event. An event with no + * entry (or this field entirely absent) syncs all observations (default). + */ + syncAfterByEventId?: { [eventId: number]: string } + } export const defaultArcGISPluginConfig = Object.freeze({ @@ -143,10 +154,9 @@ export const defaultArcGISPluginConfig = Object.freeze({ attachmentModifiedTolerance: 5000, textFieldLength: 100, textAreaFieldLength: 256, - observationIdField: 'description', + observationIdField: 'observation_id', idSeparator: '-', eventIdField: 'event_id', - lastEditedDateField: 'last_edited_date', eventNameField: 'event_name', userIdField: 'user_id', usernameField: 'username', @@ -155,7 +165,9 @@ export const defaultArcGISPluginConfig = Object.freeze({ createdAtField: 'created_at', lastModifiedField: 'last_modified', geometryType: 'geometry_type', + iconSymbolField: 'icon_symbol', fieldAttributes: {}, + syncAfterByEventId: {}, attributes: { 'symbolid': { defaults: [ diff --git a/plugins/arcgis/service/src/types/ArcObservation.ts b/plugins/arcgis/service/src/types/ArcObservation.ts index 34fa87a5b..0219d9b18 100644 --- a/plugins/arcgis/service/src/types/ArcObservation.ts +++ b/plugins/arcgis/service/src/types/ArcObservation.ts @@ -21,5 +21,6 @@ export interface ArcAttachment { lastModified: number, size: number, name: string, - contentLocator: string + contentLocator: string, + mediaType: string } diff --git a/plugins/arcgis/service/src/types/LayerInfoResult.ts b/plugins/arcgis/service/src/types/LayerInfoResult.ts index 9b1a6fab9..5f78509d4 100644 --- a/plugins/arcgis/service/src/types/LayerInfoResult.ts +++ b/plugins/arcgis/service/src/types/LayerInfoResult.ts @@ -18,6 +18,12 @@ export interface LayerInfoResult { * The feature layer id. */ id: number + + /** + * The name of the field ArcGIS uses to label/identify features from this layer. + * A field currently set as the display field cannot be deleted. + */ + displayField?: string } /** diff --git a/plugins/arcgis/web-app/package-lock.json b/plugins/arcgis/web-app/package-lock.json index 0dd320a5e..6149e7128 100644 --- a/plugins/arcgis/web-app/package-lock.json +++ b/plugins/arcgis/web-app/package-lock.json @@ -1,12 +1,12 @@ { "name": "@ngageoint/mage.arcgis.web-app.workspace", - "version": "1.0.0-beta.3", + "version": "1.0.0-beta.4", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@ngageoint/mage.arcgis.web-app.workspace", - "version": "1.0.0-beta.3", + "version": "1.0.0-beta.4", "dependencies": { "@angular/animations": "^20.3.0", "@angular/cdk": "^20.2.0", @@ -41,7 +41,7 @@ }, "../../../web-app/dist/core-lib": { "name": "@ngageoint/mage.web-core-lib", - "version": "6.6.0-beta.1", + "version": "6.7.1-beta.1", "dependencies": { "@rollup/plugin-commonjs": "^29.0.0", "@rollup/plugin-node-resolve": "^16.0.0", @@ -767,9 +767,6 @@ "arm" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -784,9 +781,6 @@ "arm" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -801,9 +795,6 @@ "arm64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -818,9 +809,6 @@ "arm64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -835,9 +823,6 @@ "loong64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -852,9 +837,6 @@ "loong64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -869,9 +851,6 @@ "ppc64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -886,9 +865,6 @@ "ppc64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -903,9 +879,6 @@ "riscv64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -920,9 +893,6 @@ "riscv64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -937,9 +907,6 @@ "s390x" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -954,9 +921,6 @@ "x64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -971,9 +935,6 @@ "x64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -4708,9 +4669,6 @@ "arm64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -4728,9 +4686,6 @@ "arm64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -4748,9 +4703,6 @@ "ppc64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -4768,9 +4720,6 @@ "riscv64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -4788,9 +4737,6 @@ "s390x" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -4808,9 +4754,6 @@ "x64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -4828,9 +4771,6 @@ "x64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -5354,9 +5294,6 @@ "arm" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -5378,9 +5315,6 @@ "arm" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -5402,9 +5336,6 @@ "arm64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -5426,9 +5357,6 @@ "arm64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -5450,9 +5378,6 @@ "x64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -5474,9 +5399,6 @@ "x64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -5828,9 +5750,6 @@ "arm" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -5845,9 +5764,6 @@ "arm" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -5862,9 +5778,6 @@ "arm64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -5879,9 +5792,6 @@ "arm64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -5896,9 +5806,6 @@ "loong64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -5913,9 +5820,6 @@ "loong64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -5930,9 +5834,6 @@ "ppc64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -5947,9 +5848,6 @@ "ppc64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -5964,9 +5862,6 @@ "riscv64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -5981,9 +5876,6 @@ "riscv64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -5998,9 +5890,6 @@ "s390x" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -6015,9 +5904,6 @@ "x64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -6032,9 +5918,6 @@ "x64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -6414,6 +6297,8 @@ }, "node_modules/@types/node": { "version": "22.20.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.20.1.tgz", + "integrity": "sha512-EANqOCF9QFyra+4pfxUcX9STKJpCLjMbObVzljIJomAWSnuSIEAvyzEU53GaajbXJEgdh0iEcPL+DGvpUd4k1Q==", "dev": true, "license": "MIT", "dependencies": { @@ -13789,6 +13674,8 @@ }, "node_modules/undici-types": { "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", "dev": true, "license": "MIT" }, diff --git a/plugins/arcgis/web-app/package.json b/plugins/arcgis/web-app/package.json index 144f4b37a..3e37c0d2b 100644 --- a/plugins/arcgis/web-app/package.json +++ b/plugins/arcgis/web-app/package.json @@ -1,6 +1,6 @@ { "name": "@ngageoint/mage.arcgis.web-app.workspace", - "version": "1.0.0-beta.3", + "version": "1.0.0-beta.4", "scripts": { "ng": "ng", "start": "ng serve", diff --git a/plugins/arcgis/web-app/projects/main/package.json b/plugins/arcgis/web-app/projects/main/package.json index dfc991647..8cdcfb3d7 100644 --- a/plugins/arcgis/web-app/projects/main/package.json +++ b/plugins/arcgis/web-app/projects/main/package.json @@ -1,6 +1,6 @@ { "name": "@ngageoint/mage.arcgis.web-app", - "version": "1.0.0-beta.3", + "version": "1.0.0-beta.4", "repository": { "type": "git", "url": "https://github.com/ngageoint/mage-server.git" diff --git a/plugins/arcgis/web-app/projects/main/src/lib/ArcGISConfig.ts b/plugins/arcgis/web-app/projects/main/src/lib/ArcGISConfig.ts index 9f3a61d8a..8c9053b4b 100644 --- a/plugins/arcgis/web-app/projects/main/src/lib/ArcGISConfig.ts +++ b/plugins/arcgis/web-app/projects/main/src/lib/ArcGISConfig.ts @@ -18,6 +18,14 @@ export interface FeatureServiceConfig { */ authenticated: boolean + /** + * True if the authenticated user's ArcGIS privileges appear to be missing + * features:user:edit, meaning they likely cannot write observations to + * feature layers. Undefined if this couldn't be determined (e.g. no known + * portal URL, or the privilege check failed). + */ + mayLackEditPrivilege?: boolean + /** * The feature layers. */ diff --git a/plugins/arcgis/web-app/projects/main/src/lib/ArcGISPluginConfig.ts b/plugins/arcgis/web-app/projects/main/src/lib/ArcGISPluginConfig.ts index 831ce8b9a..0d7dca1d9 100644 --- a/plugins/arcgis/web-app/projects/main/src/lib/ArcGISPluginConfig.ts +++ b/plugins/arcgis/web-app/projects/main/src/lib/ArcGISPluginConfig.ts @@ -75,11 +75,6 @@ export interface ArcGISPluginConfig { */ eventIdField?: string - /** - * The last edited date field attribute name on the ArcGIS server. - */ - lastEditedDateField?: string - /** * The event name field attribute name. */ @@ -111,7 +106,7 @@ export interface ArcGISPluginConfig { createdAtField?: string /** - * The last modified field attribute name from MAGE observations (may be the same as lastEditedDateField if editable). + * The last modified field attribute name from MAGE observations */ lastModifiedField?: string @@ -120,6 +115,11 @@ export interface ArcGISPluginConfig { */ geometryType?: string + /** + * The field name containing the icon symbol + */ + iconSymbolField: string + /** * Override mappings between event form fields and ArcGIS attributes as: { event: { form: { field: attribute } } } */ @@ -130,6 +130,14 @@ export interface ArcGISPluginConfig { */ attributes?: { [attribute: string]: AttributeConfig } + /** + * Per-event observation sync filter. Maps a MAGE event id to a datetime + * string; only observations whose properties.timestamp is at or after + * that value are synced to ArcGIS for that event. An event with no + * entry (or this field entirely absent) syncs all observations (default). + */ + syncAfterByEventId?: { [eventId: number]: string } + } export const defaultArcGISPluginConfig = Object.freeze({ @@ -143,10 +151,9 @@ export const defaultArcGISPluginConfig = Object.freeze({ attachmentModifiedTolerance: 5000, textFieldLength: 100, textAreaFieldLength: 256, - observationIdField: 'description', + observationIdField: 'observation_id', idSeparator: '-', // eventIdField: 'event_id', - lastEditedDateField: 'last_edited_date', eventNameField: 'event_name', userIdField: 'user_id', usernameField: 'username', @@ -155,7 +162,9 @@ export const defaultArcGISPluginConfig = Object.freeze({ createdAtField: 'created_at', lastModifiedField: 'last_modified', geometryType: 'geometry_type', + iconSymbolField: 'icon_symbol', fieldAttributes: {}, + syncAfterByEventId: {}, attributes: { 'symbolid': { defaults: [ diff --git a/plugins/arcgis/web-app/projects/main/src/lib/arc-admin/arc-admin.component.html b/plugins/arcgis/web-app/projects/main/src/lib/arc-admin/arc-admin.component.html index 6f7fec38d..c935830ca 100644 --- a/plugins/arcgis/web-app/projects/main/src/lib/arc-admin/arc-admin.component.html +++ b/plugins/arcgis/web-app/projects/main/src/lib/arc-admin/arc-admin.component.html @@ -1,5 +1,19 @@
- +
+ error_outline +
+ + Credentials for feature service {{service.url}} are invalid or expired. Observations for its mapped events are not syncing. +
+
+ Open the Feature Layers tab and re-authenticate to resume syncing. +
+ +
+
@@ -7,23 +21,21 @@ Processing - MAGE ArcGIS plugin processing settings.
- - Enabled - - true - false - - Process observations and send to configured ArcGIS feature layers - + Push observations from Mage to your configured ArcGIS feature layers. Select the target feature layers from your ArcGIS server and choose which Mage events will send data. + + Enable Observation Processing +
-
+
Base URL
- - - + + + +
@@ -108,8 +130,8 @@
- Attributes - MAGE Field to ArcGIS Attribute mappings. + Observation Field Attributes + Define each field on the ArcGIS layer to map the Mage observation fields to. The plugin will attempt to create any fields on the ArcGIS layer if they do not already exist.
@@ -118,7 +140,7 @@ Observation Id Field - The ArcGIS layer text field attribute name to store the MAGE observation id + The ArcGIS layer text field attribute name to store the Mage observation id
@@ -132,64 +154,56 @@ Event Id Field - The ArcGIS layer integer field attribute name to store the MAGE event id - -
-
- - Last Edited Date Field - - The last edited date field attribute name on the ArcGIS layer + The ArcGIS layer integer field attribute name to store the Mage event id
Event Name Field - The ArcGIS layer text field attribute name to store the MAGE event name + The ArcGIS layer text field attribute name to store the Mage event name
User Id Field - The ArcGIS layer text field attribute name to store the MAGE user id + The ArcGIS layer text field attribute name to store the Mage user id
Username Field - The ArcGIS layer text field attribute name to store the MAGE username + The ArcGIS layer text field attribute name to store the Mage username
User Display Name Field - The ArcGIS layer text field attribute name to store the MAGE user display name + The ArcGIS layer text field attribute name to store the Mage user display name
Device Id Field - The ArcGIS layer text field attribute name to store the MAGE device id + The ArcGIS layer text field attribute name to store the Mage device id
Created At Field - The ArcGIS layer date time field attribute name to store the MAGE created at date + The ArcGIS layer date time field attribute name to store the Mage created at date
Last Modified Field - The ArcGIS layer date time field attribute name to store the MAGE last modified date (may be the - same as last edited date field if editable) + The ArcGIS layer date time field attribute name to store the Mage last modified date
@@ -199,9 +213,22 @@ The ArcGIS layer text field attribute name to store the Esri geometry type
-
- - +
+ + Icon Symbol Field + + The ArcGIS layer text field attribute name to store the icon symbol name + +
+
@@ -214,17 +241,17 @@ Field Mappings & Attribute Settings - Override mappings between MAGE event form fields and ArcGIS attributes, and configure per-attribute value behavior. + Override mappings between Mage event form fields and ArcGIS attributes, and configure per-attribute value behavior.
Field Mappings - -
@@ -283,7 +310,7 @@ - + @@ -294,7 +321,7 @@
MAGE FieldMage Field ArcGIS Attribute
{{config.fieldAttributes[event][form][field]}} - - @@ -439,7 +466,7 @@ - + @@ -640,7 +667,7 @@

Attributes Configuration

(keyup)="setField('observationIdField', observationIdField.value)" #observationIdField /> @@ -663,18 +690,7 @@

Attributes Configuration

(keyup)="setField('eventIdField', eventIdField.value)" #eventIdField /> - -
- - Last Edited Date Field - - -
@@ -685,7 +701,7 @@

Attributes Configuration

(keyup)="setField('eventNameField', eventNameField.value)" #eventNameField /> @@ -696,7 +712,7 @@

Attributes Configuration

(keyup)="setField('userIdField', userIdField.value)" #userIdField /> @@ -707,7 +723,7 @@

Attributes Configuration

(keyup)="setField('usernameField', usernameField.value)" #usernameField /> @@ -718,7 +734,7 @@

Attributes Configuration

(keyup)="setField('userDisplayNameField', userDisplayNameField.value)" #userDisplayNameField /> @@ -729,7 +745,7 @@

Attributes Configuration

(keyup)="setField('deviceIdField', deviceIdField.value)" #deviceIdField /> @@ -740,7 +756,7 @@

Attributes Configuration

(keyup)="setField('createdAtField', createdAtField.value)" #createdAtField /> @@ -751,7 +767,7 @@

Attributes Configuration

(keyup)="setField('lastModifiedField', lastModifiedField.value)" #lastModifiedField /> @@ -766,21 +782,50 @@

Attributes Configuration

info_outline +
+ + Icon Symbol Field + + + +
+ +

Reset Config

+
+ This will delete the entire ArcGIS plugin configuration - feature layers, event synchronization, field mappings, and attributes - and reset it to defaults. This cannot be undone. +
+
+ + + + +
+

Delete {{editType}}?

-
+
{{editName}}

- - + + @@ -822,20 +867,31 @@

Delete {{editType}}?

- + {{editType}} - - + {{option}} - + + No mapped ArcGIS attribute names available yet. Add a field mapping in the Mappings tab first. + + + {{editType}} + + + + {{option}} + + + +
- +
@@ -855,14 +911,21 @@

Delete {{editType}}?

{{ editType == 'Field' ? 'Attribute' : 'Value' }} - + + + + {{option}} + + + Pick an existing ArcGIS attribute name to combine this field into it, or type a new one. + This name is already used by the Observation Field Attributes settings and can't be reused here.
- +
@@ -870,32 +933,43 @@

Delete {{editType}}?

{{editName}} - + + This name is already used by the Observation Field Attributes settings and can't be reused here.
- +
- + {{editName}} - - + {{option}} - + + + + {{editName}} + + + + {{option}} + + + +
- +
diff --git a/plugins/arcgis/web-app/projects/main/src/lib/arc-admin/arc-admin.component.scss b/plugins/arcgis/web-app/projects/main/src/lib/arc-admin/arc-admin.component.scss index 14b919ab3..c787a2942 100644 --- a/plugins/arcgis/web-app/projects/main/src/lib/arc-admin/arc-admin.component.scss +++ b/plugins/arcgis/web-app/projects/main/src/lib/arc-admin/arc-admin.component.scss @@ -11,17 +11,104 @@ padding: 16px 0; } +.reserved-attribute-warning { + color: #F44336; +} + +.credentials-error-banner { + display: flex; + align-items: center; + gap: 12px; + padding: 12px 16px; + margin-bottom: 16px; + border-radius: 4px; + background: rgba(244, 67, 54, 0.08); + border: 1px solid rgba(244, 67, 54, 0.24); + color: #F44336; + + mat-icon { + flex-shrink: 0; + } + + .credentials-error-banner-text { + flex: 1; + font-size: 75%; + } +} + +.nav-actions { + display: flex; + gap: 8px; +} + +.cancel-button { + color: var(--mat-sys-primary) !important; + border-color: var(--mat-sys-primary) !important; +} + +.reset-config-button { + color: var(--mat-sys-error) !important; + border-color: var(--mat-sys-error) !important; + margin-left: auto; +} + +.reset-config-message { + padding-left: 24px; + padding-right: 16px; +} + +.reset-config-confirm-button { + color: var(--mat-sys-error) !important; +} + +.remove-mapping-button { + color: var(--mat-sys-error) !important; + border-color: var(--mat-sys-error) !important; +} + +.delete-field-message { + padding-left: 24px; +} + .edit-processing-form .mat-mdc-form-field { margin-bottom: 1.5rem !important; } +.edit-enabled-field { + display: grid; + margin-bottom: 0px; +} + +.edit-enabled-field mat-slide-toggle { + padding-bottom: 16px; +} + +.edit-enabled-field mat-hint { + display: block; + padding-bottom: 16px; +} + +mat-card-header { + flex-direction: column; + align-items: flex-start; +} + +mat-card-header mat-hint { + display: block; + padding-bottom: 16px; +} + section { + margin-block-start: 2em; + margin-block-end: 2em; + >* { margin-inline-start: 0em; } header { margin-inline-start: 0; + margin-block-end: 2em; h1, h2, @@ -36,12 +123,7 @@ section { margin-block-start: 0; opacity: 0.5; } - - margin-block-end: 2em; } - - margin-block-start: 2em; - margin-block-end: 2em; } .processing-table { diff --git a/plugins/arcgis/web-app/projects/main/src/lib/arc-admin/arc-admin.component.spec.ts b/plugins/arcgis/web-app/projects/main/src/lib/arc-admin/arc-admin.component.spec.ts new file mode 100644 index 000000000..36cac91f9 --- /dev/null +++ b/plugins/arcgis/web-app/projects/main/src/lib/arc-admin/arc-admin.component.spec.ts @@ -0,0 +1,90 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { HttpTestingController, provideHttpClientTesting } from '@angular/common/http/testing'; +import { provideHttpClient } from '@angular/common/http'; +import { MatDialogModule } from '@angular/material/dialog'; +import { ArcAdminComponent } from './arc-admin.component'; +import { ArcService, baseUrl } from '../arc.service'; +import { ArcGISPluginConfig, defaultArcGISPluginConfig } from '../ArcGISPluginConfig'; + +describe('Arc Admin - Attributes tab', () => { + let component: ArcAdminComponent; + let fixture: ComponentFixture; + let httpMock: HttpTestingController; + const fakeConfig = (): ArcGISPluginConfig => ({ ...defaultArcGISPluginConfig, featureServices: [] }); + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [MatDialogModule], + declarations: [ArcAdminComponent], + providers: [ArcService, provideHttpClient(), provideHttpClientTesting()] + }).compileComponents(); + + httpMock = TestBed.inject(HttpTestingController); + fixture = TestBed.createComponent(ArcAdminComponent); + component = fixture.componentInstance; + + httpMock.expectOne(`${baseUrl}/config`).flush(fakeConfig()); + httpMock.expectOne(req => req.url.endsWith('/events')).flush([]); + + fixture.detectChanges(); + httpMock.expectOne(`${baseUrl}/config`).flush(fakeConfig()); + }); + + afterEach(() => { + httpMock.verify(); + }); + + it('saves every changed attribute field to the config and persists it', () => { + component.attributesForm.setValue({ + observationIdField: 'my_observation_id', + idSeparator: '::', + eventIdField: 'my_event_id', + eventNameField: 'my_event_name', + userIdField: 'my_user_id', + usernameField: 'my_username', + userDisplayNameField: 'my_user_display_name', + deviceIdField: 'my_device_id', + createdAtField: 'my_created_at', + lastModifiedField: 'my_last_modified', + geometryType: 'my_geometry_type', + iconSymbolField: 'my_icon_symbol' + }); + + component.onSubmit(); + + // the config should reflect every changed field immediately, before the save even completes + expect(component.config.observationIdField).toEqual('my_observation_id'); + expect(component.config.idSeparator).toEqual('::'); + expect(component.config.eventIdField).toEqual('my_event_id'); + expect(component.config.eventNameField).toEqual('my_event_name'); + expect(component.config.userIdField).toEqual('my_user_id'); + expect(component.config.usernameField).toEqual('my_username'); + expect(component.config.userDisplayNameField).toEqual('my_user_display_name'); + expect(component.config.deviceIdField).toEqual('my_device_id'); + expect(component.config.createdAtField).toEqual('my_created_at'); + expect(component.config.lastModifiedField).toEqual('my_last_modified'); + expect(component.config.geometryType).toEqual('my_geometry_type'); + expect(component.config.iconSymbolField).toEqual('my_icon_symbol'); + + // and it should have actually been persisted to the server with those same values + const saveReq = httpMock.expectOne(`${baseUrl}/config`); + expect(saveReq.request.method).toEqual('PUT'); + expect(saveReq.request.body.observationIdField).toEqual('my_observation_id'); + expect(saveReq.request.body.iconSymbolField).toEqual('my_icon_symbol'); + saveReq.flush(saveReq.request.body); + }); + + it('leaves fields left blank unchanged, only saving the ones actually edited', () => { + const originalIconSymbolField = component.config.iconSymbolField; + + component.attributesForm.patchValue({ observationIdField: 'only_this_field_changed' }); + component.onSubmit(); + + expect(component.config.observationIdField).toEqual('only_this_field_changed'); + expect(component.config.iconSymbolField).toEqual(originalIconSymbolField); + + const saveReq = httpMock.expectOne(`${baseUrl}/config`); + expect(saveReq.request.body.iconSymbolField).toEqual(originalIconSymbolField); + saveReq.flush(saveReq.request.body); + }); +}); diff --git a/plugins/arcgis/web-app/projects/main/src/lib/arc-admin/arc-admin.component.ts b/plugins/arcgis/web-app/projects/main/src/lib/arc-admin/arc-admin.component.ts index bd142a361..42452f6b0 100644 --- a/plugins/arcgis/web-app/projects/main/src/lib/arc-admin/arc-admin.component.ts +++ b/plugins/arcgis/web-app/projects/main/src/lib/arc-admin/arc-admin.component.ts @@ -1,6 +1,6 @@ import { Component, OnInit, TemplateRef, ViewChild } from '@angular/core'; import { MatDialog } from '@angular/material/dialog' -import { AttributeConfig, AttributeConcatenationConfig, AttributeDefaultConfig, AttributeValueConfig } from '../ArcGISConfig'; +import { AttributeConfig, AttributeConcatenationConfig, AttributeDefaultConfig, AttributeValueConfig, FeatureServiceConfig } from '../ArcGISConfig'; import { ArcGISPluginConfig, defaultArcGISPluginConfig } from '../ArcGISPluginConfig' import { ArcService, Form, MageEvent } from '../arc.service' import { Subject, first } from 'rxjs'; @@ -27,14 +27,34 @@ export class ArcAdminComponent implements OnInit { editName: string; editValue: any; editOptions: any[]; + editSelectValue: any; + editValueOptions: any[] = []; + editIsAttributeName = false; + editIsFieldMappingAttribute = false; + editValueInput = ''; events: MageEvent[] = []; + /** + * Feature services whose credentials are invalid/expired and that have at least one + * event actively mapped to a layer + */ + get unauthenticatedSyncingFeatureServices(): FeatureServiceConfig[] { + if (!this.config.enabled || !this.config.featureServices) { + return [] + } + return this.config.featureServices.filter(service => + !service.authenticated && service.layers?.some(layer => layer.events && layer.events.length > 0) + ) + } + @ViewChild('infoDialog', { static: true }) private infoTemplate: TemplateRef @ViewChild('editProcessingDialog', { static: true }) private editProcessingTemplate: TemplateRef @ViewChild('editAttributesDialog', { static: true }) private editAttributesTemplate: TemplateRef + @ViewChild('resetConfigDialog', { static: true }) + private resetConfigTemplate: TemplateRef @ViewChild('deleteFieldDialog', { static: true }) private deleteFieldTemplate: TemplateRef @ViewChild('addFieldDialog', { static: true }) @@ -61,10 +81,9 @@ export class ArcAdminComponent implements OnInit { this.editConfig = this.copyConfig(); this.editFieldMappings = false; this.attributesForm = this.fb.group({ - observationIdField: [''], + observationIdField: [''], idSeparator: [''], eventIdField: [''], - lastEditedDateField: [''], eventNameField: [''], userIdField: [''], usernameField: [''], @@ -72,7 +91,8 @@ export class ArcAdminComponent implements OnInit { deviceIdField: [''], createdAtField: [''], lastModifiedField: [''], - geometryType: [''] + geometryType: [''], + iconSymbolField: [''] }); arcService.fetchArcConfig().subscribe(x => { @@ -94,12 +114,11 @@ export class ArcAdminComponent implements OnInit { this.arcService.fetchArcConfig().pipe(first()).subscribe({ next: config => { if (config) { - // Populate form with values from config + // Populate form with values from config this.attributesForm.patchValue({ observationIdField: config.observationIdField || '', idSeparator: config.idSeparator || '', eventIdField: config.eventIdField || '', - lastEditedDateField: config.lastEditedDateField || '', eventNameField: config.eventNameField || '', userIdField: config.userIdField || '', usernameField: config.usernameField || '', @@ -107,9 +126,9 @@ export class ArcAdminComponent implements OnInit { deviceIdField: config.deviceIdField || '', createdAtField: config.createdAtField || '', lastModifiedField: config.lastModifiedField || '', - geometryType: config.geometryType || '' + geometryType: config.geometryType || '', + iconSymbolField: config.iconSymbolField || 'icon_symbol' }); - console.log('Form initialized with server config:', config); } }, error: error => { @@ -117,11 +136,9 @@ export class ArcAdminComponent implements OnInit { } }); } - - //Save attributes form. If left blank, assign default value - onSubmit(): void { - console.log('submitting form...'); + // Save attributes form. If left blank, assign default value + onSubmit(): void { if (this.attributesForm.valid) { const formValue = this.attributesForm.value; this.editConfig = { @@ -129,7 +146,6 @@ export class ArcAdminComponent implements OnInit { observationIdField: formValue.observationIdField || this.editConfig.observationIdField, idSeparator: formValue.idSeparator || this.editConfig.idSeparator, eventIdField: formValue.eventIdField || this.editConfig.eventIdField, - lastEditedDateField: formValue.lastEditedDateField || this.editConfig.lastEditedDateField, eventNameField: formValue.eventNameField || this.editConfig.eventNameField, userIdField: formValue.userIdField || this.editConfig.userIdField, usernameField: formValue.usernameField || this.editConfig.usernameField, @@ -137,21 +153,14 @@ export class ArcAdminComponent implements OnInit { deviceIdField: formValue.deviceIdField || this.editConfig.deviceIdField, createdAtField: formValue.createdAtField || this.editConfig.createdAtField, lastModifiedField: formValue.lastModifiedField || this.editConfig.lastModifiedField, - geometryType: formValue.geometryType || this.editConfig.geometryType - }; - - console.log('Form Submitted:', this.editConfig); - console.log('formValue: ', formValue); - + geometryType: formValue.geometryType || this.editConfig.geometryType, + iconSymbolField: formValue.iconSymbolField || this.editConfig.iconSymbolField + }; this.saveEdit(); - } - else{ - console.log('Form is invalid, please correct the errors.') - } } - + } + onCancel(): void { - console.log('Cancel selected'); this.arcService.fetchArcConfig().pipe(first()).subscribe({ next: config => { if (config) { @@ -159,7 +168,6 @@ export class ArcAdminComponent implements OnInit { observationIdField: config.observationIdField || '', idSeparator: config.idSeparator || '', eventIdField: config.eventIdField || '', - lastEditedDateField: config.lastEditedDateField || '', eventNameField: config.eventNameField || '', userIdField: config.userIdField || '', usernameField: config.usernameField || '', @@ -167,9 +175,9 @@ export class ArcAdminComponent implements OnInit { deviceIdField: config.deviceIdField || '', createdAtField: config.createdAtField || '', lastModifiedField: config.lastModifiedField || '', - geometryType: config.geometryType || '' + geometryType: config.geometryType || '', + iconSymbolField: config.iconSymbolField || 'icon_symbol' }); - console.log('Form reloaded with server config:', config); } }, error: error => { @@ -199,7 +207,6 @@ export class ArcAdminComponent implements OnInit { value = undefined } (this.editConfig as any)[field] = value - console.log('Editing field: ' + field + ', value: ' + value) } setNumberField(field: string, value: any, min: number) { @@ -214,7 +221,6 @@ export class ArcAdminComponent implements OnInit { } } (this.editConfig as any)[field] = value - console.log('Editing field: ' + field + ', value: ' + value) } copyConfig(): ArcGISPluginConfig { @@ -224,85 +230,80 @@ export class ArcAdminComponent implements OnInit { saveEdit() { if (this.editConfig.enabled != undefined && this.editConfig.enabled != this.config.enabled) { this.config.enabled = this.editConfig.enabled - console.log('Edited enabled: ' + this.config.enabled) } if (this.editConfig.intervalSeconds != undefined && this.editConfig.intervalSeconds != this.config.intervalSeconds) { this.config.intervalSeconds = this.editConfig.intervalSeconds - console.log('Edited intervalSeconds: ' + this.config.intervalSeconds) } if (this.editConfig.startupIntervalSeconds != undefined && this.editConfig.startupIntervalSeconds != this.config.startupIntervalSeconds) { this.config.startupIntervalSeconds = this.editConfig.startupIntervalSeconds - console.log('Edited startupIntervalSeconds: ' + this.config.startupIntervalSeconds) } if (this.editConfig.updateIntervalSeconds != undefined && this.editConfig.updateIntervalSeconds != this.config.updateIntervalSeconds) { this.config.updateIntervalSeconds = this.editConfig.updateIntervalSeconds - console.log('Edited updateIntervalSeconds: ' + this.config.updateIntervalSeconds) } if (this.editConfig.batchSize != undefined && this.editConfig.batchSize != this.config.batchSize) { this.config.batchSize = this.editConfig.batchSize - console.log('Edited batchSize: ' + this.config.batchSize) } if (this.editConfig.attachmentModifiedTolerance != undefined && this.editConfig.attachmentModifiedTolerance != this.config.attachmentModifiedTolerance) { this.config.attachmentModifiedTolerance = this.editConfig.attachmentModifiedTolerance - console.log('Edited attachmentModifiedTolerance: ' + this.config.attachmentModifiedTolerance) } if (this.editConfig.observationIdField != undefined && this.editConfig.observationIdField != this.config.observationIdField) { this.config.observationIdField = this.editConfig.observationIdField - console.log('Edited observationIdField: ' + this.config.observationIdField) } if (this.editConfig.idSeparator != undefined && this.editConfig.idSeparator != this.config.idSeparator) { this.config.idSeparator = this.editConfig.idSeparator - console.log('Edited idSeparator: ' + this.config.idSeparator) } if (this.editConfig.eventIdField != this.config.eventIdField) { this.config.eventIdField = this.editConfig.eventIdField - console.log('Edited eventIdField: ' + this.config.eventIdField) - } - if (this.editConfig.lastEditedDateField != this.config.lastEditedDateField) { - this.config.lastEditedDateField = this.editConfig.lastEditedDateField - console.log('Edited lastEditedDateField: ' + this.config.lastEditedDateField) } if (this.editConfig.eventNameField != this.config.eventNameField) { this.config.eventNameField = this.editConfig.eventNameField - console.log('Edited eventNameField: ' + this.config.eventNameField) } if (this.editConfig.userIdField != this.config.userIdField) { this.config.userIdField = this.editConfig.userIdField - console.log('Edited userIdField: ' + this.config.userIdField) } if (this.editConfig.usernameField != this.config.usernameField) { this.config.usernameField = this.editConfig.usernameField - console.log('Edited usernameField: ' + this.config.usernameField) } if (this.editConfig.userDisplayNameField != this.config.userDisplayNameField) { this.config.userDisplayNameField = this.editConfig.userDisplayNameField - console.log('Edited userDisplayNameField: ' + this.config.userDisplayNameField) } if (this.editConfig.deviceIdField != this.config.deviceIdField) { this.config.deviceIdField = this.editConfig.deviceIdField - console.log('Edited deviceIdField: ' + this.config.deviceIdField) } if (this.editConfig.createdAtField != this.config.createdAtField) { this.config.createdAtField = this.editConfig.createdAtField - console.log('Edited createdAtField: ' + this.config.createdAtField) } if (this.editConfig.lastModifiedField != this.config.lastModifiedField) { this.config.lastModifiedField = this.editConfig.lastModifiedField - console.log('Edited lastModifiedField: ' + this.config.lastModifiedField) } if (this.editConfig.geometryType != this.config.geometryType) { this.config.geometryType = this.editConfig.geometryType - console.log('Edited geometryType: ' + this.config.geometryType) + } + if (this.editConfig.iconSymbolField != this.config.iconSymbolField) { + this.config.iconSymbolField = this.editConfig.iconSymbolField } this.saveConfig() - console.log('Saved configuration edit') } cancelEdit() { - console.log('Canceled configuration edit') this.editConfig = this.copyConfig(); } + showResetConfig() { + this.dialog.open(this.resetConfigTemplate) + } + + // Reset the entire ArcGIS configuration to the default values and reload the page + resetConfig() { + const defaults: ArcGISPluginConfig = JSON.parse(JSON.stringify(defaultArcGISPluginConfig)); + this.arcService.putArcConfig(defaults).subscribe({ + next: () => { + window.location.reload() + }, + error: error => console.error('Failed to reset config:', error) + }) + } + keys(value: any): string[] { let keys: string[] if (value != undefined) { @@ -499,7 +500,7 @@ export class ArcAdminComponent implements OnInit { const exclude = new Set() if (this.config.attributes != undefined) { for (const attribute of Object.keys(this.config.attributes)) { - exclude.add(attribute) + exclude.add(attribute.toLowerCase()) } } @@ -509,10 +510,10 @@ export class ArcAdminComponent implements OnInit { selectableConditionAttributes(attribute: string, conditions: AttributeValueConfig[]): string[] { const exclude = new Set() - exclude.add(attribute) + exclude.add(attribute.toLowerCase()) if (conditions != undefined) { for (const condition of conditions) { - exclude.add(condition.attribute) + exclude.add(condition.attribute.toLowerCase()) } } @@ -531,6 +532,7 @@ export class ArcAdminComponent implements OnInit { this.addAttribute(this.config.createdAtField, attributes, exclude) this.addAttribute(this.config.lastModifiedField, attributes, exclude) this.addAttribute(this.config.geometryType, attributes, exclude) + this.addAttribute(this.config.iconSymbolField, attributes, exclude) if (this.config.fieldAttributes != undefined) { for (const formMappings of Object.values(this.config.fieldAttributes)) { @@ -549,7 +551,7 @@ export class ArcAdminComponent implements OnInit { if (form.fields != undefined) { for (const field of form.fields) { if (this.fieldMapping(event.name, form.name, field.title) == undefined) { - this.addAttribute(field.title, attributes, exclude) + this.addAttribute(this.normalizeAttributeName(field.title), attributes, exclude) } } } @@ -565,9 +567,10 @@ export class ArcAdminComponent implements OnInit { private addAttribute(attribute: string | undefined, attributes: string[], unique: Set) { if (attribute != undefined) { const attributeAdd = this.replaceSpaces(attribute) - if (!unique.has(attributeAdd)) { + const key = attributeAdd.toLowerCase() + if (!unique.has(key)) { attributes.push(attributeAdd) - unique.add(attributeAdd) + unique.add(key) } } } @@ -608,6 +611,45 @@ export class ArcAdminComponent implements OnInit { return name.replace(/ /g, '_') } + // Normalize a string into an ArcGIS attribute name: spaces replaced with underscores and lowercased + private normalizeAttributeName(name: string): string { + return this.replaceSpaces(name).trim().toLowerCase() + } + + /** + * The set of attribute names already claimed by the special fields configured on the + * Attributes tab (Observation Id Field, Event Id Field, Event Name Field, etc) + */ + private reservedAttributeNames(): Set { + const reserved = new Set() + const specialFields = [ + this.config.observationIdField, + this.config.eventIdField, + this.config.eventNameField, + this.config.userIdField, + this.config.usernameField, + this.config.userDisplayNameField, + this.config.deviceIdField, + this.config.createdAtField, + this.config.lastModifiedField, + this.config.geometryType, + this.config.iconSymbolField, + ] + for (const field of specialFields) { + if (field != undefined && field !== '') { + reserved.add(this.normalizeAttributeName(field)) + } + } + return reserved + } + + isReservedAttributeName(name: string): boolean { + if (!name) { + return false + } + return this.reservedAttributeNames().has(this.normalizeAttributeName(name)) + } + showInfo(title: string, message: string) { this.infoTitle = title this.infoMessage = message @@ -636,10 +678,15 @@ export class ArcAdminComponent implements OnInit { showAddField(type: string, object: any) { this.editType = type; this.editObject = object; + this.editIsAttributeName = false; + this.editIsFieldMappingAttribute = false; this.dialog.open(this.addFieldTemplate) } addField(name: string) { + if (this.editIsAttributeName) { + name = this.normalizeAttributeName(name); + } if (this.editObject == undefined) { if (this.editType == 'Event') { this.config.fieldAttributes = {} @@ -669,6 +716,8 @@ export class ArcAdminComponent implements OnInit { showAddFieldValue(type: string, object: any) { this.editType = type; this.editObject = object; + this.editIsAttributeName = false; + this.editIsFieldMappingAttribute = false; this.dialog.open(this.addFieldValueTemplate) } @@ -682,6 +731,12 @@ export class ArcAdminComponent implements OnInit { attributeValue.values = [value] this.editObject.condition.push(attributeValue) } else { + if (this.editType == 'Field') { + value = this.normalizeAttributeName(value); + if (this.isReservedAttributeName(value)) { + return + } + } this.editObject[name] = value } this.saveConfig() @@ -691,6 +746,9 @@ export class ArcAdminComponent implements OnInit { this.editType = type; this.editObject = object; this.editOptions = options; + this.editSelectValue = undefined; + this.editIsAttributeName = type === 'Attribute'; + this.editIsFieldMappingAttribute = false; this.dialog.open(this.addFieldAutoTemplate) } @@ -698,18 +756,31 @@ export class ArcAdminComponent implements OnInit { this.editType = type; this.editObject = object; this.editOptions = options; + this.editValueOptions = type === 'Field' ? this.selectableAttributes() : []; + this.editIsAttributeName = type === 'Field'; + this.editIsFieldMappingAttribute = type === 'Field'; + this.editValueInput = ''; this.dialog.open(this.addFieldAutoValueTemplate) } - showEditField(name: string, field: string, object: any, value: any) { + showEditField(name: string, field: string, object: any, value: any, isAttributeName = false, isFieldMappingAttribute = false) { this.editName = name; this.editType = field; this.editObject = object; this.editValue = value; + this.editIsAttributeName = isAttributeName; + this.editIsFieldMappingAttribute = isFieldMappingAttribute; + this.editValueInput = value; this.dialog.open(this.editFieldTemplate) } editField(value: any) { + if (this.editIsAttributeName) { + value = this.normalizeAttributeName(value); + if (this.editIsFieldMappingAttribute && this.isReservedAttributeName(value)) { + return + } + } if (value != this.editValue) { const editObjectValue = this.editObject[this.editType] const existingValue = editObjectValue[this.editValue] @@ -744,6 +815,9 @@ export class ArcAdminComponent implements OnInit { options.unshift(value); } this.editOptions = options; + this.editSelectValue = value; + this.editIsAttributeName = name === 'Attribute'; + this.editIsFieldMappingAttribute = false; this.dialog.open(this.editFieldAutoTemplate) } @@ -752,6 +826,8 @@ export class ArcAdminComponent implements OnInit { this.editType = field; this.editObject = object; this.editValue = value; + this.editIsFieldMappingAttribute = false; + this.editIsAttributeName = false; this.dialog.open(this.editBooleanFieldTemplate) } @@ -827,7 +903,9 @@ export class ArcAdminComponent implements OnInit { } private saveConfig() { - this.arcService.putArcConfig(this.config) + this.arcService.putArcConfig(this.config).subscribe({ + error: error => console.error('Failed to save config:', error) + }) } private updateConfigForDeletion() { diff --git a/plugins/arcgis/web-app/projects/main/src/lib/arc-event/ArcEvent.ts b/plugins/arcgis/web-app/projects/main/src/lib/arc-event/ArcEvent.ts index 59dbc2f3c..641f29498 100644 --- a/plugins/arcgis/web-app/projects/main/src/lib/arc-event/ArcEvent.ts +++ b/plugins/arcgis/web-app/projects/main/src/lib/arc-event/ArcEvent.ts @@ -4,10 +4,28 @@ export class ArcEvent { name: string; id: number; layers: ArcEventLayer[]; + selected: boolean; - constructor(name: string, id: number, layers: ArcEventLayer[]) { + private _syncAfter?: string; + private _syncAfterDate: Date | null = null; + + constructor(name: string, id: number, layers: ArcEventLayer[], selected = false) { this.name = name; this.id = id; this.layers = layers; + this.selected = selected; + } + + get syncAfter(): string | undefined { + return this._syncAfter; + } + + set syncAfter(value: string | undefined) { + this._syncAfter = value; + this._syncAfterDate = value ? new Date(value) : null; + } + + get syncAfterDate(): Date | null { + return this._syncAfterDate; } } \ No newline at end of file diff --git a/plugins/arcgis/web-app/projects/main/src/lib/arc-event/ArcEventsModel.ts b/plugins/arcgis/web-app/projects/main/src/lib/arc-event/ArcEventsModel.ts index 76a1e9ad3..75a86510d 100644 --- a/plugins/arcgis/web-app/projects/main/src/lib/arc-event/ArcEventsModel.ts +++ b/plugins/arcgis/web-app/projects/main/src/lib/arc-event/ArcEventsModel.ts @@ -2,10 +2,13 @@ import { ArcEvent } from "./ArcEvent"; export class ArcEventsModel { allEvents: Array; - events: Array; constructor() { - this.allEvents = new Array(); - this.events = new Array(); + this.allEvents = new Array(); + } + + // events currently turned on to synchronize, derived from allEvents so there is a single source of truth + get events(): Array { + return this.allEvents.filter((event) => event.selected); } } \ No newline at end of file diff --git a/plugins/arcgis/web-app/projects/main/src/lib/arc-event/arc-event.component.html b/plugins/arcgis/web-app/projects/main/src/lib/arc-event/arc-event.component.html index 6cc43fc4d..de3a88995 100644 --- a/plugins/arcgis/web-app/projects/main/src/lib/arc-event/arc-event.component.html +++ b/plugins/arcgis/web-app/projects/main/src/lib/arc-event/arc-event.component.html @@ -1,83 +1,109 @@
-
+
Events - Change the ArcGIS layers each MAGE event sends its observations to. + Choose which ArcGIS layers each Mage event sends observations to.
- - Select Events - - - - {{item.name}} - - - + + Filter Events + + + +
+ * Add a feature service in the Feature Layers tab before synchronizing events +
+
-
- There are currently no events synchronizing to ArcGIS layers. +
+ There are currently no events available.
- - - -
-
- {{ event.name }} -
-
+ + + +
+ + + +
- - {{ layerDisplay(featureLayer) }}, - + {{ event.name }}
+
{{ event.selected ? layerSyncSummary(event) : 'This event is not synchronizing to any ArcGIS layers.' }}
+ + + open_in_new + + +
+
- -
This event is not synchronizing to any ArcGIS layers.
-
+
+

Layers

+
+
+ + {{layerDisplay(arcLayer)}} +
-
- - open_in_new - - - + + +

Filter

+ + Only sync observations after a specific date/time + +
+ + Sync After Date + + + + + + + Sync After Time + +
- - - - +
+ +
- - + + + - -

ArcGIS layers that MAGE event named {{currentEditingEvent.name}} is synchronizing

- -
-
- - {{layerDisplay(arcLayer)}} -
+ + +
+ Push Status + Observations that have been sent to the configured ArcGIS layer(s).
- - - - - +
+ + + +
+
\ No newline at end of file diff --git a/plugins/arcgis/web-app/projects/main/src/lib/arc-event/arc-event.component.scss b/plugins/arcgis/web-app/projects/main/src/lib/arc-event/arc-event.component.scss index cd166bd75..0fe249648 100644 --- a/plugins/arcgis/web-app/projects/main/src/lib/arc-event/arc-event.component.scss +++ b/plugins/arcgis/web-app/projects/main/src/lib/arc-event/arc-event.component.scss @@ -1,9 +1,14 @@ -.arcEvent__info { +.arcEvent-info { flex-grow: 1; max-width: 93% } -.arcEvent__button { +mat-card-header mat-hint { + display: block; + padding-bottom: 16px; +} + +.arcEvent-button { width: 7%; } @@ -21,15 +26,10 @@ } -.select-event { - margin-right: 10px; +.filter-events { + display: block; width: 34%; - margin-top: 5px; -} - -.select-text-box { - height: 45px; - width: 100%; + margin-bottom: 8px; } .d-flow-root { @@ -37,26 +37,112 @@ } .event-card { - max-height: 300px; - overflow-y: auto; + max-height: 350px; + overflow-y: scroll; overflow-x: hidden; + + scrollbar-width: thin; + scrollbar-color: var(--mat-sys-outline, rgba(0, 0, 0, 0.35)) transparent; + + &::-webkit-scrollbar { + width: 8px; + } + + &::-webkit-scrollbar-track { + background: transparent; + } + + &::-webkit-scrollbar-thumb { + background-color: var(--mat-sys-outline, rgba(0, 0, 0, 0.35)); + border-radius: 4px; + } +} + +.no-feature-services-hint { + font-size: 90%; + opacity: 0.7; + margin-bottom: 12px; + color: var(--mat-sys-error); } -::ng-deep { - .mat-select-panel { - .mat-selected { - background-color: #1E88E5 !important; - color: #fff !important; - } +.arcEvent-summary { + font-size: 0.85rem; + font-weight: 400; + opacity: 0.7; +} - .mat-option { - border-bottom: 1px solid black; - border-left: 1px solid black; - border-right: 1px solid black; +.arcEvent-icons { + display: flex; + align-items: center; + gap: 4px; +} + +.arcEvent-row { + display: flex; + align-items: center; + width: 100%; +} + +.arcEvent-toggle { + display: flex; + align-items: center; + margin-right: 12px; + flex-shrink: 0; +} + +.arc-events-accordion { + .mat-expansion-panel { + box-shadow: none; + } +} + +.arcEvent-panel-body { + padding: 4px 16px 16px 48px; + border-radius: 4px; +} + +.arcEvent-section-title { + font-size: 0.85rem; + font-weight: 500; + opacity: 0.8; + margin: 8px 0; +} + +.arc-layers, +.featureLayer-value { + font-size: 0.85rem; +} + +.arcEvent-header { + height: auto !important; + padding: 8px 24px; +} + +.arc-layers { + margin-bottom: 4px; +} + +.nav-actions { + display: flex; + gap: 8px; +} + +.cancel-button { + color: var(--mat-sys-primary) !important; + border-color: var(--mat-sys-primary) !important; +} + +.push-status-card { + margin-top: 24px; +} + +.sync-after-datetime { + display: grid; + grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); + gap: 16px; + margin-top: 12px; - .mat-pseudo-checkbox { - display: none !important; - } - } + mat-form-field { + width: 100%; } } \ No newline at end of file diff --git a/plugins/arcgis/web-app/projects/main/src/lib/arc-event/arc-event.component.ts b/plugins/arcgis/web-app/projects/main/src/lib/arc-event/arc-event.component.ts index 2263c9115..52888a08a 100644 --- a/plugins/arcgis/web-app/projects/main/src/lib/arc-event/arc-event.component.ts +++ b/plugins/arcgis/web-app/projects/main/src/lib/arc-event/arc-event.component.ts @@ -3,22 +3,18 @@ import { EventEmitter, Input, OnChanges, + OnDestroy, OnInit, Output, - SimpleChanges, - TemplateRef, - ViewChild + SimpleChanges } from '@angular/core'; import { ArcGISPluginConfig, defaultArcGISPluginConfig } from '../ArcGISPluginConfig' import { FeatureLayerConfig, FeatureServiceConfig } from "../ArcGISConfig" import { ArcService, MageEvent } from '../arc.service' -import { MatDialog } from '@angular/material/dialog' import { ArcEventsModel } from './ArcEventsModel'; import { ArcEvent } from './ArcEvent'; import { ArcEventLayer } from './ArcEventLayer'; -import { Observable } from 'rxjs'; -import { MatSelect } from '@angular/material/select'; -import { MatOption, MatOptionSelectionChange } from '@angular/material/core'; +import { Observable, Subscription } from 'rxjs'; @Component({ standalone: false, @@ -26,7 +22,7 @@ import { MatOption, MatOptionSelectionChange } from '@angular/material/core'; templateUrl: './arc-event.component.html', styleUrls: ['./arc-event.component.scss'] }) -export class ArcEventComponent implements OnInit, OnChanges { +export class ArcEventComponent implements OnInit, OnChanges, OnDestroy { @Input('config') config: ArcGISPluginConfig = defaultArcGISPluginConfig; private configSet = false; @@ -55,26 +51,101 @@ export class ArcEventComponent implements OnInit, OnChanges { return this._model; } - selectedValues: number[] = []; - isLoading: boolean; - currentEditingEvent: ArcEvent; - layers: ArcEventLayer[]; - - @ViewChild('editEventDialog', { static: true }) - private editEventTemplate: TemplateRef - - @ViewChild('selectEvents', { static: true }) - private selectEventTemplate: TemplateRef - @ViewChild('matRef') matSelect: MatSelect; + private configChangedSubscription?: Subscription; + isSaving: boolean; + private savedSnapshot: string = '[]'; - constructor(private arcService: ArcService, private dialog: MatDialog) { + constructor(private arcService: ArcService) { this.config = defaultArcGISPluginConfig; this._model = new ArcEventsModel(); } - ngOnInit(): void { } + ngOnInit(): void { + // detect changes in other tabs + this.configChangedSubscription = this.configChangedNotifier?.subscribe(() => this.refreshEventLayers()); + } + + ngOnDestroy(): void { + this.configChangedSubscription?.unsubscribe(); + } + + // re-derives each event's layer list from the current config, so layers belonging to a feature + // service that was since deleted no longer appear as selectable/selected for any event + private refreshEventLayers(): void { + for (const event of this.model.allEvents) { + event.layers = this.eventLayers(event.name); + if (event.layers.length === 0) { + event.selected = false; + } + } + this.captureSnapshot(); + } + + // true once this.config has actually been replaced with fetched data, rather than still being + // the shared default placeholder the parent starts with before its own config fetch resolves + private get configReady(): boolean { + return this.config !== defaultArcGISPluginConfig; + } + + // applying the persisted event selection depends on both the events list (from this component's + // own fetchEvents() call) and the real config (fetched independently by the parent) being ready; + // whichever of those two async loads finishes last calls this to do the one-time initial application, + // so it works correctly regardless of which one happens to resolve first + private tryApplyInitialSelection(): void { + if (this.eventSet || this.model.allEvents.length === 0 || !this.configReady) { + return; + } + this.eventSet = true; + this.refreshEventLayers(); + this.LoadSelectedEvents(); + this.loadEventFilters(); + this.captureSnapshot(); + } + + // seeds each event's sync-after filter from the persisted config + private loadEventFilters(): void { + for (const event of this.model.allEvents) { + event.syncAfter = this.config.syncAfterByEventId?.[event.id]; + } + } + + // serializes the event/layer selection state so it can be compared against or restored later + private serializeEventsState(): string { + return JSON.stringify( + this.model.allEvents.map(event => ({ + name: event.name, + selected: event.selected, + syncAfter: event.syncAfter, + layers: event.layers.map(layer => ({ name: layer.name, isSelected: layer.isSelected })) + })) + ); + } + + // captures the current event/layer selection state as the baseline to compare pending changes against + private captureSnapshot(): void { + this.savedSnapshot = this.serializeEventsState(); + } + + get hasChanges(): boolean { + return this.serializeEventsState() !== this.savedSnapshot; + } + + // reverts all pending event/layer selection changes back to the last saved state + cancelChanges(): void { + const snapshot: { name: string, selected: boolean, syncAfter?: string, layers: { name: string, isSelected: boolean }[] }[] = JSON.parse(this.savedSnapshot); + for (const saved of snapshot) { + const event = this.model.allEvents.find(e => e.name === saved.name); + if (!event) continue; + event.selected = saved.selected; + event.syncAfter = saved.syncAfter; + for (const savedLayer of saved.layers) { + const layer = event.layers.find(l => l.name === savedLayer.name); + if (layer) layer.isSelected = savedLayer.isSelected; + } + } + } /// Activates On Every View Change, Is Configured to Set Initial State /// As Soon As Data is Available, Then locks Changes to Not Activate Unless @@ -82,73 +153,72 @@ export class ArcEventComponent implements OnInit, OnChanges { ngOnChanges(changes: SimpleChanges): void { if ( !this.configSet && - this.config.featureServices.length > 0 && this.model.allEvents.length === 0 ) { this.configSet = true; this.arcService.fetchEvents().subscribe(x => this.setAllEvents(x)); + return; } - else if (!this.eventSet && this.configSet && this.model.allEvents.length > 0) { - this.eventSet = true; - this.LoadSelectedEvents(); + + if (this.model.allEvents.length > 0 && changes['config'] && !changes['config'].firstChange) { + if (this.eventSet) { + // already applied the initial selection - just recompute layer availability from the + // updated config, preserving any pending (unsaved) selection the user has made + this.refreshEventLayers(); + } else { + // the real config arrived after events were already loaded from the placeholder config + this.tryApplyInitialSelection(); + } } } - /// Returns a list of values that differ between two lists of ArcEvents - getDifferences(left: ArcEvent[], right: ArcEvent[]): ArcEvent[] { - return left.filter(l => - !right.some(r => - l.id === r.id)); + /// This Returns if Something should be shown when the Filter Text Box is used + getVisibility(item: ArcEvent): boolean { + return item.name.toLocaleLowerCase().includes(this.filterValue.toLocaleLowerCase()); + } + + get sortedEvents(): ArcEvent[] { + return [...this.model.allEvents].sort((a, b) => a.name.localeCompare(b.name)); } - /// This Returns if Something should be shown when the Filter Text Box is used - getVisibility(item: ArcEvent) { - let isNotFiltered = this.model.allEvents.find((x) => - x.name === item.name)?.name.toLocaleLowerCase().includes(this.filterValue); - return isNotFiltered + get visibleEvents(): ArcEvent[] { + return this.sortedEvents.filter((event) => this.getVisibility(event)); } clearFilterValue() { this.filterValue = ""; } - getSelections() { - return this._model.events.map((x) => x.id); - } - /// On Initial Load this will store all available events into model.allEvents setAllEvents(x: MageEvent[]) { if (this.model.allEvents.map((aE) => aE.name).filter((eN) => x.map((mE) => mE.name).includes(eN)).length) return; console.log("Loading All Available Events") - let temp = new ArcEventsModel(); + const allEvents = new Array(); for (const event of x) { - let eventsLayers = this.eventLayers(event.name) - const arcEvent = new ArcEvent(event.name, event.id, eventsLayers); - temp.allEvents.push(arcEvent); + const eventsLayers = this.eventLayers(event.name) + allEvents.push(new ArcEvent(event.name, event.id, eventsLayers)); } - this.model = Object.assign({}, temp); + this.model.allEvents = allEvents; this.eventSet = false; + // config may already be real by the time this async call resolves (or may still be the + // placeholder, in which case ngOnChanges will pick this up once the real config arrives) + this.tryApplyInitialSelection(); } /// On Initial Load, this checks the database loaded value for selected events - /// And Adds them to the list of select box selected values, which in turn adds them - /// to model.events + /// And marks the matching entries in model.allEvents as selected/on LoadSelectedEvents() { console.log("Loading Previously Selected Events") let events: (string | number)[] = []; for (const fs of this.config.featureServices) { for (const l of fs.layers) { - events.push(...<[]>l.events?.map(x => x)) + l.events?.forEach(x => events.push(x)) } } - if (!events) { - console.log("No Events Found!") - return; - } - else events = [...new Set(events)]; /// needs to be distinct. - let e = null; + events = [...new Set(events)]; /// needs to be distinct. for (const event of events) { + let e = null; if (typeof (event) == "string") { e = this.model.allEvents.find((x) => x.name === event); } else if (typeof (event) == "number") { @@ -158,9 +228,7 @@ export class ArcEventComponent implements OnInit, OnChanges { console.log(`${event} not found!`) continue; } - let eventsLayers = this.eventLayers(e.name) - const arcEvent = new ArcEvent(e.name, e.id, eventsLayers); - this.selectedValues.push(arcEvent.id) + e.selected = true; } } @@ -183,42 +251,106 @@ export class ArcEventComponent implements OnInit, OnChanges { return eventsLayers } - onEditEvent(event: ArcEvent) { - console.log('Editing event synchronization for event ' + event.name); - this.layers = event.layers; - this.currentEditingEvent = event; - this.dialog.open(this.editEventTemplate) + isFilterEnabled(event: ArcEvent): boolean { + return !!event.syncAfter; } - onSelectEvents() { - this.dialog.open(this.selectEventTemplate); + getSyncAfterTime(event: ArcEvent): string { + const date = event.syncAfterDate; + return date ? this.toTimeString(date) : ''; } - onSelectionChange(arcEventModified: ArcEvent, e: MatOptionSelectionChange) { - let option: MatOption = e.source; - if (!option) return - if (option.selected) this.onAddEvent(arcEventModified); - else this.onRemoveEvent(arcEventModified) + onFilterToggle(event: ArcEvent, checked: boolean): void { + if (checked) { + const date = event.syncAfterDate || new Date(); + const time = this.getSyncAfterTime(event) || this.toTimeString(date); + this.applySyncAfter(event, date, time); + } else { + event.syncAfter = undefined; + } } - onAddEvent(event: ArcEvent) { - console.log('Adding Event to List of Selected Events'); - let temp: ArcEventsModel = { ...this.model } - temp.events.push(event); - this.model = Object.assign({}, temp); + onSyncAfterDateChange(event: ArcEvent, value: Date | null): void { + if (!value) return; + this.applySyncAfter(event, value, this.getSyncAfterTime(event)); } - onRemoveEvent(event: ArcEvent) { - console.log('Removing Event to List of Selected Events'); - let temp: ArcEventsModel = { ...this.model } - temp.events = temp.events.filter((e) => e != event); - this.model = Object.assign({}, temp); + onSyncAfterTimeChange(event: ArcEvent, value: string): void { + const date = event.syncAfterDate; + if (!date) return; + this.applySyncAfter(event, date, value); + } + + private applySyncAfter(event: ArcEvent, date: Date, time: string): void { + const combined = new Date(date); + const [hours, minutes] = time + ? time.split(':').map(Number) + : [0, 0]; + combined.setHours(hours || 0, minutes || 0, 0, 0); + event.syncAfter = combined.toISOString(); + } + + private toTimeString(date: Date): string { + const pad = (n: number) => String(n).padStart(2, '0'); + return `${pad(date.getHours())}:${pad(date.getMinutes())}`; + } + + // Turns an event's synchronization on or off + onToggleEvent(event: ArcEvent, on: boolean) { + console.log(`Turning event synchronization ${on ? 'on' : 'off'} for event ${event.name}`); + event.selected = on; + if (on) { + this.expandedEventId = event.id; + } else if (this.expandedEventId === event.id) { + this.expandedEventId = null; + } + this.blurActiveElement(); + } + + // Material's default state after enable/disable of a toggle is to leave it in focus. + // Disable that focus after a click + private blurActiveElement(): void { + setTimeout(() => { + const active = document.activeElement; + if (active instanceof HTMLElement) { + active.blur(); + } + }); + } + + // Tracks which event's panel is currently expanded, so disabling an event + // (turning its sync toggle off) can force its expanded panel closed + expandedEventId: number | null = null; + + isExpanded(event: ArcEvent): boolean { + return this.expandedEventId === event.id; + } + + onPanelOpened(event: ArcEvent): void { + this.expandedEventId = event.id; + } + + onPanelClosed(event: ArcEvent): void { + if (this.expandedEventId === event.id) { + this.expandedEventId = null; + } } getSelectedLayers(event: ArcEvent) { return event.layers.filter((x) => x.isSelected) } + layerSyncSummary(event: ArcEvent): string { + const layers = this.getSelectedLayers(event) + if (layers.length === 0) { + return 'This event is not synchronizing to any ArcGIS layers.' + } + if (layers.length === 1) { + return this.layerDisplay(layers[0]) + } + return `Synchronizing ${layers.length} layers...` + } + layerDisplay(layer: ArcEventLayer): string { let displayName = layer.name return displayName @@ -234,15 +366,15 @@ export class ArcEventComponent implements OnInit, OnChanges { getEventsInFeatureFormat(featureService: FeatureServiceConfig): FeatureLayerConfig[] { let values: FeatureLayerConfig[] = []; for (let l of featureService.layers) { + // layer identifiers may be numbers (once ArcGIS sync normalizes them to the layer id) while + // ArcEventLayer.name is always a string, so compare as strings to avoid a silent type mismatch + const layerName = String(l.layer); values.push({ layer: l.layer, geometryType: l.geometryType, - events: [...this.model.events.filter((x) => x.layers.map((y) => { - if (y.name === l.layer && y.isSelected) { - return y.name - } else return "" - }).indexOf(l.layer as string) >= 0) - .map((z) => z.name)] + events: this.model.events + .filter((x) => x.layers.some((y) => y.name === layerName && y.isSelected)) + .map((z) => z.name) }) } return values; @@ -250,11 +382,23 @@ export class ArcEventComponent implements OnInit, OnChanges { saveChanges() { console.log('Saving changes to event sync'); + this.isSaving = true; for (const featureService of this.config.featureServices) { featureService.layers = this.getEventsInFeatureFormat(featureService); } + this.config.syncAfterByEventId = {}; + for (const event of this.model.allEvents) { + if (event.syncAfter) { + this.config.syncAfterByEventId[event.id] = event.syncAfter; + } + } this.configChanged.emit(this.config); - this.arcService.putArcConfig(this.config); + this.captureSnapshot(); + this.isSaving = false; + + this.arcService.putArcConfig(this.config).subscribe({ + error: (error) => console.error('Failed to save event synchronization:', error) + }); } private domain(featureService: FeatureServiceConfig): string { diff --git a/plugins/arcgis/web-app/projects/main/src/lib/arc-layer/arc-layer-delete-dialog.component.html b/plugins/arcgis/web-app/projects/main/src/lib/arc-layer/arc-layer-delete-dialog.component.html index a1f447ef7..e2602ae86 100644 --- a/plugins/arcgis/web-app/projects/main/src/lib/arc-layer/arc-layer-delete-dialog.component.html +++ b/plugins/arcgis/web-app/projects/main/src/lib/arc-layer/arc-layer-delete-dialog.component.html @@ -4,6 +4,8 @@

Delete Feature Service?

- + \ No newline at end of file diff --git a/plugins/arcgis/web-app/projects/main/src/lib/arc-layer/arc-layer-delete-dialog.component.scss b/plugins/arcgis/web-app/projects/main/src/lib/arc-layer/arc-layer-delete-dialog.component.scss index e69de29bb..99effeef6 100644 --- a/plugins/arcgis/web-app/projects/main/src/lib/arc-layer/arc-layer-delete-dialog.component.scss +++ b/plugins/arcgis/web-app/projects/main/src/lib/arc-layer/arc-layer-delete-dialog.component.scss @@ -0,0 +1,7 @@ +mat-dialog-content { + overflow-wrap: break-word; +} + +.delete-feature-service-button { + color: var(--mat-sys-error) !important; +} diff --git a/plugins/arcgis/web-app/projects/main/src/lib/arc-layer/arc-layer-dialog.component.html b/plugins/arcgis/web-app/projects/main/src/lib/arc-layer/arc-layer-dialog.component.html index 84001a33e..76d7caf87 100644 --- a/plugins/arcgis/web-app/projects/main/src/lib/arc-layer/arc-layer-dialog.component.html +++ b/plugins/arcgis/web-app/projects/main/src/lib/arc-layer/arc-layer-dialog.component.html @@ -9,8 +9,7 @@
- + Feature Service {{featureService?.url}} @@ -21,18 +20,32 @@ -
- + + I have a URL + Browse a Portal + + + + Feature Service URL - + + {{url}} + URL is required Portal API URL - - Leave empty to use default portal URL derived from feature service URL + + + {{url}} + + Leave empty to use default portal URL derived from feature service URL @@ -74,16 +87,71 @@
+ +
+ error_outline + Your account privileges don't include the ability to edit features (features:user:edit). Ask your administrator for more info. +
+ +
+ error_outline + {{validationError}} +
+
+ + Filter by name + + + +
+ + + Searching... + + + {{discoveredTotal}} feature service{{discoveredTotal === 1 ? '' : 's'}} found + +
+ + + {{service.title}} + {{service.url}} + {{service.permission}} + + +
+ {{discoveredStart}}–{{discoveredStart + discoveredServices.length - 1}} of {{discoveredTotal}} + + + + +
+
+
- +
@@ -94,15 +162,17 @@
- + - {{layer.name}} + {{layer.name}}* +
* This layer may not allow write access for the connected account
- - + + + open_in_new + + + + diff --git a/plugins/arcgis/web-app/projects/main/src/lib/arc-layer/arc-layer.component.scss b/plugins/arcgis/web-app/projects/main/src/lib/arc-layer/arc-layer.component.scss index 3639a2efd..b676e5e81 100644 --- a/plugins/arcgis/web-app/projects/main/src/lib/arc-layer/arc-layer.component.scss +++ b/plugins/arcgis/web-app/projects/main/src/lib/arc-layer/arc-layer.component.scss @@ -2,6 +2,15 @@ mat-card { margin-bottom: 16px; } +mat-card-header { + flex-direction: column; + align-items: flex-start; +} + +mat-card-header mat-hint { + display: block; +} + .mat-list-icon { color: rgba(0, 0, 0, 0.54); } @@ -47,4 +56,8 @@ mat-card { .invalid-credentials { color: #F44336; +} + +.read-only-service { + --mdc-list-list-item-label-text-color: #F44336; } \ No newline at end of file diff --git a/plugins/arcgis/web-app/projects/main/src/lib/arc-layer/arc-layer.component.ts b/plugins/arcgis/web-app/projects/main/src/lib/arc-layer/arc-layer.component.ts index b7c91f97d..209c18ce3 100644 --- a/plugins/arcgis/web-app/projects/main/src/lib/arc-layer/arc-layer.component.ts +++ b/plugins/arcgis/web-app/projects/main/src/lib/arc-layer/arc-layer.component.ts @@ -1,4 +1,4 @@ -import { Component, EventEmitter, Input, Output } from '@angular/core' +import { Component, EventEmitter, Input, OnChanges, Output } from '@angular/core' import { ArcGISPluginConfig, defaultArcGISPluginConfig } from '../ArcGISPluginConfig' import { ArcService } from '../arc.service' import { FeatureLayerConfig, FeatureServiceConfig } from '../ArcGISConfig' @@ -13,28 +13,55 @@ import { ArcLayerDeleteDialogComponent } from './arc-layer-delete-dialog.compone templateUrl: './arc-layer.component.html', styleUrls: ['./arc-layer.component.scss'] }) -export class ArcLayerComponent { +export class ArcLayerComponent implements OnChanges { @Input('config') config: ArcGISPluginConfig @Output() configChanged = new EventEmitter() layers: ArcLayerSelectable[] - events: string[] = [] + + // service capabilities are fetched lazily and cached by url, since they aren't part of the saved config + private serviceCapabilities = new Map() constructor(private arcService: ArcService, private dialog: MatDialog) { this.config = defaultArcGISPluginConfig this.layers = new Array() + } - arcService.fetchEvents().subscribe(events => { - this.events = events.map(event => event.name) - }) + ngOnChanges(): void { + this.refreshCapabilities() + } + + private refreshCapabilities(): void { + for (const service of this.config.featureServices) { + if (this.serviceCapabilities.has(service.url)) { + continue + } + // mark as pending immediately so a slow response doesn't trigger duplicate requests + this.serviceCapabilities.set(service.url, undefined) + this.arcService.fetchFeatureServiceCapabilities(service.url).subscribe({ + next: (result) => this.serviceCapabilities.set(service.url, result.capabilities), + error: (error) => console.log('arc-layer fetchFeatureServiceCapabilities error: ' + error) + }) + } + } + + // unlike a missing/pending cache entry, only flag services we can positively confirm are read-only + isReadOnly(featureService: FeatureServiceConfig): boolean { + const capabilities = this.serviceCapabilities.get(featureService.url) + if (!capabilities) { + return false + } + const capabilityList = capabilities.split(',').map(c => c.trim()) + return !capabilityList.includes('Create') && !capabilityList.includes('Editing') } onAddService() { this.dialog.open(ArcLayerDialogComponent, { data: { featureService: undefined }, autoFocus: false, - disableClose: true + disableClose: true, + width: '700px' }).afterClosed().subscribe(featureService => { if (featureService) { this.addFeatureService(featureService) @@ -50,7 +77,8 @@ export class ArcLayerComponent { this.dialog.open(ArcLayerDialogComponent, { data: { featureService }, autoFocus: false, - disableClose: true + disableClose: true, + width: '700px' }).afterClosed().subscribe(featureService => { if (featureService) { this.addFeatureService(featureService) @@ -68,7 +96,9 @@ export class ArcLayerComponent { }) this.configChanged.emit(this.config) - this.arcService.putArcConfig(this.config) + this.arcService.putArcConfig(this.config).subscribe({ + error: error => console.error('Failed to save config:', error) + }) } }) } @@ -82,16 +112,16 @@ export class ArcLayerComponent { featureServer.layers = featureServer.layers.map((layer: FeatureLayerConfig) => { return { ...layer, - events: JSON.parse(JSON.stringify(this.events)) + events: [] } }) - + this.config.featureServices.push(featureServer) } else { existingFeatureServer.layers = featureServer.layers.map(layer => { const existing = existingFeatureServer.layers.some(edit => edit.layer === layer.layer) if (!existing) { - layer.events = JSON.parse(JSON.stringify(this.events)) + layer.events = [] } return layer @@ -99,6 +129,9 @@ export class ArcLayerComponent { } this.configChanged.emit(this.config) - this.arcService.putArcConfig(this.config) + this.arcService.putArcConfig(this.config).subscribe({ + error: error => console.error('Failed to save config:', error) + }) + this.refreshCapabilities() } } \ No newline at end of file diff --git a/plugins/arcgis/web-app/projects/main/src/lib/arc-layer/recent-feature-service-urls.service.ts b/plugins/arcgis/web-app/projects/main/src/lib/arc-layer/recent-feature-service-urls.service.ts new file mode 100644 index 000000000..e51b1ff3e --- /dev/null +++ b/plugins/arcgis/web-app/projects/main/src/lib/arc-layer/recent-feature-service-urls.service.ts @@ -0,0 +1,56 @@ +import { Injectable } from '@angular/core' + +/** + * Saving recently used feature service and portal urls in local storage to show previews + * in the dropdown when adding feature services to ArcGIS + */ + +const FEATURE_SERVICE_STORAGE_KEY = 'arcgis.recentFeatureServiceUrls' +const PORTAL_STORAGE_KEY = 'arcgis.recentPortalUrls' +const LIMIT = 10 + +@Injectable({ + providedIn: 'root' +}) +export class RecentFeatureServiceUrlsService { + getRecent(): string[] { + return this.getStored(FEATURE_SERVICE_STORAGE_KEY) + } + + addRecent(url: string): void { + this.addStored(FEATURE_SERVICE_STORAGE_KEY, url) + } + + getRecentPortalUrls(): string[] { + return this.getStored(PORTAL_STORAGE_KEY) + } + + addRecentPortalUrl(url: string): void { + this.addStored(PORTAL_STORAGE_KEY, url) + } + + private getStored(key: string): string[] { + try { + if (!('localStorage' in window) || window.localStorage === null) { + return [] + } + const raw = localStorage.getItem(key) + return raw ? JSON.parse(raw) : [] + } catch { + return [] + } + } + + private addStored(key: string, url: string): void { + try { + if (!('localStorage' in window) || window.localStorage === null) { + return + } + const recent = this.getStored(key).filter(existing => existing !== url) + recent.unshift(url) + localStorage.setItem(key, JSON.stringify(recent.slice(0, LIMIT))) + } catch { + // localStorage unavailable (private browsing, disabled storage, etc.) + } + } +} diff --git a/plugins/arcgis/web-app/projects/main/src/lib/arc-push-status/arc-push-status.component.html b/plugins/arcgis/web-app/projects/main/src/lib/arc-push-status/arc-push-status.component.html new file mode 100644 index 000000000..ff9edecc5 --- /dev/null +++ b/plugins/arcgis/web-app/projects/main/src/lib/arc-push-status/arc-push-status.component.html @@ -0,0 +1,105 @@ +
+ +
+ + Event + + {{ event.name }} + + + + +
+ +
+ inbox +

Select an event to view observations already synced to ArcGIS

+
+ + + +
+ +
+ +
+
{{ totalCount }} observation{{ totalCount === 1 ? '' : 's' }} pushed to ArcGIS
+ +
MAGE ValueMage Value ArcGIS Value
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Status + {{ row.status }} + Observation ID{{ row.id }}Attachments + + {{ hasFailedAttachments(row) ? 'error_outline' : 'attach_file' }} + {{ attachmentsSentCount(row) }}/{{ row.attachments.length }} sent + + -- + Lat/Lon + + my_location + {{ locationDisplay(row) }} + + -- + Last Modified{{ row.lastModified | date:'MMM d, HH:mm' }}Created At{{ row.createdAt | date:'MMM d, HH:mm' }}
+ +
+ {{ fieldsTooltip(hoveredRow) }} +
+ +
+ check_circle_outline +

No observations for this event have been synced to ArcGIS yet

+
+ + + + + + + diff --git a/plugins/arcgis/web-app/projects/main/src/lib/arc-push-status/arc-push-status.component.scss b/plugins/arcgis/web-app/projects/main/src/lib/arc-push-status/arc-push-status.component.scss new file mode 100644 index 000000000..c51706b2e --- /dev/null +++ b/plugins/arcgis/web-app/projects/main/src/lib/arc-push-status/arc-push-status.component.scss @@ -0,0 +1,132 @@ +.controls { + display: flex; + align-items: center; + gap: 16px; + flex-wrap: wrap; + margin-bottom: 8px; +} + +.event-select { + min-width: 280px; +} + +.refresh-btn { + width: 32px; + height: 32px; +} + +.table-container { + overflow-x: auto; +} + +.total-count { + font-size: 90%; + opacity: 0.7; + margin-bottom: 8px; +} + +.push-status-table { + width: 100%; + + .mat-mdc-row, .mat-mdc-header-row { + .mat-mdc-cell, .mat-mdc-header-cell { + border-bottom: none; + } + } + + .mat-mdc-row:hover { + background-color: var(--mat-sys-surface-variant, rgba(0, 0, 0, 0.04)); + } +} + +.status-chip { + display: inline-block; + padding: 2px 10px; + border-radius: 12px; + font-size: 12px; + font-weight: 500; + text-transform: capitalize; + + &.sent { + background-color: #e8f5e9; + color: #2e7d32; + } + + &.archived { + background-color: #f5f5f5; + color: #616161; + } +} + +.attachments-value { + display: inline-flex; + align-items: center; + gap: 4px; + font-size: 90%; + opacity: 0.8; + + &.attachments-failed { + color: var(--mat-sys-error); + opacity: 1; + } +} + +.attachments-icon { + font-size: 16px; + height: 16px; + width: 16px; +} + +.location-value { + display: inline-flex; + align-items: center; + gap: 4px; + color: var(--mat-sys-primary); +} + +.location-icon { + font-size: 18px; + height: 18px; + width: 18px; +} + +// follows the cursor (positioned via [style.left]/[style.top] bound to mouse coordinates) rather +// than being anchored to the row +.fields-tooltip { + position: fixed; + z-index: 1000; + pointer-events: none; + white-space: pre-line; + max-width: 320px; + font-size: 12px; + padding: 16px; + border-radius: 4px; + background-color: #fff; + color: #000; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3); +} + +.loading-state { + display: flex; + justify-content: center; + padding: 48px 0; +} + +.empty-state { + display: flex; + flex-direction: column; + align-items: center; + padding: 48px 0; + opacity: 0.6; + gap: 8px; + + mat-icon { + font-size: 40px; + width: 40px; + height: 40px; + } + + p { + font-size: 14px; + } +} diff --git a/plugins/arcgis/web-app/projects/main/src/lib/arc-push-status/arc-push-status.component.ts b/plugins/arcgis/web-app/projects/main/src/lib/arc-push-status/arc-push-status.component.ts new file mode 100644 index 000000000..fd1a39a48 --- /dev/null +++ b/plugins/arcgis/web-app/projects/main/src/lib/arc-push-status/arc-push-status.component.ts @@ -0,0 +1,123 @@ +import { Component, OnInit } from '@angular/core'; +import { PageEvent } from '@angular/material/paginator'; +import { ArcService, MageEvent, PushedObservation } from '../arc.service'; + +@Component({ + standalone: false, + selector: 'arc-push-status', + templateUrl: './arc-push-status.component.html', + styleUrls: ['./arc-push-status.component.scss'] +}) +export class ArcPushStatusComponent implements OnInit { + + events: MageEvent[] = []; + selectedEventId: number | undefined; + + pushedObservations: PushedObservation[] = []; + readonly displayedColumns = ['status', 'id', 'attachments', 'location', 'lastModified', 'createdAt']; + + totalCount = 0; + pageIndex = 0; + readonly pageSizeOptions = [10, 25, 50, 100]; + pageSize = this.pageSizeOptions[1]; + + isLoading = false; + + hoveredRow: PushedObservation | null = null; + tooltipX = 0; + tooltipY = 0; + + constructor(private arcService: ArcService) { } + + ngOnInit(): void { + this.arcService.fetchEvents().subscribe(events => { + this.events = [...events].sort((a, b) => a.name.localeCompare(b.name)); + }); + } + + onEventChange(): void { + this.pageIndex = 0; + this.load(); + } + + onPageChange(page: PageEvent): void { + this.pageIndex = page.pageIndex; + this.pageSize = page.pageSize; + this.load(); + } + + refresh(): void { + this.load(); + } + + // formats an observation's form field values as multi-line text for the row hover tooltip + fieldsTooltip(row: PushedObservation): string { + if (!row.fields.length) { + return 'No form data'; + } + return row.fields + .map(form => Object.entries(form) + .filter(([, value]) => value !== null && value !== undefined && value !== '') + .map(([name, value]) => `${name}: ${this.formatFieldValue(value)}`) + .join('\n')) + .filter(formText => formText.length > 0) + .join('\n\n') || 'No form data'; + } + + private formatFieldValue(value: unknown): string { + return typeof value === 'object' ? JSON.stringify(value) : String(value); + } + + // keeps the fields tooltip positioned next to the cursor rather than anchored to the row + onRowMouseMove(event: MouseEvent): void { + this.tooltipX = event.clientX; + this.tooltipY = event.clientY; + } + + attachmentsSentCount(row: PushedObservation): number { + return row.attachments?.filter(attachment => attachment.sent).length ?? 0; + } + + hasFailedAttachments(row: PushedObservation): boolean { + return !!row.attachments?.some(attachment => !attachment.sent); + } + + attachmentsTooltip(row: PushedObservation): string { + if (!row.attachments?.length) { + return ''; + } + return row.attachments + .map(attachment => `${attachment.sent ? '✓' : '✗'} ${attachment.name}`) + .join('\n'); + } + + locationDisplay(row: PushedObservation): string { + if (row.latitude == null || row.longitude == null) { + return '--'; + } + return `${row.latitude.toFixed(5)}, ${row.longitude.toFixed(5)}`; + } + + private load(): void { + if (this.selectedEventId == null) { + this.pushedObservations = []; + this.totalCount = 0; + return; + } + + this.isLoading = true; + this.arcService.fetchPushStatus(this.selectedEventId, this.pageIndex, this.pageSize).subscribe({ + next: (page) => { + this.pushedObservations = page.items; + this.totalCount = page.totalCount; + this.isLoading = false; + }, + error: (error) => { + console.error('Failed to fetch push status:', error); + this.pushedObservations = []; + this.totalCount = 0; + this.isLoading = false; + } + }); + } +} diff --git a/plugins/arcgis/web-app/projects/main/src/lib/arc.service.ts b/plugins/arcgis/web-app/projects/main/src/lib/arc.service.ts index cf814f1f4..a2347e31a 100644 --- a/plugins/arcgis/web-app/projects/main/src/lib/arc.service.ts +++ b/plugins/arcgis/web-app/projects/main/src/lib/arc.service.ts @@ -9,11 +9,43 @@ export const apiBaseUrl = '/api' export interface ArcServiceInterface { fetchArcConfig(): Observable - putArcConfig(config: ArcGISPluginConfig): void + putArcConfig(config: ArcGISPluginConfig): Observable fetchEvents(): Observable fetchPopulatedEvents(): Observable fetchFeatureServiceLayers(featureServiceUrl: string): Observable + fetchFeatureServiceCapabilities(featureServiceUrl: string): Observable<{ capabilities?: string }> validateFeatureService(request: ValidationRequest): Observable + fetchPushStatus(eventId: number, pageIndex: number, pageSize: number): Observable +} + +// 'sent': found on the ArcGIS layer and still active in MAGE. +// 'archived': found on the ArcGIS layer, but has since been archived (deleted) in MAGE. +export type PushStatus = 'sent' | 'archived' + +// whether a single attachment on a pushed observation made it to the arc feature layer +export interface PushedAttachment { + name: string + sent: boolean +} + +// a MAGE observation that has already been synced to an ArcGIS feature layer +export interface PushedObservation { + id: string + createdAt: string + lastModified: string + status: PushStatus + fields: Record[] + // undefined if the observation's geometry isn't a Point (e.g. a line/polygon) + latitude?: number + longitude?: number + attachments?: PushedAttachment[] +} + +export interface PushedObservationsPage { + items: PushedObservation[] + totalCount: number + pageIndex: number + pageSize: number } export class MageEvent { @@ -36,6 +68,7 @@ export interface FeatureLayer { id: number name: string geometryType: string + capabilities?: string } export type ValidationRequest = { @@ -43,6 +76,32 @@ export type ValidationRequest = { portalUrl?: string } & ({ token: string } | { username: string, password: string }) +export type DiscoveryRequest = { + portalUrl: string + start?: number + num?: number + filter?: string +} & ({ token: string } | { username: string, password: string } | { identityManager: string }) + +export interface DiscoveredFeatureService { + id: string + title: string + url: string + owner: string + capabilities?: string + permission: string +} + +export interface DiscoveryResult { + identityManager: string + portalUrl?: string + mayLackEditPrivilege?: boolean + services: DiscoveredFeatureService[] + total: number + start: number + num: number +} + @Injectable({ providedIn: 'root' }) @@ -60,9 +119,11 @@ export class ArcService implements ArcServiceInterface { return this.http.get(`${baseUrl}/featureService/layers?featureServiceUrl=${encodeURIComponent(featureServiceUrl)}`) } - oauth(featureServiceUrl: string, clientId: string, portalUrl?: string): Observable { - let subject = new Subject(); + fetchFeatureServiceCapabilities(featureServiceUrl: string): Observable<{ capabilities?: string }> { + return this.http.get<{ capabilities?: string }>(`${baseUrl}/featureService/capabilities?featureServiceUrl=${encodeURIComponent(featureServiceUrl)}`) + } + oauth(featureServiceUrl: string, clientId: string, portalUrl?: string): Observable { const params = new URLSearchParams({ featureServiceUrl: featureServiceUrl, clientId: clientId @@ -72,11 +133,27 @@ export class ArcService implements ArcServiceInterface { params.set('portalUrl', portalUrl); } + return this.oauthPopup(params, (data) => !!data.url); + } + + oauthDiscover(portalUrl: string, clientId: string): Observable { + const params = new URLSearchParams({ + discover: 'true', + portalUrl, + clientId + }); + + return this.oauthPopup(params, (data) => !!data.services); + } + + private oauthPopup(params: URLSearchParams, isResponse: (data: any) => boolean): Observable { + let subject = new Subject(); + const url = `${baseUrl}/oauth/signin?${params.toString()}`; const oauthWindow = window.open(url, "_blank"); const listener = (event: any) => { - if (event.data.url) { + if (isResponse(event.data)) { window.removeEventListener('message', listener, false); if (event.origin !== window.location.origin) { @@ -98,6 +175,18 @@ export class ArcService implements ArcServiceInterface { return this.http.post(`${baseUrl}/featureService/validate`, request) } + discoverFeatureServices(request: DiscoveryRequest): Observable { + return this.http.post(`${baseUrl}/featureService/discover`, request) + } + + confirmFeatureService(url: string, portalUrl: string | undefined, identityManager: string): Observable { + return this.http.post(`${baseUrl}/featureService/confirm`, { url, portalUrl, identityManager }) + } + + deleteFeatureService(featureServiceUrl: string): Observable { + return this.http.delete(`${baseUrl}/featureService?featureServiceUrl=${encodeURIComponent(featureServiceUrl)}`) + } + fetchEvents(): Observable { return this.http.get(`${apiBaseUrl}/events?populate=false&projection={"name":true,"id":true}`) } @@ -107,6 +196,10 @@ export class ArcService implements ArcServiceInterface { } putArcConfig(config: ArcGISPluginConfig) { - this.http.put(`${baseUrl}/config`, config).subscribe() + return this.http.put(`${baseUrl}/config`, config) + } + + fetchPushStatus(eventId: number, pageIndex: number, pageSize: number): Observable { + return this.http.get(`${baseUrl}/pushStatus?eventId=${eventId}&pageIndex=${pageIndex}&pageSize=${pageSize}`) } } \ No newline at end of file diff --git a/plugins/arcgis/web-app/projects/main/src/lib/mage-arc.module.ts b/plugins/arcgis/web-app/projects/main/src/lib/mage-arc.module.ts index 1a12e2e24..4bb74ebbc 100644 --- a/plugins/arcgis/web-app/projects/main/src/lib/mage-arc.module.ts +++ b/plugins/arcgis/web-app/projects/main/src/lib/mage-arc.module.ts @@ -4,15 +4,21 @@ import { FormsModule, ReactiveFormsModule } from '@angular/forms' import { MatFormFieldModule } from '@angular/material/form-field' import { MatAutocompleteModule } from '@angular/material/autocomplete' import { MatButtonModule } from '@angular/material/button' +import { MatButtonToggleModule } from '@angular/material/button-toggle' import { MatCardModule } from '@angular/material/card' +import { MatDatepickerModule } from '@angular/material/datepicker' import { MatDialogModule } from '@angular/material/dialog' import { MatExpansionModule } from '@angular/material/expansion' import { MatIconModule } from '@angular/material/icon' import { MatInputModule } from '@angular/material/input' import { MatListModule } from '@angular/material/list' +import { MatPaginatorModule } from '@angular/material/paginator' import { MatSelectModule } from '@angular/material/select' +import { MatSlideToggleModule } from '@angular/material/slide-toggle' import { MatTableModule } from '@angular/material/table' import { MatTabsModule } from '@angular/material/tabs' +import { MatTooltipModule } from '@angular/material/tooltip' +import { MatNativeDateModule } from '@angular/material/core' import { ArcAdminComponent } from './arc-admin/arc-admin.component' import { provideHttpClient, withInterceptorsFromDi } from '@angular/common/http' import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; @@ -20,6 +26,7 @@ import { ArcLayerComponent } from './arc-layer/arc-layer.component'; import { ArcEventComponent } from './arc-event/arc-event.component'; import { ArcLayerDialogComponent } from './arc-layer/arc-layer-dialog.component'; import { ArcLayerDeleteDialogComponent } from './arc-layer/arc-layer-delete-dialog.component'; +import { ArcPushStatusComponent } from './arc-push-status/arc-push-status.component'; @NgModule({ declarations: [ @@ -27,6 +34,7 @@ import { ArcLayerDeleteDialogComponent } from './arc-layer/arc-layer-delete-dial ArcLayerComponent, ArcLayerDialogComponent, ArcLayerDeleteDialogComponent, + ArcPushStatusComponent, ArcAdminComponent ], imports: [ @@ -34,16 +42,22 @@ import { ArcLayerDeleteDialogComponent } from './arc-layer/arc-layer-delete-dial FormsModule, MatAutocompleteModule, MatButtonModule, + MatButtonToggleModule, MatCardModule, + MatDatepickerModule, MatDialogModule, MatExpansionModule, MatFormFieldModule, MatIconModule, MatInputModule, MatListModule, + MatNativeDateModule, + MatPaginatorModule, MatSelectModule, + MatSlideToggleModule, MatTableModule, MatTabsModule, + MatTooltipModule, ReactiveFormsModule, MatProgressSpinnerModule, ], diff --git a/plugins/arcgis/web-app/projects/showcase/src/app/arc.service.mock.ts b/plugins/arcgis/web-app/projects/showcase/src/app/arc.service.mock.ts index f4b582b96..fd9ff6766 100644 --- a/plugins/arcgis/web-app/projects/showcase/src/app/arc.service.mock.ts +++ b/plugins/arcgis/web-app/projects/showcase/src/app/arc.service.mock.ts @@ -91,7 +91,6 @@ export class MockArcService implements ArcServiceInterface { observationIdField: 'mock_description', idSeparator: 'mock-', eventIdField: 'mock_event_id', - lastEditedDateField: 'mock_last_edited_date', eventNameField: 'mock_event_name', userIdField: 'mock_user_id', usernameField: 'mock_username', @@ -100,6 +99,7 @@ export class MockArcService implements ArcServiceInterface { createdAtField: 'mock_created_at', lastModifiedField: 'mock_last_modified', geometryType: 'mock_geometry_type', + iconSymbolField: 'mock_icon_symbol', fieldAttributes: {}, attributes: { 'symbolid': { @@ -143,7 +143,13 @@ export class MockArcService implements ArcServiceInterface { return of([mockArcGISEventResult1, mockArcGISEventResult2, mockArcGISEventResult3]); } - putArcConfig(config: ArcGISPluginConfig) {} + putArcConfig(config: ArcGISPluginConfig) { + return of(config); + } + + fetchPushStatus(eventId: number, pageIndex: number, pageSize: number) { + return of({ items: [], totalCount: 0, pageIndex, pageSize }); + } removeUserTrack(userTrackId: string): Observable { return of(defaultArcGISPluginConfig); diff --git a/plugins/image/service/package-lock.json b/plugins/image/service/package-lock.json index 935c0d968..19ddf10ae 100644 --- a/plugins/image/service/package-lock.json +++ b/plugins/image/service/package-lock.json @@ -26,7 +26,7 @@ "@types/sharp": "^0.30.4", "jasmine": "^3.10.0", "jimp": "^0.22.10", - "lodash": "^4.17.21", + "lodash": "^4.18.1", "npm-run-all": "^4.1.5", "rimraf": "^5.0.5", "typescript": "^5.0.0" diff --git a/plugins/image/service/package.json b/plugins/image/service/package.json index fb8d7cff8..445d3f50c 100644 --- a/plugins/image/service/package.json +++ b/plugins/image/service/package.json @@ -58,7 +58,7 @@ "@types/sharp": "^0.30.4", "jasmine": "^3.10.0", "jimp": "^0.22.10", - "lodash": "^4.17.21", + "lodash": "^4.18.1", "npm-run-all": "^4.1.5", "rimraf": "^5.0.5", "typescript": "^5.0.0" diff --git a/plugins/sftp/README.md b/plugins/sftp/README.md index b9138d5a0..a733d9315 100644 --- a/plugins/sftp/README.md +++ b/plugins/sftp/README.md @@ -72,92 +72,96 @@ This section covers everything needed to run the SFTP plugin locally using Docke The plugin currently authenticates to the SFTP server using an RSA private key. Password authentication is not yet supported (Kubernetes secret support is planned for a future release). -Create the `sftp-test/` directory at the repo root and generate a key pair there: - +From the repository root directory, generate a key pair. ```bash -mkdir -p sftp-test/upload -ssh-keygen -t rsa -b 4096 -f sftp-test/id_rsa -N "" +mkdir docker/sftp/key/ +ssh-keygen -t rsa -b 4096 -f docker/sftp/key/id_rsa -N "" ``` This produces two files: -- `sftp-test/id_rsa` — private key, mounted into the MAGE server container -- `sftp-test/id_rsa.pub` — public key, mounted into the SFTP server container - -> **Do not commit these files.** `sftp-test/` should be in `.gitignore`. +- `docker/sftp/key/id_rsa` — private key, mounted into the MAGE server container +- `docker/sftp/key/id_rsa.pub` — public key, mounted into the SFTP server container -### 2. Verify `docker-compose.yml` has the SFTP lines active +> The `docker/sftp/.gitignore` file will prevent commiting your key pair files. -The following lines in `docker-compose.yml` must be present and uncommented. +### 2. Verify the `mage-sftp` Compose service -**Under `mage-server` → `volumes`** (line 30): -```yaml -- ./sftp-test/id_rsa:/run/secrets/sftp_key:ro -``` +Ensure the following lines are merged into the indicated blocks in [`docker-compose.yml`](../../docker-compose.yml). -**Under `mage-server` → `environment`** (lines 44–46): ```yaml -SFTP_PLUGIN_CONFIG_SALT: "A0E6D3B4-25BD-4DD6-BBC9-B367931966AB" -MAGE_SFTP_KEY_FILE: /run/secrets/sftp_key -MAGE_PLUGINS: '{"servicePlugins":["@ngageoint/mage.sftp.service",...],"webUIPlugins":["@ngageoint/mage.sftp.web",...]}' -``` - -> Change `SFTP_PLUGIN_CONFIG_SALT` to a unique UUID before any non-local deployment. Do **not** rotate it after deployment — it is used to encrypt stored config and changing it will break existing settings. - -**The `mage-sftp` service block** (lines 79–88) — the local SFTP server for development: -```yaml -mage-sftp: - image: atmoz/sftp - command: magetest::1001 - volumes: - - ./sftp-test/id_rsa.pub:/home/magetest/.ssh/keys/id_rsa.pub:ro - - ./sftp-test/upload:/home/magetest/upload - ports: - - "2222:22" - networks: - - mage.net +services: + # ... + mage-server: + volumes: + - ./docker/sftp/key/id_rsa:/run/secrets/mage/sftp_key:ro + environment: + MAGE_SFTP_KEY_FILE: /run/secrets/mage/sftp_key + command: + - --plugin + - @ngageoint/mage.sftp.service + - --web-plugin + - @ngageoint/mage.sftp.web + + # service that provides the sftp endpoint for mage-server + mage-sftp: + image: atmoz/sftp + command: mage::1001 + volumes: + - ./docker/sftp/key/id_rsa.pub:/home/mage/.ssh/keys/id_rsa.pub:ro + - ./docker/sftp/upload:/home/mage/upload + ports: + - "2222:22" + networks: + - mage.net ``` Uploaded zip archives will appear in `sftp-test/upload/` on your host machine. -### 3. Verify the Dockerfile has the SFTP build stages active - -The SFTP plugin has two build stages in the `Dockerfile` that must be present and uncommented — one for the service plugin and one for the web UI plugin: +### 3. Verify the Dockerfile builds SFTP packages +The [`Dockerfile`](../../Dockerfile) should contain two uncommented `RUN` blocks that build the SFTP service and +web-app packages: ```dockerfile -FROM node:20.11.1 AS build-sftpserviceplugin -... -FROM node:20.11.1 AS build-sftpwebplugin -... +RUN cd ${MAGE_SERVER}/plugins/sftp/service +# ... +RUN cd ${MAGE_SERVER}/plugins/sftp/web +# ... ``` - -Both stages should already be present. Check that the corresponding `COPY` and `RUN npm install` lines in the `build-instance` stage are also active. +Both stages should already be present. Check that the corresponding `COPY` and `RUN npm install` lines in the +`build-instance` stage are also active. ### 4. Build and start ```bash docker compose up --build -d ``` +This starts at least three containers: `mage-db`, `mage-server`, and `mage-sftp`. Verify the containers are running +using the following command. +```bash +docker compose ps +``` -This starts three containers: `mage-db`, `mage-server`, and `mage-sftp`. - -### 5. Upload the private key and configure in the admin UI +### 5. Activate the plugin -1. Open `mage` and log in -2. Navigate to **Admin → Menu → SFTP** -3. Under **SFTP Client Options**, upload the private key (`sftp-test/id_rsa`) -4. Fill in the connection details for the local test server: +1. Navigate to the Mage web app. +1. Click the icon at the upper right to navigate to the admin page. +1. Click the _SFTP_ tab on the left tab strip. +1. Under **SFTP Client Options**, upload the private key. +1. Fill in the connection details for the local test server: - **Host**: `mage-sftp` - **Port**: `22` - - **Username**: `magetest` + - **Username**: `mage` - **Path**: `/upload` -5. Click **Test Connection** to verify -6. Toggle **Enabled** to `true` and save +1. Click **Test Connection** to verify. +1. Toggle **Enabled** to `true` and save. > The remote path (`/upload` in this example) must already exist on the SFTP server. The plugin will not create it automatically. ### Connecting to a real SFTP server -Replace the `mage-sftp` connection details with your server's host, port, username, and the remote directory path. Generate or obtain a key pair accepted by that server and upload the private key via the admin UI. The public key must be added to the server's `authorized_keys` (or equivalent) for the configured user. +Replace the `mage-sftp` connection details with your server's host, port, username, and the remote directory path. +Generate or obtain a key pair accepted by that server and upload the private key via the admin UI. The public key must +be added to the server's `authorized_keys` (or equivalent) for the configured user. --- diff --git a/plugins/sftp/service/src/index.ts b/plugins/sftp/service/src/index.ts index e4d6b8bee..2b0496935 100644 --- a/plugins/sftp/service/src/index.ts +++ b/plugins/sftp/service/src/index.ts @@ -49,7 +49,7 @@ const sftpPluginHooks: InitPluginHook = { getDbConnection: MongooseDbConnectionToken }, init: async (services): Promise => { - console.info('intializing sftp plugin') + console.info('initializing sftp plugin') const { stateRepository, eventRepository, observationRepository, userRepository, attachmentStore, getDbConnection } = services const dbConnection: mongoose.Connection = await getDbConnection(); @@ -206,4 +206,4 @@ const sftpPluginHooks: InitPluginHook = { } } -export = sftpPluginHooks \ No newline at end of file +export = sftpPluginHooks diff --git a/service/npm-shrinkwrap.json b/service/npm-shrinkwrap.json index 289385a88..37456742f 100644 --- a/service/npm-shrinkwrap.json +++ b/service/npm-shrinkwrap.json @@ -42,7 +42,6 @@ "moment": "2.30.1", "mongoose": "8.24.1", "multer": "2.2.0", - "node-fetch": "2.7.0", "passport": "0.7.0", "passport-anonymous": "1.0.1", "passport-http-bearer": "1.0.1", @@ -7931,48 +7930,6 @@ "node": ">=10" } }, - "node_modules/node-fetch": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", - "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", - "license": "MIT", - "dependencies": { - "whatwg-url": "^5.0.0" - }, - "engines": { - "node": "4.x || >=6.0.0" - }, - "peerDependencies": { - "encoding": "^0.1.0" - }, - "peerDependenciesMeta": { - "encoding": { - "optional": true - } - } - }, - "node_modules/node-fetch/node_modules/tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", - "license": "MIT" - }, - "node_modules/node-fetch/node_modules/webidl-conversions": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", - "license": "BSD-2-Clause" - }, - "node_modules/node-fetch/node_modules/whatwg-url": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", - "license": "MIT", - "dependencies": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" - } - }, "node_modules/normalize-package-data": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", diff --git a/service/package.json b/service/package.json index c8d7facdf..5e2123da9 100644 --- a/service/package.json +++ b/service/package.json @@ -63,7 +63,6 @@ "moment": "2.30.1", "mongoose": "8.24.1", "multer": "2.2.0", - "node-fetch": "2.7.0", "passport": "0.7.0", "passport-anonymous": "1.0.1", "passport-http-bearer": "1.0.1", diff --git a/service/src/adapters/icons/adapters.icons.db.mongoose.ts b/service/src/adapters/icons/adapters.icons.db.mongoose.ts index ea185f2bd..75206c53b 100644 --- a/service/src/adapters/icons/adapters.icons.db.mongoose.ts +++ b/service/src/adapters/icons/adapters.icons.db.mongoose.ts @@ -163,9 +163,10 @@ export class MongooseStaticIconRepository extends BaseMongooseRepository x.canResolve(sourceUrl)) diff --git a/service/src/api/form.js b/service/src/api/form.js index 7a93f73f9..57a8ffe64 100644 --- a/service/src/api/form.js +++ b/service/src/api/form.js @@ -45,7 +45,7 @@ Form.prototype.populateUserFields = function (callback) { var event = this._event; var form = this._form; - var forms = form ? [form] : event.forms; + var forms = form ? [form] : (event.forms || []); var formsUserFields = []; forms.forEach(function (form) { var userFields = getUserFields(form); diff --git a/service/src/app.impl/exports/app.impl.exports.ts b/service/src/app.impl/exports/app.impl.exports.ts index 14e5705ea..ec488c546 100644 --- a/service/src/app.impl/exports/app.impl.exports.ts +++ b/service/src/app.impl/exports/app.impl.exports.ts @@ -120,6 +120,12 @@ export function CreateExport( } } await exportsRepository.updateExportForUser(newExport.id, user.id, update) + log.info(`${(user as { username?: string }).username ?? user.id} exported data`, { + format: req.format, + event: req.context.mageEvent.name, + observations: result.observations?.count ?? 0, + locations: result.locations?.count ?? 0 + }) }).catch(async (err) => { log.error('Export error', err) await contentStore.deleteContent(newExport) diff --git a/service/src/app.impl/observations/app.impl.observations.ts b/service/src/app.impl/observations/app.impl.observations.ts index 1998bd388..bc1effe7e 100644 --- a/service/src/app.impl/observations/app.impl.observations.ts +++ b/service/src/app.impl/observations/app.impl.observations.ts @@ -13,6 +13,14 @@ import { AttachmentHook } from '../../plugins.api/plugins.api.attachments' const pipeline = util.promisify(stream.pipeline) +function principalIdentifier(req: api.SaveObservationRequest): string { + const principal = req.context.requestingPrincipal() as { id?: unknown, username?: unknown } | null | undefined + if (principal && typeof principal === 'object') { + return String(principal.username ?? principal.id ?? 'unknown') + } + return 'unknown' +} + export function AllocateObservationId(permissionService: api.ObservationPermissionService): api.AllocateObservationId { return async function allocateObservationId(req: api.AllocateObservationIdRequest): ReturnType { const denied = await permissionService.ensureCreateObservationPermission(req.context) @@ -25,7 +33,7 @@ export function AllocateObservationId(permissionService: api.ObservationPermissi } } -export function SaveObservation(permissionService: api.ObservationPermissionService, userRepo: UserRepository): api.SaveObservation { +export function SaveObservation(permissionService: api.ObservationPermissionService, userRepo: UserRepository, log: Logger = NoopLogger): api.SaveObservation { return async function saveObservation(req: api.SaveObservationRequest): ReturnType { const repo = req.context.observationRepository const mod = req.observation @@ -42,6 +50,9 @@ export function SaveObservation(permissionService: api.ObservationPermissionServ } const saved = await repo.save(obs) if (saved instanceof Observation) { + if (!existingObservation) { + log.info(`${principalIdentifier(req)} added observation: ${saved.id}`) + } const userIds = { creator: saved.userId, importantFlagger: saved.important?.userId } const userIdsLookup = Object.values(userIds).filter(x => !!x) as UserId[] const usersFound = userIdsLookup.length ? await userRepo.findAllByIds(userIdsLookup) : {} @@ -70,8 +81,8 @@ export function StoreAttachmentContent(permissionService: api.ObservationPermiss return AppResponse.error(entityNotFound(req.attachmentId, 'Attachment')) } const content = req.content - if (content.mediaType !== attachmentBefore.contentType || content.name !== attachmentBefore.name) { - const errorMessage = `attachment upload error - uploaded content name and media type ${content.name}|${content.mediaType} must match attachment ${attachmentBefore.name}|${attachmentBefore.contentType}` + if (content.mediaType !== attachmentBefore.contentType) { + const errorMessage = `attachment upload error - uploaded media type ${content.mediaType} must match attachment ${attachmentBefore.contentType}` return AppResponse.error(invalidInput(errorMessage)) } const denied = await permissionService.ensureStoreAttachmentContentPermission(req.context, obsBefore, attachmentBefore.id) diff --git a/service/src/app.ts b/service/src/app.ts index 6949973d0..d28587d3d 100644 --- a/service/src/app.ts +++ b/service/src/app.ts @@ -964,7 +964,8 @@ async function initObservationsAppLayer( ), saveObservation: observationsImpl.SaveObservation( obsPermissionsService, - repos.users.userRepo + repos.users.userRepo, + log.child({ component: 'observations' }) ), storeAttachmentContent: observationsImpl.StoreAttachmentContent( obsPermissionsService, diff --git a/service/src/entities/icons/entities.icons.ts b/service/src/entities/icons/entities.icons.ts index 3876144ef..a0691adc0 100644 --- a/service/src/entities/icons/entities.icons.ts +++ b/service/src/entities/icons/entities.icons.ts @@ -32,7 +32,7 @@ export interface StaticIcon { resolvedTimestamp?: number imageType?: 'raster' | 'vector' /** - * The icons's media type is a standard [IANA media/MIME](https://www.iana.org/assignments/media-types/media-types.xhtml) + * The icon's media type is a standard [IANA media/MIME](https://www.iana.org/assignments/media-types/media-types.xhtml) * type strings, such as `image/jpeg`. */ mediaType?: string diff --git a/service/src/models/icon.d.ts b/service/src/models/icon.d.ts index 9e0e72c53..8a3f176fe 100644 --- a/service/src/models/icon.d.ts +++ b/service/src/models/icon.d.ts @@ -21,4 +21,9 @@ export declare class IconDocument extends mongoose.Document implements IconAttrs constructor(attrs: Partial) } -export declare function getAll(options: { eventId?: number, formId?: number }, callback: (err: any | null, icons?: IconDocument[]) => any): void \ No newline at end of file +export declare function getAll(options: { eventId?: number, formId?: number }, callback: (err: any | null, icons?: IconDocument[]) => any): void + +export declare function getIcon(options: { primary?: string, variant?: string, eventId?: number, formId?: number}, + callback: (err: any | null, icon?: IconDocument) => any): void + +export declare const Model: mongoose.Model \ No newline at end of file diff --git a/service/src/routes/events.ts b/service/src/routes/events.ts index 0164de2b2..424c4c195 100644 --- a/service/src/routes/events.ts +++ b/service/src/routes/events.ts @@ -4,6 +4,7 @@ const userTransformer = require('../transformers/user') import async from 'async' import util from 'util' import fileType from 'file-type' +import mimetypes from 'mime-types' import EventModel, { FormDocument, FormSubdocumentModelInstance, MageEventDocument } from '../models/event' import express from 'express' import access from '../access' @@ -525,6 +526,10 @@ function EventRoutes(app: express.Application, security: { authentication: authe } res.format({ 'image/*': function () { + const iconContentType = mimetypes.lookup(icon.path); + if (iconContentType) { + res.type(iconContentType); + } res.sendFile(icon.path); }, 'application/json': function () { diff --git a/service/src/routes/users.js b/service/src/routes/users.js index c043bf317..210263d1a 100644 --- a/service/src/routes/users.js +++ b/service/src/routes/users.js @@ -306,6 +306,13 @@ module.exports = function(app, security) { } }; + log.info('account signup requested', { + username: user.username, + displayName: user.displayName, + email: user.email, + phones: user.phones + }); + new api.User() .create(user) .then(newUser => { diff --git a/service/test/adapters/icons/adapters.icons.db.mongoose.test.ts b/service/test/adapters/icons/adapters.icons.db.mongoose.test.ts index 396f77320..edfe8912c 100644 --- a/service/test/adapters/icons/adapters.icons.db.mongoose.test.ts +++ b/service/test/adapters/icons/adapters.icons.db.mongoose.test.ts @@ -739,19 +739,14 @@ describe('static icon mongoose repository', function () { contentStore.didNotReceive().loadContent(Arg.all()) }) - it('throws error if the icon url is invalid', async function () { + it('returns a url resolution error if the icon url is invalid', async function () { const iconId = uniqid() await model.create({ _id: iconId, sourceUrl: 'shall not pass', registeredTimestamp: Date.now() }) - try { - await repo.loadContent(iconId) as UrlResolutionError - } - catch (err: any) { - expect(err).to.be.instanceOf(Error) - expect(err.message).to.contain('Invalid URL') - return - } - expect.fail('expected error to be thrown') + const content = await repo.loadContent(iconId) as UrlResolutionError + + expect(content).to.be.instanceOf(UrlResolutionError) + expect(content.message).to.contain(`registered icon ${iconId} has an invalid source url`) }) it('loads content from url if the source url scheme is local', async function () { diff --git a/service/test/app/observations/app.observations.test.ts b/service/test/app/observations/app.observations.test.ts index a16aa9234..a7bcd4685 100644 --- a/service/test/app/observations/app.observations.test.ts +++ b/service/test/app/observations/app.observations.test.ts @@ -1835,7 +1835,7 @@ describe('observations use case interactions', function() { obsRepo.didNotReceive().save(Arg.all()) }) - it('fails if the request content name does not match', async function() { + it('disregards non-matching request content name', async function() { const attachment = obs.attachments[0] const bytesBuffer = Buffer.from('photo of something') @@ -1851,15 +1851,17 @@ describe('observations use case interactions', function() { attachmentId: attachment.id, content, } + const attachmentPatch: AttachmentContentPatchAttrs = Object.freeze({ contentLocator: uniqid(), size: 887766 }) + const afterStore = patchAttachment(obs, attachment.id, attachmentPatch) as Observation obsRepo.findById(obs.id).resolves(obs) + store.saveContent(bytes, req.attachmentId, obs).resolves(attachmentPatch) + obsRepo.patchAttachment(Arg.all()).resolves(afterStore) const res = await storeAttachmentContent(req) - const err = res.error as EntityNotFoundError - expect(res.success).to.be.null - expect(err).to.be.instanceOf(MageError) - expect(err.code).to.equal(ErrInvalidInput) - store.didNotReceive().saveContent(Arg.all()) - obsRepo.didNotReceive().save(Arg.all()) + expect(res.error).to.be.null + expect(res.success).to.exist + store.received(1).saveContent(Arg.all()) + obsRepo.received(1).patchAttachment(Arg.all()) }) it('fails if the request media type does not match', async function() { diff --git a/web-app/package-lock.json b/web-app/package-lock.json index de4ab0926..2c9c4a1fe 100644 --- a/web-app/package-lock.json +++ b/web-app/package-lock.json @@ -41,7 +41,7 @@ "leaflet": "1.9.4", "leaflet-editable": "1.3.2", "leaflet.markercluster": "1.5.3", - "lodash": "4.17.21", + "lodash": "4.18.1", "mgrs": "1.0.0", "moment": "2.30.1", "ngx-color": "8.0.3", @@ -15344,9 +15344,9 @@ } }, "node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz", + "integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==", "license": "MIT" }, "node_modules/lodash-es": { diff --git a/web-app/package.json b/web-app/package.json index 555dfcb3c..23525c29d 100644 --- a/web-app/package.json +++ b/web-app/package.json @@ -85,7 +85,7 @@ "leaflet": "1.9.4", "leaflet-editable": "1.3.2", "leaflet.markercluster": "1.5.3", - "lodash": "4.17.21", + "lodash": "4.18.1", "mgrs": "1.0.0", "moment": "2.30.1", "ngx-color": "8.0.3",