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 27 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

Checklist

  • changeset
  • tests

@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://c2692f09.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: 08b3640040230552a4bb431c4b3ec75cfb725211

Docker Bake metadata
{
"app": {
  "buildx.build.provenance": {
    "buildType": "https://mobyproject.org/buildkit@v1",
    "materials": [
      {
        "uri": "pkg:docker/node@22.13.0-slim?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": "08b3640040230552a4bb431c4b3ec75cfb725211",
          "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": "8xs5k936gdes1o03mjam1jht0",
          "local-sessionid:dockerfile": "8xs5k936gdes1o03mjam1jht0",
          "local-sessionid:shared": "8xs5k936gdes1o03mjam1jht0"
        },
        "locals": [
          {
            "name": "context"
          },
          {
            "name": "dist"
          },
          {
            "name": "dockerfile"
          },
          {
            "name": "shared"
          }
        ]
      },
      "environment": {
        "platform": "linux/arm64"
      }
    }
  },
  "buildx.build.ref": "builder-713199cf-705d-4778-8686-5fb6c1837733/builder-713199cf-705d-4778-8686-5fb6c18377330/xs3jlkohtq5biahn22w7k8tmc",
  "containerimage.config.digest": "sha256:6d7855016c0e9af61f3f95ebf26b3072b7510136645b846820869adae800df84",
  "containerimage.descriptor": {
    "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
    "digest": "sha256:070089a6344ff45a8de5fe520e9ca732d5e02ac73d8f8cce5ecc14de31e73655",
    "size": 2075,
    "platform": {
      "architecture": "arm64",
      "os": "linux"
    }
  },
  "containerimage.digest": "sha256:070089a6344ff45a8de5fe520e9ca732d5e02ac73d8f8cce5ecc14de31e73655",
  "image.name": "ghcr.io/graphql-hive/app:08b3640040230552a4bb431c4b3ec75cfb725211-arm64,ghcr.io/graphql-hive/app:console_1003-arm64"
},
"buildx.build.warnings": [
  {
    "vertex": "sha256:8fca626ca6d6e5fcf05de91423f50525022451ddd8faa6441f9cb28df47c533e",
    "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:8fca626ca6d6e5fcf05de91423f50525022451ddd8faa6441f9cb28df47c533e",
    "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:a278d71b031ff67e2765de8c5a91ace36a140b5e97d4614a9cc23c74a96ba4f4",
    "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:a278d71b031ff67e2765de8c5a91ace36a140b5e97d4614a9cc23c74a96ba4f4",
    "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:a278d71b031ff67e2765de8c5a91ace36a140b5e97d4614a9cc23c74a96ba4f4",
    "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:a278d71b031ff67e2765de8c5a91ace36a140b5e97d4614a9cc23c74a96ba4f4",
    "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
        }
      }
    ]
  },
  {
    "vertex": "sha256:a278d71b031ff67e2765de8c5a91ace36a140b5e97d4614a9cc23c74a96ba4f4",
    "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:8fca626ca6d6e5fcf05de91423f50525022451ddd8faa6441f9cb28df47c533e",
    "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:8fca626ca6d6e5fcf05de91423f50525022451ddd8faa6441f9cb28df47c533e",
    "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:8fca626ca6d6e5fcf05de91423f50525022451ddd8faa6441f9cb28df47c533e",
    "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:8fca626ca6d6e5fcf05de91423f50525022451ddd8faa6441f9cb28df47c533e",
    "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:a278d71b031ff67e2765de8c5a91ace36a140b5e97d4614a9cc23c74a96ba4f4",
    "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:8fca626ca6d6e5fcf05de91423f50525022451ddd8faa6441f9cb28df47c533e",
    "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:8fca626ca6d6e5fcf05de91423f50525022451ddd8faa6441f9cb28df47c533e",
    "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
        }
      }
    ]
  }
],
"composition-federation-2": {
  "buildx.build.provenance": {
    "buildType": "https://mobyproject.org/buildkit@v1",
    "materials": [
      {
        "uri": "pkg:docker/node@22.13.0-slim?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": "08b3640040230552a4bb431c4b3ec75cfb725211",
          "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": "8xs5k936gdes1o03mjam1jht0",
          "local-sessionid:dockerfile": "8xs5k936gdes1o03mjam1jht0",
          "local-sessionid:shared": "8xs5k936gdes1o03mjam1jht0"
        },
        "locals": [
          {
            "name": "context"
          },
          {
            "name": "dist"
          },
          {
            "name": "dockerfile"
          },
          {
            "name": "shared"
          }
        ]
      },
      "environment": {
        "platform": "linux/arm64"
      }
    }
  },
  "buildx.build.ref": "builder-713199cf-705d-4778-8686-5fb6c1837733/builder-713199cf-705d-4778-8686-5fb6c18377330/q5nztbdr4n5g7evunjs8sae2h",
  "containerimage.config.digest": "sha256:79f35efc1e8684859728707c80b879154dbd476400658ac98aead9718ce28f12",
  "containerimage.descriptor": {
    "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
    "digest": "sha256:373a64b5655de57b8404a3d6f6f1eed8b4e09b77cb1dd28d70ba22bdde22bf99",
    "size": 2075,
    "platform": {
      "architecture": "arm64",
      "os": "linux"
    }
  },
  "containerimage.digest": "sha256:373a64b5655de57b8404a3d6f6f1eed8b4e09b77cb1dd28d70ba22bdde22bf99",
  "image.name": "ghcr.io/graphql-hive/composition-federation-2:08b3640040230552a4bb431c4b3ec75cfb725211-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/node@22.13.0-slim?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": "08b3640040230552a4bb431c4b3ec75cfb725211",
          "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": "8xs5k936gdes1o03mjam1jht0",
          "local-sessionid:dockerfile": "8xs5k936gdes1o03mjam1jht0",
          "local-sessionid:shared": "8xs5k936gdes1o03mjam1jht0"
        },
        "locals": [
          {
            "name": "context"
          },
          {
            "name": "dist"
          },
          {
            "name": "dockerfile"
          },
          {
            "name": "shared"
          }
        ]
      },
      "environment": {
        "platform": "linux/arm64"
      }
    }
  },
  "buildx.build.ref": "builder-713199cf-705d-4778-8686-5fb6c1837733/builder-713199cf-705d-4778-8686-5fb6c18377330/umhswr9rwqktullswal2j6zao",
  "containerimage.config.digest": "sha256:4c4a1f0db7069b1db8e7cc964ce786a67b437e39a73a12d8ff61a14f2ec96fa9",
  "containerimage.descriptor": {
    "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
    "digest": "sha256:a86942bc32628000c9f34542aa6edc8df71adcb06c896a2252be6fda2824af9e",
    "size": 2075,
    "platform": {
      "architecture": "arm64",
      "os": "linux"
    }
  },
  "containerimage.digest": "sha256:a86942bc32628000c9f34542aa6edc8df71adcb06c896a2252be6fda2824af9e",
  "image.name": "ghcr.io/graphql-hive/emails:08b3640040230552a4bb431c4b3ec75cfb725211-arm64,ghcr.io/graphql-hive/emails:console_1003-arm64"
},
"policy": {
  "buildx.build.provenance": {
    "buildType": "https://mobyproject.org/buildkit@v1",
    "materials": [
      {
        "uri": "pkg:docker/node@22.13.0-slim?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": "08b3640040230552a4bb431c4b3ec75cfb725211",
          "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": "8xs5k936gdes1o03mjam1jht0",
          "local-sessionid:dockerfile": "8xs5k936gdes1o03mjam1jht0",
          "local-sessionid:shared": "8xs5k936gdes1o03mjam1jht0"
        },
        "locals": [
          {
            "name": "context"
          },
          {
            "name": "dist"
          },
          {
            "name": "dockerfile"
          },
          {
            "name": "shared"
          }
        ]
      },
      "environment": {
        "platform": "linux/arm64"
      }
    }
  },
  "buildx.build.ref": "builder-713199cf-705d-4778-8686-5fb6c1837733/builder-713199cf-705d-4778-8686-5fb6c18377330/y69hsq5vxilve1klii14iqowe",
  "containerimage.config.digest": "sha256:bc20d0b6d0d3db57e58424087efe22d468eac9baf8f9d8f0fb7bff8388da2b73",
  "containerimage.descriptor": {
    "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
    "digest": "sha256:4ddba75bf2229539114fa7af05be80541e5315c0cd7c2a9f420150cefe9d67c6",
    "size": 2075,
    "platform": {
      "architecture": "arm64",
      "os": "linux"
    }
  },
  "containerimage.digest": "sha256:4ddba75bf2229539114fa7af05be80541e5315c0cd7c2a9f420150cefe9d67c6",
  "image.name": "ghcr.io/graphql-hive/policy:08b3640040230552a4bb431c4b3ec75cfb725211-arm64,ghcr.io/graphql-hive/policy:console_1003-arm64"
},
"rate-limit": {
  "buildx.build.provenance": {
    "buildType": "https://mobyproject.org/buildkit@v1",
    "materials": [
      {
        "uri": "pkg:docker/node@22.13.0-slim?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": "08b3640040230552a4bb431c4b3ec75cfb725211",
          "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": "8xs5k936gdes1o03mjam1jht0",
          "local-sessionid:dockerfile": "8xs5k936gdes1o03mjam1jht0",
          "local-sessionid:shared": "8xs5k936gdes1o03mjam1jht0"
        },
        "locals": [
          {
            "name": "context"
          },
          {
            "name": "dist"
          },
          {
            "name": "dockerfile"
          },
          {
            "name": "shared"
          }
        ]
      },
      "environment": {
        "platform": "linux/arm64"
      }
    }
  },
  "buildx.build.ref": "builder-713199cf-705d-4778-8686-5fb6c1837733/builder-713199cf-705d-4778-8686-5fb6c18377330/kskzc7qy3l7xh4ov7ts34er8c",
  "containerimage.config.digest": "sha256:201b3b100bb800f56adafd065bb59dc07878d30aa4d89f8f23a049cec64fbf06",
  "containerimage.descriptor": {
    "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
    "digest": "sha256:6ff92939d027222dd10763b53ca58eaccbde8d3cfd0c78411857666ebfceb721",
    "size": 2075,
    "platform": {
      "architecture": "arm64",
      "os": "linux"
    }
  },
  "containerimage.digest": "sha256:6ff92939d027222dd10763b53ca58eaccbde8d3cfd0c78411857666ebfceb721",
  "image.name": "ghcr.io/graphql-hive/rate-limit:08b3640040230552a4bb431c4b3ec75cfb725211-arm64,ghcr.io/graphql-hive/rate-limit:console_1003-arm64"
},
"schema": {
  "buildx.build.provenance": {
    "buildType": "https://mobyproject.org/buildkit@v1",
    "materials": [
      {
        "uri": "pkg:docker/node@22.13.0-slim?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": "08b3640040230552a4bb431c4b3ec75cfb725211",
          "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": "8xs5k936gdes1o03mjam1jht0",
          "local-sessionid:dockerfile": "8xs5k936gdes1o03mjam1jht0",
          "local-sessionid:shared": "8xs5k936gdes1o03mjam1jht0"
        },
        "locals": [
          {
            "name": "context"
          },
          {
            "name": "dist"
          },
          {
            "name": "dockerfile"
          },
          {
            "name": "shared"
          }
        ]
      },
      "environment": {
        "platform": "linux/arm64"
      }
    }
  },
  "buildx.build.ref": "builder-713199cf-705d-4778-8686-5fb6c1837733/builder-713199cf-705d-4778-8686-5fb6c18377330/jop58uzcnqrxf0jyz969fk96d",
  "containerimage.config.digest": "sha256:543ff55b888915c988e16aafab1f4669a73c9a191afc1aab9cc9319e8eb0feae",
  "containerimage.descriptor": {
    "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
    "digest": "sha256:ae5961c9f37102f16dd229636e04003d5ea82db39a9f4b2141b96b56ee7238e8",
    "size": 2075,
    "platform": {
      "architecture": "arm64",
      "os": "linux"
    }
  },
  "containerimage.digest": "sha256:ae5961c9f37102f16dd229636e04003d5ea82db39a9f4b2141b96b56ee7238e8",
  "image.name": "ghcr.io/graphql-hive/schema:08b3640040230552a4bb431c4b3ec75cfb725211-arm64,ghcr.io/graphql-hive/schema:console_1003-arm64"
},
"server": {
  "buildx.build.provenance": {
    "buildType": "https://mobyproject.org/buildkit@v1",
    "materials": [
      {
        "uri": "pkg:docker/node@22.13.0-slim?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": "08b3640040230552a4bb431c4b3ec75cfb725211",
          "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": "8xs5k936gdes1o03mjam1jht0",
          "local-sessionid:dockerfile": "8xs5k936gdes1o03mjam1jht0",
          "local-sessionid:shared": "8xs5k936gdes1o03mjam1jht0"
        },
        "locals": [
          {
            "name": "context"
          },
          {
            "name": "dist"
          },
          {
            "name": "dockerfile"
          },
          {
            "name": "shared"
          }
        ]
      },
      "environment": {
        "platform": "linux/arm64"
      }
    }
  },
  "buildx.build.ref": "builder-713199cf-705d-4778-8686-5fb6c1837733/builder-713199cf-705d-4778-8686-5fb6c18377330/d529jq5nf9owu7lzx52hryxme",
  "containerimage.config.digest": "sha256:8e8f656915a68129b3e2d710da0e2f1932efc3610f4004cfb159a07fd40597d9",
  "containerimage.descriptor": {
    "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
    "digest": "sha256:abda444d2671de4a070f686ce8161841dff0896591c4fc928bb10f1f579833e8",
    "size": 2076,
    "platform": {
      "architecture": "arm64",
      "os": "linux"
    }
  },
  "containerimage.digest": "sha256:abda444d2671de4a070f686ce8161841dff0896591c4fc928bb10f1f579833e8",
  "image.name": "ghcr.io/graphql-hive/server:08b3640040230552a4bb431c4b3ec75cfb725211-arm64,ghcr.io/graphql-hive/server:console_1003-arm64"
},
"storage": {
  "buildx.build.provenance": {
    "buildType": "https://mobyproject.org/buildkit@v1",
    "materials": [
      {
        "uri": "pkg:docker/node@22.13.0-slim?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": "08b3640040230552a4bb431c4b3ec75cfb725211",
          "context:dist": "local:dist",
          "context:shared": "local:shared",
          "frontend.caps": "moby.buildkit.frontend.contexts+forward",
          "local-sessionid:context": "8xs5k936gdes1o03mjam1jht0",
          "local-sessionid:dockerfile": "8xs5k936gdes1o03mjam1jht0",
          "local-sessionid:shared": "8xs5k936gdes1o03mjam1jht0"
        },
        "locals": [
          {
            "name": "context"
          },
          {
            "name": "dist"
          },
          {
            "name": "dockerfile"
          },
          {
            "name": "shared"
          }
        ]
      },
      "environment": {
        "platform": "linux/arm64"
      }
    }
  },
  "buildx.build.ref": "builder-713199cf-705d-4778-8686-5fb6c1837733/builder-713199cf-705d-4778-8686-5fb6c18377330/bttklhsx5fztbwrjlkfl56tmf",
  "containerimage.config.digest": "sha256:3fad981df079c0850c20689fde0445bca306fc74dfc0a63b7fd31a5a8fa9f4c5",
  "containerimage.descriptor": {
    "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
    "digest": "sha256:b8add7d22d706d43dbc0ce589407d8ddcc76d649f07b19decf57b28c25517717",
    "size": 2075,
    "platform": {
      "architecture": "arm64",
      "os": "linux"
    }
  },
  "containerimage.digest": "sha256:b8add7d22d706d43dbc0ce589407d8ddcc76d649f07b19decf57b28c25517717",
  "image.name": "ghcr.io/graphql-hive/storage:08b3640040230552a4bb431c4b3ec75cfb725211-arm64,ghcr.io/graphql-hive/storage:console_1003-arm64"
},
"stripe-billing": {
  "buildx.build.provenance": {
    "buildType": "https://mobyproject.org/buildkit@v1",
    "materials": [
      {
        "uri": "pkg:docker/node@22.13.0-slim?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": "08b3640040230552a4bb431c4b3ec75cfb725211",
          "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": "8xs5k936gdes1o03mjam1jht0",
          "local-sessionid:dockerfile": "8xs5k936gdes1o03mjam1jht0",
          "local-sessionid:shared": "8xs5k936gdes1o03mjam1jht0"
        },
        "locals": [
          {
            "name": "context"
          },
          {
            "name": "dist"
          },
          {
            "name": "dockerfile"
          },
          {
            "name": "shared"
          }
        ]
      },
      "environment": {
        "platform": "linux/arm64"
      }
    }
  },
  "buildx.build.ref": "builder-713199cf-705d-4778-8686-5fb6c1837733/builder-713199cf-705d-4778-8686-5fb6c18377330/pmm099nbox87n3wf3vhwuvvvz",
  "containerimage.config.digest": "sha256:43753930639b10cc3448bc322515745e3947604e532cf134f461256dc34fe651",
  "containerimage.descriptor": {
    "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
    "digest": "sha256:7c082187940382140a0323e3666e084d463aa8bef0ac75a153ba6a48c9637ed4",
    "size": 2075,
    "platform": {
      "architecture": "arm64",
      "os": "linux"
    }
  },
  "containerimage.digest": "sha256:7c082187940382140a0323e3666e084d463aa8bef0ac75a153ba6a48c9637ed4",
  "image.name": "ghcr.io/graphql-hive/stripe-billing:08b3640040230552a4bb431c4b3ec75cfb725211-arm64,ghcr.io/graphql-hive/stripe-billing:console_1003-arm64"
},
"tokens": {
  "buildx.build.provenance": {
    "buildType": "https://mobyproject.org/buildkit@v1",
    "materials": [
      {
        "uri": "pkg:docker/node@22.13.0-slim?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": "08b3640040230552a4bb431c4b3ec75cfb725211",
          "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": "8xs5k936gdes1o03mjam1jht0",
          "local-sessionid:dockerfile": "8xs5k936gdes1o03mjam1jht0",
          "local-sessionid:shared": "8xs5k936gdes1o03mjam1jht0"
        },
        "locals": [
          {
            "name": "context"
          },
          {
            "name": "dist"
          },
          {
            "name": "dockerfile"
          },
          {
            "name": "shared"
          }
        ]
      },
      "environment": {
        "platform": "linux/arm64"
      }
    }
  },
  "buildx.build.ref": "builder-713199cf-705d-4778-8686-5fb6c1837733/builder-713199cf-705d-4778-8686-5fb6c18377330/onw1lawopdxz0iiugzaer6mgm",
  "containerimage.config.digest": "sha256:10ba0cd038f163c59755541fa78a5ec71e124e3e72dc446598d59ddb189c9702",
  "containerimage.descriptor": {
    "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
    "digest": "sha256:f9100792c3dd0cc24d30d94184b43b71acd23754ab40d46df974cfea7dae7f85",
    "size": 2075,
    "platform": {
      "architecture": "arm64",
      "os": "linux"
    }
  },
  "containerimage.digest": "sha256:f9100792c3dd0cc24d30d94184b43b71acd23754ab40d46df974cfea7dae7f85",
  "image.name": "ghcr.io/graphql-hive/tokens:08b3640040230552a4bb431c4b3ec75cfb725211-arm64,ghcr.io/graphql-hive/tokens:console_1003-arm64"
},
"usage": {
  "buildx.build.provenance": {
    "buildType": "https://mobyproject.org/buildkit@v1",
    "materials": [
      {
        "uri": "pkg:docker/node@22.13.0-slim?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": "08b3640040230552a4bb431c4b3ec75cfb725211",
          "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": "8xs5k936gdes1o03mjam1jht0",
          "local-sessionid:dockerfile": "8xs5k936gdes1o03mjam1jht0",
          "local-sessionid:shared": "8xs5k936gdes1o03mjam1jht0"
        },
        "locals": [
          {
            "name": "context"
          },
          {
            "name": "dist"
          },
          {
            "name": "dockerfile"
          },
          {
            "name": "shared"
          }
        ]
      },
      "environment": {
        "platform": "linux/arm64"
      }
    }
  },
  "buildx.build.ref": "builder-713199cf-705d-4778-8686-5fb6c1837733/builder-713199cf-705d-4778-8686-5fb6c18377330/r6fgidatg5sm8rv04m9okov40",
  "containerimage.config.digest": "sha256:f83ccbda35df182ac373b003a9fcb248f4d4a42a39a99d37820dbe0ed5e3d2a5",
  "containerimage.descriptor": {
    "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
    "digest": "sha256:46758237a9b8a3f203ada827495247a1f199d53c0857f468f6fde84b1833c435",
    "size": 2075,
    "platform": {
      "architecture": "arm64",
      "os": "linux"
    }
  },
  "containerimage.digest": "sha256:46758237a9b8a3f203ada827495247a1f199d53c0857f468f6fde84b1833c435",
  "image.name": "ghcr.io/graphql-hive/usage:08b3640040230552a4bb431c4b3ec75cfb725211-arm64,ghcr.io/graphql-hive/usage:console_1003-arm64"
},
"usage-estimator": {
  "buildx.build.provenance": {
    "buildType": "https://mobyproject.org/buildkit@v1",
    "materials": [
      {
        "uri": "pkg:docker/node@22.13.0-slim?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": "08b3640040230552a4bb431c4b3ec75cfb725211",
          "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": "8xs5k936gdes1o03mjam1jht0",
          "local-sessionid:dockerfile": "8xs5k936gdes1o03mjam1jht0",
          "local-sessionid:shared": "8xs5k936gdes1o03mjam1jht0"
        },
        "locals": [
          {
            "name": "context"
          },
          {
            "name": "dist"
          },
          {
            "name": "dockerfile"
          },
          {
            "name": "shared"
          }
        ]
      },
      "environment": {
        "platform": "linux/arm64"
      }
    }
  },
  "buildx.build.ref": "builder-713199cf-705d-4778-8686-5fb6c1837733/builder-713199cf-705d-4778-8686-5fb6c18377330/xjai3f2n8mmwhn2qof3kyvknu",
  "containerimage.config.digest": "sha256:50a685393dbcdac1d4021402d83d6267aeece1f6a04e9c8887ed54ebf6126cff",
  "containerimage.descriptor": {
    "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
    "digest": "sha256:03449ee26f38e10c2fd1c9ffb75afbbc5d73924437e556f7047f13abd5699bf0",
    "size": 2075,
    "platform": {
      "architecture": "arm64",
      "os": "linux"
    }
  },
  "containerimage.digest": "sha256:03449ee26f38e10c2fd1c9ffb75afbbc5d73924437e556f7047f13abd5699bf0",
  "image.name": "ghcr.io/graphql-hive/usage-estimator:08b3640040230552a4bb431c4b3ec75cfb725211-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/node@22.13.0-slim?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": "08b3640040230552a4bb431c4b3ec75cfb725211",
          "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": "8xs5k936gdes1o03mjam1jht0",
          "local-sessionid:dockerfile": "8xs5k936gdes1o03mjam1jht0",
          "local-sessionid:shared": "8xs5k936gdes1o03mjam1jht0"
        },
        "locals": [
          {
            "name": "context"
          },
          {
            "name": "dist"
          },
          {
            "name": "dockerfile"
          },
          {
            "name": "shared"
          }
        ]
      },
      "environment": {
        "platform": "linux/arm64"
      }
    }
  },
  "buildx.build.ref": "builder-713199cf-705d-4778-8686-5fb6c1837733/builder-713199cf-705d-4778-8686-5fb6c18377330/o78f0flr5x27fsvlvo4gw64k8",
  "containerimage.config.digest": "sha256:2fc31d1025d525b94349168916e025fdce4101b9a388931853d19ba058394555",
  "containerimage.descriptor": {
    "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
    "digest": "sha256:05bf61ce297eb08b1d3e4669c096b281708b104c7132e962ab3bdb4d65f4d881",
    "size": 2075,
    "platform": {
      "architecture": "arm64",
      "os": "linux"
    }
  },
  "containerimage.digest": "sha256:05bf61ce297eb08b1d3e4669c096b281708b104c7132e962ab3bdb4d65f4d881",
  "image.name": "ghcr.io/graphql-hive/usage-ingestor:08b3640040230552a4bb431c4b3ec75cfb725211-arm64,ghcr.io/graphql-hive/usage-ingestor:console_1003-arm64"
},
"webhooks": {
  "buildx.build.provenance": {
    "buildType": "https://mobyproject.org/buildkit@v1",
    "materials": [
      {
        "uri": "pkg:docker/node@22.13.0-slim?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": "08b3640040230552a4bb431c4b3ec75cfb725211",
          "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": "8xs5k936gdes1o03mjam1jht0",
          "local-sessionid:dockerfile": "8xs5k936gdes1o03mjam1jht0",
          "local-sessionid:shared": "8xs5k936gdes1o03mjam1jht0"
        },
        "locals": [
          {
            "name": "context"
          },
          {
            "name": "dist"
          },
          {
            "name": "dockerfile"
          },
          {
            "name": "shared"
          }
        ]
      },
      "environment": {
        "platform": "linux/arm64"
      }
    }
  },
  "buildx.build.ref": "builder-713199cf-705d-4778-8686-5fb6c1837733/builder-713199cf-705d-4778-8686-5fb6c18377330/ycvdokvds2zkxtr4w9r9p6z1z",
  "containerimage.config.digest": "sha256:c1af4e3d95d91c2228c9613f4d92277750ce2088708056aea75657ff69ff51b2",
  "containerimage.descriptor": {
    "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
    "digest": "sha256:d46b7799e6de356b19b86024165b492fee6c7165519f00317dfb403073b26a96",
    "size": 2075,
    "platform": {
      "architecture": "arm64",
      "os": "linux"
    }
  },
  "containerimage.digest": "sha256:d46b7799e6de356b19b86024165b492fee6c7165519f00317dfb403073b26a96",
  "image.name": "ghcr.io/graphql-hive/webhooks:08b3640040230552a4bb431c4b3ec75cfb725211-arm64,ghcr.io/graphql-hive/webhooks:console_1003-arm64"
}
}

@jasonkuhrt jasonkuhrt marked this pull request as ready for review February 6, 2025 17:33
cypress.config.ts Outdated Show resolved Hide resolved
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

    • The embedded code editor now offers real-time JavaScript diagnostics with immediate feedback and suggestions, enhancing the code authoring experience.
  • Improvements

    • The preflight interface has updated labels and a refined layout for a cleaner, more intuitive workflow.
    • Editor integrations have been modernized to support smoother interactions and improved performance.
  • Chores

    • Underlying updates bolster test reliability and add support for local end-to-end testing.

Walkthrough

The changes involve a refactor of Cypress tests, replacing the laboratory object (imported from ../support/testkit) with a new cyLaboratory namespace (imported from ./__cypress__) for interacting with the GraphiQL editor and managing tabs. This includes the addition of new methods and the restructuring of existing test cases. A new cyMonaco namespace has been introduced for Monaco editor interactions, and a dedent function has been added for template string processing. Additionally, updates to TypeScript configurations, documentation, and package dependencies support these changes.

Changes

File(s) Change Summary
cypress/e2e/laboratory/collections.cy.ts, cypress/e2e/laboratory/tabs.cy.ts Updated import and usage: replaced laboratory from ../support/testkit with cyLaboratory from ./__cypress__; updated method calls (e.g., closeTabsUntilOneLeft, updateEditorValue, etc.).
cypress/e2e/laboratory/preflight.cy.ts Updated selectors to use cyLaboratory.preflight.selectors; removed helper setMonacoEditorContents and replaced it with cyPreflight.setScriptEditorContent and cyPreflight.setEnvironmentEditorContent; improved test descriptions and added TypeScript validation test.
cypress/e2e/laboratory/__cypress__.ts Introduced the new cyLaboratory namespace with methods for editor value manipulation and tab management, plus a nested preflight namespace for preflight editor functionality.
cypress/support/monaco.ts Added the cyMonaco namespace with helper functions: setContent, goToNextProblem, and nextProblemContains for interacting with the Monaco editor.
cypress/support/dedent.ts Introduced a new dedent function for processing template strings with proper indentation removal.
cypress/support/e2e.ts Added an import for cypress-real-events and explanatory comments about Cypress limitations on simulating real events.
cypress/support/testkit.ts Removed the legacy laboratory object methods (e.g., updateEditorValue, getEditorValue, openNewTab, etc.) and the dedent utility.
package.json Added a new script (test:e2e:local) for local testing and new devDependencies: cypress-real-events and monaco-editor.
packages/web/app/src/lib/preflight/graphiql-plugin.tsx Updated Monaco Editor configuration to enable JavaScript diagnostics and validation during code editing.
cypress/tsconfig.json Updated TypeScript compiler options: added types cypress-real-events and monaco-editor, and set "moduleResolution": "node".
.changeset/funny-poets-hang.md Updated documentation related to Laboratory Preflight, including TypeScript validation details and guidance for handling TS errors.

Possibly related PRs


📜 Recent review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 865a7bf and 08b3640.

📒 Files selected for processing (1)
  • package.json (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • package.json
⏰ Context from checks skipped due to timeout of 90000ms (11)
  • GitHub Check: alpha / npm / snapshot
  • 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: code-style / eslint-and-prettier
  • GitHub Check: build / dockerize (linux/arm64)
  • GitHub Check: build / dockerize (linux/amd64)
  • GitHub Check: alpha / cli-artifacts
  • GitHub Check: SonarCloud

Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 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.

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)
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
@jasonkuhrt jasonkuhrt marked this pull request as draft February 10, 2025 14:59
@@ -0,0 +1,52 @@
export function dedent(strings: TemplateStringsArray, ...values: unknown[]): string {
Copy link
Contributor

Choose a reason for hiding this comment

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

What's the benefit of having dedent.ts that exports dedent function?
Why not just have libs.ts or shared.ts or similar?

Copy link
Contributor

Choose a reason for hiding this comment

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

Now when we change it's name to dedentString we rename a file

Copy link
Contributor

Choose a reason for hiding this comment

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

and change ~5 imports and can loose git history if file rename is done incorrectly.

Copy link
Member Author

@jasonkuhrt jasonkuhrt Feb 12, 2025

Choose a reason for hiding this comment

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

I find large modules that accumulate dozens of imports and thousands of lines that span various concerns difficult to cognitively navigate. Since the contents of these files change significantly more often than their file names, the readability gains have outweighed the diff loss for me.

If we could only write code in one file, I would try writing large comment section headers that divide areas of concern. Inline namespaces would be good too, but being only TS, not real ESM, there's always that tradeoff.

Copy link
Contributor

Choose a reason for hiding this comment

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

To me, a file with 500LOC and 20 functions is fine

Copy link
Contributor

Choose a reason for hiding this comment

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

We don't need to have a style guide, because it's not like we use X, but not Y, everywhere, always and that's the rule. We had 200 eslint rules, because somebody wanted to enforce patterns, and it did not work, was blocking progress, because the amount of time we spent on fighting the missing object destruction, was too annoying.
In general, we just start simple, see when it breaks, we do not try to predict future (imo it's always a failure, no matter how long you are on this world) and improve over time.

We had so many refactors, especially on the frontend that having to rename files was just super hard to track and most of the time the git history for a file, was gone. Maybe it did not happen to you, but it did to me, a few times in past 2 years.

You say that the content of the file changes more frequently that the file names. That's your assumption and based on my experience from this codebase, it's not true, not for frontend and not for tests.

Regarding the style. When I enter a new codebase, I look how people write it. You did see a chunk of the codebase already, and you can tell what the style is (I assume, because you worked on the CLI and probably looked at other pieces).
You see how Laboratory is written, yes it's not the best thing in the world, but not because it has 1 file instead of 5, or 3 components instead of 7, but rather the logic is hard to follow.

Copy link
Contributor

Choose a reason for hiding this comment

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

I don't mind refactors when they are necessary (like when you can't achieve something and it's blocking a new feature). Even when they are nice-to-have and we already delivered something to customers, but when we discuss it.

Copy link
Member Author

@jasonkuhrt jasonkuhrt Feb 12, 2025

Choose a reason for hiding this comment

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

Thanks for all your insights on this project. You describe thresholds that I see crossed and you don't. For example the graphiql module's range of concerns is a some kind of blocking like factor for me. most things aren't actually blocking, we're talking about what it takes for a human to develop sustainably. What I see ahead is an approach leaning on code ownership rather than principals, which for me personally is a more confusing way to work. I like spontaneity and creativity but I have no agency to exercise that here so from a group perspective, if the cohesion is not high, then principals and rules thereof are the most transparent form of collaboration I think.

In regards to asking for permission to refactor things, I've taken note of that I understand the point. One downside for me is that there's need and opportunity for improvement in this project and a lot of that doesn't cross a need-threshold that matches the energy I have to summon to negotiate permission, especially in an async multi-timezone setup where what I would do according to my experience would require permission, waiting until tomorrow for a response in many cases (time zones), probably being told no, etc. So instead I just slog through repeatedly which is not satisfying.

Copy link
Contributor

Choose a reason for hiding this comment

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

To discuss it first, I mean, show it to the group to see if people like or not, or they see it necessary, or not. You mentions guidelines and rules, but then you have issues discussing something or to get an approval, to me it's conflicting.

Copy link
Member Author

@jasonkuhrt jasonkuhrt Feb 12, 2025

Choose a reason for hiding this comment

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

I mention a downside I see to permission processes which is a kind of group level social organizing rule in lieu of (in this case) craft principals or rules that would obviate the need for such permission process in some degree of cases. I see no contradiction, hopefully that's clear.

Copy link
Contributor

github-actions bot commented Feb 12, 2025

🚀 Snapshot Release (alpha)

The latest changes of this PR are available as alpha on npm (based on the declared changesets):

Package Version Info
@graphql-hive/cli 0.48.2-alpha-20250212202247-08b3640040230552a4bb431c4b3ec75cfb725211 npm ↗︎ unpkg ↗︎
hive 5.2.0-alpha-20250212202247-08b3640040230552a4bb431c4b3ec75cfb725211 npm ↗︎ unpkg ↗︎
@graphql-hive/federation-link-utils 0.0.1 npm ↗︎ unpkg ↗︎

@jasonkuhrt jasonkuhrt marked this pull request as ready for review February 12, 2025 14:51
@jasonkuhrt
Copy link
Member Author

@n1ru4l Please let me know if refactors to the Cypress tests should also be undone in this PR, or if undoing just the app refactor is enough.

@jasonkuhrt
Copy link
Member Author

jasonkuhrt commented Feb 12, 2025

I just found a bug while writing the changeset. After saving, upon re-opening, Monaco seems to retain previous validation state such that the content will be seen as, for example, duplicating identifiers.

Cannot redeclare block-scoped variable 'b2'.(2451)

I will write a test for this and fix it.

CleanShot 2025-02-12 at 10 25 21@2x

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

🧹 Nitpick comments (5)
cypress/support/monaco.ts (1)

23-25: Document platform-specific keyboard shortcut behavior.

The keyboard shortcut Alt+F8 might not work consistently across different operating systems.

Add a comment explaining platform-specific considerations:

  export function goToNextProblem(editorCyName: string) {
+   // Note: Alt+F8 is the default Monaco keyboard shortcut for "Go to Next Problem".
+   // This might need adjustment for different operating systems (e.g., Option+F8 on macOS).
    cy.dataCy(editorCyName).find('textarea').focus().realPress(['Alt', 'F8']);
  }
cypress/e2e/laboratory/__cypress__.ts (1)

52-60: Consider adding a safety check for infinite recursion.

The recursive closeTabsUntilOneLeft function could potentially cause a stack overflow if the tab count doesn't decrease.

Add a safety check:

  export function closeTabsUntilOneLeft() {
+   let attempts = 0;
+   const maxAttempts = 100; // Adjust based on expected maximum number of tabs
+   
+   function closeTabsRecursive() {
      cy.get('li.graphiql-tab').then($tabs => {
        if ($tabs.length > 1) {
+         if (attempts++ > maxAttempts) {
+           throw new Error('Failed to close tabs after maximum attempts');
+         }
          closeActiveTab();
-         return closeTabsUntilOneLeft();
+         return closeTabsRecursive();
        }
      });
+   }
+   
+   closeTabsRecursive();
  }
packages/web/app/src/lib/preflight/graphiql-plugin.tsx (1)

649-660: Consider documenting compiler options choices.

The compiler options are well-chosen but would benefit from documentation explaining the rationale.

Add comments explaining key choices:

    monaco.languages.typescript.javascriptDefaults.setCompilerOptions({
      allowNonTsExtensions: true,
      allowJs: true,
      checkJs: true,
+     // Using ES2020 for modern JavaScript features while maintaining broad compatibility
      target: monaco.languages.typescript.ScriptTarget.ES2020,
+     // Using webworker lib as the script runs in a sandboxed environment
      lib: ['webworker'],
+     // Enabling strict mode and related checks for better error detection
      strict: true,
      noUnusedLocals: true,
      noUnusedParameters: true,
      noImplicitReturns: true,
      noFallthroughCasesInSwitch: true,
    });
.changeset/funny-poets-hang.md (2)

5-9: Enhance readability with proper punctuation and more details.

The content is informative but needs some grammatical improvements:

-Also, the `WebWorker` runtime types are applied giving you confidence about what globals are available to you in your script.
+Also, the `WebWorker` runtime types are applied, giving you confidence about what globals are available to you in your script.

-However at this time we do not prevent script saving because of static type errors. Therefore your workflow should only at worst be visually impacted.
+However, at this time we do not prevent script saving because of static type errors. Therefore, your workflow should only, at worst, be visually impacted.

Consider adding an example of what kind of script code might now trigger static errors.

🧰 Tools
🪛 LanguageTool

[uncategorized] ~5-~5: A comma might be missing here.
Context: ...Also, the WebWorker runtime types are applied giving you confidence about what global...

(AI_EN_LECTOR_MISSING_PUNCTUATION_COMMA)


[uncategorized] ~9-~9: A comma may be missing after the conjunctive/linking adverb ‘However’.
Context: ...iously not statically errored will now. However at this time we do not prevent script s...

(SENT_START_CONJUNCTIVE_LINKING_ADVERB_COMMA)


[uncategorized] ~9-~9: A comma may be missing after the conjunctive/linking adverb ‘Therefore’.
Context: ...t saving because of static type errors. Therefore your workflow should only at worst be v...

(SENT_START_CONJUNCTIVE_LINKING_ADVERB_COMMA)


23-32: Enhance code example relevance and clarity.

Consider making the example more relevant to the WebWorker context and clarifying the difference between the two TypeScript directives:

-let a = 1;
-let b = '';
+// Example with WebWorker-specific globals
+let response = '';
+let headers = new Headers();
 // @ts-ignore
-a = b;
+response = headers; // Silently ignores the type error
 // @ts-expect-error
-a = b;
+response = headers; // Will error if types become compatible

This example better demonstrates the usage in the context of WebWorker globals while maintaining the explanation of TypeScript directives.

🧰 Tools
🪛 LanguageTool

[uncategorized] ~32-~32: Possible missing comma found.
Context: ...ore, then the comment itself becomes an error whereas @ts-ignore sits there quietly...

(AI_HYDRA_LEO_MISSING_COMMA)

📜 Review details

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

📥 Commits

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

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (8)
  • .changeset/funny-poets-hang.md (1 hunks)
  • cypress/e2e/laboratory/__cypress__.ts (1 hunks)
  • cypress/e2e/laboratory/collections.cy.ts (9 hunks)
  • cypress/e2e/laboratory/preflight.cy.ts (17 hunks)
  • cypress/e2e/laboratory/tabs.cy.ts (2 hunks)
  • cypress/support/monaco.ts (1 hunks)
  • package.json (3 hunks)
  • packages/web/app/src/lib/preflight/graphiql-plugin.tsx (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • cypress/e2e/laboratory/tabs.cy.ts
  • package.json
  • cypress/e2e/laboratory/collections.cy.ts
🧰 Additional context used
📓 Path-based instructions (1)
`.changeset/**`: A special folder with the files created/man...

.changeset/**: A special folder with the files created/managed by Changesets tool.
In PRs that already includes this file, please make sure that the content of the markdown file includes:

  • Full description of the change
  • A link to the relevant GitHub issues (if any)
  • Example with before/after
  • If the change is of type major, please ensure that the content have content about migration with before/after.
  • .changeset/funny-poets-hang.md
🧠 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.
🪛 LanguageTool
.changeset/funny-poets-hang.md

[uncategorized] ~5-~5: A comma might be missing here.
Context: ...Also, the WebWorker runtime types are applied giving you confidence about what global...

(AI_EN_LECTOR_MISSING_PUNCTUATION_COMMA)


[uncategorized] ~9-~9: A comma may be missing after the conjunctive/linking adverb ‘However’.
Context: ...iously not statically errored will now. However at this time we do not prevent script s...

(SENT_START_CONJUNCTIVE_LINKING_ADVERB_COMMA)


[uncategorized] ~9-~9: A comma may be missing after the conjunctive/linking adverb ‘Therefore’.
Context: ...t saving because of static type errors. Therefore your workflow should only at worst be v...

(SENT_START_CONJUNCTIVE_LINKING_ADVERB_COMMA)


[uncategorized] ~32-~32: Possible missing comma found.
Context: ...ore, then the comment itself becomes an error whereas @ts-ignore sits there quietly...

(AI_HYDRA_LEO_MISSING_COMMA)

⏰ Context from checks skipped due to timeout of 90000ms (11)
  • GitHub Check: alpha / npm / snapshot
  • GitHub Check: storybook-preview / deployment
  • GitHub Check: typescript / typecheck
  • GitHub Check: alpha / cli-artifacts
  • GitHub Check: test / unit
  • GitHub Check: static-analysis / analyze (typescript)
  • GitHub Check: static-analysis / analyze (javascript)
  • GitHub Check: build / dockerize (linux/arm64)
  • GitHub Check: build / dockerize (linux/amd64)
  • GitHub Check: code-style / eslint-and-prettier
  • GitHub Check: SonarCloud
🔇 Additional comments (6)
cypress/support/monaco.ts (2)

5-21: LGTM! Well-structured helper function for setting Monaco editor content.

The function properly handles the Monaco editor instance lookup and includes appropriate error handling.


27-32: Consider alternatives to hardcoded wait.

The hardcoded wait of 1000ms is an anti-pattern in Cypress and could make tests flaky.

While the comment indicates this is a known limitation, let's verify if there are better alternatives:

✅ Verification successful

🌐 Web query:

What are the best practices for handling Monaco editor validation timing in Cypress tests?

💡 Result:

When handling validation timing in Monaco Editor during Cypress tests, implement these best practices to ensure reliable test execution:

1. Target Editor Elements Effectively

Use role-based selectors combined with Monaco's DOM structure:

cy.get('[role="code"]').first().click().type('{selectall}New content')

This mimics real user interaction better than direct API calls[3][6].

2. Leverage Cypress Retry Mechanism

Add explicit assertions with custom timeouts:

cy.get('.suggest-widget', { timeout: 15000 })
  .should('be.visible')
  .contains('Expected Validation Message')

Cypress automatically retries until DOM elements meet specified conditions or timeout[2][6].

3. Avoid Static Waits

Replace arbitrary delays with dynamic validation:

// Anti-pattern
cy.wait(5000)

// Recommended
cy.get('.monaco-editor')
  .should('contain', 'Expected Validation Text')
  .and('have.class', 'valid-state')

4. Use Direct API Access Sparingly

For complex validation scenarios, access Monaco's API through Cypress:

cy.window().then((win) => {
  const model = win.monaco.editor.getModels()[0]
  expect(model.getValue()).to.include('expected content')
})

Reserve this for validation checks rather than interactions[3][9].

5. Handle Keyboard Interactions

Simulate real user input patterns:

cy.get('[role="code"]')
  .click()
  .type('{ctrl}a') // Select all
  .type('New content{enter}')

6. Configure Timeout Settings

Adjust timeouts for Monaco-specific operations:

Cypress.config('defaultCommandTimeout', 10000)

Validation Timing Strategies

Approach Use Case Example
DOM Assertions Visual validation states cy.get('.error-marker').should('exist')
API Validation Content accuracy checks win.monaco.editor.getModel().getValue()
Network Stubbing Validate backend integration cy.intercept('POST', '/validate').as('validation')

Key Considerations:

  • Use data-testid attributes for stable element targeting[6]
  • Clear editor state between tests using beforeEach hooks[4]
  • Combine visual assertions with content validation checks[2]
  • Monitor network requests for validation callbacks[6]

These techniques address Monaco's dynamic rendering behavior while maintaining test reliability and execution speed[3][9]. For complex validation scenarios, consider combining DOM assertions with direct model checks through Monaco's API[3][9].

Citations:


Action: Replace Hardcoded Waits with Dynamic Assertions

The use of a static cy.wait(waitMs) is an anti-pattern in Cypress and can indeed lead to flaky tests. As confirmed by best practices for handling Monaco Editor timing in Cypress, it’s preferable to replace arbitrary delays with dynamic assertions. For example:

  • Use DOM Assertions: Instead of a fixed wait, wait for a clear condition or DOM element to appear (e.g., using cy.get('.monaco-editor').should('contain', 'expected content')).
  • Leverage Cypress Retry: With explicit, expectation-based waits, Cypress can automatically retry until the condition is met.
  • Utilize Editor API if Needed: In more complex scenarios, accessing Monaco’s API via cy.window() can provide additional state validation.

These changes will improve test reliability by synchronizing the test flow with the actual application state, eliminating unnecessary wait times.

cypress/e2e/laboratory/__cypress__.ts (1)

62-94: LGTM! Well-organized preflight namespace.

The preflight namespace is well-structured with clear separation of selectors and editor functions.

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

54-57: Document known limitation with Monaco editor validation.

The test uses nextProblemContains which internally uses a hardcoded wait. While this is not ideal, it's a known limitation when testing Monaco editor validation.

Add a comment to document this:

  it('script is validated with TypeScript', () => {
+   // Note: Monaco editor validation timing is unpredictable in tests.
+   // Using nextProblemContains with a wait is a known workaround.
    cyPreflight.setScriptEditorContent('let a = 1; a = ""');
    cyMonaco.nextProblemContains(selectors.modal.editorCy, "Type 'string' is not assignable to type 'number'."); // prettier-ignore
  });
packages/web/app/src/lib/preflight/graphiql-plugin.tsx (1)

643-648: LGTM! Comprehensive diagnostic options for JavaScript validation.

The configuration enables all necessary validation features for a robust development experience.

.changeset/funny-poets-hang.md (1)

1-3: LGTM! Version bump is appropriate.

The 'minor' version bump correctly reflects the addition of TypeScript validation while maintaining backwards compatibility.

cypress/e2e/laboratory/__cypress__.ts 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

♻️ Duplicate comments (1)
cypress/e2e/laboratory/preflight.cy.ts (1)

64-71: 🛠️ Refactor suggestion

Replace hardcoded wait with proper assertions.

The test uses a hardcoded wait(1000), which is an anti-pattern in Cypress and could make the test flaky.

Consider using Cypress's built-in retry-ability and assertions:

-    cy.wait(1000);
+    // Wait for Monaco editor to be ready
+    cy.get(selectors.modal.scriptEditor).should('exist');
+    cy.get('.monaco-editor').should('exist');
🧹 Nitpick comments (2)
cypress/e2e/laboratory/__cypress__.ts (1)

52-60: Consider using a loop instead of recursion.

The recursive implementation could be replaced with a loop for better readability and to avoid potential stack overflow with a large number of tabs.

-  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();
-      }
-    });
-  }
+  export function closeTabsUntilOneLeft() {
+    cy.get('li.graphiql-tab').then($tabs => {
+      while ($tabs.length > 1) {
+        closeActiveTab();
+        $tabs = Cypress.$('li.graphiql-tab');
+      }
+    });
+  }
.changeset/funny-poets-hang.md (1)

5-5: Fix missing commas in sentences.

Add commas after conjunctive adverbs and in compound sentences:

-Also, the `WebWorker` runtime types are applied giving you confidence about what globals are available to you in your script.
+Also, the `WebWorker` runtime types are applied, giving you confidence about what globals are available to you in your script.

-However at this time we do not prevent script saving because of static type errors. Therefore your workflow should only at worst be visually impacted.
+However, at this time we do not prevent script saving because of static type errors. Therefore, your workflow should only at worst be visually impacted.

Also applies to: 9-9

🧰 Tools
🪛 LanguageTool

[uncategorized] ~5-~5: A comma might be missing here.
Context: ...Also, the WebWorker runtime types are applied giving you confidence about what global...

(AI_EN_LECTOR_MISSING_PUNCTUATION_COMMA)

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 7c25002 and 0aa198e.

📒 Files selected for processing (4)
  • .changeset/funny-poets-hang.md (1 hunks)
  • cypress/e2e/laboratory/__cypress__.ts (1 hunks)
  • cypress/e2e/laboratory/preflight.cy.ts (17 hunks)
  • cypress/support/monaco.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • cypress/support/monaco.ts
🧰 Additional context used
📓 Path-based instructions (1)
`.changeset/**`: A special folder with the files created/man...

.changeset/**: A special folder with the files created/managed by Changesets tool.
In PRs that already includes this file, please make sure that the content of the markdown file includes:

  • Full description of the change
  • A link to the relevant GitHub issues (if any)
  • Example with before/after
  • If the change is of type major, please ensure that the content have content about migration with before/after.
  • .changeset/funny-poets-hang.md
🧠 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.
🪛 LanguageTool
.changeset/funny-poets-hang.md

[uncategorized] ~5-~5: A comma might be missing here.
Context: ...Also, the WebWorker runtime types are applied giving you confidence about what global...

(AI_EN_LECTOR_MISSING_PUNCTUATION_COMMA)


[uncategorized] ~9-~9: A comma may be missing after the conjunctive/linking adverb ‘However’.
Context: ...iously not statically errored will now. However at this time we do not prevent script s...

(SENT_START_CONJUNCTIVE_LINKING_ADVERB_COMMA)


[uncategorized] ~9-~9: A comma may be missing after the conjunctive/linking adverb ‘Therefore’.
Context: ...t saving because of static type errors. Therefore your workflow should only at worst be v...

(SENT_START_CONJUNCTIVE_LINKING_ADVERB_COMMA)

⏰ Context from checks skipped due to timeout of 90000ms (11)
  • GitHub Check: alpha / npm / snapshot
  • GitHub Check: test / unit
  • GitHub Check: typescript / typecheck
  • GitHub Check: storybook-preview / deployment
  • GitHub Check: static-analysis / analyze (typescript)
  • GitHub Check: static-analysis / analyze (javascript)
  • 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
🔇 Additional comments (2)
cypress/e2e/laboratory/__cypress__.ts (1)

8-11: Add error handling for CodeMirror instance.

The code uses type assertion without checking if the CodeMirror instance exists.

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

54-57: LGTM! TypeScript validation test is well-implemented.

The test effectively verifies that TypeScript type checking is working by asserting on the expected error message.

@jasonkuhrt
Copy link
Member Author

This may be related suren-atoyan/monaco-react#517

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

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

71-476: 💡 Verification agent

🧩 Analysis chain

Ensure proper test isolation.

The tests share state through localStorage and environment variables. Consider:

  1. Clearing all shared state in beforeEach hooks
  2. Using unique identifiers for each test to prevent interference
  3. Adding afterEach hooks to clean up any remaining state

🏁 Script executed:

#!/bin/bash
# Check for potential state leaks between tests
rg -A 5 'localStorage|environment' cypress/e2e/laboratory/

Length of output: 5203


Test Isolation Needs Improvement

The tests exhibit shared state usage—evidenced by a call to

window.localStorage.setItem('hive:laboratory:environment', '{}');

and multiple invocations to lab.environment.set. This indicates that state can persist across tests, potentially leading to interference. Consider the following improvements:

  • BeforeEach Hooks: Reset shared state (e.g., clear localStorage and reset environment variables) at the start of each test.
  • AfterEach Hooks: Clean up any state modifications to ensure tests remain independent.
  • Unique Identifiers: Use unique identifiers or values for environment settings where applicable to avoid conflicts.
🧹 Nitpick comments (5)
cypress/e2e/laboratory/preflight.cy.ts (1)

54-70: Consider adding more test cases for TypeScript validation.

While the current tests cover basic scenarios, consider adding tests for:

  1. Complex type inference scenarios
  2. Multiple type errors in the same script
  3. Edge cases like any type usage
  4. Global type definitions from the preflight context
packages/web/app/src/lib/preflight/graphiql-plugin.tsx (4)

641-677: Document the module detection workaround.

The module detection workaround uses a magic number (3) for moduleDetection. This should be documented more clearly or use a proper enum value.

Apply this diff to improve clarity:

-      // This is a workaround.
-      // 3 = 'force'
-      //
-      // Problem: https://github.com/graphql-hive/console/pull/6476#issuecomment-2654056957
-      // Solution: https://github.com/microsoft/monaco-editor/issues/2976#issuecomment-2334468503
-      // Reference: https://www.typescriptlang.org/tsconfig/#moduleDetection
-      moduleDetection: 3,
+      // Force module detection to prevent errors with top-level 'this' and 'await'
+      // See: https://github.com/microsoft/monaco-editor/issues/2976#issuecomment-2334468503
+      moduleDetection: monaco.languages.typescript.ModuleDetectionKind.Force,

649-667: Consider making compiler options configurable.

The TypeScript compiler options are hardcoded. Consider:

  1. Making them configurable through props
  2. Using environment variables for different environments
  3. Adding comments explaining the choice of options

604-833: Optimize component performance.

Consider these performance improvements:

  1. Memoize callback functions with useCallback
  2. Use useMemo for complex computations
  3. Split the component into smaller, more focused components

Example optimization:

+ const memoizedHandleMonacoEditorBeforeMount = useMemo(
+   () => (monaco: Monaco) => {
+     // Existing setup code
+   },
+   []
+ );

- beforeMount={handleMonacoEditorBeforeMount}
+ beforeMount={memoizedHandleMonacoEditorBeforeMount}

169-407: Enhance error handling robustness.

The error handling could be improved by:

  1. Adding timeouts for long-running operations
  2. Implementing retry logic for transient failures
  3. Adding more detailed error messages

Example implementation:

const EXECUTION_TIMEOUT = 30000; // 30 seconds

async function execute(script: string, isPreview = false): Promise<PreflightResultData> {
  const timeoutPromise = new Promise((_, reject) => {
    setTimeout(() => reject(new Error('Execution timeout')), EXECUTION_TIMEOUT);
  });

  try {
    const result = await Promise.race([executeScript(script, isPreview), timeoutPromise]);
    return result;
  } catch (error) {
    if (error instanceof Error) {
      setLogs(prev => [
        ...prev,
        {
          level: 'error',
          message: `Execution failed: ${error.message}`,
        },
      ]);
    }
    throw error;
  }
}
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 0aa198e and 60803b1.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (3)
  • cypress/e2e/laboratory/preflight.cy.ts (17 hunks)
  • cypress/support/monaco.ts (1 hunks)
  • packages/web/app/src/lib/preflight/graphiql-plugin.tsx (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • cypress/support/monaco.ts
🧰 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 (11)
  • GitHub Check: alpha / npm / snapshot
  • GitHub Check: test / unit
  • GitHub Check: storybook-preview / deployment
  • GitHub Check: static-analysis / analyze (typescript)
  • GitHub Check: static-analysis / analyze (javascript)
  • GitHub Check: alpha / cli-artifacts
  • GitHub Check: typescript / typecheck
  • GitHub Check: build / dockerize (linux/arm64)
  • GitHub Check: build / dockerize (linux/amd64)
  • GitHub Check: code-style / eslint-and-prettier
  • GitHub Check: SonarCloud
🔇 Additional comments (1)
cypress/e2e/laboratory/preflight.cy.ts (1)

1-22: Well-structured imports and setup!

The code demonstrates good organization with clear separation of concerns. The use of the cyLaboratory namespace improves modularity and maintainability.

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/preflight.cy.ts (2)

33-34: Consider using a more robust wait strategy.

Instead of waiting for text to disappear, consider waiting for a specific state or element attribute.

-    // Wait loading disappears
-    cy.dataCy('preflight-editor-mini').should('not.contain', 'Loading');
+    // Wait for editor to be ready
+    cy.dataCy('preflight-editor-mini').should('have.attr', 'data-ready', 'true');

407-410: Document the force click workaround.

Add a more detailed comment explaining why the force click is necessary and any potential risks.

-    // it's because the button is not fully visible on the screen
+    // Force click is required because the button might be partially obscured by other elements.
+    // This is a known limitation of the current UI layout and has been verified as safe.
     force: true,
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 60803b1 and fb29bdd.

📒 Files selected for processing (2)
  • cypress/e2e/laboratory/preflight.cy.ts (17 hunks)
  • cypress/support/e2e.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • cypress/support/e2e.ts
🧰 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 (11)
  • GitHub Check: alpha / npm / snapshot
  • GitHub Check: test / unit
  • GitHub Check: storybook-preview / deployment
  • GitHub Check: typescript / typecheck
  • GitHub Check: static-analysis / analyze (typescript)
  • GitHub Check: static-analysis / analyze (javascript)
  • 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 (5)
cypress/e2e/laboratory/preflight.cy.ts (5)

1-8: LGTM! Clean import organization and setup.

The imports are well-organized, and the new utility constants improve code maintainability by centralizing selectors and preflight-related functionality.


54-62: Well-structured TypeScript validation tests.

The new tests effectively validate both TypeScript type checking and syntax restrictions, aligning with the PR's objective to improve editor content validation.

Note: The use of cyMonaco.nextProblemContains is a good approach to handle Monaco editor's asynchronous validation.


64-70: Good regression test for validation state.

The test ensures that previous validation states are cleared when reopening the editor, preventing stale error messages from persisting.


51-51: Consistent use of the new editor content setting methods.

The refactor successfully standardizes the use of cyPreflight.setScriptEditorContent and cyPreflight.setEnvironmentEditorContent across all test cases, improving maintainability.

Also applies to: 73-73, 85-85, 89-89, 112-112, 140-140, 163-163, 174-174, 184-184, 190-190, 204-204, 230-230, 260-260, 314-314, 341-341, 373-373, 374-374, 392-392, 436-436


24-24: Clear and descriptive test organization.

The test suite is well-organized with clear describe blocks that accurately represent the functionality being tested:

  • "Preflight Tab" for tab-specific functionality
  • "Preflight Modal" for modal-specific features

Also applies to: 45-45

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)

196-476: Consider adding error case tests for header manipulation.

While the execution tests are comprehensive, consider adding tests for:

  • Invalid header values
  • Malformed environment variables
  • Network failures during header substitution
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between fb29bdd and 865a7bf.

📒 Files selected for processing (1)
  • cypress/e2e/laboratory/preflight.cy.ts (17 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: alpha / npm / snapshot
  • 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: code-style / eslint-and-prettier
  • GitHub Check: alpha / cli-artifacts
  • GitHub Check: SonarCloud
🔇 Additional comments (4)
cypress/e2e/laboratory/preflight.cy.ts (4)

1-7: LGTM! Well-structured imports and namespace organization.

The refactoring improves modularity by separating concerns into dedicated modules (dedent, cyMonaco, cyLaboratory).


24-43: LGTM! Comprehensive test coverage for the Preflight Tab.

Tests effectively cover regression cases and validate read-only behavior of the mini editor.


54-70: LGTM! Comprehensive TypeScript validation tests.

The tests effectively verify:

  1. Type checking functionality
  2. Prevention of TypeScript syntax
  3. Proper clearing of validation state

51-51: LGTM! Consistent use of cyPreflight methods.

The refactoring consistently uses cyPreflight.setScriptEditorContent and cyPreflight.setEnvironmentEditorContent across all tests, improving maintainability.

Also applies to: 73-73, 85-85, 89-89, 112-112, 140-140, 163-163, 174-174, 184-184, 190-190, 204-204, 230-230, 260-260, 314-314, 341-341, 373-373, 392-392, 436-436

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.

4 participants