Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

improve(laboratory): validate editor content with TypeScript #6476

Open
wants to merge 12 commits into
base: main
Choose a base branch
from

Conversation

jasonkuhrt
Copy link
Member

@jasonkuhrt jasonkuhrt commented Feb 5, 2025

Background

This brings TypeScript validation to the laboratory editor so that users have a better experience writing code, because the compiler will catch more of their mistakes for them.

Description

Monaco React has built in support for this so not much doing was needed.

CleanShot 2025-02-05 at 15 50 00@2x

@jasonkuhrt
Copy link
Member Author

jasonkuhrt commented Feb 5, 2025

Draft because there are no tests yet.

@jasonkuhrt jasonkuhrt requested review from jdolle and n1ru4l February 5, 2025 20:51
Copy link
Contributor

github-actions bot commented Feb 5, 2025

📚 Storybook Deployment

The latest changes are available as preview in: https://039f4fc2.hive-storybook.pages.dev

Copy link
Contributor

github-actions bot commented Feb 5, 2025

🐋 This PR was built and pushed to the following Docker images:

Targets: build

Platforms: linux/arm64

Image Tag: ebe9be3990d1597cbe8fe62869d9a000f1ac751f

Docker Bake metadata
{
"app": {
  "buildx.build.provenance": {
    "buildType": "https://mobyproject.org/buildkit@v1",
    "materials": [
      {
        "uri": "pkg:docker/[email protected]?platform=linux%2Farm64",
        "digest": {
          "sha256": "f5a0871ab03b035c58bdb3007c3d177b001c2145c18e81817b71624dcf7d8bff"
        }
      }
    ],
    "invocation": {
      "configSource": {
        "entryPoint": "services.dockerfile"
      },
      "parameters": {
        "frontend": "dockerfile.v0",
        "args": {
          "build-arg:HEALTHCHECK_CMD": "wget --spider -q http://127.0.0.1:${PORT}/api/health",
          "build-arg:IMAGE_DESCRIPTION": "The app of the GraphQL Hive project.",
          "build-arg:IMAGE_TITLE": "graphql-hive/app",
          "build-arg:PORT": "3000",
          "build-arg:RELEASE": "ebe9be3990d1597cbe8fe62869d9a000f1ac751f",
          "build-arg:SERVICE_DIR_NAME": "@hive/app",
          "context:dist": "local:dist",
          "context:shared": "local:shared",
          "frontend.caps": "moby.buildkit.frontend.contexts+forward",
          "local-sessionid:context": "nqqyrvjz465mpn6ocxkgpb1mu",
          "local-sessionid:dockerfile": "nqqyrvjz465mpn6ocxkgpb1mu",
          "local-sessionid:shared": "nqqyrvjz465mpn6ocxkgpb1mu"
        },
        "locals": [
          {
            "name": "context"
          },
          {
            "name": "dist"
          },
          {
            "name": "dockerfile"
          },
          {
            "name": "shared"
          }
        ]
      },
      "environment": {
        "platform": "linux/arm64"
      }
    }
  },
  "buildx.build.ref": "builder-5b941d15-1932-4436-bc2b-bd94ca461f03/builder-5b941d15-1932-4436-bc2b-bd94ca461f030/ro66433ngj95t5xi7bzu0ts3b",
  "containerimage.config.digest": "sha256:d2bcb3ddfd79aa0c79ec70b403633e6cd104869bbd4557c8ba93ff3592166db4",
  "containerimage.descriptor": {
    "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
    "digest": "sha256:210d16afffa66988a00b6fe70ed0c02a13701fd6e921d5a37574bb76fcc72a7f",
    "size": 2075,
    "platform": {
      "architecture": "arm64",
      "os": "linux"
    }
  },
  "containerimage.digest": "sha256:210d16afffa66988a00b6fe70ed0c02a13701fd6e921d5a37574bb76fcc72a7f",
  "image.name": "ghcr.io/graphql-hive/app:ebe9be3990d1597cbe8fe62869d9a000f1ac751f-arm64,ghcr.io/graphql-hive/app:console_1003-arm64"
},
"buildx.build.warnings": [
  {
    "vertex": "sha256:3c5087129863cca940b0056dbb435f61c5bb863b47557c5fa5a66269e337a9b1",
    "level": 1,
    "short": "VW5kZWZpbmVkVmFyOiBVc2FnZSBvZiB1bmRlZmluZWQgdmFyaWFibGUgJyRSRUxFQVNFJyAobGluZSAxMyk=",
    "detail": [
      "VmFyaWFibGVzIHNob3VsZCBiZSBkZWZpbmVkIGJlZm9yZSB0aGVpciB1c2U="
    ],
    "url": "https://docs.docker.com/go/dockerfile/rule/undefined-var/",
    "sourceInfo": {
      "filename": "services.dockerfile",
      "data": "RlJPTSBub2RlOjIyLjEzLjAtc2xpbQoKUlVOIGFwdC1nZXQgdXBkYXRlICYmIGFwdC1nZXQgaW5zdGFsbCAteSB3Z2V0IGNhLWNlcnRpZmljYXRlcyAmJiBybSAtcmYgL3Zhci9saWIvYXB0L2xpc3RzLyoKCkFSRyBTRVJWSUNFX0RJUl9OQU1FCldPUktESVIgL3Vzci9zcmMvYXBwLyRTRVJWSUNFX0RJUl9OQU1FCgpDT1BZIC0tZnJvbT1kaXN0IC4gL3Vzci9zcmMvYXBwLyRTRVJWSUNFX0RJUl9OQU1FLwpDT1BZIC0tZnJvbT1zaGFyZWQgLiAvCgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UubGljZW5zZXM9TUlUCkxBQkVMIG9yZy5vcGVuY29udGFpbmVycy5pbWFnZS50aXRsZT0kSU1BR0VfVElUTEUKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLnZlcnNpb249JFJFTEVBU0UKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLmRlc2NyaXB0aW9uPSRJTUFHRV9ERVNDUklQVElPTgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UuYXV0aG9ycz0iVGhlIEd1aWxkIgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UudmVuZG9yPSJLYW1pbCBLaXNpZWxhIgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UudXJsPSJodHRwczovL2dpdGh1Yi5jb20vZ3JhcGhxbC1oaXZlL3BsYXRmb3JtIgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2Uuc291cmNlPSJodHRwczovL2dpdGh1Yi5jb20vZ3JhcGhxbC1oaXZlL3BsYXRmb3JtIgoKRU5WIEVOVklST05NRU5UIHByb2R1Y3Rpb24KRU5WIFJFTEVBU0UgJFJFTEVBU0UKRU5WIFBPUlQgJFBPUlQKCkhFQUxUSENIRUNLIC0taW50ZXJ2YWw9NXMgXAogIC0tdGltZW91dD01cyBcCiAgLS1zdGFydC1wZXJpb2Q9NXMgXAogIC0tcmV0cmllcz02IFwKICBDTUQgJEhFQUxUSENIRUNLX0NNRAoKRU5UUllQT0lOVCBbICIvZW50cnlwb2ludC5zaCIgXQo=",
      "language": "Dockerfile"
    },
    "range": [
      {
        "start": {
          "line": 13
        },
        "end": {
          "line": 13
        }
      }
    ]
  },
  {
    "vertex": "sha256:3c5087129863cca940b0056dbb435f61c5bb863b47557c5fa5a66269e337a9b1",
    "level": 1,
    "short": "VW5kZWZpbmVkVmFyOiBVc2FnZSBvZiB1bmRlZmluZWQgdmFyaWFibGUgJyRJTUFHRV9ERVNDUklQVElPTicgKGxpbmUgMTQp",
    "detail": [
      "VmFyaWFibGVzIHNob3VsZCBiZSBkZWZpbmVkIGJlZm9yZSB0aGVpciB1c2U="
    ],
    "url": "https://docs.docker.com/go/dockerfile/rule/undefined-var/",
    "sourceInfo": {
      "filename": "services.dockerfile",
      "data": "RlJPTSBub2RlOjIyLjEzLjAtc2xpbQoKUlVOIGFwdC1nZXQgdXBkYXRlICYmIGFwdC1nZXQgaW5zdGFsbCAteSB3Z2V0IGNhLWNlcnRpZmljYXRlcyAmJiBybSAtcmYgL3Zhci9saWIvYXB0L2xpc3RzLyoKCkFSRyBTRVJWSUNFX0RJUl9OQU1FCldPUktESVIgL3Vzci9zcmMvYXBwLyRTRVJWSUNFX0RJUl9OQU1FCgpDT1BZIC0tZnJvbT1kaXN0IC4gL3Vzci9zcmMvYXBwLyRTRVJWSUNFX0RJUl9OQU1FLwpDT1BZIC0tZnJvbT1zaGFyZWQgLiAvCgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UubGljZW5zZXM9TUlUCkxBQkVMIG9yZy5vcGVuY29udGFpbmVycy5pbWFnZS50aXRsZT0kSU1BR0VfVElUTEUKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLnZlcnNpb249JFJFTEVBU0UKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLmRlc2NyaXB0aW9uPSRJTUFHRV9ERVNDUklQVElPTgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UuYXV0aG9ycz0iVGhlIEd1aWxkIgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UudmVuZG9yPSJLYW1pbCBLaXNpZWxhIgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UudXJsPSJodHRwczovL2dpdGh1Yi5jb20vZ3JhcGhxbC1oaXZlL3BsYXRmb3JtIgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2Uuc291cmNlPSJodHRwczovL2dpdGh1Yi5jb20vZ3JhcGhxbC1oaXZlL3BsYXRmb3JtIgoKRU5WIEVOVklST05NRU5UIHByb2R1Y3Rpb24KRU5WIFJFTEVBU0UgJFJFTEVBU0UKRU5WIFBPUlQgJFBPUlQKCkhFQUxUSENIRUNLIC0taW50ZXJ2YWw9NXMgXAogIC0tdGltZW91dD01cyBcCiAgLS1zdGFydC1wZXJpb2Q9NXMgXAogIC0tcmV0cmllcz02IFwKICBDTUQgJEhFQUxUSENIRUNLX0NNRAoKRU5UUllQT0lOVCBbICIvZW50cnlwb2ludC5zaCIgXQo=",
      "language": "Dockerfile"
    },
    "range": [
      {
        "start": {
          "line": 14
        },
        "end": {
          "line": 14
        }
      }
    ]
  },
  {
    "vertex": "sha256:3c5087129863cca940b0056dbb435f61c5bb863b47557c5fa5a66269e337a9b1",
    "level": 1,
    "short": "VW5kZWZpbmVkVmFyOiBVc2FnZSBvZiB1bmRlZmluZWQgdmFyaWFibGUgJyRQT1JUJyAobGluZSAyMik=",
    "detail": [
      "VmFyaWFibGVzIHNob3VsZCBiZSBkZWZpbmVkIGJlZm9yZSB0aGVpciB1c2U="
    ],
    "url": "https://docs.docker.com/go/dockerfile/rule/undefined-var/",
    "sourceInfo": {
      "filename": "services.dockerfile",
      "data": "RlJPTSBub2RlOjIyLjEzLjAtc2xpbQoKUlVOIGFwdC1nZXQgdXBkYXRlICYmIGFwdC1nZXQgaW5zdGFsbCAteSB3Z2V0IGNhLWNlcnRpZmljYXRlcyAmJiBybSAtcmYgL3Zhci9saWIvYXB0L2xpc3RzLyoKCkFSRyBTRVJWSUNFX0RJUl9OQU1FCldPUktESVIgL3Vzci9zcmMvYXBwLyRTRVJWSUNFX0RJUl9OQU1FCgpDT1BZIC0tZnJvbT1kaXN0IC4gL3Vzci9zcmMvYXBwLyRTRVJWSUNFX0RJUl9OQU1FLwpDT1BZIC0tZnJvbT1zaGFyZWQgLiAvCgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UubGljZW5zZXM9TUlUCkxBQkVMIG9yZy5vcGVuY29udGFpbmVycy5pbWFnZS50aXRsZT0kSU1BR0VfVElUTEUKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLnZlcnNpb249JFJFTEVBU0UKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLmRlc2NyaXB0aW9uPSRJTUFHRV9ERVNDUklQVElPTgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UuYXV0aG9ycz0iVGhlIEd1aWxkIgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UudmVuZG9yPSJLYW1pbCBLaXNpZWxhIgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UudXJsPSJodHRwczovL2dpdGh1Yi5jb20vZ3JhcGhxbC1oaXZlL3BsYXRmb3JtIgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2Uuc291cmNlPSJodHRwczovL2dpdGh1Yi5jb20vZ3JhcGhxbC1oaXZlL3BsYXRmb3JtIgoKRU5WIEVOVklST05NRU5UIHByb2R1Y3Rpb24KRU5WIFJFTEVBU0UgJFJFTEVBU0UKRU5WIFBPUlQgJFBPUlQKCkhFQUxUSENIRUNLIC0taW50ZXJ2YWw9NXMgXAogIC0tdGltZW91dD01cyBcCiAgLS1zdGFydC1wZXJpb2Q9NXMgXAogIC0tcmV0cmllcz02IFwKICBDTUQgJEhFQUxUSENIRUNLX0NNRAoKRU5UUllQT0lOVCBbICIvZW50cnlwb2ludC5zaCIgXQo=",
      "language": "Dockerfile"
    },
    "range": [
      {
        "start": {
          "line": 22
        },
        "end": {
          "line": 22
        }
      }
    ]
  },
  {
    "vertex": "sha256:57248d07ea1e3bf6065526c28942441717372a5dfc14c736cb22a7bae6ce3fb6",
    "level": 1,
    "short": "VW5kZWZpbmVkVmFyOiBVc2FnZSBvZiB1bmRlZmluZWQgdmFyaWFibGUgJyRSRUxFQVNFJyAobGluZSAxMik=",
    "detail": [
      "VmFyaWFibGVzIHNob3VsZCBiZSBkZWZpbmVkIGJlZm9yZSB0aGVpciB1c2U="
    ],
    "url": "https://docs.docker.com/go/dockerfile/rule/undefined-var/",
    "sourceInfo": {
      "filename": "migrations.dockerfile",
      "data": "RlJPTSBub2RlOjIyLjEzLjAtc2xpbQoKUlVOIGFwdC1nZXQgdXBkYXRlICYmIGFwdC1nZXQgaW5zdGFsbCAteSBjYS1jZXJ0aWZpY2F0ZXMKCldPUktESVIgL3Vzci9zcmMvYXBwCgpDT1BZIC0tZnJvbT1kaXN0IC4gL3Vzci9zcmMvYXBwLwpDT1BZIC0tZnJvbT1zaGFyZWQgLiAvCgpFTlYgRU5WSVJPTk1FTlQgcHJvZHVjdGlvbgpFTlYgTk9ERV9FTlYgcHJvZHVjdGlvbgpFTlYgUkVMRUFTRSAkUkVMRUFTRQoKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLmxpY2Vuc2VzPU1JVApMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UudGl0bGU9JElNQUdFX1RJVExFCkxBQkVMIG9yZy5vcGVuY29udGFpbmVycy5pbWFnZS52ZXJzaW9uPSRSRUxFQVNFCkxBQkVMIG9yZy5vcGVuY29udGFpbmVycy5pbWFnZS5kZXNjcmlwdGlvbj0kSU1BR0VfREVTQ1JJUFRJT04KTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLmF1dGhvcnM9IlRoZSBHdWlsZCIKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLnZlbmRvcj0iS2FtaWwgS2lzaWVsYSIKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLnVybD0iaHR0cHM6Ly9naXRodWIuY29tL2dyYXBocWwtaGl2ZS9wbGF0Zm9ybSIKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLnNvdXJjZT0iaHR0cHM6Ly9naXRodWIuY29tL2dyYXBocWwtaGl2ZS9wbGF0Zm9ybSIKCkVOVFJZUE9JTlQgWyAiL2VudHJ5cG9pbnQuc2giIF0K",
      "language": "Dockerfile"
    },
    "range": [
      {
        "start": {
          "line": 12
        },
        "end": {
          "line": 12
        }
      }
    ]
  },
  {
    "vertex": "sha256:3c5087129863cca940b0056dbb435f61c5bb863b47557c5fa5a66269e337a9b1",
    "level": 1,
    "short": "TGVnYWN5S2V5VmFsdWVGb3JtYXQ6ICJFTlYga2V5PXZhbHVlIiBzaG91bGQgYmUgdXNlZCBpbnN0ZWFkIG9mIGxlZ2FjeSAiRU5WIGtleSB2YWx1ZSIgZm9ybWF0IChsaW5lIDIxKQ==",
    "detail": [
      "TGVnYWN5IGtleS92YWx1ZSBmb3JtYXQgd2l0aCB3aGl0ZXNwYWNlIHNlcGFyYXRvciBzaG91bGQgbm90IGJlIHVzZWQ="
    ],
    "url": "https://docs.docker.com/go/dockerfile/rule/legacy-key-value-format/",
    "sourceInfo": {
      "filename": "services.dockerfile",
      "data": "RlJPTSBub2RlOjIyLjEzLjAtc2xpbQoKUlVOIGFwdC1nZXQgdXBkYXRlICYmIGFwdC1nZXQgaW5zdGFsbCAteSB3Z2V0IGNhLWNlcnRpZmljYXRlcyAmJiBybSAtcmYgL3Zhci9saWIvYXB0L2xpc3RzLyoKCkFSRyBTRVJWSUNFX0RJUl9OQU1FCldPUktESVIgL3Vzci9zcmMvYXBwLyRTRVJWSUNFX0RJUl9OQU1FCgpDT1BZIC0tZnJvbT1kaXN0IC4gL3Vzci9zcmMvYXBwLyRTRVJWSUNFX0RJUl9OQU1FLwpDT1BZIC0tZnJvbT1zaGFyZWQgLiAvCgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UubGljZW5zZXM9TUlUCkxBQkVMIG9yZy5vcGVuY29udGFpbmVycy5pbWFnZS50aXRsZT0kSU1BR0VfVElUTEUKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLnZlcnNpb249JFJFTEVBU0UKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLmRlc2NyaXB0aW9uPSRJTUFHRV9ERVNDUklQVElPTgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UuYXV0aG9ycz0iVGhlIEd1aWxkIgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UudmVuZG9yPSJLYW1pbCBLaXNpZWxhIgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UudXJsPSJodHRwczovL2dpdGh1Yi5jb20vZ3JhcGhxbC1oaXZlL3BsYXRmb3JtIgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2Uuc291cmNlPSJodHRwczovL2dpdGh1Yi5jb20vZ3JhcGhxbC1oaXZlL3BsYXRmb3JtIgoKRU5WIEVOVklST05NRU5UIHByb2R1Y3Rpb24KRU5WIFJFTEVBU0UgJFJFTEVBU0UKRU5WIFBPUlQgJFBPUlQKCkhFQUxUSENIRUNLIC0taW50ZXJ2YWw9NXMgXAogIC0tdGltZW91dD01cyBcCiAgLS1zdGFydC1wZXJpb2Q9NXMgXAogIC0tcmV0cmllcz02IFwKICBDTUQgJEhFQUxUSENIRUNLX0NNRAoKRU5UUllQT0lOVCBbICIvZW50cnlwb2ludC5zaCIgXQo=",
      "language": "Dockerfile"
    },
    "range": [
      {
        "start": {
          "line": 21
        },
        "end": {
          "line": 21
        }
      }
    ]
  },
  {
    "vertex": "sha256:57248d07ea1e3bf6065526c28942441717372a5dfc14c736cb22a7bae6ce3fb6",
    "level": 1,
    "short": "TGVnYWN5S2V5VmFsdWVGb3JtYXQ6ICJFTlYga2V5PXZhbHVlIiBzaG91bGQgYmUgdXNlZCBpbnN0ZWFkIG9mIGxlZ2FjeSAiRU5WIGtleSB2YWx1ZSIgZm9ybWF0IChsaW5lIDEwKQ==",
    "detail": [
      "TGVnYWN5IGtleS92YWx1ZSBmb3JtYXQgd2l0aCB3aGl0ZXNwYWNlIHNlcGFyYXRvciBzaG91bGQgbm90IGJlIHVzZWQ="
    ],
    "url": "https://docs.docker.com/go/dockerfile/rule/legacy-key-value-format/",
    "sourceInfo": {
      "filename": "migrations.dockerfile",
      "data": "RlJPTSBub2RlOjIyLjEzLjAtc2xpbQoKUlVOIGFwdC1nZXQgdXBkYXRlICYmIGFwdC1nZXQgaW5zdGFsbCAteSBjYS1jZXJ0aWZpY2F0ZXMKCldPUktESVIgL3Vzci9zcmMvYXBwCgpDT1BZIC0tZnJvbT1kaXN0IC4gL3Vzci9zcmMvYXBwLwpDT1BZIC0tZnJvbT1zaGFyZWQgLiAvCgpFTlYgRU5WSVJPTk1FTlQgcHJvZHVjdGlvbgpFTlYgTk9ERV9FTlYgcHJvZHVjdGlvbgpFTlYgUkVMRUFTRSAkUkVMRUFTRQoKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLmxpY2Vuc2VzPU1JVApMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UudGl0bGU9JElNQUdFX1RJVExFCkxBQkVMIG9yZy5vcGVuY29udGFpbmVycy5pbWFnZS52ZXJzaW9uPSRSRUxFQVNFCkxBQkVMIG9yZy5vcGVuY29udGFpbmVycy5pbWFnZS5kZXNjcmlwdGlvbj0kSU1BR0VfREVTQ1JJUFRJT04KTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLmF1dGhvcnM9IlRoZSBHdWlsZCIKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLnZlbmRvcj0iS2FtaWwgS2lzaWVsYSIKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLnVybD0iaHR0cHM6Ly9naXRodWIuY29tL2dyYXBocWwtaGl2ZS9wbGF0Zm9ybSIKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLnNvdXJjZT0iaHR0cHM6Ly9naXRodWIuY29tL2dyYXBocWwtaGl2ZS9wbGF0Zm9ybSIKCkVOVFJZUE9JTlQgWyAiL2VudHJ5cG9pbnQuc2giIF0K",
      "language": "Dockerfile"
    },
    "range": [
      {
        "start": {
          "line": 10
        },
        "end": {
          "line": 10
        }
      }
    ]
  },
  {
    "vertex": "sha256:57248d07ea1e3bf6065526c28942441717372a5dfc14c736cb22a7bae6ce3fb6",
    "level": 1,
    "short": "TGVnYWN5S2V5VmFsdWVGb3JtYXQ6ICJFTlYga2V5PXZhbHVlIiBzaG91bGQgYmUgdXNlZCBpbnN0ZWFkIG9mIGxlZ2FjeSAiRU5WIGtleSB2YWx1ZSIgZm9ybWF0IChsaW5lIDExKQ==",
    "detail": [
      "TGVnYWN5IGtleS92YWx1ZSBmb3JtYXQgd2l0aCB3aGl0ZXNwYWNlIHNlcGFyYXRvciBzaG91bGQgbm90IGJlIHVzZWQ="
    ],
    "url": "https://docs.docker.com/go/dockerfile/rule/legacy-key-value-format/",
    "sourceInfo": {
      "filename": "migrations.dockerfile",
      "data": "RlJPTSBub2RlOjIyLjEzLjAtc2xpbQoKUlVOIGFwdC1nZXQgdXBkYXRlICYmIGFwdC1nZXQgaW5zdGFsbCAteSBjYS1jZXJ0aWZpY2F0ZXMKCldPUktESVIgL3Vzci9zcmMvYXBwCgpDT1BZIC0tZnJvbT1kaXN0IC4gL3Vzci9zcmMvYXBwLwpDT1BZIC0tZnJvbT1zaGFyZWQgLiAvCgpFTlYgRU5WSVJPTk1FTlQgcHJvZHVjdGlvbgpFTlYgTk9ERV9FTlYgcHJvZHVjdGlvbgpFTlYgUkVMRUFTRSAkUkVMRUFTRQoKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLmxpY2Vuc2VzPU1JVApMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UudGl0bGU9JElNQUdFX1RJVExFCkxBQkVMIG9yZy5vcGVuY29udGFpbmVycy5pbWFnZS52ZXJzaW9uPSRSRUxFQVNFCkxBQkVMIG9yZy5vcGVuY29udGFpbmVycy5pbWFnZS5kZXNjcmlwdGlvbj0kSU1BR0VfREVTQ1JJUFRJT04KTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLmF1dGhvcnM9IlRoZSBHdWlsZCIKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLnZlbmRvcj0iS2FtaWwgS2lzaWVsYSIKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLnVybD0iaHR0cHM6Ly9naXRodWIuY29tL2dyYXBocWwtaGl2ZS9wbGF0Zm9ybSIKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLnNvdXJjZT0iaHR0cHM6Ly9naXRodWIuY29tL2dyYXBocWwtaGl2ZS9wbGF0Zm9ybSIKCkVOVFJZUE9JTlQgWyAiL2VudHJ5cG9pbnQuc2giIF0K",
      "language": "Dockerfile"
    },
    "range": [
      {
        "start": {
          "line": 11
        },
        "end": {
          "line": 11
        }
      }
    ]
  },
  {
    "vertex": "sha256:57248d07ea1e3bf6065526c28942441717372a5dfc14c736cb22a7bae6ce3fb6",
    "level": 1,
    "short": "VW5kZWZpbmVkVmFyOiBVc2FnZSBvZiB1bmRlZmluZWQgdmFyaWFibGUgJyRJTUFHRV9USVRMRScgKGxpbmUgMTUp",
    "detail": [
      "VmFyaWFibGVzIHNob3VsZCBiZSBkZWZpbmVkIGJlZm9yZSB0aGVpciB1c2U="
    ],
    "url": "https://docs.docker.com/go/dockerfile/rule/undefined-var/",
    "sourceInfo": {
      "filename": "migrations.dockerfile",
      "data": "RlJPTSBub2RlOjIyLjEzLjAtc2xpbQoKUlVOIGFwdC1nZXQgdXBkYXRlICYmIGFwdC1nZXQgaW5zdGFsbCAteSBjYS1jZXJ0aWZpY2F0ZXMKCldPUktESVIgL3Vzci9zcmMvYXBwCgpDT1BZIC0tZnJvbT1kaXN0IC4gL3Vzci9zcmMvYXBwLwpDT1BZIC0tZnJvbT1zaGFyZWQgLiAvCgpFTlYgRU5WSVJPTk1FTlQgcHJvZHVjdGlvbgpFTlYgTk9ERV9FTlYgcHJvZHVjdGlvbgpFTlYgUkVMRUFTRSAkUkVMRUFTRQoKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLmxpY2Vuc2VzPU1JVApMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UudGl0bGU9JElNQUdFX1RJVExFCkxBQkVMIG9yZy5vcGVuY29udGFpbmVycy5pbWFnZS52ZXJzaW9uPSRSRUxFQVNFCkxBQkVMIG9yZy5vcGVuY29udGFpbmVycy5pbWFnZS5kZXNjcmlwdGlvbj0kSU1BR0VfREVTQ1JJUFRJT04KTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLmF1dGhvcnM9IlRoZSBHdWlsZCIKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLnZlbmRvcj0iS2FtaWwgS2lzaWVsYSIKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLnVybD0iaHR0cHM6Ly9naXRodWIuY29tL2dyYXBocWwtaGl2ZS9wbGF0Zm9ybSIKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLnNvdXJjZT0iaHR0cHM6Ly9naXRodWIuY29tL2dyYXBocWwtaGl2ZS9wbGF0Zm9ybSIKCkVOVFJZUE9JTlQgWyAiL2VudHJ5cG9pbnQuc2giIF0K",
      "language": "Dockerfile"
    },
    "range": [
      {
        "start": {
          "line": 15
        },
        "end": {
          "line": 15
        }
      }
    ]
  },
  {
    "vertex": "sha256:3c5087129863cca940b0056dbb435f61c5bb863b47557c5fa5a66269e337a9b1",
    "level": 1,
    "short": "VW5kZWZpbmVkVmFyOiBVc2FnZSBvZiB1bmRlZmluZWQgdmFyaWFibGUgJyRJTUFHRV9USVRMRScgKGxpbmUgMTIp",
    "detail": [
      "VmFyaWFibGVzIHNob3VsZCBiZSBkZWZpbmVkIGJlZm9yZSB0aGVpciB1c2U="
    ],
    "url": "https://docs.docker.com/go/dockerfile/rule/undefined-var/",
    "sourceInfo": {
      "filename": "services.dockerfile",
      "data": "RlJPTSBub2RlOjIyLjEzLjAtc2xpbQoKUlVOIGFwdC1nZXQgdXBkYXRlICYmIGFwdC1nZXQgaW5zdGFsbCAteSB3Z2V0IGNhLWNlcnRpZmljYXRlcyAmJiBybSAtcmYgL3Zhci9saWIvYXB0L2xpc3RzLyoKCkFSRyBTRVJWSUNFX0RJUl9OQU1FCldPUktESVIgL3Vzci9zcmMvYXBwLyRTRVJWSUNFX0RJUl9OQU1FCgpDT1BZIC0tZnJvbT1kaXN0IC4gL3Vzci9zcmMvYXBwLyRTRVJWSUNFX0RJUl9OQU1FLwpDT1BZIC0tZnJvbT1zaGFyZWQgLiAvCgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UubGljZW5zZXM9TUlUCkxBQkVMIG9yZy5vcGVuY29udGFpbmVycy5pbWFnZS50aXRsZT0kSU1BR0VfVElUTEUKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLnZlcnNpb249JFJFTEVBU0UKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLmRlc2NyaXB0aW9uPSRJTUFHRV9ERVNDUklQVElPTgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UuYXV0aG9ycz0iVGhlIEd1aWxkIgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UudmVuZG9yPSJLYW1pbCBLaXNpZWxhIgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UudXJsPSJodHRwczovL2dpdGh1Yi5jb20vZ3JhcGhxbC1oaXZlL3BsYXRmb3JtIgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2Uuc291cmNlPSJodHRwczovL2dpdGh1Yi5jb20vZ3JhcGhxbC1oaXZlL3BsYXRmb3JtIgoKRU5WIEVOVklST05NRU5UIHByb2R1Y3Rpb24KRU5WIFJFTEVBU0UgJFJFTEVBU0UKRU5WIFBPUlQgJFBPUlQKCkhFQUxUSENIRUNLIC0taW50ZXJ2YWw9NXMgXAogIC0tdGltZW91dD01cyBcCiAgLS1zdGFydC1wZXJpb2Q9NXMgXAogIC0tcmV0cmllcz02IFwKICBDTUQgJEhFQUxUSENIRUNLX0NNRAoKRU5UUllQT0lOVCBbICIvZW50cnlwb2ludC5zaCIgXQo=",
      "language": "Dockerfile"
    },
    "range": [
      {
        "start": {
          "line": 12
        },
        "end": {
          "line": 12
        }
      }
    ]
  },
  {
    "vertex": "sha256:3c5087129863cca940b0056dbb435f61c5bb863b47557c5fa5a66269e337a9b1",
    "level": 1,
    "short": "VW5kZWZpbmVkVmFyOiBVc2FnZSBvZiB1bmRlZmluZWQgdmFyaWFibGUgJyRSRUxFQVNFJyAobGluZSAyMSk=",
    "detail": [
      "VmFyaWFibGVzIHNob3VsZCBiZSBkZWZpbmVkIGJlZm9yZSB0aGVpciB1c2U="
    ],
    "url": "https://docs.docker.com/go/dockerfile/rule/undefined-var/",
    "sourceInfo": {
      "filename": "services.dockerfile",
      "data": "RlJPTSBub2RlOjIyLjEzLjAtc2xpbQoKUlVOIGFwdC1nZXQgdXBkYXRlICYmIGFwdC1nZXQgaW5zdGFsbCAteSB3Z2V0IGNhLWNlcnRpZmljYXRlcyAmJiBybSAtcmYgL3Zhci9saWIvYXB0L2xpc3RzLyoKCkFSRyBTRVJWSUNFX0RJUl9OQU1FCldPUktESVIgL3Vzci9zcmMvYXBwLyRTRVJWSUNFX0RJUl9OQU1FCgpDT1BZIC0tZnJvbT1kaXN0IC4gL3Vzci9zcmMvYXBwLyRTRVJWSUNFX0RJUl9OQU1FLwpDT1BZIC0tZnJvbT1zaGFyZWQgLiAvCgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UubGljZW5zZXM9TUlUCkxBQkVMIG9yZy5vcGVuY29udGFpbmVycy5pbWFnZS50aXRsZT0kSU1BR0VfVElUTEUKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLnZlcnNpb249JFJFTEVBU0UKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLmRlc2NyaXB0aW9uPSRJTUFHRV9ERVNDUklQVElPTgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UuYXV0aG9ycz0iVGhlIEd1aWxkIgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UudmVuZG9yPSJLYW1pbCBLaXNpZWxhIgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UudXJsPSJodHRwczovL2dpdGh1Yi5jb20vZ3JhcGhxbC1oaXZlL3BsYXRmb3JtIgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2Uuc291cmNlPSJodHRwczovL2dpdGh1Yi5jb20vZ3JhcGhxbC1oaXZlL3BsYXRmb3JtIgoKRU5WIEVOVklST05NRU5UIHByb2R1Y3Rpb24KRU5WIFJFTEVBU0UgJFJFTEVBU0UKRU5WIFBPUlQgJFBPUlQKCkhFQUxUSENIRUNLIC0taW50ZXJ2YWw9NXMgXAogIC0tdGltZW91dD01cyBcCiAgLS1zdGFydC1wZXJpb2Q9NXMgXAogIC0tcmV0cmllcz02IFwKICBDTUQgJEhFQUxUSENIRUNLX0NNRAoKRU5UUllQT0lOVCBbICIvZW50cnlwb2ludC5zaCIgXQo=",
      "language": "Dockerfile"
    },
    "range": [
      {
        "start": {
          "line": 21
        },
        "end": {
          "line": 21
        }
      }
    ]
  },
  {
    "vertex": "sha256:3c5087129863cca940b0056dbb435f61c5bb863b47557c5fa5a66269e337a9b1",
    "level": 1,
    "short": "TGVnYWN5S2V5VmFsdWVGb3JtYXQ6ICJFTlYga2V5PXZhbHVlIiBzaG91bGQgYmUgdXNlZCBpbnN0ZWFkIG9mIGxlZ2FjeSAiRU5WIGtleSB2YWx1ZSIgZm9ybWF0IChsaW5lIDIyKQ==",
    "detail": [
      "TGVnYWN5IGtleS92YWx1ZSBmb3JtYXQgd2l0aCB3aGl0ZXNwYWNlIHNlcGFyYXRvciBzaG91bGQgbm90IGJlIHVzZWQ="
    ],
    "url": "https://docs.docker.com/go/dockerfile/rule/legacy-key-value-format/",
    "sourceInfo": {
      "filename": "services.dockerfile",
      "data": "RlJPTSBub2RlOjIyLjEzLjAtc2xpbQoKUlVOIGFwdC1nZXQgdXBkYXRlICYmIGFwdC1nZXQgaW5zdGFsbCAteSB3Z2V0IGNhLWNlcnRpZmljYXRlcyAmJiBybSAtcmYgL3Zhci9saWIvYXB0L2xpc3RzLyoKCkFSRyBTRVJWSUNFX0RJUl9OQU1FCldPUktESVIgL3Vzci9zcmMvYXBwLyRTRVJWSUNFX0RJUl9OQU1FCgpDT1BZIC0tZnJvbT1kaXN0IC4gL3Vzci9zcmMvYXBwLyRTRVJWSUNFX0RJUl9OQU1FLwpDT1BZIC0tZnJvbT1zaGFyZWQgLiAvCgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UubGljZW5zZXM9TUlUCkxBQkVMIG9yZy5vcGVuY29udGFpbmVycy5pbWFnZS50aXRsZT0kSU1BR0VfVElUTEUKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLnZlcnNpb249JFJFTEVBU0UKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLmRlc2NyaXB0aW9uPSRJTUFHRV9ERVNDUklQVElPTgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UuYXV0aG9ycz0iVGhlIEd1aWxkIgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UudmVuZG9yPSJLYW1pbCBLaXNpZWxhIgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UudXJsPSJodHRwczovL2dpdGh1Yi5jb20vZ3JhcGhxbC1oaXZlL3BsYXRmb3JtIgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2Uuc291cmNlPSJodHRwczovL2dpdGh1Yi5jb20vZ3JhcGhxbC1oaXZlL3BsYXRmb3JtIgoKRU5WIEVOVklST05NRU5UIHByb2R1Y3Rpb24KRU5WIFJFTEVBU0UgJFJFTEVBU0UKRU5WIFBPUlQgJFBPUlQKCkhFQUxUSENIRUNLIC0taW50ZXJ2YWw9NXMgXAogIC0tdGltZW91dD01cyBcCiAgLS1zdGFydC1wZXJpb2Q9NXMgXAogIC0tcmV0cmllcz02IFwKICBDTUQgJEhFQUxUSENIRUNLX0NNRAoKRU5UUllQT0lOVCBbICIvZW50cnlwb2ludC5zaCIgXQo=",
      "language": "Dockerfile"
    },
    "range": [
      {
        "start": {
          "line": 22
        },
        "end": {
          "line": 22
        }
      }
    ]
  },
  {
    "vertex": "sha256:57248d07ea1e3bf6065526c28942441717372a5dfc14c736cb22a7bae6ce3fb6",
    "level": 1,
    "short": "TGVnYWN5S2V5VmFsdWVGb3JtYXQ6ICJFTlYga2V5PXZhbHVlIiBzaG91bGQgYmUgdXNlZCBpbnN0ZWFkIG9mIGxlZ2FjeSAiRU5WIGtleSB2YWx1ZSIgZm9ybWF0IChsaW5lIDEyKQ==",
    "detail": [
      "TGVnYWN5IGtleS92YWx1ZSBmb3JtYXQgd2l0aCB3aGl0ZXNwYWNlIHNlcGFyYXRvciBzaG91bGQgbm90IGJlIHVzZWQ="
    ],
    "url": "https://docs.docker.com/go/dockerfile/rule/legacy-key-value-format/",
    "sourceInfo": {
      "filename": "migrations.dockerfile",
      "data": "RlJPTSBub2RlOjIyLjEzLjAtc2xpbQoKUlVOIGFwdC1nZXQgdXBkYXRlICYmIGFwdC1nZXQgaW5zdGFsbCAteSBjYS1jZXJ0aWZpY2F0ZXMKCldPUktESVIgL3Vzci9zcmMvYXBwCgpDT1BZIC0tZnJvbT1kaXN0IC4gL3Vzci9zcmMvYXBwLwpDT1BZIC0tZnJvbT1zaGFyZWQgLiAvCgpFTlYgRU5WSVJPTk1FTlQgcHJvZHVjdGlvbgpFTlYgTk9ERV9FTlYgcHJvZHVjdGlvbgpFTlYgUkVMRUFTRSAkUkVMRUFTRQoKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLmxpY2Vuc2VzPU1JVApMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UudGl0bGU9JElNQUdFX1RJVExFCkxBQkVMIG9yZy5vcGVuY29udGFpbmVycy5pbWFnZS52ZXJzaW9uPSRSRUxFQVNFCkxBQkVMIG9yZy5vcGVuY29udGFpbmVycy5pbWFnZS5kZXNjcmlwdGlvbj0kSU1BR0VfREVTQ1JJUFRJT04KTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLmF1dGhvcnM9IlRoZSBHdWlsZCIKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLnZlbmRvcj0iS2FtaWwgS2lzaWVsYSIKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLnVybD0iaHR0cHM6Ly9naXRodWIuY29tL2dyYXBocWwtaGl2ZS9wbGF0Zm9ybSIKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLnNvdXJjZT0iaHR0cHM6Ly9naXRodWIuY29tL2dyYXBocWwtaGl2ZS9wbGF0Zm9ybSIKCkVOVFJZUE9JTlQgWyAiL2VudHJ5cG9pbnQuc2giIF0K",
      "language": "Dockerfile"
    },
    "range": [
      {
        "start": {
          "line": 12
        },
        "end": {
          "line": 12
        }
      }
    ]
  },
  {
    "vertex": "sha256:3c5087129863cca940b0056dbb435f61c5bb863b47557c5fa5a66269e337a9b1",
    "level": 1,
    "short": "TGVnYWN5S2V5VmFsdWVGb3JtYXQ6ICJFTlYga2V5PXZhbHVlIiBzaG91bGQgYmUgdXNlZCBpbnN0ZWFkIG9mIGxlZ2FjeSAiRU5WIGtleSB2YWx1ZSIgZm9ybWF0IChsaW5lIDIwKQ==",
    "detail": [
      "TGVnYWN5IGtleS92YWx1ZSBmb3JtYXQgd2l0aCB3aGl0ZXNwYWNlIHNlcGFyYXRvciBzaG91bGQgbm90IGJlIHVzZWQ="
    ],
    "url": "https://docs.docker.com/go/dockerfile/rule/legacy-key-value-format/",
    "sourceInfo": {
      "filename": "services.dockerfile",
      "data": "RlJPTSBub2RlOjIyLjEzLjAtc2xpbQoKUlVOIGFwdC1nZXQgdXBkYXRlICYmIGFwdC1nZXQgaW5zdGFsbCAteSB3Z2V0IGNhLWNlcnRpZmljYXRlcyAmJiBybSAtcmYgL3Zhci9saWIvYXB0L2xpc3RzLyoKCkFSRyBTRVJWSUNFX0RJUl9OQU1FCldPUktESVIgL3Vzci9zcmMvYXBwLyRTRVJWSUNFX0RJUl9OQU1FCgpDT1BZIC0tZnJvbT1kaXN0IC4gL3Vzci9zcmMvYXBwLyRTRVJWSUNFX0RJUl9OQU1FLwpDT1BZIC0tZnJvbT1zaGFyZWQgLiAvCgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UubGljZW5zZXM9TUlUCkxBQkVMIG9yZy5vcGVuY29udGFpbmVycy5pbWFnZS50aXRsZT0kSU1BR0VfVElUTEUKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLnZlcnNpb249JFJFTEVBU0UKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLmRlc2NyaXB0aW9uPSRJTUFHRV9ERVNDUklQVElPTgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UuYXV0aG9ycz0iVGhlIEd1aWxkIgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UudmVuZG9yPSJLYW1pbCBLaXNpZWxhIgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UudXJsPSJodHRwczovL2dpdGh1Yi5jb20vZ3JhcGhxbC1oaXZlL3BsYXRmb3JtIgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2Uuc291cmNlPSJodHRwczovL2dpdGh1Yi5jb20vZ3JhcGhxbC1oaXZlL3BsYXRmb3JtIgoKRU5WIEVOVklST05NRU5UIHByb2R1Y3Rpb24KRU5WIFJFTEVBU0UgJFJFTEVBU0UKRU5WIFBPUlQgJFBPUlQKCkhFQUxUSENIRUNLIC0taW50ZXJ2YWw9NXMgXAogIC0tdGltZW91dD01cyBcCiAgLS1zdGFydC1wZXJpb2Q9NXMgXAogIC0tcmV0cmllcz02IFwKICBDTUQgJEhFQUxUSENIRUNLX0NNRAoKRU5UUllQT0lOVCBbICIvZW50cnlwb2ludC5zaCIgXQo=",
      "language": "Dockerfile"
    },
    "range": [
      {
        "start": {
          "line": 20
        },
        "end": {
          "line": 20
        }
      }
    ]
  },
  {
    "vertex": "sha256:57248d07ea1e3bf6065526c28942441717372a5dfc14c736cb22a7bae6ce3fb6",
    "level": 1,
    "short": "VW5kZWZpbmVkVmFyOiBVc2FnZSBvZiB1bmRlZmluZWQgdmFyaWFibGUgJyRJTUFHRV9ERVNDUklQVElPTicgKGxpbmUgMTcp",
    "detail": [
      "VmFyaWFibGVzIHNob3VsZCBiZSBkZWZpbmVkIGJlZm9yZSB0aGVpciB1c2U="
    ],
    "url": "https://docs.docker.com/go/dockerfile/rule/undefined-var/",
    "sourceInfo": {
      "filename": "migrations.dockerfile",
      "data": "RlJPTSBub2RlOjIyLjEzLjAtc2xpbQoKUlVOIGFwdC1nZXQgdXBkYXRlICYmIGFwdC1nZXQgaW5zdGFsbCAteSBjYS1jZXJ0aWZpY2F0ZXMKCldPUktESVIgL3Vzci9zcmMvYXBwCgpDT1BZIC0tZnJvbT1kaXN0IC4gL3Vzci9zcmMvYXBwLwpDT1BZIC0tZnJvbT1zaGFyZWQgLiAvCgpFTlYgRU5WSVJPTk1FTlQgcHJvZHVjdGlvbgpFTlYgTk9ERV9FTlYgcHJvZHVjdGlvbgpFTlYgUkVMRUFTRSAkUkVMRUFTRQoKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLmxpY2Vuc2VzPU1JVApMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UudGl0bGU9JElNQUdFX1RJVExFCkxBQkVMIG9yZy5vcGVuY29udGFpbmVycy5pbWFnZS52ZXJzaW9uPSRSRUxFQVNFCkxBQkVMIG9yZy5vcGVuY29udGFpbmVycy5pbWFnZS5kZXNjcmlwdGlvbj0kSU1BR0VfREVTQ1JJUFRJT04KTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLmF1dGhvcnM9IlRoZSBHdWlsZCIKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLnZlbmRvcj0iS2FtaWwgS2lzaWVsYSIKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLnVybD0iaHR0cHM6Ly9naXRodWIuY29tL2dyYXBocWwtaGl2ZS9wbGF0Zm9ybSIKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLnNvdXJjZT0iaHR0cHM6Ly9naXRodWIuY29tL2dyYXBocWwtaGl2ZS9wbGF0Zm9ybSIKCkVOVFJZUE9JTlQgWyAiL2VudHJ5cG9pbnQuc2giIF0K",
      "language": "Dockerfile"
    },
    "range": [
      {
        "start": {
          "line": 17
        },
        "end": {
          "line": 17
        }
      }
    ]
  }
],
"composition-federation-2": {
  "buildx.build.provenance": {
    "buildType": "https://mobyproject.org/buildkit@v1",
    "materials": [
      {
        "uri": "pkg:docker/[email protected]?platform=linux%2Farm64",
        "digest": {
          "sha256": "f5a0871ab03b035c58bdb3007c3d177b001c2145c18e81817b71624dcf7d8bff"
        }
      }
    ],
    "invocation": {
      "configSource": {
        "entryPoint": "services.dockerfile"
      },
      "parameters": {
        "frontend": "dockerfile.v0",
        "args": {
          "build-arg:HEALTHCHECK_CMD": "wget --spider -q http://127.0.0.1:${PORT}/_readiness",
          "build-arg:IMAGE_DESCRIPTION": "Federation 2 Composition Service for GraphQL Hive.",
          "build-arg:IMAGE_TITLE": "graphql-hive/composition-federation-2",
          "build-arg:PORT": "3069",
          "build-arg:RELEASE": "ebe9be3990d1597cbe8fe62869d9a000f1ac751f",
          "build-arg:SERVICE_DIR_NAME": "@hive/external-composition",
          "context:dist": "local:dist",
          "context:shared": "local:shared",
          "frontend.caps": "moby.buildkit.frontend.contexts+forward",
          "local-sessionid:context": "nqqyrvjz465mpn6ocxkgpb1mu",
          "local-sessionid:dockerfile": "nqqyrvjz465mpn6ocxkgpb1mu",
          "local-sessionid:shared": "nqqyrvjz465mpn6ocxkgpb1mu"
        },
        "locals": [
          {
            "name": "context"
          },
          {
            "name": "dist"
          },
          {
            "name": "dockerfile"
          },
          {
            "name": "shared"
          }
        ]
      },
      "environment": {
        "platform": "linux/arm64"
      }
    }
  },
  "buildx.build.ref": "builder-5b941d15-1932-4436-bc2b-bd94ca461f03/builder-5b941d15-1932-4436-bc2b-bd94ca461f030/2b5i8iom0iqsl0draa3njsy7c",
  "containerimage.config.digest": "sha256:198eda3f253e9c4df078e344b01737e9e6b8bf0b5db6652d523a998aba87f580",
  "containerimage.descriptor": {
    "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
    "digest": "sha256:d8c88fe12dddacfe920d725ec21d37df157b9b7102572ddde4c5a5ec067f2e31",
    "size": 2075,
    "platform": {
      "architecture": "arm64",
      "os": "linux"
    }
  },
  "containerimage.digest": "sha256:d8c88fe12dddacfe920d725ec21d37df157b9b7102572ddde4c5a5ec067f2e31",
  "image.name": "ghcr.io/graphql-hive/composition-federation-2:ebe9be3990d1597cbe8fe62869d9a000f1ac751f-arm64,ghcr.io/graphql-hive/composition-federation-2:console_1003-arm64"
},
"emails": {
  "buildx.build.provenance": {
    "buildType": "https://mobyproject.org/buildkit@v1",
    "materials": [
      {
        "uri": "pkg:docker/[email protected]?platform=linux%2Farm64",
        "digest": {
          "sha256": "f5a0871ab03b035c58bdb3007c3d177b001c2145c18e81817b71624dcf7d8bff"
        }
      }
    ],
    "invocation": {
      "configSource": {
        "entryPoint": "services.dockerfile"
      },
      "parameters": {
        "frontend": "dockerfile.v0",
        "args": {
          "build-arg:HEALTHCHECK_CMD": "wget --spider -q http://127.0.0.1:${PORT}/_readiness",
          "build-arg:IMAGE_DESCRIPTION": "The emails service of the GraphQL Hive project.",
          "build-arg:IMAGE_TITLE": "graphql-hive/emails",
          "build-arg:PORT": "3006",
          "build-arg:RELEASE": "ebe9be3990d1597cbe8fe62869d9a000f1ac751f",
          "build-arg:SERVICE_DIR_NAME": "@hive/emails",
          "context:dist": "local:dist",
          "context:shared": "local:shared",
          "frontend.caps": "moby.buildkit.frontend.contexts+forward",
          "local-sessionid:context": "nqqyrvjz465mpn6ocxkgpb1mu",
          "local-sessionid:dockerfile": "nqqyrvjz465mpn6ocxkgpb1mu",
          "local-sessionid:shared": "nqqyrvjz465mpn6ocxkgpb1mu"
        },
        "locals": [
          {
            "name": "context"
          },
          {
            "name": "dist"
          },
          {
            "name": "dockerfile"
          },
          {
            "name": "shared"
          }
        ]
      },
      "environment": {
        "platform": "linux/arm64"
      }
    }
  },
  "buildx.build.ref": "builder-5b941d15-1932-4436-bc2b-bd94ca461f03/builder-5b941d15-1932-4436-bc2b-bd94ca461f030/efj6b5izxdmjkhicgkk1rez1r",
  "containerimage.config.digest": "sha256:28783635b253ba9d1dccba7a62ecdefc742fd42d1380cf005fdf253cf92281b1",
  "containerimage.descriptor": {
    "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
    "digest": "sha256:b529090b1f1f8038014063dea094f25b37f34352589d26fba2e3f2703d2bead5",
    "size": 2075,
    "platform": {
      "architecture": "arm64",
      "os": "linux"
    }
  },
  "containerimage.digest": "sha256:b529090b1f1f8038014063dea094f25b37f34352589d26fba2e3f2703d2bead5",
  "image.name": "ghcr.io/graphql-hive/emails:ebe9be3990d1597cbe8fe62869d9a000f1ac751f-arm64,ghcr.io/graphql-hive/emails:console_1003-arm64"
},
"policy": {
  "buildx.build.provenance": {
    "buildType": "https://mobyproject.org/buildkit@v1",
    "materials": [
      {
        "uri": "pkg:docker/[email protected]?platform=linux%2Farm64",
        "digest": {
          "sha256": "f5a0871ab03b035c58bdb3007c3d177b001c2145c18e81817b71624dcf7d8bff"
        }
      }
    ],
    "invocation": {
      "configSource": {
        "entryPoint": "services.dockerfile"
      },
      "parameters": {
        "frontend": "dockerfile.v0",
        "args": {
          "build-arg:HEALTHCHECK_CMD": "wget --spider -q http://127.0.0.1:${PORT}/_readiness",
          "build-arg:IMAGE_DESCRIPTION": "The policy service of the GraphQL Hive project.",
          "build-arg:IMAGE_TITLE": "graphql-hive/policy",
          "build-arg:PORT": "3012",
          "build-arg:RELEASE": "ebe9be3990d1597cbe8fe62869d9a000f1ac751f",
          "build-arg:SERVICE_DIR_NAME": "@hive/policy",
          "context:dist": "local:dist",
          "context:shared": "local:shared",
          "frontend.caps": "moby.buildkit.frontend.contexts+forward",
          "local-sessionid:context": "nqqyrvjz465mpn6ocxkgpb1mu",
          "local-sessionid:dockerfile": "nqqyrvjz465mpn6ocxkgpb1mu",
          "local-sessionid:shared": "nqqyrvjz465mpn6ocxkgpb1mu"
        },
        "locals": [
          {
            "name": "context"
          },
          {
            "name": "dist"
          },
          {
            "name": "dockerfile"
          },
          {
            "name": "shared"
          }
        ]
      },
      "environment": {
        "platform": "linux/arm64"
      }
    }
  },
  "buildx.build.ref": "builder-5b941d15-1932-4436-bc2b-bd94ca461f03/builder-5b941d15-1932-4436-bc2b-bd94ca461f030/kxlfmejqu9e4al6sp6xzuv41p",
  "containerimage.config.digest": "sha256:ba1cc7a70f13836ae683a76a98aab562076ac7173ac2681cad86b5f46eede0fd",
  "containerimage.descriptor": {
    "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
    "digest": "sha256:e3618cdd0ce9087d08497ac3076fd5117beaa07efbc7e31c8e739e4ba20d89b6",
    "size": 2075,
    "platform": {
      "architecture": "arm64",
      "os": "linux"
    }
  },
  "containerimage.digest": "sha256:e3618cdd0ce9087d08497ac3076fd5117beaa07efbc7e31c8e739e4ba20d89b6",
  "image.name": "ghcr.io/graphql-hive/policy:ebe9be3990d1597cbe8fe62869d9a000f1ac751f-arm64,ghcr.io/graphql-hive/policy:console_1003-arm64"
},
"rate-limit": {
  "buildx.build.provenance": {
    "buildType": "https://mobyproject.org/buildkit@v1",
    "materials": [
      {
        "uri": "pkg:docker/[email protected]?platform=linux%2Farm64",
        "digest": {
          "sha256": "f5a0871ab03b035c58bdb3007c3d177b001c2145c18e81817b71624dcf7d8bff"
        }
      }
    ],
    "invocation": {
      "configSource": {
        "entryPoint": "services.dockerfile"
      },
      "parameters": {
        "frontend": "dockerfile.v0",
        "args": {
          "build-arg:HEALTHCHECK_CMD": "wget --spider -q http://127.0.0.1:${PORT}/_readiness",
          "build-arg:IMAGE_DESCRIPTION": "The rate limit service of the GraphQL Hive project.",
          "build-arg:IMAGE_TITLE": "graphql-hive/rate-limit",
          "build-arg:PORT": "3009",
          "build-arg:RELEASE": "ebe9be3990d1597cbe8fe62869d9a000f1ac751f",
          "build-arg:SERVICE_DIR_NAME": "@hive/rate-limit",
          "context:dist": "local:dist",
          "context:shared": "local:shared",
          "frontend.caps": "moby.buildkit.frontend.contexts+forward",
          "local-sessionid:context": "nqqyrvjz465mpn6ocxkgpb1mu",
          "local-sessionid:dockerfile": "nqqyrvjz465mpn6ocxkgpb1mu",
          "local-sessionid:shared": "nqqyrvjz465mpn6ocxkgpb1mu"
        },
        "locals": [
          {
            "name": "context"
          },
          {
            "name": "dist"
          },
          {
            "name": "dockerfile"
          },
          {
            "name": "shared"
          }
        ]
      },
      "environment": {
        "platform": "linux/arm64"
      }
    }
  },
  "buildx.build.ref": "builder-5b941d15-1932-4436-bc2b-bd94ca461f03/builder-5b941d15-1932-4436-bc2b-bd94ca461f030/tf9o4pow7p2nuwvw1861oqjv0",
  "containerimage.config.digest": "sha256:7b3d9f0f562a27b7d21221b843ac7206b937ff6936cdf4fed90613f5ab175d41",
  "containerimage.descriptor": {
    "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
    "digest": "sha256:c2ec197585b48745e291b06096cb9d199e4f6487d897daa0842a5201090e70e6",
    "size": 2075,
    "platform": {
      "architecture": "arm64",
      "os": "linux"
    }
  },
  "containerimage.digest": "sha256:c2ec197585b48745e291b06096cb9d199e4f6487d897daa0842a5201090e70e6",
  "image.name": "ghcr.io/graphql-hive/rate-limit:ebe9be3990d1597cbe8fe62869d9a000f1ac751f-arm64,ghcr.io/graphql-hive/rate-limit:console_1003-arm64"
},
"schema": {
  "buildx.build.provenance": {
    "buildType": "https://mobyproject.org/buildkit@v1",
    "materials": [
      {
        "uri": "pkg:docker/[email protected]?platform=linux%2Farm64",
        "digest": {
          "sha256": "f5a0871ab03b035c58bdb3007c3d177b001c2145c18e81817b71624dcf7d8bff"
        }
      }
    ],
    "invocation": {
      "configSource": {
        "entryPoint": "services.dockerfile"
      },
      "parameters": {
        "frontend": "dockerfile.v0",
        "args": {
          "build-arg:HEALTHCHECK_CMD": "wget --spider -q http://127.0.0.1:${PORT}/_readiness",
          "build-arg:IMAGE_DESCRIPTION": "The schema service of the GraphQL Hive project.",
          "build-arg:IMAGE_TITLE": "graphql-hive/schema",
          "build-arg:PORT": "3002",
          "build-arg:RELEASE": "ebe9be3990d1597cbe8fe62869d9a000f1ac751f",
          "build-arg:SERVICE_DIR_NAME": "@hive/schema",
          "context:dist": "local:dist",
          "context:shared": "local:shared",
          "frontend.caps": "moby.buildkit.frontend.contexts+forward",
          "local-sessionid:context": "nqqyrvjz465mpn6ocxkgpb1mu",
          "local-sessionid:dockerfile": "nqqyrvjz465mpn6ocxkgpb1mu",
          "local-sessionid:shared": "nqqyrvjz465mpn6ocxkgpb1mu"
        },
        "locals": [
          {
            "name": "context"
          },
          {
            "name": "dist"
          },
          {
            "name": "dockerfile"
          },
          {
            "name": "shared"
          }
        ]
      },
      "environment": {
        "platform": "linux/arm64"
      }
    }
  },
  "buildx.build.ref": "builder-5b941d15-1932-4436-bc2b-bd94ca461f03/builder-5b941d15-1932-4436-bc2b-bd94ca461f030/9ewnzhzax41hn0vaq0bs130w1",
  "containerimage.config.digest": "sha256:6a8ddc598ff3ce3bf3c3da672a823e85b2763ab03d22fb9df86fb3bb80a18391",
  "containerimage.descriptor": {
    "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
    "digest": "sha256:149451aeac3ca971c93724bceb0081f7d1f70dcffffb293227e207a905a30867",
    "size": 2075,
    "platform": {
      "architecture": "arm64",
      "os": "linux"
    }
  },
  "containerimage.digest": "sha256:149451aeac3ca971c93724bceb0081f7d1f70dcffffb293227e207a905a30867",
  "image.name": "ghcr.io/graphql-hive/schema:ebe9be3990d1597cbe8fe62869d9a000f1ac751f-arm64,ghcr.io/graphql-hive/schema:console_1003-arm64"
},
"server": {
  "buildx.build.provenance": {
    "buildType": "https://mobyproject.org/buildkit@v1",
    "materials": [
      {
        "uri": "pkg:docker/[email protected]?platform=linux%2Farm64",
        "digest": {
          "sha256": "f5a0871ab03b035c58bdb3007c3d177b001c2145c18e81817b71624dcf7d8bff"
        }
      }
    ],
    "invocation": {
      "configSource": {
        "entryPoint": "services.dockerfile"
      },
      "parameters": {
        "frontend": "dockerfile.v0",
        "args": {
          "build-arg:HEALTHCHECK_CMD": "wget --spider -q http://127.0.0.1:${PORT}/_readiness",
          "build-arg:IMAGE_DESCRIPTION": "The server service of the GraphQL Hive project.",
          "build-arg:IMAGE_TITLE": "graphql-hive/server",
          "build-arg:PORT": "3001",
          "build-arg:RELEASE": "ebe9be3990d1597cbe8fe62869d9a000f1ac751f",
          "build-arg:SERVICE_DIR_NAME": "@hive/server",
          "context:dist": "local:dist",
          "context:shared": "local:shared",
          "frontend.caps": "moby.buildkit.frontend.contexts+forward",
          "local-sessionid:context": "nqqyrvjz465mpn6ocxkgpb1mu",
          "local-sessionid:dockerfile": "nqqyrvjz465mpn6ocxkgpb1mu",
          "local-sessionid:shared": "nqqyrvjz465mpn6ocxkgpb1mu"
        },
        "locals": [
          {
            "name": "context"
          },
          {
            "name": "dist"
          },
          {
            "name": "dockerfile"
          },
          {
            "name": "shared"
          }
        ]
      },
      "environment": {
        "platform": "linux/arm64"
      }
    }
  },
  "buildx.build.ref": "builder-5b941d15-1932-4436-bc2b-bd94ca461f03/builder-5b941d15-1932-4436-bc2b-bd94ca461f030/jzkoyphf06aq606s0j5w106kf",
  "containerimage.config.digest": "sha256:2d0f815ff3770afe798d34a0a3f5df2188febcc072f28f3d1ee9dd9d924682fb",
  "containerimage.descriptor": {
    "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
    "digest": "sha256:454cf5eae21f2e279e011bf237ea64a4bacb937b3677303fcf6c666946b15a58",
    "size": 2076,
    "platform": {
      "architecture": "arm64",
      "os": "linux"
    }
  },
  "containerimage.digest": "sha256:454cf5eae21f2e279e011bf237ea64a4bacb937b3677303fcf6c666946b15a58",
  "image.name": "ghcr.io/graphql-hive/server:ebe9be3990d1597cbe8fe62869d9a000f1ac751f-arm64,ghcr.io/graphql-hive/server:console_1003-arm64"
},
"storage": {
  "buildx.build.provenance": {
    "buildType": "https://mobyproject.org/buildkit@v1",
    "materials": [
      {
        "uri": "pkg:docker/[email protected]?platform=linux%2Farm64",
        "digest": {
          "sha256": "f5a0871ab03b035c58bdb3007c3d177b001c2145c18e81817b71624dcf7d8bff"
        }
      }
    ],
    "invocation": {
      "configSource": {
        "entryPoint": "migrations.dockerfile"
      },
      "parameters": {
        "frontend": "dockerfile.v0",
        "args": {
          "build-arg:IMAGE_DESCRIPTION": "The migrations service of the GraphQL Hive project.",
          "build-arg:IMAGE_TITLE": "graphql-hive/storage",
          "build-arg:RELEASE": "ebe9be3990d1597cbe8fe62869d9a000f1ac751f",
          "context:dist": "local:dist",
          "context:shared": "local:shared",
          "frontend.caps": "moby.buildkit.frontend.contexts+forward",
          "local-sessionid:context": "nqqyrvjz465mpn6ocxkgpb1mu",
          "local-sessionid:dockerfile": "nqqyrvjz465mpn6ocxkgpb1mu",
          "local-sessionid:shared": "nqqyrvjz465mpn6ocxkgpb1mu"
        },
        "locals": [
          {
            "name": "context"
          },
          {
            "name": "dist"
          },
          {
            "name": "dockerfile"
          },
          {
            "name": "shared"
          }
        ]
      },
      "environment": {
        "platform": "linux/arm64"
      }
    }
  },
  "buildx.build.ref": "builder-5b941d15-1932-4436-bc2b-bd94ca461f03/builder-5b941d15-1932-4436-bc2b-bd94ca461f030/hzceqiepa0rakqcvodbyhr55j",
  "containerimage.config.digest": "sha256:f3b23b21edcdfbc535b6f0e126c401042a989aaee0509494627719cc5654513d",
  "containerimage.descriptor": {
    "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
    "digest": "sha256:6507bcd83ed043b4ca847aceaf2006b0f0f22fb13d26debb482b890171fe38d2",
    "size": 2075,
    "platform": {
      "architecture": "arm64",
      "os": "linux"
    }
  },
  "containerimage.digest": "sha256:6507bcd83ed043b4ca847aceaf2006b0f0f22fb13d26debb482b890171fe38d2",
  "image.name": "ghcr.io/graphql-hive/storage:ebe9be3990d1597cbe8fe62869d9a000f1ac751f-arm64,ghcr.io/graphql-hive/storage:console_1003-arm64"
},
"stripe-billing": {
  "buildx.build.provenance": {
    "buildType": "https://mobyproject.org/buildkit@v1",
    "materials": [
      {
        "uri": "pkg:docker/[email protected]?platform=linux%2Farm64",
        "digest": {
          "sha256": "f5a0871ab03b035c58bdb3007c3d177b001c2145c18e81817b71624dcf7d8bff"
        }
      }
    ],
    "invocation": {
      "configSource": {
        "entryPoint": "services.dockerfile"
      },
      "parameters": {
        "frontend": "dockerfile.v0",
        "args": {
          "build-arg:HEALTHCHECK_CMD": "wget --spider -q http://127.0.0.1:${PORT}/_readiness",
          "build-arg:IMAGE_DESCRIPTION": "The stripe billing service of the GraphQL Hive project.",
          "build-arg:IMAGE_TITLE": "graphql-hive/stripe-billing",
          "build-arg:PORT": "3010",
          "build-arg:RELEASE": "ebe9be3990d1597cbe8fe62869d9a000f1ac751f",
          "build-arg:SERVICE_DIR_NAME": "@hive/stripe-billing",
          "context:dist": "local:dist",
          "context:shared": "local:shared",
          "frontend.caps": "moby.buildkit.frontend.contexts+forward",
          "local-sessionid:context": "nqqyrvjz465mpn6ocxkgpb1mu",
          "local-sessionid:dockerfile": "nqqyrvjz465mpn6ocxkgpb1mu",
          "local-sessionid:shared": "nqqyrvjz465mpn6ocxkgpb1mu"
        },
        "locals": [
          {
            "name": "context"
          },
          {
            "name": "dist"
          },
          {
            "name": "dockerfile"
          },
          {
            "name": "shared"
          }
        ]
      },
      "environment": {
        "platform": "linux/arm64"
      }
    }
  },
  "buildx.build.ref": "builder-5b941d15-1932-4436-bc2b-bd94ca461f03/builder-5b941d15-1932-4436-bc2b-bd94ca461f030/o268lzu2xgogumd9w7nm0a7ia",
  "containerimage.config.digest": "sha256:b66aa5cde8d6d2e38a2bc20721c07eb1294ddb5ea2365f5338afa8f5828d40b2",
  "containerimage.descriptor": {
    "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
    "digest": "sha256:3c0e621394c008b973a3f3a541453fc7055887fff34b180dad6ee6d81b1e6001",
    "size": 2075,
    "platform": {
      "architecture": "arm64",
      "os": "linux"
    }
  },
  "containerimage.digest": "sha256:3c0e621394c008b973a3f3a541453fc7055887fff34b180dad6ee6d81b1e6001",
  "image.name": "ghcr.io/graphql-hive/stripe-billing:ebe9be3990d1597cbe8fe62869d9a000f1ac751f-arm64,ghcr.io/graphql-hive/stripe-billing:console_1003-arm64"
},
"tokens": {
  "buildx.build.provenance": {
    "buildType": "https://mobyproject.org/buildkit@v1",
    "materials": [
      {
        "uri": "pkg:docker/[email protected]?platform=linux%2Farm64",
        "digest": {
          "sha256": "f5a0871ab03b035c58bdb3007c3d177b001c2145c18e81817b71624dcf7d8bff"
        }
      }
    ],
    "invocation": {
      "configSource": {
        "entryPoint": "services.dockerfile"
      },
      "parameters": {
        "frontend": "dockerfile.v0",
        "args": {
          "build-arg:HEALTHCHECK_CMD": "wget --spider -q http://127.0.0.1:${PORT}/_readiness",
          "build-arg:IMAGE_DESCRIPTION": "The tokens service of the GraphQL Hive project.",
          "build-arg:IMAGE_TITLE": "graphql-hive/tokens",
          "build-arg:PORT": "3003",
          "build-arg:RELEASE": "ebe9be3990d1597cbe8fe62869d9a000f1ac751f",
          "build-arg:SERVICE_DIR_NAME": "@hive/tokens",
          "context:dist": "local:dist",
          "context:shared": "local:shared",
          "frontend.caps": "moby.buildkit.frontend.contexts+forward",
          "local-sessionid:context": "nqqyrvjz465mpn6ocxkgpb1mu",
          "local-sessionid:dockerfile": "nqqyrvjz465mpn6ocxkgpb1mu",
          "local-sessionid:shared": "nqqyrvjz465mpn6ocxkgpb1mu"
        },
        "locals": [
          {
            "name": "context"
          },
          {
            "name": "dist"
          },
          {
            "name": "dockerfile"
          },
          {
            "name": "shared"
          }
        ]
      },
      "environment": {
        "platform": "linux/arm64"
      }
    }
  },
  "buildx.build.ref": "builder-5b941d15-1932-4436-bc2b-bd94ca461f03/builder-5b941d15-1932-4436-bc2b-bd94ca461f030/elkuzrnbzox6sey1k6no8nzz5",
  "containerimage.config.digest": "sha256:114d91e955bde2cb9993d6cdb0d4006f9bafa5da1491abe5c80bd77ac8b6b1b0",
  "containerimage.descriptor": {
    "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
    "digest": "sha256:13b68676cab5896f796735702ac22f8105783c8707276d67c29b543a9f6809e6",
    "size": 2075,
    "platform": {
      "architecture": "arm64",
      "os": "linux"
    }
  },
  "containerimage.digest": "sha256:13b68676cab5896f796735702ac22f8105783c8707276d67c29b543a9f6809e6",
  "image.name": "ghcr.io/graphql-hive/tokens:ebe9be3990d1597cbe8fe62869d9a000f1ac751f-arm64,ghcr.io/graphql-hive/tokens:console_1003-arm64"
},
"usage": {
  "buildx.build.provenance": {
    "buildType": "https://mobyproject.org/buildkit@v1",
    "materials": [
      {
        "uri": "pkg:docker/[email protected]?platform=linux%2Farm64",
        "digest": {
          "sha256": "f5a0871ab03b035c58bdb3007c3d177b001c2145c18e81817b71624dcf7d8bff"
        }
      }
    ],
    "invocation": {
      "configSource": {
        "entryPoint": "services.dockerfile"
      },
      "parameters": {
        "frontend": "dockerfile.v0",
        "args": {
          "build-arg:HEALTHCHECK_CMD": "wget --spider -q http://127.0.0.1:${PORT}/_readiness",
          "build-arg:IMAGE_DESCRIPTION": "The usage ingestor service of the GraphQL Hive project.",
          "build-arg:IMAGE_TITLE": "graphql-hive/usage",
          "build-arg:PORT": "3006",
          "build-arg:RELEASE": "ebe9be3990d1597cbe8fe62869d9a000f1ac751f",
          "build-arg:SERVICE_DIR_NAME": "@hive/usage",
          "context:dist": "local:dist",
          "context:shared": "local:shared",
          "frontend.caps": "moby.buildkit.frontend.contexts+forward",
          "local-sessionid:context": "nqqyrvjz465mpn6ocxkgpb1mu",
          "local-sessionid:dockerfile": "nqqyrvjz465mpn6ocxkgpb1mu",
          "local-sessionid:shared": "nqqyrvjz465mpn6ocxkgpb1mu"
        },
        "locals": [
          {
            "name": "context"
          },
          {
            "name": "dist"
          },
          {
            "name": "dockerfile"
          },
          {
            "name": "shared"
          }
        ]
      },
      "environment": {
        "platform": "linux/arm64"
      }
    }
  },
  "buildx.build.ref": "builder-5b941d15-1932-4436-bc2b-bd94ca461f03/builder-5b941d15-1932-4436-bc2b-bd94ca461f030/n1d6hpfpjk8562i3504hkh3k3",
  "containerimage.config.digest": "sha256:a8d8c57227084c90a444910a17dfcd8bfb9f1fbf6257056452cf20fa45e18469",
  "containerimage.descriptor": {
    "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
    "digest": "sha256:b68d155917bca424b977966f0513ad6d439a365704836fdc623018f31ed393e8",
    "size": 2075,
    "platform": {
      "architecture": "arm64",
      "os": "linux"
    }
  },
  "containerimage.digest": "sha256:b68d155917bca424b977966f0513ad6d439a365704836fdc623018f31ed393e8",
  "image.name": "ghcr.io/graphql-hive/usage:ebe9be3990d1597cbe8fe62869d9a000f1ac751f-arm64,ghcr.io/graphql-hive/usage:console_1003-arm64"
},
"usage-estimator": {
  "buildx.build.provenance": {
    "buildType": "https://mobyproject.org/buildkit@v1",
    "materials": [
      {
        "uri": "pkg:docker/[email protected]?platform=linux%2Farm64",
        "digest": {
          "sha256": "f5a0871ab03b035c58bdb3007c3d177b001c2145c18e81817b71624dcf7d8bff"
        }
      }
    ],
    "invocation": {
      "configSource": {
        "entryPoint": "services.dockerfile"
      },
      "parameters": {
        "frontend": "dockerfile.v0",
        "args": {
          "build-arg:HEALTHCHECK_CMD": "wget --spider -q http://127.0.0.1:${PORT}/_readiness",
          "build-arg:IMAGE_DESCRIPTION": "The usage estimator service of the GraphQL Hive project.",
          "build-arg:IMAGE_TITLE": "graphql-hive/usage-estimator",
          "build-arg:PORT": "3008",
          "build-arg:RELEASE": "ebe9be3990d1597cbe8fe62869d9a000f1ac751f",
          "build-arg:SERVICE_DIR_NAME": "@hive/usage-estimator",
          "context:dist": "local:dist",
          "context:shared": "local:shared",
          "frontend.caps": "moby.buildkit.frontend.contexts+forward",
          "local-sessionid:context": "nqqyrvjz465mpn6ocxkgpb1mu",
          "local-sessionid:dockerfile": "nqqyrvjz465mpn6ocxkgpb1mu",
          "local-sessionid:shared": "nqqyrvjz465mpn6ocxkgpb1mu"
        },
        "locals": [
          {
            "name": "context"
          },
          {
            "name": "dist"
          },
          {
            "name": "dockerfile"
          },
          {
            "name": "shared"
          }
        ]
      },
      "environment": {
        "platform": "linux/arm64"
      }
    }
  },
  "buildx.build.ref": "builder-5b941d15-1932-4436-bc2b-bd94ca461f03/builder-5b941d15-1932-4436-bc2b-bd94ca461f030/a2hgammjnpi4igitixk42ps1o",
  "containerimage.config.digest": "sha256:da2969ae7ac08c0910b56811d0d2a4965d79cb017a6c193296a3c8c2bd13a8f4",
  "containerimage.descriptor": {
    "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
    "digest": "sha256:0dad703d35c2c10b0ec5be9ab1cf11923e59dae52e98f354c050a67c2b04ccea",
    "size": 2075,
    "platform": {
      "architecture": "arm64",
      "os": "linux"
    }
  },
  "containerimage.digest": "sha256:0dad703d35c2c10b0ec5be9ab1cf11923e59dae52e98f354c050a67c2b04ccea",
  "image.name": "ghcr.io/graphql-hive/usage-estimator:ebe9be3990d1597cbe8fe62869d9a000f1ac751f-arm64,ghcr.io/graphql-hive/usage-estimator:console_1003-arm64"
},
"usage-ingestor": {
  "buildx.build.provenance": {
    "buildType": "https://mobyproject.org/buildkit@v1",
    "materials": [
      {
        "uri": "pkg:docker/[email protected]?platform=linux%2Farm64",
        "digest": {
          "sha256": "f5a0871ab03b035c58bdb3007c3d177b001c2145c18e81817b71624dcf7d8bff"
        }
      }
    ],
    "invocation": {
      "configSource": {
        "entryPoint": "services.dockerfile"
      },
      "parameters": {
        "frontend": "dockerfile.v0",
        "args": {
          "build-arg:HEALTHCHECK_CMD": "wget --spider -q http://127.0.0.1:${PORT}/_readiness",
          "build-arg:IMAGE_DESCRIPTION": "The usage ingestor service of the GraphQL Hive project.",
          "build-arg:IMAGE_TITLE": "graphql-hive/usage-ingestor",
          "build-arg:PORT": "3007",
          "build-arg:RELEASE": "ebe9be3990d1597cbe8fe62869d9a000f1ac751f",
          "build-arg:SERVICE_DIR_NAME": "@hive/usage-ingestor",
          "context:dist": "local:dist",
          "context:shared": "local:shared",
          "frontend.caps": "moby.buildkit.frontend.contexts+forward",
          "local-sessionid:context": "nqqyrvjz465mpn6ocxkgpb1mu",
          "local-sessionid:dockerfile": "nqqyrvjz465mpn6ocxkgpb1mu",
          "local-sessionid:shared": "nqqyrvjz465mpn6ocxkgpb1mu"
        },
        "locals": [
          {
            "name": "context"
          },
          {
            "name": "dist"
          },
          {
            "name": "dockerfile"
          },
          {
            "name": "shared"
          }
        ]
      },
      "environment": {
        "platform": "linux/arm64"
      }
    }
  },
  "buildx.build.ref": "builder-5b941d15-1932-4436-bc2b-bd94ca461f03/builder-5b941d15-1932-4436-bc2b-bd94ca461f030/aylnj41jb39pjbmfe8bihvmem",
  "containerimage.config.digest": "sha256:5be5d145d17b56cd5e080a352f5a2afbb824cb1349d7c93cab3eda356bd371b6",
  "containerimage.descriptor": {
    "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
    "digest": "sha256:82bfec9517bce23b9b45f9db659c96c65c2971ee020560e485c7f574957bcacb",
    "size": 2075,
    "platform": {
      "architecture": "arm64",
      "os": "linux"
    }
  },
  "containerimage.digest": "sha256:82bfec9517bce23b9b45f9db659c96c65c2971ee020560e485c7f574957bcacb",
  "image.name": "ghcr.io/graphql-hive/usage-ingestor:ebe9be3990d1597cbe8fe62869d9a000f1ac751f-arm64,ghcr.io/graphql-hive/usage-ingestor:console_1003-arm64"
},
"webhooks": {
  "buildx.build.provenance": {
    "buildType": "https://mobyproject.org/buildkit@v1",
    "materials": [
      {
        "uri": "pkg:docker/[email protected]?platform=linux%2Farm64",
        "digest": {
          "sha256": "f5a0871ab03b035c58bdb3007c3d177b001c2145c18e81817b71624dcf7d8bff"
        }
      }
    ],
    "invocation": {
      "configSource": {
        "entryPoint": "services.dockerfile"
      },
      "parameters": {
        "frontend": "dockerfile.v0",
        "args": {
          "build-arg:HEALTHCHECK_CMD": "wget --spider -q http://127.0.0.1:${PORT}/_readiness",
          "build-arg:IMAGE_DESCRIPTION": "The webhooks ingestor service of the GraphQL Hive project.",
          "build-arg:IMAGE_TITLE": "graphql-hive/webhooks",
          "build-arg:PORT": "3005",
          "build-arg:RELEASE": "ebe9be3990d1597cbe8fe62869d9a000f1ac751f",
          "build-arg:SERVICE_DIR_NAME": "@hive/webhooks",
          "context:dist": "local:dist",
          "context:shared": "local:shared",
          "frontend.caps": "moby.buildkit.frontend.contexts+forward",
          "local-sessionid:context": "nqqyrvjz465mpn6ocxkgpb1mu",
          "local-sessionid:dockerfile": "nqqyrvjz465mpn6ocxkgpb1mu",
          "local-sessionid:shared": "nqqyrvjz465mpn6ocxkgpb1mu"
        },
        "locals": [
          {
            "name": "context"
          },
          {
            "name": "dist"
          },
          {
            "name": "dockerfile"
          },
          {
            "name": "shared"
          }
        ]
      },
      "environment": {
        "platform": "linux/arm64"
      }
    }
  },
  "buildx.build.ref": "builder-5b941d15-1932-4436-bc2b-bd94ca461f03/builder-5b941d15-1932-4436-bc2b-bd94ca461f030/js3i1bkpdmt62xu4l53v1pnci",
  "containerimage.config.digest": "sha256:f8a543d2ad83a834c59d44740085e86b82cc7869db7917788e10e9fe67c2e224",
  "containerimage.descriptor": {
    "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
    "digest": "sha256:c4cda4d18f97914cf61c0eb2f5c90cf571cdea8a97a47384c146951075b8482d",
    "size": 2075,
    "platform": {
      "architecture": "arm64",
      "os": "linux"
    }
  },
  "containerimage.digest": "sha256:c4cda4d18f97914cf61c0eb2f5c90cf571cdea8a97a47384c146951075b8482d",
  "image.name": "ghcr.io/graphql-hive/webhooks:ebe9be3990d1597cbe8fe62869d9a000f1ac751f-arm64,ghcr.io/graphql-hive/webhooks:console_1003-arm64"
}
}

@jasonkuhrt jasonkuhrt marked this pull request as ready for review February 6, 2025 17:33
Comment on lines +11 to +18
if (process.env.RUN_AGAINST_LOCAL_SERVICES === '1') {
process.env.SUPERTOKENS_API_KEY = process.env.SUPERTOKENS_API_KEY ?? 'bubatzbieber6942096420';
process.env.SUPERTOKENS_CONNECTION_URI =
process.env.SUPERTOKENS_CONNECTION_URI ?? 'http://localhost:3567';
// It seems that this has to be set in the environment that the cypress cli is executed from.
// process.env.CYPRESS_BASE_URL = process.env.CYPRESS_BASE_URL ?? 'http://localhost:3000';
}

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove this (and package script) and rebase once #6475 is merged.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried creating a laboratory domain of its sub sections and co-located the Cypress helpers.

I can undo this change if its out of scope or worse than the previous setup.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moved this to its own module since its just its own lib.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Created a monaco lib to keep this generic utility away from specific monaco instances.

Copy link
Contributor

coderabbitai bot commented Feb 6, 2025

📝 Walkthrough

Summary by CodeRabbit

  • New Features

    • Launched a revamped preflight interface featuring a dedicated modal with integrated script and environment editors, along with an enhanced log display for clearer output during execution.
  • Refactor

    • Streamlined the overall preflight experience and improved underlying test utilities for enhanced stability and a more intuitive user workflow.
  • Chores

    • Updated configuration settings and dependencies to support local testing and more realistic event simulation.

Walkthrough

The changes update the Cypress configuration and testing utilities by introducing a conditional block in the Cypress config to set environment variables for local services. A new namespace, cyLaboratory, is created to encapsulate functions for managing the GraphiQL/Monaco editor and tab operations. Existing tests are refactored to use cyLaboratory instead of legacy laboratory methods. New helper functions for dedenting strings and programmatically setting Monaco editor contents are added, while old implementations in the testkit are removed. Configuration updates in package.json, tsconfig.json, and editor setup further complement these modifications.

Changes

File(s) Change Summary
cypress.config.ts Added a new conditional block: if RUN_AGAINST_LOCAL_SERVICES is '1', default values are assigned to SUPERTOKENS_API_KEY and SUPERTOKENS_CONNECTION_URI; commented-out CYPRESS_BASE_URL remains inactive.
cypress/e2e/.../_cy.ts
cypress/e2e/.../collections.cy.ts
cypress/e2e/.../preflight.cy.ts
cypress/e2e/.../tabs.cy.ts
Introduced the new cyLaboratory namespace with methods (updateEditorValue, getEditorValue, openNewTab, assertActiveTab, closeActiveTab, closeTabsUntilOneLeft) and nested preflight namespace (selectors and setEditorContent). Updated tests to import and use cyLaboratory instead of legacy laboratory functions.
cypress/support/dedent.ts
cypress/support/monaco.ts
Added new helper functions: dedent (for processing and removing common indentation from template strings) and setMonacoEditorContents (to programmatically set the Monaco editor’s content after ensuring it is fully loaded).
cypress/support/testkit.ts Removed the legacy laboratory object methods (updateEditorValue, getEditorValue, openNewTab, assertActiveTab, closeActiveTab, closeTabsUntilOneLeft) and the dedent function.
cypress/support/e2e.ts Added comments explaining Cypress limitations on real events and inserted a new import for the cypress-real-events module.
package.json Added a new script “test:e2e:local” to set CYPRESS_BASE_URL and RUN_AGAINST_LOCAL_SERVICES for local testing, and introduced a new dev dependency “cypress-real-events” version 1.14.0.
packages/.../preflight/graphiql-plugin.tsx Enhanced Monaco Editor configuration by adding new editor features (quickSuggestions, tabCompletion, folding, etc.) and improved pre-mount setup for JavaScript defaults with stricter diagnostic and compiler options.
cypress/tsconfig.json Updated the "types" array to include "cypress-real-events" in addition to "node" and "cypress".

Sequence Diagram(s)

sequenceDiagram
    participant Test
    participant cyLab as cyLaboratory
    participant Editor as GraphiQL Editor
    Test->>cyLab: updateEditorValue(value)
    cyLab->>Editor: Access CodeMirror, set value
    Editor-->>cyLab: Acknowledge update
    cyLab-->>Test: Confirm value update
Loading
sequenceDiagram
    participant Test
    participant SetContent as setMonacoEditorContents
    participant Window
    participant Monaco as Monaco Editor Instance
    Test->>SetContent: Call setMonacoEditorContents(editorCyName, text)
    SetContent->>Window: Wait for editor element & access window.Monaco
    Window-->>SetContent: Return editor instances
    SetContent->>Monaco: Find matching editor and set text
    Monaco-->>SetContent: Confirm content update
    SetContent-->>Test: Operation complete
Loading

Possibly related PRs

  • chore(tests/e2e): script to run locally #6475: The changes in the main PR and the retrieved PR are related as both modify the cypress.config.ts file to introduce a conditional block based on the RUN_AGAINST_LOCAL_SERVICES environment variable, although they implement different functionalities.

🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (8)
package.json (1)

80-80: Consider pinning the cypress-real-events version.

Using ^1.14.0 could lead to unexpected updates. Consider pinning to an exact version for better reproducibility.

-    "cypress-real-events": "^1.14.0",
+    "cypress-real-events": "1.14.0",
cypress/support/monaco.ts (2)

7-7: Consider type safety improvements for Monaco editor access.

The (win as any).monaco cast bypasses type checking. Consider using a more type-safe approach.

-    const editor = (win as any).monaco.editor
+    interface MonacoWindow extends Window {
+      monaco: {
+        editor: {
+          getEditors(): Array<{
+            getContainerDomNode(): HTMLElement;
+            setValue(value: string): void;
+          }>;
+        };
+      };
+    }
+    const editor = (win as MonacoWindow).monaco.editor

14-16: Enhance error message with more context.

The error message could be more helpful by including the editorCyName in the message.

-      throw new Error('Monaco editor not found on the window or frames[0]');
+      throw new Error(`Monaco editor not found for data-cy="${editorCyName}"`);
cypress/e2e/laboratory/_cy.ts (3)

8-11: Improve type safety for CodeMirror access.

The CodeMirror instance is accessed using any type. Consider using proper types.

+    interface CodeMirrorEditor {
+      CodeMirror: {
+        setValue(value: string): void;
+        getValue(): string;
+      };
+    }
-    const editor = ($editor[0] as any).CodeMirror;
+    const editor = ($editor[0] as CodeMirrorEditor).CodeMirror;

52-60: Consider non-recursive implementation for closeTabsUntilOneLeft.

The recursive implementation could be replaced with a simpler while loop to avoid potential stack overflow.

   export function closeTabsUntilOneLeft() {
-    cy.get('li.graphiql-tab').then($tabs => {
-      if ($tabs.length > 1) {
-        closeActiveTab();
-        // Recurse until there's only one tab left
-        return closeTabsUntilOneLeft();
-      }
-    });
+    cy.get('li.graphiql-tab').then($tabs => {
+      const closeTabs = () => {
+        if ($tabs.length > 1) {
+          closeActiveTab();
+          cy.get('li.graphiql-tab').then($newTabs => {
+            $tabs = $newTabs;
+            closeTabs();
+          });
+        }
+      };
+      closeTabs();
+    });

63-79: Consider using an enum for selector constants.

The selectors object could be defined as an enum or const enum for better type safety and maintainability.

-    export const selectors = {
+    export const enum Selectors {
+      ButtonGraphiQLPreflight = '[aria-label*="Preflight Script"]',
+      ButtonModalCy = 'preflight-modal-button',
+      // ... rest of the selectors
+    }
packages/web/app/src/lib/preflight/graphiql-plugin.tsx (1)

649-669: LGTM! Comprehensive TypeScript validation enabled.

The configuration enables thorough TypeScript validation with strict mode and modern JavaScript features. Consider documenting these settings in a configuration file or README to help other developers understand the validation rules.

Add a comment block explaining the TypeScript validation settings:

+    // Enable comprehensive TypeScript validation with strict mode
+    // - Semantic and syntax validation for better error detection
+    // - Modern JavaScript features (ESNext) with DOM types
+    // - Strict checks for improved code quality
     monaco.languages.typescript.javascriptDefaults.setDiagnosticsOptions({
cypress/support/dedent.ts (1)

25-44: Consider optimizing the indentation detection logic.

While the implementation is correct, there are a few potential improvements:

  1. The TypeScript type narrowing comment could be more descriptive
  2. The indentation detection could be more robust by handling mixed indentation

Consider this alternative implementation:

-    const m = mindent; // appease TypeScript
+    const m = mindent; // TypeScript needs this assignment to narrow the type from 'number | null' to 'number'
     result = lines.map(l => (l[0] === ' ' || l[0] === '\t' ? l.slice(m) : l)).join('\n');

Additionally, consider adding a warning when mixed indentation (spaces and tabs) is detected:

   const lines = result.split('\n');
   let mindent: null | number = null;
+  let hasSpaces = false;
+  let hasTabs = false;
   for (const l of lines) {
     const m = l.match(/^(\s+)\S+/);
     if (m) {
+      if (m[1].includes(' ')) hasSpaces = true;
+      if (m[1].includes('\t')) hasTabs = true;
       const indent = m[1].length;
       if (!mindent) {
         // this is the first indented line
         mindent = indent;
       } else {
         mindent = Math.min(mindent, indent);
       }
     }
   }
+
+  if (hasSpaces && hasTabs) {
+    console.warn('Mixed indentation detected: both spaces and tabs are used');
+  }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6bef900 and de26096.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (12)
  • cypress.config.ts (1 hunks)
  • cypress/e2e/laboratory/_cy.ts (1 hunks)
  • cypress/e2e/laboratory/collections.cy.ts (9 hunks)
  • cypress/e2e/laboratory/preflight.cy.ts (16 hunks)
  • cypress/e2e/laboratory/tabs.cy.ts (2 hunks)
  • cypress/support/dedent.ts (1 hunks)
  • cypress/support/e2e.ts (1 hunks)
  • cypress/support/monaco.ts (1 hunks)
  • cypress/support/testkit.ts (0 hunks)
  • package.json (2 hunks)
  • packages/web/app/src/lib/preflight/graphiql-plugin.tsx (2 hunks)
  • tsconfig.json (1 hunks)
💤 Files with no reviewable changes (1)
  • cypress/support/testkit.ts
✅ Files skipped from review due to trivial changes (1)
  • cypress/support/e2e.ts
⏰ Context from checks skipped due to timeout of 90000ms (10)
  • GitHub Check: test / unit
  • GitHub Check: typescript / typecheck
  • GitHub Check: static-analysis / analyze (typescript)
  • GitHub Check: storybook-preview / deployment
  • GitHub Check: static-analysis / analyze (javascript)
  • GitHub Check: build / dockerize (linux/arm64)
  • GitHub Check: build / dockerize (linux/amd64)
  • GitHub Check: alpha / cli-artifacts
  • GitHub Check: code-style / eslint-and-prettier
  • GitHub Check: SonarCloud
🔇 Additional comments (11)
cypress.config.ts (1)

11-17: Consider moving environment variable configuration to a separate file.

The local service configuration block could be moved to a dedicated configuration file to improve maintainability and separation of concerns.

Also, the commented-out CYPRESS_BASE_URL line suggests a potential issue with environment variable configuration.

Please verify if the CYPRESS_BASE_URL needs to be set here or if it's correctly handled through the CLI command in package.json.

tsconfig.json (1)

6-6: LGTM! Types configuration is correct.

The addition of "cypress-real-events" types is necessary for TypeScript support.

package.json (2)

49-49: LGTM! New script enhances local testing capabilities.

The script correctly sets up the environment for local end-to-end testing.


47-49: Verify test coverage for TypeScript validation.

The PR is marked as draft due to missing tests. Consider adding end-to-end tests that verify the TypeScript validation functionality in the laboratory editor.

Would you like help generating test cases for the TypeScript validation feature?

cypress/e2e/laboratory/tabs.cy.ts (1)

1-35: LGTM!

The changes correctly implement the new cyLaboratory namespace while maintaining the existing test logic.

cypress/e2e/laboratory/collections.cy.ts (1)

1-250: LGTM!

The changes correctly implement the new cyLaboratory namespace while maintaining the existing test logic and coverage.

cypress/e2e/laboratory/preflight.cy.ts (1)

54-66: 🛠️ Refactor suggestion

Improve test reliability and fix type issues.

The test case has the following issues:

  1. The hardcoded delay (cy.wait(1000)) could make the test flaky.
  2. The @ts-expect-error annotation suggests a type issue with realPress.

Apply this diff to improve test reliability:

-    cy.wait(1000); // :(
+    // Wait for TypeScript validation to complete
+    cy.contains(tsErrorMessage, { timeout: 5000 });

Let's verify if there's a better way to handle keyboard events in Cypress:

✅ Verification successful

🌐 Web query:

What is the recommended way to handle keyboard events in Cypress? Are there any alternatives to cypress-real-events?

💡 Result:

The recommended approach for handling keyboard events in Cypress involves a combination of built-in commands and specialized plugins depending on testing requirements:

## Core Cypress Methods
For most use cases:
1. **`.type()` command** handles simulated keyboard input with automatic event sequencing:
   - Fires `keydown`, `keypress`, `input`, and `keyup` events[1]
   - Supports special sequences like `{enter}` and modifier keys (`{ctrl}`, `{alt}`)[9]
   ```javascript
   cy.get('input').type('Hello{enter}') // Types text and presses Enter
   ```
2. **`.trigger()`** manually dispatches events but has limitations:
   - Only creates generic `Event` objects without native `keyCode`/`code` properties[4]
   - Requires manual event configuration

## Real Event Simulation (cypress-real-events)
When testing requires authentic browser interactions:
- **Chromium-only solution** using Chrome DevTools Protocol[8]
- Key features:
  ```javascript
  cy.get('input').realType('Text') // Native keystrokes
  cy.realPress('Tab') // Actual tab navigation
  cy.realPress(['Meta', 'A']) // Key combinations
  ```
- Advantages:
  - Generates trusted (`isTrusted: true`) events[8]
  - Handles focus management like real users[10]
  - Supports hover states and system dialogs

| Feature               | Built-in Commands | cypress-real-events |
|-----------------------|-------------------|---------------------|
| Trusted Events        | ❌                | ✅                  |
| Cross-browser Support | ✅                | ❌ (Chromium only)  |
| Physical Key Presses  | ❌                | ✅                  |

## Alternatives & Workarounds
1. **Focus management**:
   ```javascript
   cy.get('input').focus().type('text') // Ensure element focus
   ```
2. **Custom event dispatching** (for specific key properties):
   ```javascript
   cy.get('input').trigger('keydown', { 
     key: 'a',
     code: 'KeyA',
     keyCode: 65 
   })
   ```
3. **Community plugins**:
   - `cypress-plugin-tab` for tab navigation[10]
   - `cypress-keycloak-commands` for specialized auth flows

For most projects, combining native Cypress commands with selective use of cypress-real-events provides optimal coverage while maintaining cross-browser compatibility where needed[1][8]. Reserve low-level event triggering for edge cases requiring specific keyboard property validation[4].

Citations:

- 1: https://docs.cypress.io/api/commands/type
- 2: https://dev.to/samelawrence/my-favorite-cytype-tips-and-tricks-3166
- 3: https://betterstack.com/community/comparisons/cypress-alternative/
- 4: https://github.com/cypress-io/cypress/issues/29040
- 5: https://glebbahmutov.com/blog/how-to-pick-cypress-plugins/
- 6: https://www.lambdatest.com/blog/handling-touch-and-mouse-events-in-cypress/
- 7: https://www.browserstack.com/guide/handle-click-events-in-cypress
- 8: https://github.com/dmtrKovalenko/cypress-real-events
- 9: https://www.lambdatest.com/learning-hub/cypress-tips-and-tricks
- 10: https://platform.text.com/resource-center/updates/cypress-axe-automated-testing

Update test to wait on validations and resolve TypeScript issues

  • Replace the hardcoded delay (cy.wait(1000)) with an explicit wait for the expected error message (e.g. using cy.contains(tsErrorMessage, { timeout: 5000 })).
  • Investigate the TypeScript error on realPress: ensure you’re installing and importing the correct type definitions (or update them as needed) so that the plugin’s API is recognized without needing @ts-expect-error.
packages/web/app/src/lib/preflight/graphiql-plugin.tsx (1)

107-112: LGTM! Enhanced editor features enabled.

The Monaco editor configuration enables essential features for a better coding experience:

  • Quick suggestions and auto-completion
  • Code folding based on indentation
cypress/support/dedent.ts (3)

6-23: LGTM! Robust string interpolation implementation.

The implementation correctly handles raw template strings, escape sequences, and interpolation values.


46-52: LGTM! Clean final string processing.

The implementation correctly handles whitespace trimming and preserves escaped newlines in the right order.


1-4: Consider resolving moduleResolution issues instead of copying code.

While the implementation is properly attributed, copying code due to moduleResolution issues might lead to maintenance overhead and missed updates from the original package.

Let's verify if the moduleResolution issues can be resolved:

✅ Verification successful

🏁 Script executed:

#!/bin/bash
# Check if dedent package is already listed in package.json
if ! grep -q '"dedent":' package.json; then
  # Try installing dedent package to verify if it works
  npm install --save-dev dedent
  # Check TypeScript compilation
  npx tsc --noEmit
fi

Length of output: 1641


Module Resolution Conflict Prevents Switching from Local Implementation

The attempt to install the dedent package failed with a dependency resolution error related to ESLint and eslint-plugin-cypress. This confirms that the moduleResolution issues preventing the use of the package still exist. Therefore, until you update your dependency tree (e.g., upgrading ESLint to meet peer requirements) or adjust the conflicting packages, using the local implementation in cypress/support/dedent.ts remains the most viable workaround.

  • The dedent package installation fails due to a peer dependency conflict (ESLint v8 vs. eslint-plugin-cypress expecting ESLint ≥9).
  • Upgrading these dependencies might resolve the issue, but it could have wider implications.
  • Retaining the local dedent implementation is acceptable for now given the current dependency constraints.

Comment on lines 54 to 66
it('code is validated with TypeScript', () => {
const tsErrorMessage = "Type 'string' is not assignable to type 'number'.";
const script = 'let a = 1; a = ""';
cyp.setEditorContent(script);
cy.wait(1000); // :(
cy.dataCy(s.modal.editorCy)
.find('textarea')
.focus()
// Followed instructions but does not work https://github.com/dmtrKovalenko/cypress-real-events?tab=readme-ov-file#installation
// @ts-expect-error
.realPress(['Alt', 'F8']);
cy.contains(tsErrorMessage);
});
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given the noisy diff caused by refactoring, highlighting that here is the new test for this feature.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🔭 Outside diff range comments (1)
cypress/e2e/laboratory/preflight.cy.ts (1)

267-292:

🧩 Analysis chain

Add security tests for header injection.

The current test suite verifies header substitution but doesn't validate against header injection attacks. Consider adding tests to ensure that malicious header values are properly sanitized.

Consider adding these test cases:

 it('header placeholders are substituted with environment variables', () => {
   // ... existing test code ...
 });
+
+it('prevents header injection attacks', () => {
+  cy.dataCy('toggle-preflight').click();
+  cy.get('[data-name="headers"]').click();
+  // Test with potentially malicious header values
+  cy.get('.graphiql-editor-tool .graphiql-editor:last-child textarea').type(
+    '{ "__test": "{{foo}}\r\nMalicious-Header: value" }',
+    {
+      force: true,
+      parseSpecialCharSequences: false,
+    }
+  );
+  cy.dataCy('env-editor-mini').within(() => {
+    cy.get('textarea').type('{"foo":"injected"}', {
+      force: true,
+      parseSpecialCharSequences: false,
+    });
+  });
+
+  // Verify that header injection is prevented
+  cy.intercept({
+    method: 'POST',
+    headers: {
+      __test: 'injected\r\nMalicious-Header: value',
+    },
+  }).as('post');
+  cy.get('.graphiql-execute-button').click();
+  cy.wait('@post');
+});

🏁 Script executed:

#!/bin/bash
# Search for header sanitization in the codebase
rg -A 5 'headers\.append|headers\.set' --type ts

Length of output: 4757


Attention: Missing Security Validation for Header Injection

The current test in cypress/e2e/laboratory/preflight.cy.ts only verifies header substitution against environment variables and does not check for header injection scenarios. Our codebase (as seen with calls to headers.append and headers.set) lacks explicit sanitization checks against header injection attacks. Consider adding dedicated tests (for example, using malicious characters to attempt header injection) to ensure that the application properly escapes or sanitizes header values.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2d772e4 and ce4c80f.

📒 Files selected for processing (1)
  • cypress/e2e/laboratory/preflight.cy.ts (16 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (10)
  • GitHub Check: static-analysis / analyze (typescript)
  • GitHub Check: typescript / typecheck
  • GitHub Check: static-analysis / analyze (javascript)
  • GitHub Check: storybook-preview / deployment
  • GitHub Check: test / unit
  • GitHub Check: build / dockerize (linux/arm64)
  • GitHub Check: build / dockerize (linux/amd64)
  • GitHub Check: code-style / eslint-and-prettier
  • GitHub Check: alpha / cli-artifacts
  • GitHub Check: SonarCloud
🔇 Additional comments (1)
cypress/e2e/laboratory/preflight.cy.ts (1)

1-7: LGTM! Clean import refactoring.

The imports have been refactored to use the new cyLaboratory namespace, and the code introduces clear aliases (s and cyp) for better readability.

Comment on lines 54 to 65
it('code is validated with TypeScript', () => {
const tsErrorMessage = "Type 'string' is not assignable to type 'number'.";
const script = 'let a = 1; a = ""';
cyp.setEditorContent(script);
cy.wait(1000); // :(
cy.dataCy(s.modal.editorCy)
.find('textarea')
.focus()
// @ts-expect-error - Followed instructions but does not work https://github.com/dmtrKovalenko/cypress-real-events?tab=readme-ov-file#installation
.realPress(['Alt', 'F8']);
cy.contains(tsErrorMessage);
});
Copy link
Contributor

@coderabbitai coderabbitai bot Feb 6, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Replace hardcoded wait with proper assertions.

The test has several potential issues:

  1. Using a hardcoded wait(1000) is an anti-pattern in Cypress and could make the test flaky.
  2. The keyboard shortcut Alt+F8 might not work consistently across different platforms.

Consider this refactor:

 it('code is validated with TypeScript', () => {
   const tsErrorMessage = "Type 'string' is not assignable to type 'number'.";
   const script = 'let a = 1; a = ""';
   cyp.setEditorContent(script);
-  cy.wait(1000); // :(
+  // Wait for TypeScript validation to complete
+  cy.get(s.modal.editorCy).should('exist');
+  cy.get('.monaco-editor').should('exist');
   cy.dataCy(s.modal.editorCy)
     .find('textarea')
     .focus()
     // @ts-expect-error - Followed instructions but does not work https://github.com/dmtrKovalenko/cypress-real-events?tab=readme-ov-file#installation
     .realPress(['Alt', 'F8']);
-  cy.contains(tsErrorMessage);
+  // Wait for error message to appear and verify it
+  cy.contains(tsErrorMessage).should('be.visible');
 });

Additionally, consider adding a comment explaining the keyboard shortcut's purpose and documenting any platform-specific considerations.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
it('code is validated with TypeScript', () => {
const tsErrorMessage = "Type 'string' is not assignable to type 'number'.";
const script = 'let a = 1; a = ""';
cyp.setEditorContent(script);
cy.wait(1000); // :(
cy.dataCy(s.modal.editorCy)
.find('textarea')
.focus()
// @ts-expect-error - Followed instructions but does not work https://github.com/dmtrKovalenko/cypress-real-events?tab=readme-ov-file#installation
.realPress(['Alt', 'F8']);
cy.contains(tsErrorMessage);
});
it('code is validated with TypeScript', () => {
const tsErrorMessage = "Type 'string' is not assignable to type 'number'.";
const script = 'let a = 1; a = ""';
cyp.setEditorContent(script);
// Wait for TypeScript validation to complete
cy.get(s.modal.editorCy).should('exist');
cy.get('.monaco-editor').should('exist');
cy.dataCy(s.modal.editorCy)
.find('textarea')
.focus()
// @ts-expect-error - Followed instructions but does not work https://github.com/dmtrKovalenko/cypress-real-events?tab=readme-ov-file#installation
.realPress(['Alt', 'F8']);
// Wait for error message to appear and verify it
cy.contains(tsErrorMessage).should('be.visible');
});

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried this but it doens't work. Getting this to work in Cypress required hacking around. I'll leave a comment making the point that we know its hacky.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jasonkuhrt Understood, thanks for the clarification. I apologize for the incorrect suggestion. You're right about documenting the limitation.

(_/)/
(•-•) nods in understanding
/> <>


✏️ Learnings added
Learnt from: jasonkuhrt
PR: graphql-hive/console#6476
File: cypress/e2e/laboratory/preflight.cy.ts:0-0
Timestamp: 2025-02-06T19:32:04.951Z
Learning: TypeScript validation in Cypress tests for Monaco editor may require workarounds like hardcoded waits, as proper assertions may not work reliably.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (2)
package.json (2)

49-49: New Script Addition: "test:e2e:local".
This script provides a clear and concise way to run Cypress tests against your local environment by explicitly setting CYPRESS_BASE_URL and RUN_AGAINST_LOCAL_SERVICES. It adheres to the PR's objective of improving local testing capabilities. Consider adding a brief note or updating your documentation (e.g., in a README) to inform developers about when and how to use this script.


80-80: New Dev Dependency: "cypress-real-events".
Introducing "cypress-real-events" at version 1.14.0 is a good move for simulating realistic user interactions in your E2E tests—especially relevant given the enhancements to the laboratory editor validation. Ensure that its usage is clearly demonstrated in your test files and that any setup requirements (if not already documented) are communicated to your team.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ce4c80f and 7dfbe35.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (1)
  • package.json (2 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (12)
  • GitHub Check: storybook-preview / deployment
  • GitHub Check: test / unit
  • GitHub Check: typescript / typecheck
  • GitHub Check: static-analysis / analyze (typescript)
  • GitHub Check: static-analysis / analyze (javascript)
  • GitHub Check: db-types / diff
  • GitHub Check: test / db-migrations
  • GitHub Check: alpha / cli-artifacts
  • GitHub Check: build / dockerize (linux/arm64)
  • GitHub Check: build / dockerize (linux/amd64)
  • GitHub Check: code-style / eslint-and-prettier
  • GitHub Check: SonarCloud

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7dfbe35 and 3bcfb5d.

📒 Files selected for processing (2)
  • cypress/e2e/laboratory/preflight.cy.ts (16 hunks)
  • cypress/tsconfig.json (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (10)
  • GitHub Check: alpha / cli-artifacts
  • GitHub Check: typescript / typecheck
  • GitHub Check: test / unit
  • GitHub Check: storybook-preview / deployment
  • GitHub Check: static-analysis / analyze (typescript)
  • GitHub Check: static-analysis / analyze (javascript)
  • GitHub Check: build / dockerize (linux/arm64)
  • GitHub Check: code-style / eslint-and-prettier
  • GitHub Check: build / dockerize (linux/amd64)
  • GitHub Check: SonarCloud
🔇 Additional comments (3)
cypress/tsconfig.json (1)

5-5: LGTM! Configuration updated correctly.

The addition of "cypress-real-events" to the types array properly enables TypeScript support for the cypress-real-events library.

cypress/e2e/laboratory/preflight.cy.ts (2)

24-24: LGTM! Well-organized test structure.

The tests are logically grouped into clear describe blocks that separate different aspects of functionality:

  • Preflight Tab
  • Preflight Modal
  • Execution

This organization makes the test suite easy to understand and maintain.

Also applies to: 45-45, 187-187


54-61: ⚠️ Potential issue

Replace hardcoded wait with proper assertions.

The test has several potential issues:

  1. Using a hardcoded cy.wait(1000) is an anti-pattern in Cypress and could make the test flaky
  2. The keyboard shortcut Alt+F8 might not work consistently across different platforms

Apply this diff to make the test more robust:

  it('code is validated with TypeScript', () => {
    const tsErrorMessage = "Type 'string' is not assignable to type 'number'.";
    const script = 'let a = 1; a = ""';
    cyp.setEditorContent(script);
-   cy.wait(1000); // :(
+   // Wait for TypeScript validation to complete
+   cy.get(s.modal.editorCy).should('exist');
+   cy.get('.monaco-editor').should('exist')
+   cy.get('.monaco-editor .error-widget').should('exist');
    cy.dataCy(s.modal.editorCy)
      .find('textarea')
      .focus()
      .realPress(['Alt', 'F8']);
-   cy.contains(tsErrorMessage);
+   // Wait for error message to appear and verify it
+   cy.contains(tsErrorMessage).should('be.visible');

Additionally, consider adding a comment explaining the keyboard shortcut's purpose and documenting any platform-specific considerations.

Likely invalid or redundant comment.

cypress/e2e/laboratory/preflight.cy.ts Outdated Show resolved Hide resolved
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (2)
cypress/e2e/laboratory/_cy.ts (2)

8-11: Improve type safety when accessing CodeMirror instance.

The current implementation uses type assertion to any which bypasses TypeScript's type checking. Consider adding proper type definitions for CodeMirror.

+// Add at the top of the file
+interface CodeMirrorEditor {
+  CodeMirror: {
+    setValue: (value: string) => void;
+    getValue: () => string;
+  };
+}

 export function updateEditorValue(value: string) {
   cy.get('.graphiql-query-editor .cm-s-graphiql').then($editor => {
-    const editor = ($editor[0] as any).CodeMirror;
+    const editor = ($editor[0] as CodeMirrorEditor).CodeMirror;
     editor.setValue(value);
   });
 }

Also applies to: 18-21


52-60: Optimize tab closing logic to prevent potential stack overflow.

The recursive implementation could cause stack overflow with many tabs. Consider using a loop instead.

 export function closeTabsUntilOneLeft() {
-  cy.get('li.graphiql-tab').then($tabs => {
-    if ($tabs.length > 1) {
-      closeActiveTab();
-      // Recurse until there's only one tab left
-      return closeTabsUntilOneLeft();
-    }
+  cy.get('li.graphiql-tab').then(function closeTabs($tabs) {
+    if ($tabs.length <= 1) return;
+    closeActiveTab();
+    cy.get('li.graphiql-tab').then(closeTabs);
   });
 }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3bcfb5d and 2b725b2.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (5)
  • cypress/e2e/laboratory/_cy.ts (1 hunks)
  • cypress/e2e/laboratory/preflight.cy.ts (18 hunks)
  • cypress/support/monaco.ts (1 hunks)
  • cypress/tsconfig.json (1 hunks)
  • package.json (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • cypress/tsconfig.json
  • cypress/support/monaco.ts
  • package.json
⏰ Context from checks skipped due to timeout of 90000ms (10)
  • GitHub Check: storybook-preview / deployment
  • GitHub Check: alpha / cli-artifacts
  • GitHub Check: test / unit
  • GitHub Check: typescript / typecheck
  • GitHub Check: static-analysis / analyze (typescript)
  • GitHub Check: static-analysis / analyze (javascript)
  • GitHub Check: code-style / eslint-and-prettier
  • GitHub Check: build / dockerize (linux/arm64)
  • GitHub Check: build / dockerize (linux/amd64)
  • GitHub Check: SonarCloud
🔇 Additional comments (5)
cypress/e2e/laboratory/_cy.ts (2)

62-80: LGTM! Well-organized selectors.

The selectors are well-structured and follow a consistent naming convention.


84-93: LGTM! Clean implementation of editor content management.

The functions are well-organized and follow DRY principles.

cypress/e2e/laboratory/preflight.cy.ts (3)

53-60: Replace hardcoded wait with proper assertions.

The previous review comment about replacing the hardcoded wait with proper assertions is still valid.


23-44: LGTM! Well-organized test structure.

The tests are logically grouped with clear descriptions and consistent patterns.


173-183: LGTM! Robust error handling tests.

The error cases are well-covered with specific assertions.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
cypress/e2e/laboratory/preflight.cy.ts (1)

53-61: Document the TypeScript validation workaround.

The test includes a hardcoded wait and keyboard shortcut that might be platform-dependent. While this is a known limitation (as per retrieved learnings), it should be documented more thoroughly.

     const script = 'let a = 1; a = ""';
     cyp.setEditorContent(script);
-    // Hack: Seemingly only way to reliably interact with the monaco text area from Cypress.
+    // HACK: Monaco editor's TypeScript validation is challenging to test in Cypress:
+    // 1. The hardcoded wait is necessary as there's no reliable way to detect when validation completes
+    // 2. The Alt+F8 shortcut might not work on all platforms (e.g., macOS)
+    // See: graphql-hive/console#6476 for more context
     cy.wait(1000);
     cy.dataCy(s.modal.editorCy).find('textarea').focus().realPress(['Alt', 'F8']);
     cy.contains(tsErrorMessage);
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2b725b2 and ebe9be3.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (1)
  • cypress/e2e/laboratory/preflight.cy.ts (18 hunks)
🧰 Additional context used
📓 Learnings (1)
cypress/e2e/laboratory/preflight.cy.ts (1)
Learnt from: jasonkuhrt
PR: graphql-hive/console#6476
File: cypress/e2e/laboratory/preflight.cy.ts:0-0
Timestamp: 2025-02-06T19:32:05.091Z
Learning: TypeScript validation in Cypress tests for Monaco editor may require workarounds like hardcoded waits, as proper assertions may not work reliably.
⏰ Context from checks skipped due to timeout of 90000ms (10)
  • GitHub Check: test / unit
  • GitHub Check: typescript / typecheck
  • GitHub Check: static-analysis / analyze (typescript)
  • GitHub Check: storybook-preview / deployment
  • GitHub Check: static-analysis / analyze (javascript)
  • GitHub Check: build / dockerize (linux/arm64)
  • GitHub Check: build / dockerize (linux/amd64)
  • GitHub Check: alpha / cli-artifacts
  • GitHub Check: code-style / eslint-and-prettier
  • GitHub Check: SonarCloud
🔇 Additional comments (3)
cypress/e2e/laboratory/preflight.cy.ts (3)

1-6: LGTM! Clean refactor of imports and selector references.

The code has been improved by:

  • Importing utilities from dedicated modules
  • Using a consistent selector reference through cyLaboratory.preflight.selectors

50-51: LGTM! Consistent use of editor content setting methods.

The code has been standardized to use cyp.setEditorContent and cyp.setEnvironmentEditorContent throughout the test file, improving maintainability.

Also applies to: 64-65, 76-77, 98-99, 154-155, 165-166, 175-176, 181-182, 195-196, 219-220, 247-251, 301-302, 328-334, 360-361, 379-387, 423-431


23-24: LGTM! Improved test organization.

The test suites have been renamed for better clarity:

  • "Laboratory > Preflight Script" → "Preflight Tab"
  • "Preflight Script Modal" → "Preflight Modal"

Also applies to: 44-45

@theguild-bot theguild-bot temporarily deployed to development February 7, 2025 08:56 Inactive
@n1ru4l
Copy link
Contributor

n1ru4l commented Feb 7, 2025

I know this was not introduced in this PR but would it be easily possible to disable these suggestions and limit it to the APIs available within the preflight script context?

image

@n1ru4l
Copy link
Contributor

n1ru4l commented Feb 7, 2025

The editor does not flag TypeScript annotation as an error, is it possible to support typescript linting without allowing type annotations/TypeScript specific syntax?

Otherwise, we must go down the rabbit hole of transpiling the code before running it

image

Copy link
Member Author

jasonkuhrt commented Feb 7, 2025

@n1ru4l

is it possible to support typescript linting without allowing type annotations/TypeScript specific syntax?

Great point, I'll look into this to see what our options are.

I know this was not introduced in this PR but would it be easily possible to disable these suggestions and limit it to the APIs available within the preflight script context?

@n1ru4l oh yeah, currently Dom has been added, which is of course a large set of global types. What triggered me adding it was the fact that console was giving a type error which is allowed in Preflight. Do we have documented somewhere what the allowed globals are in Preflight? Without knowing that I'm just guessing about what types to allow.

@n1ru4l
Copy link
Contributor

n1ru4l commented Feb 7, 2025

packages/web/app/src/lib/preflight/allowed-globals.ts is the file you are looking for. I hope there is a way for adding these without having to pluck the dom typings 😢

@jasonkuhrt
Copy link
Member Author

@n1ru4l awesome thanks. My hope is we'll just need to list them in the tsconfig lib settings given to Monaco, and that there is a 1:1 between dom lib names and those globals. tbd...

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (4)
packages/web/app/src/lib/preflight/components/PreflightModal.tsx (1)

59-68: Remove or clarify commented-out code for maintainability.

Lines 67–68 appear to be leftover or experimental and are commented out, which can lead to confusion. Removing or clarifying these lines helps keep the codebase clean and more understandable.

packages/web/app/src/lib/preflight/graphiql-plugin.tsx (3)

23-39: Consider removing commented-out code.

The commented-out icon class in classes object is marked as unused. If it's no longer needed, it should be removed to maintain code cleanliness.

 const classes = {
   monacoMini: clsx('h-32 *:rounded-md *:bg-[#10151f]'),
-  // todo: was unused, commented out for now, remove?
-  // icon: clsx('absolute -left-5 top-px'),
 };

530-538: Consider adding JSON schema validation for environment variables.

Since the environment editor is used for JSON input, consider adding JSON schema validation to catch errors early and provide better autocompletion.

 <EnvironmentEditor
   height={128}
   value={preflight.environmentVariables}
   onChange={value => preflight.setEnvironmentVariables(value ?? '')}
   className={classes.monacoMini}
   wrapperProps={{
     ['data-cy']: 'env-editor-mini',
   }}
+  options={{
+    // Enable JSON validation
+    formatOnPaste: true,
+    formatOnType: true,
+    // Add schema validation
+    schemas: [{
+      uri: "preflight-env-schema.json",
+      fileMatch: ["*"],
+      schema: {
+        type: "object",
+        additionalProperties: {
+          type: ["string", "number", "boolean", "null"]
+        }
+      }
+    }]
+  }}
 />

381-384: Improve documentation for sandbox permissions.

The comment about sandbox permissions in DEV mode could be more explicit about security implications.

-/**
- * In DEV we need to use "allow-same-origin", as otherwise the embed can not instantiate the webworker (which is loaded from an URL).
- * In PROD the webworker is not
- */
+/**
+ * Sandbox permissions:
+ * - DEV: Requires "allow-same-origin" for webworker instantiation from URL
+ * - PROD: Uses bundled webworker, so "allow-same-origin" is not needed
+ * 
+ * Security note: "allow-same-origin" is safe in DEV as the content is trusted
+ */
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ebe9be3 and 7692fc4.

📒 Files selected for processing (11)
  • packages/web/app/src/lib/MonacoEditorReact/index.ts (1 hunks)
  • packages/web/app/src/lib/preflight/components/EditorTitle.tsx (1 hunks)
  • packages/web/app/src/lib/preflight/components/EnvironmentEditor.tsx (1 hunks)
  • packages/web/app/src/lib/preflight/components/LogLine.tsx (1 hunks)
  • packages/web/app/src/lib/preflight/components/PreflightModal.tsx (1 hunks)
  • packages/web/app/src/lib/preflight/components/ScriptEditor.tsx (1 hunks)
  • packages/web/app/src/lib/preflight/components/_defaultEditorProps.ts (1 hunks)
  • packages/web/app/src/lib/preflight/graphiql-plugin.tsx (3 hunks)
  • packages/web/app/src/lib/preflight/index.ts (1 hunks)
  • packages/web/app/src/lib/preflight/shared-types.ts (1 hunks)
  • packages/web/app/src/pages/target-laboratory.tsx (1 hunks)
✅ Files skipped from review due to trivial changes (3)
  • packages/web/app/src/lib/MonacoEditorReact/index.ts
  • packages/web/app/src/lib/preflight/index.ts
  • packages/web/app/src/pages/target-laboratory.tsx
🧰 Additional context used
🪛 Biome (1.9.4)
packages/web/app/src/lib/preflight/shared-types.ts

[error] 6-9: The enum declaration should not be const

Const enums are not supported by bundlers and are incompatible with the 'isolatedModules' mode. Their use can lead to import inexistent values.
See TypeScript Docs for more details.
Safe fix: Turn the const enum into a regular enum.

(lint/suspicious/noConstEnum)

⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: Mend Security Check
🔇 Additional comments (13)
packages/web/app/src/lib/preflight/components/PreflightModal.tsx (3)

87-151: Ensure all required global declarations are included.

You’ve declared many console and global API definitions here. Consider verifying that you’ve included all Preflight-specific global types you might rely on (e.g., setInterval, localStorage) to avoid potential runtime errors. Conversely, if some declarations are not actually used in this environment, pruning them can keep the code lean.


175-182: Preventing modal closure on Escape while editing is helpful—double-check user expectations.

You block the Escape key from closing the modal when users focus the Monaco editor. This is generally good, but confirm this aligns with user expectations in your design. Some might expect Escape to still close the modal unless code is actively being typed.


210-213: Confirm no concurrency issues with run/stop flow.

The code toggles execution based on the worker state. Ensure that rapid consecutive clicks, or potential race conditions in “running” → “aborting” → “running” transitions, are gracefully handled. Otherwise, you may end up in an inconsistent state if the worker has unhandled asynchronous tasks.

packages/web/app/src/lib/preflight/components/EditorTitle.tsx (1)

4-10: Lightweight component usage looks good.

This component nicely abstracts out a title style for editors. It's short, readable, and effectively uses the utility class function, so no issues spotted here.

packages/web/app/src/lib/preflight/components/_defaultEditorProps.ts (1)

4-17: Great approach for shared editor defaults.

Defining global editor props here centralizes styling and features. If you need environment-specific styling (e.g., read-only in certain contexts), consider making some fields dynamically configurable. Otherwise, this is a clean, straightforward default configuration.

packages/web/app/src/lib/preflight/components/EnvironmentEditor.tsx (2)

4-12: LGTM! Well-configured JSON editor settings.

The default props are well-configured for JSON editing with appropriate settings like disabled line numbers and 2-space indentation, which aligns with common JSON formatting conventions.


14-25: LGTM! Clean component implementation.

The component follows React best practices with proper props spreading order, ensuring that user-provided props can override defaults while maintaining type safety.

packages/web/app/src/lib/preflight/components/ScriptEditor.tsx (2)

19-30: LGTM! Clean component implementation.

The component follows React best practices with proper props spreading order, ensuring that user-provided props can override defaults while maintaining type safety.


4-17: Verify TypeScript validation settings.

The editor is configured well for JavaScript, but based on the PR objectives to improve TypeScript validation, we should verify if additional TypeScript-specific settings are needed.

Also, consider adding these TypeScript-specific settings to align with the PR objectives:

 options: {
   ...defaultEditorProps.options,
   quickSuggestions: true,
   suggestOnTriggerCharacters: true,
   acceptSuggestionOnEnter: 'on',
   tabCompletion: 'on',
   folding: true,
   foldingStrategy: 'indentation',
+  // Enable TypeScript validation
+  diagnostics: true,
+  validate: true,
 },
packages/web/app/src/lib/preflight/components/LogLine.tsx (2)

4-22: LGTM! Well-structured error handling and type checking.

The component implements proper type guards and error handling with Sentry integration. The conditional rendering logic is clean and maintainable.


24-29: LGTM! Semantic color scheme.

The color scheme follows common conventions for log levels (red for errors, yellow for warnings, etc.), enhancing readability and user experience.

packages/web/app/src/lib/preflight/shared-types.ts (1)

11-11: LGTM! Clear type definition.

The union type for LogRecord is well-defined and provides good type safety for different log entry types.

packages/web/app/src/lib/preflight/graphiql-plugin.tsx (1)

503-519: Enhance TypeScript validation in the script editor.

Based on the PR objectives to improve TypeScript validation, consider configuring the Monaco editor with TypeScript-specific options:

  1. Enable TypeScript validation while limiting suggestions to allowed APIs
  2. Configure the editor to flag TypeScript-specific syntax as errors
 <ScriptEditor
   height={128}
   value={preflight.content}
   className={cn(classes.monacoMini, 'z-10')}
   wrapperProps={{
     ['data-cy']: 'preflight-editor-mini',
   }}
   options={{
     lineNumbers: 'off',
     domReadOnly: true,
     readOnly: true,
     hover: {
       enabled: false,
     },
+    // Enable TypeScript validation
+    javascriptDefaults: {
+      validate: true,
+      // Limit suggestions to allowed APIs
+      suggest: {
+        includeCompletionsForModuleExports: false,
+      },
+    },
+    // Flag TypeScript-specific syntax as errors
+    typescript: {
+      validate: {
+        syntaxValidation: 'error',
+      },
+    },
   }}
 />

Let's verify the allowed globals in Preflight:

✅ Verification successful

🏁 Script executed:

#!/bin/bash
# Description: Check the allowed globals in Preflight
cat packages/web/app/src/lib/preflight/allowed-globals.ts

Length of output: 1055


Action: Enhance TypeScript validation in the Monaco editor configuration

The allowed globals list (from packages/web/app/src/lib/preflight/allowed-globals.ts) is intact and matches expectations. The proposed diff in —which adds TypeScript-specific options (enabling validation, restricting module export suggestions, and flagging syntax errors)—is consistent with the intent to restrict the editor’s suggestions to allowed APIs. No discrepancies were found with the allowed globals definition.

• Allowed globals are defined as expected.
• The reviewer’s suggestion to add TypeScript validation options in the ScriptEditor’s options is appropriate.

Comment on lines +155 to +158
onContentChange(scriptEditorRef.current?.getValue() ?? '');
onEnvValueChange(envEditorRef.current?.getValue() ?? '');
toggle();
}, []);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Consider validating editor data before saving.

At the time of submission (handleSubmit), you capture script and environment values directly from the editors. If either editor content requires JSON or special parsing, introduce validation (or error messages) to catch invalid inputs before applying them globally.

Comment on lines +5 to +9
// todo stop using enums
export const enum PreflightWorkerState {
running,
ready,
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Convert const enum to regular enum or union type.

The static analysis tool correctly flags that const enums can cause issues with bundlers and the 'isolatedModules' mode. Consider these alternatives:

-// todo stop using enums
-export const enum PreflightWorkerState {
-  running,
-  ready,
-}
+// Option 1: Regular enum
+export enum PreflightWorkerState {
+  running,
+  ready,
+}
+
+// Option 2: Union type (preferred, aligns with TODO)
+export type PreflightWorkerState = 'running' | 'ready';

Committable suggestion skipped: line range outside the PR's diff.

🧰 Tools
🪛 Biome (1.9.4)

[error] 6-9: The enum declaration should not be const

Const enums are not supported by bundlers and are incompatible with the 'isolatedModules' mode. Their use can lead to import inexistent values.
See TypeScript Docs for more details.
Safe fix: Turn the const enum into a regular enum.

(lint/suspicious/noConstEnum)

@jasonkuhrt
Copy link
Member Author

jasonkuhrt commented Feb 7, 2025

Hey @n1ru4l, I made some progress.

  1. TS syntax is no longer allowed statically. ✅

  2. Custom globals. This is WIP. We don't get the granular dom lib types I had for some reason hoped for but should have known wouldn't be there. So I've had to go brute force on this. Luckily, most of the types IIUC in your list are not from dom but ES standard. I have therefore included all the ES2020 types. If that's not acceptable then we're going to have to do the same thing for that which I'm doing for DOM types, which is tricky/error prone IMO so not great.

    For the DOM I've finished console and setTimeout as proof of concept. Fetch is not done yet, and that is the huge one, because of all the stream and http types it drags in. It is many hundreds of lines of type code that we'll need to copy paste in, and I don't think its just one contiguous block of type code either, but I have to go and trace the type paths to cherry pick just whats needed. I feel this is something that the community may have dealt with before so will pause to look for external solutions, e.g. a types package that already slimmed down to just fetch.

    Another idea is that there is a package @types/web, and I might be able to import and then type a manual global type with just what we want. That would be ideal, however, unless we can put the declaration file through some kind of tree shaking bundle step, we would be pulling in the whole of web declaration into the preflight bundle which must be MBs big... Maybe we could serve that on an agressively cached cdn to mitigate the issue... or...

    I have that feeling of there "must be a simpler way", maybe I'll think of something over the weekend or have some new energy next week that better thinks about this.

    Welcome any ideas from ya'll about this!

    CleanShot 2025-02-07 at 12 30 54@2x

    CleanShot 2025-02-07 at 12 31 02@2x

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

Successfully merging this pull request may close these issues.

3 participants