diff --git a/.github/workflows/api-ci-review.yml b/.github/workflows/api-ci-review.yml index 53862b9c0..d134295a4 100644 --- a/.github/workflows/api-ci-review.yml +++ b/.github/workflows/api-ci-review.yml @@ -7,7 +7,6 @@ on: # on: # workflow_dispatch: # This triggers the workflow manually - jobs: test: runs-on: ubuntu-latest @@ -62,15 +61,25 @@ jobs: SECRET_KEY: secret-test-value run: python manage.py check + - name: URL Monitoring Check + working-directory: ./api + shell: bash + env: + CERAMIC_CACHE_SCORER_ID: "" + SECRET_KEY: secret-test-value + UPTIME_ROBOT_READONLY_API_KEY: ${{ secrets.UPTIME_ROBOT_READONLY_API_KEY }} + IGNORE_UNMONITORED_URLS: ${{ vars.IGNORE_UNMONITORED_URLS }} + run: + python manage.py show_urls -f json > urls.json && + python manage.py get_unmonitored_urls --urls urls.json --base-url https://api.scorer.gitcoin.co --out unmonitored.json --allow-paused True && + [ -f unmonitored.json ] && [ `cat unmonitored.json | wc -m` -eq 2 ] + - name: Run API unittests working-directory: ./api run: pytest env: CERAMIC_CACHE_SCORER_ID: "" SECRET_KEY: secret-test-value - DATABASE_URL: postgres://passport_scorer:passport_scorer_pwd@localhost:5432/passport_scorer - DATA_MODEL_DATABASE_URL: postgres://passport_scorer:passport_scorer_pwd@localhost:5432/passport_scorer - FF_API_ANALYTICS: on build-api: runs-on: ubuntu-latest diff --git a/.github/workflows/api-promote-prod.yml b/.github/workflows/api-promote-prod.yml index 6eddbafd7..5aa321537 100644 --- a/.github/workflows/api-promote-prod.yml +++ b/.github/workflows/api-promote-prod.yml @@ -65,6 +65,19 @@ jobs: working-directory: ./api run: pip3 install -r requirements.txt + - name: URL Monitoring Check + working-directory: ./api + shell: bash + env: + CERAMIC_CACHE_SCORER_ID: "" + SECRET_KEY: secret-test-value + UPTIME_ROBOT_READONLY_API_KEY: ${{ secrets.UPTIME_ROBOT_READONLY_API_KEY }} + IGNORE_UNMONITORED_URLS: ${{ vars.IGNORE_UNMONITORED_URLS }} + run: + python manage.py show_urls -f json > urls.json && + python manage.py get_unmonitored_urls --urls urls.json --base-url https://api.scorer.gitcoin.co --out unmanaged.json && + [ -f unmanaged.json ] && [ `cat unmanaged.json | wc -m` -eq 2 ] + - name: Run API unittests working-directory: ./api run: pytest diff --git a/.github/workflows/api-promote-staging.yml b/.github/workflows/api-promote-staging.yml index af70570a6..3636ac354 100644 --- a/.github/workflows/api-promote-staging.yml +++ b/.github/workflows/api-promote-staging.yml @@ -72,6 +72,19 @@ jobs: SECRET_KEY: secret-test-value run: python manage.py check + - name: URL Monitoring Check + working-directory: ./api + shell: bash + env: + CERAMIC_CACHE_SCORER_ID: "" + SECRET_KEY: secret-test-value + UPTIME_ROBOT_READONLY_API_KEY: ${{ secrets.UPTIME_ROBOT_READONLY_API_KEY }} + IGNORE_UNMONITORED_URLS: ${{ vars.IGNORE_UNMONITORED_URLS }} + run: + python manage.py show_urls -f json > urls.json && + python manage.py get_unmonitored_urls --urls urls.json --base-url https://api.scorer.gitcoin.co --out unmanaged.json --allow-paused True && + [ -f unmanaged.json ] && [ `cat unmanaged.json | wc -m` -eq 2 ] + - name: Run API unittests working-directory: ./api run: pytest diff --git a/api/.env-sample b/api/.env-sample index 57ffb3dc5..498b3e724 100644 --- a/api/.env-sample +++ b/api/.env-sample @@ -49,3 +49,4 @@ CGRANTS_API_TOKEN=abc REGISTRY_API_READ_DB=default STAKING_SUBGRAPH_API_KEY=abc +UPTIME_ROBOT_READONLY_API_KEY=abc diff --git a/api/registry/api/v2.py b/api/registry/api/v2.py index 8066e98e2..1af6141d0 100644 --- a/api/registry/api/v2.py +++ b/api/registry/api/v2.py @@ -4,8 +4,8 @@ import api_logging as logging # --- Deduplication Modules -from account.models import Account, Community -from django.db.models import Max, Q +from account.models import Community +from django.db.models import Q from ninja import Router from registry.api import common, v1 from registry.api.schema import ( @@ -15,7 +15,6 @@ DetailedScoreResponse, ErrorMessageResponse, SigningMessageResponse, - StakeSchema, StampDisplayResponse, SubmitPassportPayload, ) @@ -282,24 +281,6 @@ def get_gtc_stake_legacy(request, address: str, round_id: str): return v1.get_gtc_stake_legacy(request, address, round_id) -@router.get( - "/gtc-stake/{str:address}", - # auth=ApiKey(), - auth=None, - response={ - 200: List[StakeSchema], - 400: ErrorMessageResponse, - }, - summary="Retrieve GTC stake amounts for the GTC Staking stamp", - description="Get self and community GTC stakes for an address", -) -def get_gtc_stake(request, address: str) -> List[StakeSchema]: - """ - Get GTC relevant stakes for an address - """ - return v1.get_gtc_stake(request, address) - - @router.get( common.history_endpoint["url"], auth=common.history_endpoint["auth"], diff --git a/api/registry/management/commands/generate_ceramiccache_access_token.py b/api/registry/management/commands/generate_ceramiccache_access_token.py new file mode 100644 index 000000000..b798fb96b --- /dev/null +++ b/api/registry/management/commands/generate_ceramiccache_access_token.py @@ -0,0 +1,30 @@ +from datetime import timedelta +from django.core.management.base import BaseCommand + +from ceramic_cache.api.v1 import DbCacheToken + + +class LongLivedToken(DbCacheToken): + lifetime: timedelta = timedelta(days=100 * 365) + + +class Command(BaseCommand): + help = "Removes stamp data and sets score to 0 for users in the provided list" + + def add_arguments(self, parser): + parser.add_argument( + "--address", + type=str, + help="""Address of the user for whom to generate the access token.""", + required=True, + ) + + def handle(self, *args, **kwargs): + address = kwargs["address"].lower() + self.stdout.write(f"Generating long-lived access token for {address}") + + token = LongLivedToken() + token["did"] = f"did:pkh:eip155:1:{address}" + + self.stdout.write("Access token:") + self.stdout.write(f"{token.access_token}") diff --git a/api/registry/management/commands/get_unmonitored_urls.py b/api/registry/management/commands/get_unmonitored_urls.py new file mode 100644 index 000000000..d5c2bd63a --- /dev/null +++ b/api/registry/management/commands/get_unmonitored_urls.py @@ -0,0 +1,188 @@ +from django.core.management.base import BaseCommand, CommandError +from django.conf import settings +from http.client import HTTPSConnection + +import json +import re + +# Will ignore e.g. anything starting with /admin/ +IGNORED_PATH_ROOTS = [ + "admin", + "social", +] + +# Will ignore exact path matches +IGNORED_URLS = [ + "/registry/feature/openapi.json", + "/registry/feature/docs", + "/registry/feature/scorer/generic", +] + +# This should only be used to get a release out quickly when necessary, after +# which the URL should be added to the hardcoded IGNORED_URLS above to be +# ignored in the future +if settings.IGNORE_UNMONITORED_URLS: + IGNORED_URLS.extend(settings.IGNORE_UNMONITORED_URLS) + + +class Command(BaseCommand): + help = "Removes stamp data and sets score to 0 for users in the provided list" + + def add_arguments(self, parser): + parser.add_argument( + "--urls", + type=str, + help="""Local path to a file containing json output of `show_urls` command""", + required=True, + ) + parser.add_argument( + "--out", + type=str, + help="Output file, json list of unmonitored paths", + required=True, + ) + parser.add_argument( + "--base-url", + type=str, + help="Base URL for the site (uptime robot URLs will be filtered using this)", + required=True, + ) + parser.add_argument( + "--allow-paused", + type=bool, + help="Allow paused monitors to be considered monitored (default: False)", + default=False, + ) + + def handle(self, *args, **kwargs): + self.stdout.write("Running ...") + self.stdout.write(f"args : {args}") + self.stdout.write(f"kwargs : {kwargs}") + + if not settings.UPTIME_ROBOT_READONLY_API_KEY: + raise CommandError("UPTIME_ROBOT_READONLY_API_KEY is not set") + + all_django_urls = self.get_all_urls(kwargs["urls"]) + django_urls = self.filter_urls(all_django_urls) + + self.stdout.write(f"Total URLs: {len(all_django_urls)}") + self.stdout.write(f"Ignoring url path roots: {IGNORED_PATH_ROOTS}") + self.stdout.write(f"Ignoring urls: {IGNORED_URLS}") + self.stdout.write(f"URLs to check: {len(django_urls)}") + + monitored_urls = self.get_uptime_robot_urls( + base_url=kwargs["base_url"], allow_paused=kwargs["allow_paused"] + ) + self.stdout.write(f"Allowing paused monitors: {kwargs['allow_paused']}") + self.stdout.write(f"Uptime Robot URLs: {len(monitored_urls)}") + self.stdout.write(f"Uptime robot URLs: {json.dumps(monitored_urls, indent=2)}") + + unmonitored_urls = [] + for django_url in django_urls: + url_regex = self.convert_django_url_to_regex(django_url) + + # Weird one-liner but it works. Basically the inner () is a generator + # and if anything matches the regex, it will result in a generator that + # will yield (return to next()) a value, the index of the matching url. + # Otherwise next() will return the default value of None + matching_monitored_url_index = next( + (i for i, url in enumerate(monitored_urls) if re.match(url_regex, url)), + None, + ) + + if matching_monitored_url_index is None: + unmonitored_urls.append(django_url) + else: + self.stdout.write( + f"Matched: {django_url} to {monitored_urls[matching_monitored_url_index]}" + ) + # remove matching monitored url so it doesn't get matched again + monitored_urls.pop(matching_monitored_url_index) + + self.stdout.write(f"Unmonitored URLs: {len(unmonitored_urls)}") + + with open(kwargs["out"], "w") as out_file: + json.dump(unmonitored_urls, out_file) + + self.stdout.write("Done") + + def convert_django_url_to_regex(self, url: str): + # Have to do sub and then replace because re.sub interprets + # the replacement string as an invalid group reference + + # Sub integers + url_regex = re.sub("", "", url).replace("", "\\d+") + # Sub strings + url_regex = re.sub("", "", url_regex).replace("", "[^/]+") + # Sub untyped params + url_regex = re.sub("<[^:]+?>", "", url_regex).replace("", "[^/]+") + + return url_regex + "(\\?|$)" + + def get_uptime_robot_urls(self, base_url: str, allow_paused: bool): + limit = 50 + offset = 0 + + monitors = [] + while True: + data = self.uptime_robot_monitors_request(limit, offset) + + total = data["pagination"]["total"] + monitors.extend(data["monitors"]) + + if len(monitors) >= total: + break + + offset += limit + + if base_url.endswith("/"): + base_url = base_url[:-1] + + urls = [ + monitor["url"] + for monitor in monitors + if (allow_paused or monitor["status"] != 0) + ] + + return [url.replace(base_url, "") for url in urls if url.startswith(base_url)] + + def uptime_robot_monitors_request(self, limit, offset): + conn = HTTPSConnection("api.uptimerobot.com") + + payload = f"api_key={settings.UPTIME_ROBOT_READONLY_API_KEY}&format=json&limit={limit}&offset={offset}" + + headers = { + "content-type": "application/x-www-form-urlencoded", + "cache-control": "no-cache", + } + + conn.request("POST", "/v2/getMonitors", payload, headers) + + res = conn.getresponse() + data = res.read() + + return json.loads(data.decode("utf-8")) + + def get_all_urls(self, urls_file_path): + with open(urls_file_path) as urls_file: + urls_json = json.load(urls_file) + + return [entry["url"] for entry in urls_json] + + def filter_urls(self, urls): + # Remove urls that are just a path with a trailing slash, we + # don't use these in our api + filtered_urls = [url for url in urls if not re.match(r"^[^?]*/$", url)] + + filtered_urls = [ + url + for url in filtered_urls + if not any(url.startswith("/" + root + "/") for root in IGNORED_PATH_ROOTS) + ] + + filtered_urls = [ + url + for url in filtered_urls + if not any(url == ignored for ignored in IGNORED_URLS) + ] + return filtered_urls diff --git a/api/scorer/settings/base.py b/api/scorer/settings/base.py index 05a7484a3..f395204ba 100644 --- a/api/scorer/settings/base.py +++ b/api/scorer/settings/base.py @@ -36,6 +36,10 @@ SECURE_SSL_REDIRECT = env("SECURE_SSL_REDIRECT", default=False) SECURE_PROXY_SSL_HEADER = env.json("SECURE_PROXY_SSL_HEADER", default=None) +UPTIME_ROBOT_READONLY_API_KEY = env("UPTIME_ROBOT_READONLY_API_KEY", default="") +# comma separated list of urls to ignore +IGNORE_UNMONITORED_URLS = env.json("IGNORE_UNMONITORED_URLS", default=[]) + STAKING_SUBGRAPH_API_KEY = env("STAKING_SUBGRAPH_API_KEY", default="api-key") GENERIC_COMMUNITY_CREATION_LIMIT = env.int( diff --git a/infra/.example-env b/infra/.example-env new file mode 100644 index 000000000..65bf466d0 --- /dev/null +++ b/infra/.example-env @@ -0,0 +1 @@ +UPTIME_ROBOT_API_KEY=123 diff --git a/infra/README.md b/infra/README.md new file mode 100644 index 000000000..62e80d9e7 --- /dev/null +++ b/infra/README.md @@ -0,0 +1,3 @@ +# Create monitors in uptime robot + +npx tsx scripts/uptime_robot/create_monitor.ts --help diff --git a/infra/package-lock.json b/infra/package-lock.json index 236d05839..00560a113 100644 --- a/infra/package-lock.json +++ b/infra/package-lock.json @@ -11,7 +11,8 @@ "@pulumi/pulumi": "^3.79.0" }, "devDependencies": { - "@types/node": "^14" + "@types/node": "^14", + "typescript": "^5.4.5" } }, "node_modules/@grpc/grpc-js": { @@ -421,6 +422,18 @@ "node": ">=10" } }, + "node_modules/@pulumi/pulumi/node_modules/typescript": { + "version": "3.8.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.8.3.tgz", + "integrity": "sha512-MYlEfn5VrLNsgudQTVJeNaQFUAI7DkhnOjdpAp4T+ku1TfQClewlbSuTVHiA+8skNBgaf02TL/kLOvig4y3G8w==", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + }, "node_modules/@pulumi/query": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/@pulumi/query/-/query-0.3.0.tgz", @@ -2167,15 +2180,16 @@ } }, "node_modules/typescript": { - "version": "3.8.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.8.3.tgz", - "integrity": "sha512-MYlEfn5VrLNsgudQTVJeNaQFUAI7DkhnOjdpAp4T+ku1TfQClewlbSuTVHiA+8skNBgaf02TL/kLOvig4y3G8w==", + "version": "5.4.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz", + "integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==", + "dev": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" }, "engines": { - "node": ">=4.2.0" + "node": ">=14.17" } }, "node_modules/unbox-primitive": { diff --git a/infra/package.json b/infra/package.json index e89a02581..8ff1a114f 100644 --- a/infra/package.json +++ b/infra/package.json @@ -1,7 +1,12 @@ { "name": "infra", "devDependencies": { - "@types/node": "^14" + "@types/node": "^14", + "axios": "^1.6.8", + "commander": "^12.0.0", + "dotenv": "^16.4.5", + "tsx": "^4.9.3", + "typescript": "^5.4.5" }, "dependencies": { "@pulumi/aws": "^6.0.2", diff --git a/infra/scripts/uptime_robot/command.ts b/infra/scripts/uptime_robot/command.ts new file mode 100644 index 000000000..2423cfc25 --- /dev/null +++ b/infra/scripts/uptime_robot/command.ts @@ -0,0 +1,228 @@ +// Generic command utility +// Call generateProgram with a list of sub-command definitions + +import "dotenv/config"; +import axios from "axios"; +import { Command, Option } from "commander"; + +const api_key = process.env.UPTIME_ROBOT_API_KEY!; + +const checkEnv = () => { + if (!api_key) { + console.log("UPTIME_ROBOT_API_KEY must be set in ENV"); + process.exit(1); + } +}; + +export const HTTP_METHOD = { + GET: "2", + POST: "3", + PUT: "4", + PATCH: "5", + DELETE: "6", + OPTIONS: "7", +}; + +const httpMethodToName = (methodId: string) => + Object.entries(HTTP_METHOD).find(([_, id]) => id === methodId)?.[0]; + +const POST_TYPE = { + KEY_VALUE_PAIRS: "1", + RAW_DATA: "2", +}; + +const bigLine = "=".repeat(40); +const line = "-".repeat(40); + +// This type is not exhaustive and should be updated as needed +type CreateModelRequestBody = { + api_key: string; + format: string; + type: string; + interval: string; + timeout: string; + url: string; + status: string; + http_method?: string; + custom_http_headers?: Record; + custom_http_statuses?: string; + post_value?: string; + post_type?: string; + friendly_name?: string; +}; + +// This function should return a partial CreateModelRequestBody +// which overrides any defaults specified in the CreateMonitor +// function (see below) and adds any additional fields required +type GenerateCreateModelRequestBody = ( + options: T +) => Partial; + +type CommandParams = { + name: string; + description: string; + generateCreateModelRequestBody: GenerateCreateModelRequestBody; + summary?: string; // Optional shorter description listed in top-level help + additionalOptions?: Option[]; +}; + +export type BaseMonitorOptions = { + timeout: string; + interval: string; + paused: boolean; + accept404: boolean; +}; + +async function createMonitors( + baseUrl: string, + paths: string[], + options: T, + generateCreateModelRequestBody: GenerateCreateModelRequestBody +) { + const successfulUrls: string[] = []; + const failed: { url: string; data: any }[] = []; + + await Promise.all( + paths.map(async (path) => { + const { data, url } = await createMonitor( + baseUrl, + path, + options, + generateCreateModelRequestBody + ); + const { stat } = data; + if (stat === "ok") { + successfulUrls.push(url); + } else { + failed.push({ url, data }); + } + }) + ); + + return { successfulUrls, failed }; +} + +async function createMonitor( + baseUrl: string, + path: string, + options: T, + generateCreateModelRequestBody: GenerateCreateModelRequestBody +) { + const { interval, timeout, paused } = options; + + const url = `${baseUrl}/${path}`; + + const body: CreateModelRequestBody = { + api_key, + interval, + timeout, + url, + format: "json", + type: "1", // HTTP(s) + status: paused ? "0" : "1", + custom_http_statuses: options.accept404 ? "404:1_2xx:1" : undefined, + ...generateCreateModelRequestBody(options), + }; + + body.http_method = body.http_method || HTTP_METHOD.GET; + const httpMethodName = httpMethodToName(body.http_method); + + body.friendly_name = + body.friendly_name || `[auto] Scorer ${httpMethodName} /${path}`; + + if (body.http_method !== HTTP_METHOD.GET && !body.post_type) { + body.post_type = POST_TYPE.RAW_DATA; + } + + let data: any; + try { + const response = await axios.post( + "https://api.uptimerobot.com/v2/newMonitor", + body, + { + headers: { + "content-type": "application/x-www-form-urlencoded", + "cache-control": "no-cache", + }, + } + ); + + data = response.data; + } catch (exception) { + data = { exception }; + } + + return { + data, + url, + }; +} + +async function summarize( + successfulUrls: string[], + failed: { url: string; data: any }[] +) { + console.log(bigLine); + console.log(`Successfully created ${successfulUrls.length} monitors:`); + successfulUrls.map((url) => console.log(url)); + console.log(bigLine); + + console.log(`Failed to create ${failed.length} monitors:`); + failed.forEach(({ url, data }) => { + console.log(line); + console.log("URL:", url); + console.log("Data:", data); + }); + console.log(bigLine); + console.log("Done"); +} + +export async function generateProgram(commands: CommandParams[]) { + const program = new Command(); + program.description("Utilities for creating Uptime Robot monitors"); + + commands.map( + ({ + name, + description, + summary, + generateCreateModelRequestBody, + additionalOptions, + }) => { + const command = program + .command(name) + .description(description) + .argument("base-url", "Base URL for the monitor(s)") + .argument("", "URL path(s) to monitor (space delimited)") + .option("-i, --interval ", "Interval in seconds", "60") + .option("-t, --timeout ", "Timeout in seconds", "30") + .option("-p, --paused", "Create the monitor in paused state") + .option("--accept-404", "Accept 404 status code as success"); + + additionalOptions?.forEach((option) => command.addOption(option)); + + summary && command.summary(summary); + + command.action( + async (rawBaseUrl: string, rawPaths: string[], options) => { + checkEnv(); + const baseUrl = rawBaseUrl.replace(/\/$/, ""); + const paths = rawPaths.map((path) => path.replace(/^\//, "")); + + console.log(`Creating monitors for ${paths.length} URLs`); + + const { successfulUrls, failed } = await createMonitors( + baseUrl, + paths, + options, + generateCreateModelRequestBody + ); + + summarize(successfulUrls, failed); + } + ); + } + ); + + program.parse(); +} diff --git a/infra/scripts/uptime_robot/create_monitor.ts b/infra/scripts/uptime_robot/create_monitor.ts new file mode 100644 index 000000000..ad88dc11e --- /dev/null +++ b/infra/scripts/uptime_robot/create_monitor.ts @@ -0,0 +1,103 @@ +import "dotenv/config"; + +import { BaseMonitorOptions, generateProgram, HTTP_METHOD } from "./command"; +import { Option } from "commander"; + +type NonGetMonitorOptions = BaseMonitorOptions & { + postBody?: string; + method?: keyof typeof HTTP_METHOD; +}; + +type ApiKeyMonitorOptions = NonGetMonitorOptions & { + scorerApiKey: string; +}; + +type TokenMonitorOptions = NonGetMonitorOptions & { + token: string; +}; + +const commands = []; + +commands.push({ + name: "simple_get", + description: "Create a new monitor for a public GET endpoint", + generateCreateModelRequestBody: (_options: BaseMonitorOptions) => { + return {}; + }, +}); + +const NON_GET_OPTIONS = [ + new Option( + "-d, --data ", + "JSON string data (body), by default turns into POST request" + ), + new Option( + "-m, --method ", + "HTTP method (default GET, or POST if -d is provided)" + ), +]; + +function getHttpMethod(options: T) { + const { postBody, method } = options; + if (method) { + return HTTP_METHOD[method]; + } else { + return postBody ? HTTP_METHOD.POST : HTTP_METHOD.GET; + } +} + +commands.push({ + name: "api_key_auth", + summary: "Create a new monitor for an endpoint with API key auth", + description: + "Create a new monitor for an endpoint with API key auth (defaults to GET request)", + additionalOptions: [ + new Option( + "-k, --scorer-api-key ", + "(required) Scorer API key to be passed as X-API-Key" + ).makeOptionMandatory(), + ...NON_GET_OPTIONS, + ], + generateCreateModelRequestBody: (options: ApiKeyMonitorOptions) => { + const { postBody, scorerApiKey } = options; + + const http_method = getHttpMethod(options); + + return { + http_method, + custom_http_headers: { + "X-API-KEY": scorerApiKey, + }, + post_value: postBody, + }; + }, +}); + +commands.push({ + name: "token_auth", + summary: "Create a new monitor for an endpoint with JWT token auth", + description: + "Create a new monitor for an endpoint with JWT token auth (defaults to GET request)", + additionalOptions: [ + new Option( + "-j, --jwt-token ", + "(required) JWT auth token (generate with django command `generate_ceramiccache_access_token`)" + ).makeOptionMandatory(), + ...NON_GET_OPTIONS, + ], + generateCreateModelRequestBody: (options: TokenMonitorOptions) => { + const { postBody, token } = options; + + const http_method = getHttpMethod(options); + + return { + http_method, + custom_http_headers: { + AUTHORIZATION: `Bearer ${token}`, + }, + post_value: postBody, + }; + }, +}); + +generateProgram(commands); diff --git a/infra/yarn.lock b/infra/yarn.lock index 5e0693551..bf7689e69 100644 --- a/infra/yarn.lock +++ b/infra/yarn.lock @@ -2,6 +2,121 @@ # yarn lockfile v1 +"@esbuild/aix-ppc64@0.20.2": + version "0.20.2" + resolved "https://registry.yarnpkg.com/@esbuild/aix-ppc64/-/aix-ppc64-0.20.2.tgz#a70f4ac11c6a1dfc18b8bbb13284155d933b9537" + integrity sha512-D+EBOJHXdNZcLJRBkhENNG8Wji2kgc9AZ9KiPr1JuZjsNtyHzrsfLRrY0tk2H2aoFu6RANO1y1iPPUCDYWkb5g== + +"@esbuild/android-arm64@0.20.2": + version "0.20.2" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.20.2.tgz#db1c9202a5bc92ea04c7b6840f1bbe09ebf9e6b9" + integrity sha512-mRzjLacRtl/tWU0SvD8lUEwb61yP9cqQo6noDZP/O8VkwafSYwZ4yWy24kan8jE/IMERpYncRt2dw438LP3Xmg== + +"@esbuild/android-arm@0.20.2": + version "0.20.2" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.20.2.tgz#3b488c49aee9d491c2c8f98a909b785870d6e995" + integrity sha512-t98Ra6pw2VaDhqNWO2Oph2LXbz/EJcnLmKLGBJwEwXX/JAN83Fym1rU8l0JUWK6HkIbWONCSSatf4sf2NBRx/w== + +"@esbuild/android-x64@0.20.2": + version "0.20.2" + resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.20.2.tgz#3b1628029e5576249d2b2d766696e50768449f98" + integrity sha512-btzExgV+/lMGDDa194CcUQm53ncxzeBrWJcncOBxuC6ndBkKxnHdFJn86mCIgTELsooUmwUm9FkhSp5HYu00Rg== + +"@esbuild/darwin-arm64@0.20.2": + version "0.20.2" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.20.2.tgz#6e8517a045ddd86ae30c6608c8475ebc0c4000bb" + integrity sha512-4J6IRT+10J3aJH3l1yzEg9y3wkTDgDk7TSDFX+wKFiWjqWp/iCfLIYzGyasx9l0SAFPT1HwSCR+0w/h1ES/MjA== + +"@esbuild/darwin-x64@0.20.2": + version "0.20.2" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.20.2.tgz#90ed098e1f9dd8a9381695b207e1cff45540a0d0" + integrity sha512-tBcXp9KNphnNH0dfhv8KYkZhjc+H3XBkF5DKtswJblV7KlT9EI2+jeA8DgBjp908WEuYll6pF+UStUCfEpdysA== + +"@esbuild/freebsd-arm64@0.20.2": + version "0.20.2" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.20.2.tgz#d71502d1ee89a1130327e890364666c760a2a911" + integrity sha512-d3qI41G4SuLiCGCFGUrKsSeTXyWG6yem1KcGZVS+3FYlYhtNoNgYrWcvkOoaqMhwXSMrZRl69ArHsGJ9mYdbbw== + +"@esbuild/freebsd-x64@0.20.2": + version "0.20.2" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.20.2.tgz#aa5ea58d9c1dd9af688b8b6f63ef0d3d60cea53c" + integrity sha512-d+DipyvHRuqEeM5zDivKV1KuXn9WeRX6vqSqIDgwIfPQtwMP4jaDsQsDncjTDDsExT4lR/91OLjRo8bmC1e+Cw== + +"@esbuild/linux-arm64@0.20.2": + version "0.20.2" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.20.2.tgz#055b63725df678379b0f6db9d0fa85463755b2e5" + integrity sha512-9pb6rBjGvTFNira2FLIWqDk/uaf42sSyLE8j1rnUpuzsODBq7FvpwHYZxQ/It/8b+QOS1RYfqgGFNLRI+qlq2A== + +"@esbuild/linux-arm@0.20.2": + version "0.20.2" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.20.2.tgz#76b3b98cb1f87936fbc37f073efabad49dcd889c" + integrity sha512-VhLPeR8HTMPccbuWWcEUD1Az68TqaTYyj6nfE4QByZIQEQVWBB8vup8PpR7y1QHL3CpcF6xd5WVBU/+SBEvGTg== + +"@esbuild/linux-ia32@0.20.2": + version "0.20.2" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.20.2.tgz#c0e5e787c285264e5dfc7a79f04b8b4eefdad7fa" + integrity sha512-o10utieEkNPFDZFQm9CoP7Tvb33UutoJqg3qKf1PWVeeJhJw0Q347PxMvBgVVFgouYLGIhFYG0UGdBumROyiig== + +"@esbuild/linux-loong64@0.20.2": + version "0.20.2" + resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.20.2.tgz#a6184e62bd7cdc63e0c0448b83801001653219c5" + integrity sha512-PR7sp6R/UC4CFVomVINKJ80pMFlfDfMQMYynX7t1tNTeivQ6XdX5r2XovMmha/VjR1YN/HgHWsVcTRIMkymrgQ== + +"@esbuild/linux-mips64el@0.20.2": + version "0.20.2" + resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.20.2.tgz#d08e39ce86f45ef8fc88549d29c62b8acf5649aa" + integrity sha512-4BlTqeutE/KnOiTG5Y6Sb/Hw6hsBOZapOVF6njAESHInhlQAghVVZL1ZpIctBOoTFbQyGW+LsVYZ8lSSB3wkjA== + +"@esbuild/linux-ppc64@0.20.2": + version "0.20.2" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.20.2.tgz#8d252f0b7756ffd6d1cbde5ea67ff8fd20437f20" + integrity sha512-rD3KsaDprDcfajSKdn25ooz5J5/fWBylaaXkuotBDGnMnDP1Uv5DLAN/45qfnf3JDYyJv/ytGHQaziHUdyzaAg== + +"@esbuild/linux-riscv64@0.20.2": + version "0.20.2" + resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.20.2.tgz#19f6dcdb14409dae607f66ca1181dd4e9db81300" + integrity sha512-snwmBKacKmwTMmhLlz/3aH1Q9T8v45bKYGE3j26TsaOVtjIag4wLfWSiZykXzXuE1kbCE+zJRmwp+ZbIHinnVg== + +"@esbuild/linux-s390x@0.20.2": + version "0.20.2" + resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.20.2.tgz#3c830c90f1a5d7dd1473d5595ea4ebb920988685" + integrity sha512-wcWISOobRWNm3cezm5HOZcYz1sKoHLd8VL1dl309DiixxVFoFe/o8HnwuIwn6sXre88Nwj+VwZUvJf4AFxkyrQ== + +"@esbuild/linux-x64@0.20.2": + version "0.20.2" + resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.20.2.tgz#86eca35203afc0d9de0694c64ec0ab0a378f6fff" + integrity sha512-1MdwI6OOTsfQfek8sLwgyjOXAu+wKhLEoaOLTjbijk6E2WONYpH9ZU2mNtR+lZ2B4uwr+usqGuVfFT9tMtGvGw== + +"@esbuild/netbsd-x64@0.20.2": + version "0.20.2" + resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.20.2.tgz#e771c8eb0e0f6e1877ffd4220036b98aed5915e6" + integrity sha512-K8/DhBxcVQkzYc43yJXDSyjlFeHQJBiowJ0uVL6Tor3jGQfSGHNNJcWxNbOI8v5k82prYqzPuwkzHt3J1T1iZQ== + +"@esbuild/openbsd-x64@0.20.2": + version "0.20.2" + resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.20.2.tgz#9a795ae4b4e37e674f0f4d716f3e226dd7c39baf" + integrity sha512-eMpKlV0SThJmmJgiVyN9jTPJ2VBPquf6Kt/nAoo6DgHAoN57K15ZghiHaMvqjCye/uU4X5u3YSMgVBI1h3vKrQ== + +"@esbuild/sunos-x64@0.20.2": + version "0.20.2" + resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.20.2.tgz#7df23b61a497b8ac189def6e25a95673caedb03f" + integrity sha512-2UyFtRC6cXLyejf/YEld4Hajo7UHILetzE1vsRcGL3earZEW77JxrFjH4Ez2qaTiEfMgAXxfAZCm1fvM/G/o8w== + +"@esbuild/win32-arm64@0.20.2": + version "0.20.2" + resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.20.2.tgz#f1ae5abf9ca052ae11c1bc806fb4c0f519bacf90" + integrity sha512-GRibxoawM9ZCnDxnP3usoUDO9vUkpAxIIZ6GQI+IlVmr5kP3zUq+l17xELTHMWTWzjxa2guPNyrpq1GWmPvcGQ== + +"@esbuild/win32-ia32@0.20.2": + version "0.20.2" + resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.20.2.tgz#241fe62c34d8e8461cd708277813e1d0ba55ce23" + integrity sha512-HfLOfn9YWmkSKRQqovpnITazdtquEW8/SoHW7pWpuEeguaZI4QnCRW6b+oZTztdBnZOS2hqJ6im/D5cPzBTTlQ== + +"@esbuild/win32-x64@0.20.2": + version "0.20.2" + resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.20.2.tgz#9c907b21e30a52db959ba4f80bb01a0cc403d5cc" + integrity sha512-N49X4lJX27+l9jbLKSqZ6bKNjzQvHaT8IIFUy+YIqmXQdjYCToGWwOItDrfby14c78aDd5NHQl29xingXfCdLQ== + "@grpc/grpc-js@^1.8.16": version "1.9.1" resolved "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.9.1.tgz" @@ -339,6 +454,11 @@ asap@^2.0.0: resolved "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz" integrity sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA== +asynckit@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" + integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q== + available-typed-arrays@^1.0.5: version "1.0.5" resolved "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz" @@ -360,6 +480,15 @@ aws-sdk@^2.0.0: uuid "8.0.0" xml2js "0.5.0" +axios@^1.6.8: + version "1.6.8" + resolved "https://registry.yarnpkg.com/axios/-/axios-1.6.8.tgz#66d294951f5d988a00e87a0ffb955316a619ea66" + integrity sha512-v/ZHtJDU39mDpyBoFVkETcd/uNdxrWRrg3bKpOKzXFA6Bvqopts6ALSMU3y6ijYxbw2B+wPrIv46egTzJXCLGQ== + dependencies: + follow-redirects "^1.15.6" + form-data "^4.0.0" + proxy-from-env "^1.1.0" + balanced-match@^1.0.0: version "1.0.2" resolved "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz" @@ -426,6 +555,18 @@ color-name@~1.1.4: resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz" integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== +combined-stream@^1.0.8: + version "1.0.8" + resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" + integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== + dependencies: + delayed-stream "~1.0.0" + +commander@^12.0.0: + version "12.0.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-12.0.0.tgz#b929db6df8546080adfd004ab215ed48cf6f2592" + integrity sha512-MwVNWlYjDTtOjX5PiD7o5pK0UrFU/OYgcJfjjK4RaHZETNtjJqrZa9Y9ds88+A+f+d5lv+561eZ+yCKoS3gbAA== + concat-map@0.0.1: version "0.0.1" resolved "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz" @@ -460,6 +601,11 @@ define-properties@^1.1.3, define-properties@^1.1.4, define-properties@^1.2.0: has-property-descriptors "^1.0.0" object-keys "^1.1.1" +delayed-stream@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" + integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ== + dezalgo@^1.0.0: version "1.0.4" resolved "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.4.tgz" @@ -473,6 +619,11 @@ diff@^3.1.0: resolved "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz" integrity sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA== +dotenv@^16.4.5: + version "16.4.5" + resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.4.5.tgz#cdd3b3b604cb327e286b4762e13502f717cb099f" + integrity sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg== + emoji-regex@^8.0.0: version "8.0.0" resolved "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz" @@ -546,6 +697,35 @@ es-to-primitive@^1.2.1: is-date-object "^1.0.1" is-symbol "^1.0.2" +esbuild@~0.20.2: + version "0.20.2" + resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.20.2.tgz#9d6b2386561766ee6b5a55196c6d766d28c87ea1" + integrity sha512-WdOOppmUNU+IbZ0PaDiTst80zjnrOkyJNHoKupIcVyU8Lvla3Ugx94VzkQ32Ijqd7UhHJy75gNWDMUekcrSJ6g== + optionalDependencies: + "@esbuild/aix-ppc64" "0.20.2" + "@esbuild/android-arm" "0.20.2" + "@esbuild/android-arm64" "0.20.2" + "@esbuild/android-x64" "0.20.2" + "@esbuild/darwin-arm64" "0.20.2" + "@esbuild/darwin-x64" "0.20.2" + "@esbuild/freebsd-arm64" "0.20.2" + "@esbuild/freebsd-x64" "0.20.2" + "@esbuild/linux-arm" "0.20.2" + "@esbuild/linux-arm64" "0.20.2" + "@esbuild/linux-ia32" "0.20.2" + "@esbuild/linux-loong64" "0.20.2" + "@esbuild/linux-mips64el" "0.20.2" + "@esbuild/linux-ppc64" "0.20.2" + "@esbuild/linux-riscv64" "0.20.2" + "@esbuild/linux-s390x" "0.20.2" + "@esbuild/linux-x64" "0.20.2" + "@esbuild/netbsd-x64" "0.20.2" + "@esbuild/openbsd-x64" "0.20.2" + "@esbuild/sunos-x64" "0.20.2" + "@esbuild/win32-arm64" "0.20.2" + "@esbuild/win32-ia32" "0.20.2" + "@esbuild/win32-x64" "0.20.2" + escalade@^3.1.1: version "3.1.1" resolved "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz" @@ -584,6 +764,11 @@ find-up@^6.3.0: locate-path "^7.1.0" path-exists "^5.0.0" +follow-redirects@^1.15.6: + version "1.15.6" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.6.tgz#7f815c0cda4249c74ff09e95ef97c23b5fd0399b" + integrity sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA== + for-each@^0.3.3: version "0.3.3" resolved "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz" @@ -591,11 +776,25 @@ for-each@^0.3.3: dependencies: is-callable "^1.1.3" +form-data@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452" + integrity sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.8" + mime-types "^2.1.12" + fs.realpath@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz" integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== +fsevents@~2.3.3: + version "2.3.3" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6" + integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw== + function-bind@^1.1.1: version "1.1.1" resolved "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz" @@ -644,6 +843,13 @@ get-symbol-description@^1.0.0: call-bind "^1.0.2" get-intrinsic "^1.1.1" +get-tsconfig@^4.7.3: + version "4.7.4" + resolved "https://registry.yarnpkg.com/get-tsconfig/-/get-tsconfig-4.7.4.tgz#228e1a3e37125aeb4467e9b992b92c4533093bd2" + integrity sha512-ofbkKj+0pjXjhejr007J/fLf+sW+8H7K5GCm+msC8q3IpvgjobpyPqSRFemNyIMxklC0zeJpi7VDFna19FacvQ== + dependencies: + resolve-pkg-maps "^1.0.0" + glob@^7.1.1: version "7.2.3" resolved "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz" @@ -955,6 +1161,18 @@ merge-stream@^2.0.0: resolved "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz" integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== +mime-db@1.52.0: + version "1.52.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" + integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== + +mime-types@^2.1.12: + version "2.1.35" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" + integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== + dependencies: + mime-db "1.52.0" + mime@^2.0.0: version "2.6.0" resolved "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz" @@ -1130,6 +1348,11 @@ protobufjs@^7.2.4: "@types/node" ">=13.7.0" long "^5.0.0" +proxy-from-env@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2" + integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg== + punycode@1.3.2: version "1.3.2" resolved "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz" @@ -1197,6 +1420,11 @@ require-in-the-middle@^5.0.3: module-details-from-path "^1.0.3" resolve "^1.22.1" +resolve-pkg-maps@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz#616b3dc2c57056b5588c31cdf4b3d64db133720f" + integrity sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw== + resolve@^1.10.0, resolve@^1.22.1, resolve@^1.7.1: version "1.22.4" resolved "https://registry.npmjs.org/resolve/-/resolve-1.22.4.tgz" @@ -1384,6 +1612,16 @@ ts-node@^7.0.1: source-map-support "^0.5.6" yn "^2.0.0" +tsx@^4.9.3: + version "4.9.3" + resolved "https://registry.yarnpkg.com/tsx/-/tsx-4.9.3.tgz#801ca18ca22b3d2f7acd89d4b888aa2425ea1302" + integrity sha512-czVbetlILiyJZI5zGlj2kw9vFiSeyra9liPD4nG+Thh4pKTi0AmMEQ8zdV/L2xbIVKrIqif4sUNrsMAOksx9Zg== + dependencies: + esbuild "~0.20.2" + get-tsconfig "^4.7.3" + optionalDependencies: + fsevents "~2.3.3" + typed-array-buffer@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.0.tgz" @@ -1423,6 +1661,11 @@ typed-array-length@^1.0.4: for-each "^0.3.3" is-typed-array "^1.1.9" +typescript@^5.4.5: + version "5.4.5" + resolved "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz" + integrity sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ== + typescript@~3.8.3: version "3.8.3" resolved "https://registry.npmjs.org/typescript/-/typescript-3.8.3.tgz"