From a5e4fee3a335a8323c446640974f604005f55fb3 Mon Sep 17 00:00:00 2001 From: CJ_Clippy Date: Sat, 31 Aug 2024 02:42:28 -0800 Subject: [PATCH] switch chatops from streams to vods --- Tiltfile | 1 + charts/fp/templates/bot.yaml | 2 + charts/fp/templates/capture.yaml | 2 + charts/fp/templates/chihaya.yaml | 79 + charts/fp/templates/scout.yaml | 18 + charts/fp/values.yaml | 3 + dockerfiles/bot.dockerfile | 2 +- dockerfiles/capture.dockerfile | 2 +- dockerfiles/factory.dockerfile | 2 +- dockerfiles/mailbox.dockerfile | 2 +- dockerfiles/meal.dockerfile | 2 +- dockerfiles/migrations.dockerfile | 2 +- dockerfiles/next.dockerfile | 2 +- dockerfiles/scout.dockerfile | 2 +- dockerfiles/strapi.dockerfile | 2 +- dockerfiles/strapi.dockerfile.full | 2 +- dockerfiles/worker.dockerfile | 2 +- packages/image/src/index.spec.ts | 2 +- packages/types/index.d.ts | 265 --- packages/types/package.json | 2 +- packages/types/src/index.ts | 348 ++++ packages/types/tsconfig.json | 4 +- services/bot/crontab | 4 +- services/bot/package.json | 1 - services/bot/pnpm-lock.yaml | 39 +- services/bot/src/commands/cancel.ts | 8 +- services/bot/src/commands/ping.ts | 2 - services/bot/src/commands/process.ts | 9 +- services/bot/src/commands/record.ts | 16 +- services/bot/src/config.ts | 3 + services/bot/src/events/interactionCreate.ts | 4 +- .../bot/src/fetchers/findOrCreateVtuber.ts | 7 +- services/bot/src/fetchers/findVod.ts | 28 + services/bot/src/index.ts | 12 +- .../bot/src/tasks/update_discord_message.ts | 64 +- services/bot/src/tasks/update_vod_statuses.ts | 166 +- services/capture/package.json | 1 - services/capture/pnpm-lock.yaml | 39 +- services/capture/src/Record.ts | 4 +- services/capture/src/cb.js | 7 +- services/capture/src/config.ts | 3 + .../src/fetchers/createSegmentInDatabase.ts | 4 +- .../src/fetchers/createSegmentsVodLink.ts | 5 +- .../capture/src/fetchers/getPlaylistUrl.ts | 21 + .../src/fetchers/updateSegmentInDatabase.ts | 22 +- services/capture/src/tasks/record.ts | 24 +- services/factory/src/fetchers/getVod.ts | 24 + .../src/tasks/combine_video_segments.ts | 8 +- services/factory/src/tasks/process_video.ts | 57 +- ...00041_add-is_recording_aborted-to-vods.sql | 3 + .../migrations/00042_segments-vods-fk.sql | 6 + .../00043_update_vod_on_segment_update.sql | 23 + .../migrations/00044_segments-uuid.sql | 6 + .../00045_recreate-segments_vod_links.sql | 10 + .../00046_segments_vod_links-many-to-one.sql | 10 + ...ecording_aborted-to-vods-default-false.sql | 5 + .../00048_segments-vod-links-perms.sql | 3 + .../00049_add-discord_message_id-on-vods.sql | 4 + .../migrations/00050_add-status-to-vod.sql | 3 + .../migrations/00051_set-vod-status.sql | 21 + .../00052_correct-typo-filesize-to-bytes.sql | 21 + .../00053_add-default-status-on-vods.sql | 5 + services/scout/package.json | 5 +- services/scout/pnpm-lock.yaml | 1643 ++--------------- services/scout/src/fastify.js | 106 -- services/scout/src/fastify.ts | 159 ++ services/scout/src/index.ts | 3 +- services/scout/src/schemas.ts | 28 + services/scout/src/scrapeVtuberData.ts | 12 +- services/scout/src/ytdlp.ts | 2 +- 70 files changed, 1321 insertions(+), 2087 deletions(-) create mode 100644 charts/fp/templates/chihaya.yaml delete mode 100644 packages/types/index.d.ts create mode 100644 packages/types/src/index.ts create mode 100644 services/bot/src/fetchers/findVod.ts create mode 100644 services/capture/src/fetchers/getPlaylistUrl.ts create mode 100644 services/factory/src/fetchers/getVod.ts create mode 100644 services/migrations/migrations/00041_add-is_recording_aborted-to-vods.sql create mode 100644 services/migrations/migrations/00042_segments-vods-fk.sql create mode 100644 services/migrations/migrations/00043_update_vod_on_segment_update.sql create mode 100644 services/migrations/migrations/00044_segments-uuid.sql create mode 100644 services/migrations/migrations/00045_recreate-segments_vod_links.sql create mode 100644 services/migrations/migrations/00046_segments_vod_links-many-to-one.sql create mode 100644 services/migrations/migrations/00047_add-is_recording_aborted-to-vods-default-false.sql create mode 100644 services/migrations/migrations/00048_segments-vod-links-perms.sql create mode 100644 services/migrations/migrations/00049_add-discord_message_id-on-vods.sql create mode 100644 services/migrations/migrations/00050_add-status-to-vod.sql create mode 100644 services/migrations/migrations/00051_set-vod-status.sql create mode 100644 services/migrations/migrations/00052_correct-typo-filesize-to-bytes.sql create mode 100644 services/migrations/migrations/00053_add-default-status-on-vods.sql delete mode 100644 services/scout/src/fastify.js create mode 100644 services/scout/src/fastify.ts create mode 100644 services/scout/src/schemas.ts diff --git a/Tiltfile b/Tiltfile index a3af526..d75b9fd 100644 --- a/Tiltfile +++ b/Tiltfile @@ -207,6 +207,7 @@ docker_build( ], dockerfile='./dockerfiles/scout.dockerfile', target='dev', + # target='prod', live_update=[ sync('./services/scout', '/app/services/scout') ] diff --git a/charts/fp/templates/bot.yaml b/charts/fp/templates/bot.yaml index 338247d..b74e2ef 100644 --- a/charts/fp/templates/bot.yaml +++ b/charts/fp/templates/bot.yaml @@ -20,6 +20,8 @@ spec: - name: bot image: "{{ .Values.bot.imageName }}" env: + - name: SCOUT_URL + value: "{{ .Values.scout.url }}" - name: POSTGREST_URL value: "{{ .Values.postgrest.url }}" - name: NODE_ENV diff --git a/charts/fp/templates/capture.yaml b/charts/fp/templates/capture.yaml index 504612b..30cf93a 100644 --- a/charts/fp/templates/capture.yaml +++ b/charts/fp/templates/capture.yaml @@ -40,6 +40,8 @@ spec: - name: capture-worker image: "{{ .Values.capture.imageName }}" env: + - name: SCOUT_URL + value: "{{ .Values.scout.url }}" - name: FUNCTION value: worker - name: WORKER_CONNECTION_STRING diff --git a/charts/fp/templates/chihaya.yaml b/charts/fp/templates/chihaya.yaml new file mode 100644 index 0000000..a5773b4 --- /dev/null +++ b/charts/fp/templates/chihaya.yaml @@ -0,0 +1,79 @@ + +--- +apiVersion: v1 +kind: Service +metadata: + name: chihaya + namespace: futureporn + annotations: + external-dns.alpha.kubernetes.io/hostname: "{{ .Values.chihaya.hostname }}" +spec: + type: LoadBalancer + selector: + app: chihaya + ports: + - name: http + port: 80 + targetPort: 80 + +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: chihaya +spec: + replicas: 1 + selector: + matchLabels: + app: chihaya + template: + metadata: + labels: + app: chihaya + spec: + containers: + - name: chihaya + image: "quay.io/jzelinskie/chihaya-git:latest" + ports: + - containerPort: 80 + +--- +apiVersion: traefik.io/v1alpha1 +kind: IngressRoute +metadata: + name: chihaya-http + namespace: futureporn +spec: + entryPoints: + - web + routes: + - match: Host(`chihaya.fp.sbtp.xyz`) + kind: Rule + middlewares: + - name: redirect + namespace: futureporn + services: + - name: chihaya + port: web + +--- +apiVersion: traefik.io/v1alpha1 +kind: IngressRoute +metadata: + name: chihaya-https + namespace: futureporn + annotations: + cert-manager.io/cluster-issuer: "{{ .Values.certManager.issuer }}" +spec: + entryPoints: + - websecure + routes: + - match: Host(`chihaya.fp.sbtp.xyz`) + kind: Rule + services: + - name: chihaya + namespace: futureporn + port: web + tls: + secretName: chihaya-tls + diff --git a/charts/fp/templates/scout.yaml b/charts/fp/templates/scout.yaml index f555ea6..33dd94f 100644 --- a/charts/fp/templates/scout.yaml +++ b/charts/fp/templates/scout.yaml @@ -19,6 +19,9 @@ spec: containers: - name: scout image: "{{ .Values.scout.imageName }}" + ports: + - name: http + containerPort: {{ .Values.scout.port }} env: - name: POSTGREST_URL value: "{{ .Values.postgrest.url }}" @@ -47,3 +50,18 @@ spec: memory: 1024Mi restartPolicy: Always + +--- +apiVersion: v1 +kind: Service +metadata: + name: scout + namespace: futureporn +spec: + type: ClusterIP + selector: + app: scout + ports: + - name: http + port: {{ .Values.scout.port }} + targetPort: http diff --git a/charts/fp/values.yaml b/charts/fp/values.yaml index 7370ac1..7a13350 100644 --- a/charts/fp/values.yaml +++ b/charts/fp/values.yaml @@ -72,10 +72,13 @@ bot: discordGuildId: "1084674137391374338" imageName: fp/bot replicas: 1 +chihaya: + hostname: tracker.fp.sbtp.xyz scout: imageName: fp/scout replicas: 1 port: 5134 + url: http://scout.futureporn.svc.cluster.local:5134 postgrest: url: http://postgrest.futureporn.svc.cluster.local:9000 image: postgrest/postgrest diff --git a/dockerfiles/bot.dockerfile b/dockerfiles/bot.dockerfile index 0172372..e87cfde 100644 --- a/dockerfiles/bot.dockerfile +++ b/dockerfiles/bot.dockerfile @@ -2,7 +2,7 @@ FROM node:20 AS base ENV PNPM_HOME="/pnpm" ENV PATH="$PNPM_HOME:$PATH" WORKDIR /app -RUN corepack enable && corepack prepare --activate +RUN corepack enable && corepack prepare pnpm@9.6.0 --activate ENTRYPOINT ["pnpm"] FROM base AS install diff --git a/dockerfiles/capture.dockerfile b/dockerfiles/capture.dockerfile index 01758e2..20a2b0d 100644 --- a/dockerfiles/capture.dockerfile +++ b/dockerfiles/capture.dockerfile @@ -2,7 +2,7 @@ FROM node:20-alpine AS base ## Install dependencies only when needed ## Check https://github.com/nodejs/docker-node/tree/b4117f9333da4138b03a546ec926ef50a31506c3#nodealpine to understand why libc6-compat might be needed. RUN apk add --no-cache libc6-compat -RUN corepack enable && corepack prepare --activate +RUN corepack enable && corepack prepare pnpm@9.6.0 --activate ## Enable `pnpm add --global` on Alpine Linux by setting ## home location environment variable to a location already in $PATH diff --git a/dockerfiles/factory.dockerfile b/dockerfiles/factory.dockerfile index 9286d94..dee2c3e 100644 --- a/dockerfiles/factory.dockerfile +++ b/dockerfiles/factory.dockerfile @@ -10,7 +10,7 @@ ENV PATH="$PNPM_HOME:$PATH" WORKDIR /app COPY --from=mwader/static-ffmpeg:7.0.2 /ffmpeg /usr/local/bin/ COPY --from=mwader/static-ffmpeg:7.0.2 /ffprobe /usr/local/bin/ -RUN corepack enable && corepack prepare --activate +RUN corepack enable && corepack prepare pnpm@9.6.0 --activate ENTRYPOINT ["pnpm"] FROM base AS install diff --git a/dockerfiles/mailbox.dockerfile b/dockerfiles/mailbox.dockerfile index 0a5f3fb..582144a 100644 --- a/dockerfiles/mailbox.dockerfile +++ b/dockerfiles/mailbox.dockerfile @@ -18,7 +18,7 @@ FROM node:20 AS base ENV PNPM_HOME="/pnpm" ENV PATH="$PNPM_HOME:$PATH" WORKDIR /app -RUN corepack enable && corepack prepare --activate +RUN corepack enable && corepack prepare pnpm@9.6.0 --activate FROM base AS build WORKDIR /app diff --git a/dockerfiles/meal.dockerfile b/dockerfiles/meal.dockerfile index a173339..cc32d69 100644 --- a/dockerfiles/meal.dockerfile +++ b/dockerfiles/meal.dockerfile @@ -4,7 +4,7 @@ FROM node:20.15 AS base ENV PNPM_HOME="/pnpm" ENV PATH="$PNPM_HOME:$PATH" WORKDIR /app -RUN corepack enable && corepack prepare --activate +RUN corepack enable && corepack prepare pnpm@9.6.0 --activate FROM base AS build # ENV NODE_ENV=development diff --git a/dockerfiles/migrations.dockerfile b/dockerfiles/migrations.dockerfile index b8d9baf..3af7b97 100644 --- a/dockerfiles/migrations.dockerfile +++ b/dockerfiles/migrations.dockerfile @@ -2,7 +2,7 @@ FROM node:20-alpine AS base ENV PNPM_HOME="/pnpm" ENV PATH="$PNPM_HOME:$PATH" WORKDIR /app -RUN corepack enable && corepack prepare --activate +RUN corepack enable && corepack prepare pnpm@9.6.0 --activate FROM base AS build COPY ./pnpm-workspace.yaml ./.npmrc . diff --git a/dockerfiles/next.dockerfile b/dockerfiles/next.dockerfile index 588313d..aedbd2c 100644 --- a/dockerfiles/next.dockerfile +++ b/dockerfiles/next.dockerfile @@ -6,7 +6,7 @@ FROM node:20-slim AS base FROM base AS deps ENV PNPM_HOME="/pnpm" ENV PATH="$PNPM_HOME:$PATH" -RUN corepack enable && corepack prepare --activate +RUN corepack enable && corepack prepare pnpm@9.6.0 --activate WORKDIR /app diff --git a/dockerfiles/scout.dockerfile b/dockerfiles/scout.dockerfile index 582e93c..7f89e5d 100644 --- a/dockerfiles/scout.dockerfile +++ b/dockerfiles/scout.dockerfile @@ -2,7 +2,7 @@ FROM node:20 AS base ENV PNPM_HOME="/pnpm" ENV PATH="$PNPM_HOME:$PATH" WORKDIR /app - +RUN curl -s https://api.github.com/repos/yt-dlp/yt-dlp/releases/latest | grep "browser_download_url.*yt-dlp_linux\"" | cut -d : -f 2,3 | tr -d "\"" | wget -q -O /usr/local/bin/yt-dlp -i - && chmod +x /usr/local/bin/yt-dlp ## @important If pnpm is downloading node during the build, that's a bandwidth-expensive mistake. ## Node already exists in the docker image at /usr/local/bin/node. ## We should use the node version that exists in the docker image. diff --git a/dockerfiles/strapi.dockerfile b/dockerfiles/strapi.dockerfile index 0942efc..5b47918 100644 --- a/dockerfiles/strapi.dockerfile +++ b/dockerfiles/strapi.dockerfile @@ -1,6 +1,6 @@ FROM node:20 AS strapi WORKDIR /usr/src/app/ -RUN corepack enable && corepack prepare --activate +RUN corepack enable && corepack prepare pnpm@9.6.0 --activate ENV PNPM_HOME="/pnpm" ENV PATH="$PNPM_HOME:$PATH" # ENV NODE_EXTRA_CA_CERTS ${NODE_EXTRA_CA_CERTS} diff --git a/dockerfiles/strapi.dockerfile.full b/dockerfiles/strapi.dockerfile.full index 9b230c1..a852ab7 100644 --- a/dockerfiles/strapi.dockerfile.full +++ b/dockerfiles/strapi.dockerfile.full @@ -2,7 +2,7 @@ FROM node:20-alpine3.18 AS base ## Installing libvips-dev for sharp Compatibility ## (only necessary for alpine docker images) RUN apk update && apk add --no-cache build-base gcc autoconf automake zlib-dev libpng-dev nasm bash vips-dev git -RUN corepack enable && corepack prepare --activate +RUN corepack enable && corepack prepare pnpm@9.6.0 --activate ENV PNPM_HOME="/pnpm" ENV PATH="$PNPM_HOME:$PATH" ARG NODE_ENV=development diff --git a/dockerfiles/worker.dockerfile b/dockerfiles/worker.dockerfile index acef01a..0fd2fcc 100644 --- a/dockerfiles/worker.dockerfile +++ b/dockerfiles/worker.dockerfile @@ -28,7 +28,7 @@ RUN mkdir -p /app/packages/worker && mkdir -p /prod/worker ## Copy manfiests, lockfiles, and configs into docker context COPY package.json pnpm-lock.yaml .npmrc . -RUN corepack enable && corepack prepare --activate +RUN corepack enable && corepack prepare pnpm@9.6.0 --activate # COPY ./packages/image/pnpm-lock.yaml ./packages/image/package.json ./packages/image/ # COPY ./packages/storage/pnpm-lock.yaml ./packages/storage/package.json ./packages/storage/ # COPY ./packages/types/pnpm-lock.yaml ./packages/types/package.json ./packages/types/ diff --git a/packages/image/src/index.spec.ts b/packages/image/src/index.spec.ts index 8c780d3..3e60a01 100644 --- a/packages/image/src/index.spec.ts +++ b/packages/image/src/index.spec.ts @@ -26,7 +26,7 @@ describe('image', function () { this.timeout(1000*60*15) it('should accept a URL and return a path to image on disk', async function () { // const url = 'https://futureporn-b2.b-cdn.net/projektmelody-chaturbate-2024-06-25.mp4' - const url = 'https://futureporn-b2.b-cdn.net/projektmelody-chaturbate-2024-08-10.mp4' + const url = 'https://futureporn-b2.b-cdn.net/projektmelody-chaturbate-2024-08-31.mp4' const imagePath = await getStoryboard(url) expect(imagePath).to.match(/\.png/) }) diff --git a/packages/types/index.d.ts b/packages/types/index.d.ts deleted file mode 100644 index 03f0626..0000000 --- a/packages/types/index.d.ts +++ /dev/null @@ -1,265 +0,0 @@ - -export = Futureporn; -export as namespace Futureporn; - -declare namespace Futureporn { - - type PlatformNotificationType = 'email' | 'manual' | 'twitter' - type ArchiveStatus = 'good' | 'issue' | 'missing' - type RecordingState = 'recording' | 'stalled' | 'aborted' | 'failed' | 'finished' - type ProcessingState = 'processing' - type WaitingState = 'pending_recording' - type Status = Partial - - interface S3File { - s3_key: string; - s3_id?: string; - bucket: string; - created_at?: Date; - updated_at?: Date; - } - - interface VtuberResponse { - id: string; - } - - interface VtuberRecord { - id: string; - display_name: string; - chaturbate: string; - twitter: string; - patreon: string; - twitch: string; - tiktok: string; - onlyfans: string; - youtube: string; - linktree: string; - carrd: string; - fansly: string; - pornhub: string; - discord: string; - reddit: string; - throne: string; - instagram: string; - facebook: string; - merch: string; - slug: string; - image: string; - theme_color: string; - image_blur: string; - fansly_id: string; - chaturbate_id: string; - twitter_id: string; - } - - interface VodRecord { - id: string; - stream: Stream; - stream_id: string; - created_at: string; - updated_at: string; - published_at?: string; - title?: string; - date: string; - mux_asset: MuxAsset; - thumbnail?: S3File; - vtuber: string; - ipfs_cid?: string; - torrent?: string; - announce_title?: string; - announce_url?: string; - note?: string; - url: string - } - - interface VodResponse { - id: string; - stream: Stream; - stream_id: string; - created_at: string; - updated_at: string; - published_at?: string; - title?: string; - date: string; - mux_asset: MuxAsset; - thumbnail?: S3File; - vtuber: Vtuber; - tags?: Tag[]; - timestamps?: Timestamp[]; - ipfs_cid?: string; - s3_file?: S3File; - torrent?: string; - announce_title?: string; - announce_url?: string; - uploader?: User; - note?: string; - url: string - } - - interface Stream { - id: string; - url: string; - platform_notification_type: PlatformNotificationType; - discord_message_id: string; - date: Date; - created_at: Date; - updated_at: Date; - vtuber: string; - tweet: string; - vods?: Vod[]; - archive_status: ArchiveStatus; - is_chaturbate_stream: Boolean; - is_fansly_stream: Boolean; - is_recording_aborted: Boolean; - status: Status; - segments?: Segment[] - } - - interface RecordingRecord { - id: number; - recording_state: RecordingState; - file_size: number; - discord_message_id: string; - is_recording_aborted: boolean; - updated_at: Date; - created_at: Date; - } - - interface Segment { - id: number; - s3_key: string; - s3_id: string; - bytes: number; - stream?: Stream[]; - created_at: Date; - updated_at: Date; - } - - - - interface IMuxAsset { - id: number; - attributes: { - playbackId: string; - assetId: string; - } - } - - interface IPagination { - page: number; - pageSize: number; - pageCount: number; - total: number; - } - - interface IMuxAssetResponse { - data: IMuxAsset; - meta: IMeta; - } - - interface IMeta { - pagination: IPagination; - } - - - - interface IPlatformNotification { - id: number; - attributes: { - source: string; - platform: string; - date: string; - date2: string; - vtuber: number; - } - } - - interface IPlatformNotificationResponse { - data: IPlatformNotification; - meta: IMeta; - error?: any; - } - - interface IStream { - id: number; - attributes: { - date: string; - date2: string; - archiveStatus: ArchiveStatus; - vods: IVodsResponse; - cuid: string; - vtuber: IVtuberResponse; - tweet: ITweetResponse; - isChaturbateStream: boolean; - isFanslyStream: boolean; - platformNotifications: IPlatformNotification[]; - } - } - - interface IStreamResponse { - data: IStream; - meta: IMeta; - error?: any; - } - - interface IStreamsResponse { - data: IStream[]; - meta: IMeta; - } - - - interface IVtuber { - id: number; - attributes: { - slug: string; - displayName: string; - chaturbate?: string; - twitter?: string; - patreon?: string; - twitch?: string; - tiktok?: string; - onlyfans?: string; - youtube?: string; - linktree?: string; - carrd?: string; - fansly?: string; - pornhub?: string; - discord?: string; - reddit?: string; - throne?: string; - instagram?: string; - facebook?: string; - merch?: string; - vods: IVod[]; - description1: string; - description2?: string; - image: string; - imageBlur?: string; - themeColor: string; - fanslyId?: string; - chaturbateId?: string; - twitterId?: string; - } - } - - interface IVtuberResponse { - data: IVtuber; - meta: IMeta; - } - - interface IVtubersResponse { - data: IVtuber[]; - meta: IMeta; - } - - type NotificationData = { - isMatch: boolean; - url?: string; - platform?: string; - channel?: string; - displayName?: string; - date?: string; - userId?: string | null; - avatar?: string; - }; -} \ No newline at end of file diff --git a/packages/types/package.json b/packages/types/package.json index f30b689..f4c6d72 100644 --- a/packages/types/package.json +++ b/packages/types/package.json @@ -8,7 +8,7 @@ "clean": "rm -rf dist", "superclean": "rm -rf node_modules && rm -rf pnpm-lock.yaml && rm -rf dist" }, - "main": "index.d.ts", + "main": "src/index.ts", "keywords": [], "author": "", "license": "Unlicense", diff --git a/packages/types/src/index.ts b/packages/types/src/index.ts new file mode 100644 index 0000000..dbccff4 --- /dev/null +++ b/packages/types/src/index.ts @@ -0,0 +1,348 @@ + + +export type PlatformNotificationType = 'email' | 'manual' | 'twitter' +export type ArchiveStatus = 'good' | 'issue' | 'missing' +export type RecordingState = 'recording' | 'stalled' | 'aborted' | 'failed' | 'finished' +export type ProcessingState = 'processing' +export type WaitingState = 'pending_recording' +export type Status = Partial + +export interface S3File { + s3_key: string; + s3_id?: string; + bucket: string; + created_at?: Date; + updated_at?: Date; +} + +export interface VtuberResponse { + id: string; +} + +export interface VtuberDataScrape { + display_name: string; + slug: string; + chaturbate_id?: string; + chaturbate?: string; + fansly_id?: string; + fansly?: string; +} + +export interface ScoutResponse { + error: boolean; + message: string; + data: any; +} + +export interface ChaturbateRoomSummary { + url: string; + name: string; +} + +export interface ChaturbateRoom { + name: string; + username: string; + room_subject: string; + tags: string[]; + is_new: boolean; + num_users: number; + num_followers: number; + current_show: string; + location?: string; + country?: string; + spoken_languages?: string; + display_name?: string; + birthday?: string; + is_hd: boolean; + age: number; + seconds_online: number; + image_url: string; + image_url_360x270: string; + chat_room_url_revshare: string; + iframe_embed_revshare: string; + chat_room_url: string; + iframe_embed: string; + slug: string; + } + +export interface VtuberRecord { + id: string; + display_name: string; + chaturbate: string; + twitter: string; + patreon: string; + twitch: string; + tiktok: string; + onlyfans: string; + youtube: string; + linktree: string; + carrd: string; + fansly: string; + pornhub: string; + discord: string; + reddit: string; + throne: string; + instagram: string; + facebook: string; + merch: string; + slug: string; + image: string; + theme_color: string; + image_blur: string; + fansly_id: string; + chaturbate_id: string; + twitter_id: string; +} + +export interface VodRecord { + id: string; + stream: Stream; + stream_id: string; + created_at: string; + updated_at: string; + published_at?: string; + title?: string; + date: string; + mux_asset: MuxAssetRecord; + thumbnail?: S3File; + vtuber: string; + ipfs_cid?: string; + torrent?: string; + announce_title?: string; + announce_url?: string; + note?: string; + url: string; + discord_message_id: string; +} + +export interface TagRecord { + id: string +} + +export interface Timestamp { + id: string +} + +export interface User { + id: string +} + +export interface VodResponse { + id: string; + stream: Stream; + stream_id: string; + created_at: string; + updated_at: string; + published_at?: string; + title?: string; + date: string; + mux_asset: MuxAssetRecord; + thumbnail?: S3File; + vtuber: VtuberRecord; + tags?: TagRecord[]; + timestamps?: Timestamp[]; + ipfs_cid?: string; + s3_file?: S3File; + torrent?: string; + announce_title?: string; + announce_url?: string; + uploader?: User; + note?: string; + url: string; + segments?: SegmentResponse[]; + status: Status; + discord_message_id: string; + is_recording_aborted: boolean; +} + + + +export interface Stream { + id: string; + url: string; + platform_notification_type: PlatformNotificationType; + discord_message_id: string; + date: Date; + created_at: Date; + updated_at: Date; + vtuber: string; + tweet: string; + vods?: Vod[]; + archive_status: ArchiveStatus; + is_chaturbate_stream: Boolean; + is_fansly_stream: Boolean; + is_recording_aborted: Boolean; + status: Status; + segments?: SegmentResponse[] +} + +export interface RecordingRecord { + id: number; + recording_state: RecordingState; + file_size: number; + discord_message_id: string; + is_recording_aborted: boolean; + updated_at: Date; + created_at: Date; +} + +export interface SegmentResponse { + id: number; + s3_key: string; + s3_id: string; + bytes: number; + vod?: VodResponse; + created_at: string; + updated_at: string; +} + + + +export interface MuxAssetRecord { + id: number; + playbackId: string; + assetId: string; +} + + +export interface IMuxAsset { + id: number; + attributes: { + playbackId: string; + assetId: string; + } +} + +export interface IPagination { + page: number; + pageSize: number; + pageCount: number; + total: number; +} + +export interface IMuxAssetResponse { + data: IMuxAsset; + meta: IMeta; +} + +export interface IMeta { + pagination: IPagination; +} + + + +export interface IPlatformNotification { + id: number; + attributes: { + source: string; + platform: string; + date: string; + date2: string; + vtuber: number; + } +} + +export interface IPlatformNotificationResponse { + data: IPlatformNotification; + meta: IMeta; + error?: any; +} + +export interface Vod { + id: string +} + +export interface IVodsResponse { + id: string +} + +export interface IVod { + id: string +} + +export interface ITweetResponse { + id: string +} + +export interface IStream { + id: number; + attributes: { + date: string; + date2: string; + archiveStatus: ArchiveStatus; + vods: IVodsResponse; + cuid: string; + vtuber: IVtuberResponse; + tweet: ITweetResponse; + isChaturbateStream: boolean; + isFanslyStream: boolean; + platformNotifications: IPlatformNotification[]; + } +} + +export interface IStreamResponse { + data: IStream; + meta: IMeta; + error?: any; +} + +export interface IStreamsResponse { + data: IStream[]; + meta: IMeta; +} + + +export interface IVtuber { + id: number; + attributes: { + slug: string; + displayName: string; + chaturbate?: string; + twitter?: string; + patreon?: string; + twitch?: string; + tiktok?: string; + onlyfans?: string; + youtube?: string; + linktree?: string; + carrd?: string; + fansly?: string; + pornhub?: string; + discord?: string; + reddit?: string; + throne?: string; + instagram?: string; + facebook?: string; + merch?: string; + vods: IVod[]; + description1: string; + description2?: string; + image: string; + imageBlur?: string; + themeColor: string; + fanslyId?: string; + chaturbateId?: string; + twitterId?: string; + } +} + +export interface IVtuberResponse { + data: IVtuber; + meta: IMeta; +} + +export interface IVtubersResponse { + data: IVtuber[]; + meta: IMeta; +} + +export type NotificationData = { + isMatch: boolean; + url?: string; + platform?: string; + channel?: string; + displayName?: string; + date?: string; + userId?: string | null; + avatar?: string; +}; diff --git a/packages/types/tsconfig.json b/packages/types/tsconfig.json index 262a829..6619133 100644 --- a/packages/types/tsconfig.json +++ b/packages/types/tsconfig.json @@ -4,7 +4,7 @@ "esModuleInterop": true, "skipLibCheck": true, "target": "es2022", - "allowJs": true, + "allowJs": false, "resolveJsonModule": true, "moduleDetection": "force", "isolatedModules": true, @@ -28,7 +28,7 @@ }, // Include the necessary files for your project "files": [ - "index.d.ts" + "src/index.ts" ], "exclude": [ "node_modules" diff --git a/services/bot/crontab b/services/bot/crontab index 24ea2df..6a5d8ce 100644 --- a/services/bot/crontab +++ b/services/bot/crontab @@ -13,6 +13,6 @@ # * * * * * task ?opts {payload} -## every n minutes, we see which /vods are stale and we mark them as such. +## every 1 minutes, we see which /vods are stale and we mark them as such. ## this prevents stalled Record updates by marking stalled recordings as stopped -* * * * * update_svod_statuses ?max=1 { stalled_minutes:1 } \ No newline at end of file +* * * * * update_vod_statuses ?max=1 { stalled_minutes:1, finished_minutes:2 } \ No newline at end of file diff --git a/services/bot/package.json b/services/bot/package.json index e8d16c5..d0e8cf2 100644 --- a/services/bot/package.json +++ b/services/bot/package.json @@ -25,7 +25,6 @@ "dependencies": { "@discordeno/bot": "19.0.0-next.746f0a9", "@discordeno/rest": "19.0.0-next.b3a8c86", - "@futureporn/scout": "workspace:^", "@paralleldrive/cuid2": "^2.2.2", "@types/node": "^22.2.0", "@types/qs": "^6.9.15", diff --git a/services/bot/pnpm-lock.yaml b/services/bot/pnpm-lock.yaml index cd5d879..eae2b7b 100644 --- a/services/bot/pnpm-lock.yaml +++ b/services/bot/pnpm-lock.yaml @@ -14,9 +14,6 @@ importers: '@discordeno/rest': specifier: 19.0.0-next.b3a8c86 version: 19.0.0-next.b3a8c86 - '@futureporn/scout': - specifier: workspace:^ - version: link:../../packages/scout '@paralleldrive/cuid2': specifier: ^2.2.2 version: 2.2.2 @@ -91,6 +88,42 @@ importers: specifier: ^5.5.4 version: 5.5.4 + ../..: {} + + ../../packages/image: {} + + ../../packages/infra: {} + + ../../packages/meal: {} + + ../../packages/old: {} + + ../../packages/storage: {} + + ../../packages/taco: {} + + ../../packages/types: {} + + ../../packages/utils: {} + + ../../packages/video: {} + + ../capture: {} + + ../factory: {} + + ../mailbox: {} + + ../migrations: {} + + ../next: {} + + ../scout: {} + + ../strapi: {} + + ../uppy: {} + packages: '@babel/code-frame@7.24.7': diff --git a/services/bot/src/commands/cancel.ts b/services/bot/src/commands/cancel.ts index 776f060..a4c0970 100644 --- a/services/bot/src/commands/cancel.ts +++ b/services/bot/src/commands/cancel.ts @@ -15,7 +15,7 @@ createCommand({ if (!message) return bot.logger.error('interaction.message was missing'); if (!message.id) return bot.logger.error(`interaction.message.id was missing`); - const url = `${configs.postgrestUrl}/streams?discord_message_id=eq.${message.id}`; + const url = `${configs.postgrestUrl}/vods?discord_message_id=eq.${message.id}`; const options = { method: 'PATCH', headers: { @@ -30,15 +30,15 @@ createCommand({ }) }; - let streamId: string; + let vodId: string; try { const response = await fetch(url, options); bot.logger.info(`response.ok=${response.ok}`) const data: any = await response.json(); - streamId = data?.at(0).id + vodId = data?.at(0).id bot.logger.info(interaction.user); - interaction.respond(`<@${interaction.user.id}> cancelled recording on Stream ${streamId}`, { isPrivate: false }) + interaction.respond(`<@${interaction.user.id}> cancelled recording on VOD ${vodId}`, { isPrivate: false }) bot.logger.info(`Cancel command successfully ran on message.id=${message.id}`) } catch (error) { diff --git a/services/bot/src/commands/ping.ts b/services/bot/src/commands/ping.ts index 4daf074..bf448a5 100644 --- a/services/bot/src/commands/ping.ts +++ b/services/bot/src/commands/ping.ts @@ -7,9 +7,7 @@ createCommand({ type: ApplicationCommandTypes.ChatInput, async execute(interaction: Interaction) { const ping = Date.now() - snowflakeToTimestamp(interaction.id) - const embeds = createEmbeds().setTitle(`The bot ping is ${ping}ms`) - await interaction.respond({ embeds }) }, }) \ No newline at end of file diff --git a/services/bot/src/commands/process.ts b/services/bot/src/commands/process.ts index 71ae448..a9412ea 100644 --- a/services/bot/src/commands/process.ts +++ b/services/bot/src/commands/process.ts @@ -5,6 +5,7 @@ import getStreamFromDatabase from '../fetchers/getStreamFromDatabase.ts' import patchStreamInDatabase from '../fetchers/patchStreamInDatabase.ts' import { quickAddJob, type WorkerUtilsOptions } from 'graphile-worker' import { configs } from '../config.ts' +import findVod from '../fetchers/findVod.ts' function throwErr(msg: string) { logger.error(msg) @@ -19,12 +20,12 @@ createCommand({ const discord_message_id = String(interaction?.message?.id) logger.info(`process command begins.`) if (!discord_message_id) return throwErr('failed to get discord message id'); - const stream = await getStreamFromDatabase(discord_message_id) - if (!stream) return throwErr('failed to get stream'); + const vod = await findVod({ discord_message_id }) + if (!vod) return throwErr('failed to get vod while finding vod to process'); const options: WorkerUtilsOptions = { connectionString: configs.connectionString } logger.info(`now we will quickAddJob process_video`) - await quickAddJob(options, 'process_video', { stream_id: stream.id }) + await quickAddJob(options, 'process_video', { vod_id: vod.id }) logger.info(`now we will patchStreamInDatabase`) - await patchStreamInDatabase(stream.id, { status: 'processing' }) + await patchStreamInDatabase(vod.id, { status: 'processing' }) }, }) diff --git a/services/bot/src/commands/record.ts b/services/bot/src/commands/record.ts index 6739582..b07e713 100644 --- a/services/bot/src/commands/record.ts +++ b/services/bot/src/commands/record.ts @@ -16,10 +16,15 @@ import createVod from '../fetchers/createVod.ts' import findOrCreateVtuber from '../fetchers/findOrCreateVtuber.ts' import findOrCreateStream from '../fetchers/findOrCreateStream.ts' + +/** + * + * Get the livestream URL from the pre-existing discord message. IDK why we do this + */ async function getUrlFromMessage(interaction: Interaction): Promise { const messageId = interaction.message?.id - const pgRequestUrl = `${configs.postgrestUrl}/streams?discord_message_id=eq.${messageId}` + const pgRequestUrl = `${configs.postgrestUrl}/vods?discord_message_id=eq.${messageId}` logger.info(`pgRequestUrl=${pgRequestUrl}`) const requestOptions = { method: 'GET', @@ -37,8 +42,8 @@ async function getUrlFromMessage(interaction: Interaction): Promise throw new Error(`Problem during getUrlFromMessage. res.status=${res.status}, res.statusText=${res.statusText}`) } const json = await res.json() as Stream[] - const stream = json[0] - const url = stream?.url + const vod = json[0] + const url = vod?.url if (!url) return null else return url } catch (e) { @@ -109,12 +114,13 @@ createCommand({ console.error(msg) throw new Error(msg) } - + + const discord_message_id = message.id.toString() const date = new Date() const vtuberId = await findOrCreateVtuber({ url }) const streamId = await findOrCreateStream({ vtuberId, date }) if (!streamId) throw new Error(`failed to find or create a Stream in database`); - const vodId = await createVod({ stream_id: streamId, vtuber: vtuberId, url }) + const vodId = await createVod({ stream_id: streamId, vtuber: vtuberId, url, discord_message_id }) logger.info(`Success! We have created VOD id=${vodId}`) } catch (e) { diff --git a/services/bot/src/config.ts b/services/bot/src/config.ts index 0ce84e4..9db60d5 100644 --- a/services/bot/src/config.ts +++ b/services/bot/src/config.ts @@ -9,6 +9,7 @@ const requiredEnvVars = [ 'DISCORD_GUILD_ID', 'DISCORD_APPLICATION_ID', 'AUTOMATION_USER_JWT', + 'SCOUT_URL' ] as const; const getEnvVar = (key: typeof requiredEnvVars[number]): string => { @@ -28,10 +29,12 @@ export interface Config { discordChannelId: string; connectionString: string; discordApplicationId: string; + scoutUrl: string; } export const configs: Config = { + scoutUrl: getEnvVar('SCOUT_URL'), connectionString: getEnvVar('WORKER_CONNECTION_STRING'), httpProxy: getEnvVar('HTTP_PROXY'), postgrestUrl: getEnvVar('POSTGREST_URL'), diff --git a/services/bot/src/events/interactionCreate.ts b/services/bot/src/events/interactionCreate.ts index b5a7bf0..2d0cbf7 100644 --- a/services/bot/src/events/interactionCreate.ts +++ b/services/bot/src/events/interactionCreate.ts @@ -22,7 +22,7 @@ const handleApplicationCommand = async function handleApplicationCommand (intera const command = commands.get(interaction.data.name) if (!command) { - bot.logger.error(`Command ${interaction.data.name} (customId=${interaction.data.customId}) not found`) + bot.logger.error(`Command ${interaction.data.name} not found`) return } @@ -34,7 +34,7 @@ const handleMessageComponent = async function handleMessageComponent (interactio if (!interaction.data) return if (!interaction.data.customId) return const command = commands.get(interaction.data.customId) - if (!command) return bot.logger.error(`Command ${interaction.data.customId} not found`); + if (!command) return bot.logger.error(`Command customId=${interaction.data.customId} not found`); execCommand(command, interaction) } diff --git a/services/bot/src/fetchers/findOrCreateVtuber.ts b/services/bot/src/fetchers/findOrCreateVtuber.ts index ea68fc2..f6f16f0 100644 --- a/services/bot/src/fetchers/findOrCreateVtuber.ts +++ b/services/bot/src/fetchers/findOrCreateVtuber.ts @@ -1,6 +1,5 @@ import { configs } from "../config.ts" import type { VtuberRecord, VtuberResponse } from "@futureporn/types" -import scrapeVtuberData from '@futureporn/scout/scrapeVtuberData.ts' import { bot } from '../bot.ts' import qs from 'qs' @@ -59,6 +58,7 @@ async function findVtuber(query: Partial, options?: Partial): Promise { throw new Error(msg) } const json = await res.json() as VtuberResponse[] + bot.logger.info(`createVtuber with vtuber as follows`) + bot.logger.info(vtuber) bot.logger.info(json) const vtuberData = json[0] if (!vtuberData) throw new Error('failed to createVtuber') @@ -99,7 +101,8 @@ export default async function findOrCreateVtuber(query: Partial => res.json() as any) return createVtuber(vtuber) } else { return foundVtuber diff --git a/services/bot/src/fetchers/findVod.ts b/services/bot/src/fetchers/findVod.ts new file mode 100644 index 0000000..98bee01 --- /dev/null +++ b/services/bot/src/fetchers/findVod.ts @@ -0,0 +1,28 @@ +import type { VodResponse } from "@futureporn/types"; +import { bot } from "../bot.ts"; +import { configs } from "../config.ts"; + +export default async function findVod({ vod_id, discord_message_id }: { vod_id?: string, discord_message_id?: string }): Promise { + const fetchUrl = (!!vod_id) + ? `${configs.postgrestUrl}/vods?id=eq.${vod_id}&select=*,segments(bytes,updated_at,created_at)` + : `${configs.postgrestUrl}/vods?discord_message_id=eq.${discord_message_id}&select=*,segments(bytes,updated_at,created_at)` + bot.logger.info(fetchUrl) + const fetchOptions = { + method: 'GET', + headers: { + 'Content-Type': 'application/json', + 'Accept': 'application/json', + 'Prefer': 'return=representation' + } + } + const res = await fetch(fetchUrl, fetchOptions) + if (!res.ok) { + const msg = `request failed. status=${res.status}, statusText=${res.statusText}` + bot.logger.error(msg) + throw new Error(msg) + } + const json = await res.json() as VodResponse[] + // bot.logger.info(`vod results as follows.`) + // bot.logger.info(json) + return json?.at(0) || null +} \ No newline at end of file diff --git a/services/bot/src/index.ts b/services/bot/src/index.ts index 8e194cb..742dbd7 100644 --- a/services/bot/src/index.ts +++ b/services/bot/src/index.ts @@ -30,7 +30,6 @@ async function setupGraphileWorker() { const runnerOptions: RunnerOptions = { preset } - const runner = await run(runnerOptions) if (!runner) throw new Error('failed to initialize graphile worker'); await runner.promise @@ -39,22 +38,21 @@ async function setupGraphileWorker() { async function setupBot() { - bot.logger.info('Starting @futureporn/bot.') - bot.logger.info('Loading commands...') await importDirectory(join(__dirname, './commands')) - bot.logger.info('Loading events...') await importDirectory(join(__dirname, './events')) - await bot.start() } -async function main() { +async function main() { + console.log('setting up bot') await setupBot() - await setupGraphileWorker() + console.log('setting up graphile worker') + await setupGraphileWorker() // this needs to run after setupBot() has run + console.log('updating discord application commands') await updateApplicationCommands() // this needs to run after importDirectory() has run } diff --git a/services/bot/src/tasks/update_discord_message.ts b/services/bot/src/tasks/update_discord_message.ts index e236c21..127c6d0 100644 --- a/services/bot/src/tasks/update_discord_message.ts +++ b/services/bot/src/tasks/update_discord_message.ts @@ -1,5 +1,5 @@ import 'dotenv/config' -import type { Status, Stream, Segment } from '@futureporn/types' +import type { Status, Stream, SegmentResponse, VodRecord, VodResponse } from '@futureporn/types' import { type Task, type Helpers } from 'graphile-worker' import { intervalToDuration, formatDuration, isBefore, sub, max } from 'date-fns' import prettyBytes from 'pretty-bytes' @@ -13,12 +13,13 @@ import { } from '@discordeno/bot' import { bot } from '../bot.ts' import { configs } from '../config.ts' +import findVod from '../fetchers/findVod.ts' const yeahEmojiId = BigInt('1253191939461873756') interface Payload { - vod_id: number; + vod_id: string; } @@ -26,39 +27,29 @@ interface Payload { function assertPayload(payload: any): asserts payload is Payload { if (typeof payload !== "object" || !payload) throw new Error("invalid payload"); if (!payload.vod_id) throw new Error(`vod_id was absent in the payload`); + if (typeof payload.vod_id !== 'string') throw new Error(`vod_id was not a string`); } -async function editDiscordMessage({ helpers, stream }: { stream: Stream, helpers: Helpers }) { - - const discordMessageId = stream.discord_message_id +async function editDiscordMessage({ helpers, vod }: { vod: VodResponse, helpers: Helpers }) { + // bot.logger.info(`editDiscordmessage with vod status=${vod.status} `) + // bot.logger.info(vod) + const discordMessageId = vod.discord_message_id if (!discordMessageId) throw new Error(`discordMessageId was missing!`); if (typeof discordMessageId !== 'string') throw new Error(`discordMessageId was not a string!`); const channelId = BigInt(configs.discordChannelId) const updatedMessage: EditMessage = { - embeds: getEmbeds(stream), - components: getButtonRow(stream.status) + embeds: getEmbeds(vod, helpers), + components: getButtonRow(vod.status) } bot.helpers.editMessage(channelId, discordMessageId, updatedMessage) - - - } -async function getStreamFromDatabase(streamId: number) { - const res = await fetch(`${configs.postgrestUrl}/streams?select=*,segments(*)&id=eq.${streamId}`) - if (!res.ok) { - throw new Error(`failed fetching stream ${streamId}. status=${res.status}, statusText=${res.statusText}`) - } - const body = await res.json() as Stream[] - return body[0]; -} - /** @@ -72,12 +63,13 @@ export const update_discord_message: Task = async function (payload, helpers: He try { assertPayload(payload) const { vod_id } = payload - const streamId = vod_id + const vodId = vod_id - const stream = await getStreamFromDatabase(streamId) - if (!stream) throw new Error('failed to get stream from database'); - // helpers.logger.info(`update_discord_message with streamId=${streamId}. stream=${JSON.stringify(stream)}`) - editDiscordMessage({ helpers, stream }) + const vod = await findVod({ vod_id: vodId }) + if (!vod) throw new Error('failed to get vod from database'); + // helpers.logger.info(`update_discord_message got the following vod`) + // helpers.logger.info(JSON.stringify(vod, null, 2)) + await editDiscordMessage({ helpers, vod }) } catch (e) { helpers.logger.error(`caught an error during update_discord_message. e=${e}`) } @@ -85,13 +77,14 @@ export const update_discord_message: Task = async function (payload, helpers: He -function getEmbeds(stream: Stream) { - const streamId = stream.id - const url = stream.url - const segments = stream?.segments - const status = stream.status +function getEmbeds(vod: VodResponse, helpers: Helpers) { + const vodId = vod.id + const url = vod.url + const segments = vod?.segments + const status = vod.status || 'unknown' + bot.logger.info(`getEmbeds with vodId=${vodId}, status=${vod.status}, segments.length=${segments?.length}`) const embeds = new EmbedsBuilder() - .setTitle(`Stream ${streamId}`) + .setTitle(`VOD ${vodId}`) .setFields([ { name: 'Status', value: status.charAt(0).toUpperCase()+status.slice(1), inline: true }, // { name: 'Filesize', value: prettyBytes(fileSize), inline: true }, // filesize isn't on stream. filesize is on segment. keeping for reference. @todo @@ -103,7 +96,7 @@ function getEmbeds(stream: Stream) { .setColor(2326507) } else if (status === 'recording') { embeds - .setDescription('The stream is being recorded.') + .setDescription('The vod is being recorded.') .setColor(392960) } else if (status === 'aborted') { embeds @@ -123,7 +116,7 @@ function getEmbeds(stream: Stream) { .setColor(392960) } else if (status === 'stalled') { embeds - .setDescription("We have not received a progress update in the past two minutes.") + .setDescription("We have not received a progress update recently.") .setColor(8289651) } else { embeds @@ -133,11 +126,11 @@ function getEmbeds(stream: Stream) { // Add an Embed for segments if (segments) { - const getDuration = (s: Segment) => formatDuration(intervalToDuration({ start: s.created_at, end: s.updated_at })) + const getDuration = (s: SegmentResponse) => formatDuration(intervalToDuration({ start: s.created_at, end: s.updated_at })) embeds.newEmbed() .setTitle(`Recording Segments`) - .setFields(segments.map((s, i) => ( - { + .setFields(segments.sort((a, b) => new Date(a.created_at).getTime() - new Date(b.created_at).getTime()).map((s, i) => ( + { name: `Segment ${i+1}`, value: `${getDuration(s)} (${prettyBytes(s.bytes)})`, inline: false @@ -211,6 +204,7 @@ function getButtonRow(streamStatus: Status): ActionRow[] { components.push(processButton) } else { components.push(retryButton) + components.push(processButton) } diff --git a/services/bot/src/tasks/update_vod_statuses.ts b/services/bot/src/tasks/update_vod_statuses.ts index 50f35b3..2c44d88 100644 --- a/services/bot/src/tasks/update_vod_statuses.ts +++ b/services/bot/src/tasks/update_vod_statuses.ts @@ -7,108 +7,109 @@ import { configs } from '../config.ts' interface Payload { stalled_minutes: number; + finished_minutes: number; } function assertPayload(payload: any): asserts payload is Payload { if (typeof payload !== "object" || !payload) throw new Error("invalid payload"); if (!payload.stalled_minutes) throw new Error(`stalled_minutes was absent in the payload`); + if (!payload.finished_minutes) throw new Error(`finished_minutes was absent in the payload`); if (typeof payload.stalled_minutes !== 'number') throw new Error(`stalled_minutes parameter was not a number`); + if (typeof payload.finished_minutes !== 'number') throw new Error(`finished_minutes parameter was not a number`); +} + +async function updateFinishedVods({ + helpers, + finished_minutes, + url +}: { + helpers: Helpers, + finished_minutes: number, + url: string +}) { + helpers.logger.info(`updateFinishedVods with finished_minutes=${finished_minutes}, url=${url}`) + + // 1. identify and update stalled /vods + // Any vods that was updated earlier than n minute ago AND is in 'pending_recording' or 'recording' state is marked as stalled. + const timestamp = sub(new Date(), { minutes: finished_minutes }).toISOString() + const queryOptions = { + updated_at: `lt.${timestamp}`, + or: '(status.eq.pending_recording,status.eq.recording)' + } + const updatePayload = { + updated_at: new Date().toISOString(), + status: 'stalled' as Status + } + // helpers.logger.info(JSON.stringify(updatePayload)) + const query = qs.stringify(queryOptions) + const res = await fetch (`${url}?${query}`, { + method: 'PATCH', + headers: { + 'Content-Type': 'application/json', + 'Authorization': `Bearer ${configs.automationUserJwt}`, + 'Prefer': 'return=headers-only' + }, + body: JSON.stringify(updatePayload) + }) + if (!res.ok) { + const body = await res.text() + helpers.logger.info(JSON.stringify(res.headers)) + helpers.logger.error(`Response code was not 200. status=${res.status}, statusText=${res.statusText}`) + helpers.logger.error(body) + return; + } } async function updateStalledVods({ helpers, - stalled_minutes, - url + url, + stalled_minutes = 1, }: { helpers: Helpers, - stalled_minutes: number, - url: string + url: string, + stalled_minutes?: number, }) { + // Identify and update stalled vods + const stalledTimestamp = sub(new Date(), { minutes: stalled_minutes }).toISOString(); + const stalledQueryOptions = { + select: 'status,id,segments!inner(updated_at)', + 'segments.updated_at': `lt.${stalledTimestamp}`, + or: '(status.eq.pending_recording,status.eq.recording)', + }; + const stalledUpdatePayload = { + status: 'stalled', + }; - // 1. identify and update stalled /vods - // Any vods that was updated earlier than n minute ago AND is in 'pending_recording' or 'recording' state is marked as stalled. - const timestamp = sub(new Date(), { minutes: stalled_minutes }).toISOString() - const queryOptions = { - updated_at: `lt.${timestamp}`, - or: '(status.eq.pending_recording,status.eq.recording)' - } - const updatePayload = { - updated_at: new Date().toISOString(), - status: 'stalled' as Status - } - // helpers.logger.info(JSON.stringify(updatePayload)) - const query = qs.stringify(queryOptions) - const res = await fetch (`${url}?${query}`, { - method: 'PATCH', - headers: { - 'Content-Type': 'application/json', - 'Authorization': `Bearer ${configs.automationUserJwt}`, - 'Prefer': 'return=headers-only' - }, - body: JSON.stringify(updatePayload) - }) - if (!res.ok) { - const body = await res.text() - helpers.logger.info(JSON.stringify(res.headers)) - helpers.logger.error(`Response code was not 200. status=${res.status}, statusText=${res.statusText}`) - helpers.logger.error(body) - return; - } - -} - -async function updateRecordingVods({ - helpers, - url -}: { - helpers: Helpers, - url: string -}) { - - // identify and update recording /vods - // Any vods that has a segment that was updated within the past 1 minutes is considered recording - const timestamp = sub(new Date(), { minutes: 1 }).toISOString() - const queryOptions = { - select: 'status,id,segments!inner(updated_at)', - 'segments.updated_at': `lt.${timestamp}`, - or: '(status.eq.pending_recording,status.eq.recording)', - } - const updatePayload = { - status: 'recording' - } - // helpers.logger.info(JSON.stringify(updatePayload)) - const query = qs.stringify(queryOptions) - const options = { - method: 'PATCH', - headers: { - 'Content-Type': 'application/json', - 'Authorization': `Bearer ${configs.automationUserJwt}`, - 'Prefer': 'return=headers-only' - }, - body: JSON.stringify(updatePayload) - } - const res = await fetch (`${url}?${query}`, options) - if (!res.ok) { - const body = await res.text() - helpers.logger.info(JSON.stringify(res.headers)) - helpers.logger.error(`Response code was not 200. status=${res.status}, statusText=${res.statusText}`) - helpers.logger.error(body) - return; - } - + const stalledQuery = qs.stringify(stalledQueryOptions, { encode: false }); + const stalledFetchUrl = `${url}?${stalledQuery}`; + helpers.logger.info(`updateStalledVods with stalledFetchUrl=${stalledFetchUrl}, stalled_minutes=${stalled_minutes}, url=${url}`); + const stalledOptions = { + method: 'PATCH', + headers: { + 'Content-Type': 'application/json', + 'Authorization': `Bearer ${configs.automationUserJwt}`, + 'Prefer': 'return=headers-only', + }, + body: JSON.stringify(stalledUpdatePayload), + }; + const stalledRes = await fetch(stalledFetchUrl, stalledOptions); + if (!stalledRes.ok) { + const stalledBody = await stalledRes.text(); + helpers.logger.info(JSON.stringify(stalledRes.headers)); + helpers.logger.error(`Stalled response code was not 200. status=${stalledRes.status}, statusText=${stalledRes.statusText}`); + helpers.logger.error(stalledBody); + return; + } } export const update_vod_statuses: Task = async function (payload: unknown, helpers: Helpers) { assertPayload(payload) - const { stalled_minutes } = payload - // helpers.logger.info(`update_vod_statuses has begun.`) - + const { stalled_minutes, finished_minutes } = payload + helpers.logger.info(` & & update_vod_statuses has begun.`) const url = 'http://postgrest.futureporn.svc.cluster.local:9000/vods' - try { - // await updateStalledVods({ helpers, url, stalled_minutes }) - await updateRecordingVods({ helpers, url }) - + await updateStalledVods({ helpers, url, stalled_minutes }) + await updateFinishedVods({ helpers, url, finished_minutes }) } catch (e: any) { if (e instanceof Error) { helpers.logger.error(`hi there we encountered an error while fetching /vods`) @@ -116,7 +117,6 @@ export const update_vod_statuses: Task = async function (payload: unknown, helpe } else { helpers.logger.error(e) } - } } diff --git a/services/capture/package.json b/services/capture/package.json index da74a27..a2e3188 100644 --- a/services/capture/package.json +++ b/services/capture/package.json @@ -21,7 +21,6 @@ "@aws-sdk/client-s3": "^3.617.0", "@aws-sdk/lib-storage": "^3.588.0", "@aws-sdk/types": "^3.609.0", - "@futureporn/scout": "workspace:^", "@futureporn/types": "workspace:^", "@futureporn/utils": "workspace:^", "@paralleldrive/cuid2": "^2.2.2", diff --git a/services/capture/pnpm-lock.yaml b/services/capture/pnpm-lock.yaml index 7e34753..b58db37 100644 --- a/services/capture/pnpm-lock.yaml +++ b/services/capture/pnpm-lock.yaml @@ -17,9 +17,6 @@ importers: '@aws-sdk/types': specifier: ^3.609.0 version: 3.609.0 - '@futureporn/scout': - specifier: workspace:^ - version: link:../../packages/scout '@futureporn/types': specifier: workspace:^ version: link:../../packages/types @@ -196,6 +193,42 @@ importers: specifier: ^5.5.3 version: 5.5.4 + ../..: {} + + ../../packages/image: {} + + ../../packages/infra: {} + + ../../packages/meal: {} + + ../../packages/old: {} + + ../../packages/storage: {} + + ../../packages/taco: {} + + ../../packages/types: {} + + ../../packages/utils: {} + + ../../packages/video: {} + + ../bot: {} + + ../factory: {} + + ../mailbox: {} + + ../migrations: {} + + ../next: {} + + ../scout: {} + + ../strapi: {} + + ../uppy: {} + packages: '@aws-crypto/crc32@5.2.0': diff --git a/services/capture/src/Record.ts b/services/capture/src/Record.ts index f1ba5a3..77d826e 100644 --- a/services/capture/src/Record.ts +++ b/services/capture/src/Record.ts @@ -91,13 +91,13 @@ export default class Record { static getFFmpegStream({ url }: getFFmpegOptions): Readable { console.log(`getFFmpegStream using url=${url}`) const ffmpegProc = spawn('ffmpeg', [ - '-headers', `"User-Agent: ${ua0}"`, + '-headers', `"User-Agent: ${ua0}"`, '-i', url, '-c:v', 'copy', '-c:a', 'copy', '-movflags', 'faststart', '-y', - '-f', 'mpegts', + '-f', 'mpegts', '-loglevel', 'quiet', 'pipe:1' ], { diff --git a/services/capture/src/cb.js b/services/capture/src/cb.js index 24125a3..3195731 100644 --- a/services/capture/src/cb.js +++ b/services/capture/src/cb.js @@ -1,10 +1,11 @@ -import cheerio from 'cheerio' +import { load } from 'cheerio' import fetch from 'node-fetch' +import scrapingFetch from '@futureporn/scout/scrapingFetch.ts' export async function getRandomRoom () { - const res = await fetch('https://chaturbate.com/') + const res = await scrapingFetch('https://chaturbate.com/') const body = await res.text() - const $ = cheerio.load(body) + const $ = load(body) let roomsRaw = $('a[data-room]') let rooms = [] $(roomsRaw).each((_, e) => { diff --git a/services/capture/src/config.ts b/services/capture/src/config.ts index bbf3ee3..d522961 100644 --- a/services/capture/src/config.ts +++ b/services/capture/src/config.ts @@ -1,6 +1,7 @@ import 'dotenv/config' const requiredEnvVars = [ + 'SCOUT_URL', 'S3_ACCESS_KEY_ID', 'S3_SECRET_ACCESS_KEY', 'S3_REGION', @@ -19,6 +20,7 @@ const getEnvVar = (key: typeof requiredEnvVars[number]): string => { }; export interface Config { + scoutUrl: string; postgrestUrl: string; automationUserJwt: string; s3AccessKeyId: string; @@ -30,6 +32,7 @@ export interface Config { export const configs: Config = { + scoutUrl: getEnvVar('SCOUT_URL'), postgrestUrl: getEnvVar('POSTGREST_URL'), automationUserJwt: getEnvVar('AUTOMATION_USER_JWT'), s3AccessKeyId: getEnvVar('S3_ACCESS_KEY_ID'), diff --git a/services/capture/src/fetchers/createSegmentInDatabase.ts b/services/capture/src/fetchers/createSegmentInDatabase.ts index 458ed7b..c7ffd50 100644 --- a/services/capture/src/fetchers/createSegmentInDatabase.ts +++ b/services/capture/src/fetchers/createSegmentInDatabase.ts @@ -2,7 +2,7 @@ import type { Helpers } from 'graphile-worker' import { configs } from '../config.ts' import querystring from 'node:querystring' -export default async function createSegmentInDatabase(s3_key: string, vod_id: string, helpers: Helpers): Promise { +export default async function createSegmentInDatabase(s3_key: string, vod_id: string, helpers: Helpers): Promise { if (!s3_key) throw new Error('getSegments requires {string} s3_key as first arg'); const segmentPayload = { s3_key, @@ -34,5 +34,5 @@ export default async function createSegmentInDatabase(s3_key: string, vod_id: st if (Array.isArray(segmentsId)) throw new Error('segmentsId was an array which is unexpected'); const id = segmentsId.split('.').at(-1) if (!id) throw new Error('failed to get id '); - return parseInt(id) + return id } diff --git a/services/capture/src/fetchers/createSegmentsVodLink.ts b/services/capture/src/fetchers/createSegmentsVodLink.ts index d227582..b030bb1 100644 --- a/services/capture/src/fetchers/createSegmentsVodLink.ts +++ b/services/capture/src/fetchers/createSegmentsVodLink.ts @@ -2,9 +2,10 @@ import type { Helpers } from 'graphile-worker' import { configs } from '../config.ts' import querystring from 'node:querystring' -export default async function createSegmentsVodLink(vod_id: string, segment_id: number, helpers: Helpers): Promise { +export default async function createSegmentsVodLink(vod_id: string, segment_id: string, helpers: Helpers): Promise { + helpers.logger.info(`createSegmentsVodLink with vod_id=${vod_id}, segment_id=${segment_id}`) if (!vod_id) throw new Error('createSegmentsVodLink requires {string} vod_id as first arg'); - if (!segment_id) throw new Error('createSegmentsVodLink requires {Number} segment_id as second arg'); + if (!segment_id) throw new Error('createSegmentsVodLink requires {string} segment_id as second arg'); const segmentVodLinkPayload = { vod_id, segment_id diff --git a/services/capture/src/fetchers/getPlaylistUrl.ts b/services/capture/src/fetchers/getPlaylistUrl.ts new file mode 100644 index 0000000..e6cfcd7 --- /dev/null +++ b/services/capture/src/fetchers/getPlaylistUrl.ts @@ -0,0 +1,21 @@ +import { configs } from '../config.ts' + +export default async function getPlaylistUrl (url: string): Promise { + if (!url) throw new Error(`getPlaylistUrl requires a url, but it was undefined.`); + + const res = await fetch(`${configs.scoutUrl}/ytdlp/playlist-url?url=${url}`) + if (!res.ok) { + const body = await res.text() + console.error(`failed to getPlaylistUrl res.status=${res.status}, res.statusText=${res.statusText}, body=${body}`) + return null + } else { + const data = await res.json() as any + console.log(`>>>>>> getPlaylistUrl got a data payload as follows`) + console.log(data) + if (!!data.error) { + return null; + } else { + return data.data.url + } + } +} \ No newline at end of file diff --git a/services/capture/src/fetchers/updateSegmentInDatabase.ts b/services/capture/src/fetchers/updateSegmentInDatabase.ts index 997657f..bd8a730 100644 --- a/services/capture/src/fetchers/updateSegmentInDatabase.ts +++ b/services/capture/src/fetchers/updateSegmentInDatabase.ts @@ -1,22 +1,32 @@ -import type { Segment } from '@futureporn/types' +import type { SegmentResponse } from '@futureporn/types' import type { Helpers } from 'graphile-worker' import { configs } from '../config.ts' + +/** + * updateSegmentInDatabase + * + * updates the segment in the database with the new filesize + * + * resolves with the updated segment and the is_recording_aborted column of the related vod + */ export default async function updateSegmentInDatabase({ segment_id, fileSize, helpers }: { - segment_id: number, + segment_id: string, fileSize: number, helpers: Helpers -}): Promise { +}): Promise { const payload: any = { bytes: fileSize } - const res = await fetch(`${configs.postgrestUrl}/segments?id=eq.${segment_id}&select=stream:streams(is_recording_aborted)`, { + const fetchUrl =`${configs.postgrestUrl}/segments?id=eq.${segment_id}&select=vod:vods(is_recording_aborted)` + // helpers.logger.info(`updateSegmentInDatabase > fetchUrl=${fetchUrl}`) + const res = await fetch(fetchUrl, { method: 'PATCH', headers: { 'Content-Type': 'application/json', @@ -28,12 +38,12 @@ export default async function updateSegmentInDatabase({ }) if (!res.ok) { const body = await res.text() - const msg = `failed to updateDatabaseRecord. status=${res.status}, statusText=${res.statusText}, body=${body}` + const msg = `failed to updateSegmentInDatabase. status=${res.status}, statusText=${res.statusText}, body=${body}` helpers.logger.error(msg) throw new Error(msg); } // helpers.logger.info(`response was OK~`) - const body = await res.json() as Segment[]; + const body = await res.json() as SegmentResponse[]; if (!body[0]) throw new Error(`failed to get a segment that matched segment_id=${segment_id}`); const bod = body[0] // helpers.logger.info('the following was the response from PATCH-ing /segments') diff --git a/services/capture/src/tasks/record.ts b/services/capture/src/tasks/record.ts index 352ff32..f00d002 100644 --- a/services/capture/src/tasks/record.ts +++ b/services/capture/src/tasks/record.ts @@ -3,12 +3,13 @@ import updateSegmentInDatabase from '../fetchers/updateSegmentInDatabase.ts' import { Helpers, type Task } from 'graphile-worker' import Record from '../Record.ts' -import { getPlaylistUrl } from '@futureporn/scout/ytdlp.ts' -import type { Segment } from '@futureporn/types' +import type { SegmentResponse, ScoutResponse } from '@futureporn/types' import { configs } from '../config.ts' import { createId } from '@paralleldrive/cuid2' import createSegmentInDatabase from '../fetchers/createSegmentInDatabase.ts' import createSegmentsVodLink from '../fetchers/createSegmentsVodLink.ts' +import getPlaylistUrl from '../fetchers/getPlaylistUrl.ts' +import { String } from 'aws-sdk/clients/acm' /** * url is the URL to be recorded. Ex: chaturbate.com/projektmelody @@ -29,7 +30,8 @@ function assertPayload(payload: any): asserts payload is Payload { } -async function getRecordInstance(url: string, segment_id: number, helpers: Helpers) { +async function getRecordInstance(url: string, segment_id: string, helpers: Helpers) { + helpers.logger.info(`getRecordInstance() with url=${url}, segment_id=${segment_id}`) const abortController = new AbortController() const abortSignal = abortController.signal const accessKeyId = configs.s3AccessKeyId; @@ -38,9 +40,11 @@ async function getRecordInstance(url: string, segment_id: number, helpers: Helpe const endpoint = configs.s3Endpoint; const bucket = configs.s3Bucket; const playlistUrl = await getPlaylistUrl(url) + if (!playlistUrl) throw new Error('failed to getPlaylistUrl'); + helpers.logger.info(`playlistUrl=${playlistUrl}`) const s3Client = Record.makeS3Client({ accessKeyId, secretAccessKey, region, endpoint }) const inputStream = Record.getFFmpegStream({ url: playlistUrl }) - const onProgress = (fileSize: number) => { + const onProgress = (fileSize: number) => { updateSegmentInDatabase({ segment_id, fileSize, helpers }) .then(checkIfAborted) .then((isAborted) => { @@ -55,8 +59,10 @@ async function getRecordInstance(url: string, segment_id: number, helpers: Helpe return record } -function checkIfAborted(segment: Partial): boolean { - return (!!segment?.stream?.at(0)?.is_recording_aborted) +function checkIfAborted(segment: Partial): boolean { + // console.log(`checkIfAborted with following segment`) + // console.log(segment) + return (!!segment?.vod?.is_recording_aborted) } @@ -69,11 +75,11 @@ function checkIfAborted(segment: Partial): boolean { * * Ideally, we record the entire livestream, but the universe is not so kind. Network interruptions are common, so we handle the situation as best as we can. * - * This function creates a new segments and segments_streams_links entry in the db via Postgrest REST API. + * This function creates a new segments and vods_segments_links entry in the db via Postgrest REST API. * * This function also names the S3 file (s3_key) with a datestamp and a cuid. */ -const doRecordSegment = async function doRecordSegment(url: string, vod_id: string, helpers: Helpers): Promise { +const doRecordSegment = async function doRecordSegment(url: string, vod_id: string, helpers: Helpers) { const s3_key = `${new Date().toISOString()}-${createId()}.ts` helpers.logger.info(`let's create a segment using vod_id=${vod_id}, url=${url}`) const segment_id = await createSegmentInDatabase(s3_key, vod_id, helpers) @@ -81,7 +87,7 @@ const doRecordSegment = async function doRecordSegment(url: string, vod_id: stri const segmentsVodLinkId = await createSegmentsVodLink(vod_id, segment_id, helpers) helpers.logger.info(`doTheRecording with createSegmentsVodLink segmentsVodLinkId=${segmentsVodLinkId}, vod_id=${vod_id}, segment_id=${segment_id}, url=${url}`) const record = await getRecordInstance(url, segment_id, helpers) - await record.start() + return record.start() } diff --git a/services/factory/src/fetchers/getVod.ts b/services/factory/src/fetchers/getVod.ts new file mode 100644 index 0000000..e390f06 --- /dev/null +++ b/services/factory/src/fetchers/getVod.ts @@ -0,0 +1,24 @@ +import { configs } from "../config" +import { Helpers } from "graphile-worker" +import { Stream } from "@futureporn/types" + +export default async function getVod(vodId: string, helpers: Helpers) { + const url = `${configs.postgrestUrl}/streams?select=*,segments(*)&id=eq.${vodId}` + try { + const res = await fetch(url) + if (!res.ok) { + throw new Error(`failed fetching stream ${vodId}. status=${res.status}, statusText=${res.statusText}`) + } + const body = await res.json() as Stream[] + if (!body[0]) throw new Error('body[0] was expected to be Stream data, but it was either null or undefined.'); + return body[0]; + } catch (e) { + helpers.logger.error(`encountered an error during getStreamFromDatabase()`) + if (e instanceof Error) { + helpers.logger.error(e.message) + } else { + helpers.logger.error(JSON.stringify(e)) + } + return null + } +} \ No newline at end of file diff --git a/services/factory/src/tasks/combine_video_segments.ts b/services/factory/src/tasks/combine_video_segments.ts index dfc7981..6c66220 100644 --- a/services/factory/src/tasks/combine_video_segments.ts +++ b/services/factory/src/tasks/combine_video_segments.ts @@ -22,7 +22,6 @@ interface s3File { interface Payload { s3_manifest: s3File[]; - stream_id?: string; vod_id?: string; } @@ -177,10 +176,9 @@ export const combine_video_segments: Task = async function (payload: unknown, he // helpers.logger.info(payload) // helpers.logger.info(JSON.stringify(payload?.s3_manifest)) assertPayload(payload) - const { s3_manifest, vod_id, stream_id } = payload - if (!stream_id) helpers.logger.warn(`combine_video_segments was called without a stream_id. This is not recommended.`); + const { s3_manifest, vod_id } = payload if (!vod_id) helpers.logger.warn(`combine_video_segments was called without a vod_id. This is not recommended.`); - helpers.logger.info(`combine_video_segments started with s3_manifest=${JSON.stringify(s3_manifest)}, vod_id=${vod_id}, stream_id=${stream_id}`) + helpers.logger.info(`combine_video_segments started with s3_manifest=${JSON.stringify(s3_manifest)}, vod_id=${vod_id}`) /** * Here we take a manifest of S3 files and we download each of them. @@ -217,7 +215,7 @@ export const combine_video_segments: Task = async function (payload: unknown, he await upload.done() - if (vod_id && stream_id) { + if (vod_id) { // update the vod with the s3_file of the combined video const s3File: S3File = { s3_key: s3KeyName, diff --git a/services/factory/src/tasks/process_video.ts b/services/factory/src/tasks/process_video.ts index 610d0a8..d1f9355 100644 --- a/services/factory/src/tasks/process_video.ts +++ b/services/factory/src/tasks/process_video.ts @@ -2,37 +2,18 @@ import type { Helpers, Task } from "graphile-worker" import { configs } from "../config" import type { Stream } from '@futureporn/types' import createVod from "../fetchers/createVod" +import getVod from "../fetchers/getVod" interface Payload { - stream_id: string; + vod_id: string; } function assertPayload(payload: any): asserts payload is Payload { if (typeof payload !== "object" || !payload) throw new Error("invalid payload (it must be an object)"); - if (typeof payload.stream_id !== "string") throw new Error("payload.stream_id was not a string"); + if (typeof payload.vod_id !== "string") throw new Error("payload.vod_id was not a string"); } -async function getStreamFromDatabase(streamId: string, helpers: Helpers) { - const url = `${configs.postgrestUrl}/streams?select=*,segments(*)&id=eq.${streamId}` - try { - const res = await fetch(url) - if (!res.ok) { - throw new Error(`failed fetching stream ${streamId}. status=${res.status}, statusText=${res.statusText}`) - } - const body = await res.json() as Stream[] - if (!body[0]) throw new Error('body[0] was expected to be Stream data, but it was either null or undefined.'); - return body[0]; - } catch (e) { - helpers.logger.error(`encountered an error during getStreamFromDatabase()`) - if (e instanceof Error) { - helpers.logger.error(e.message) - } else { - helpers.logger.error(JSON.stringify(e)) - } - return null - } -} /** @@ -40,11 +21,11 @@ async function getStreamFromDatabase(streamId: string, helpers: Helpers) { * # process_video * * We just recorded a livestream. Now what? - * process_video takes a /streams record and runs a bunch of processes to get it ready for publishing. + * process_video takes a /vods record and runs a bunch of processes to get it ready for publishing. * * The following are graphile-worker tasks which process_video is responsible for adding to the job queue. - * Some of these tasks are run conditionally based on the structure of the /streams record. - * For example, combine_video_segments is only useful on a stream recording which ended up with multiple segments. + * Some of these tasks are run conditionally based on the structure of the /vods record. + * For example, combine_video_segments is only useful on a vod recording which ended up with multiple segments. * * - combine_video_segments * - generate_thumbnail @@ -67,25 +48,19 @@ async function getStreamFromDatabase(streamId: string, helpers: Helpers) { */ const process_video: Task = async function (payload: unknown, helpers: Helpers) { assertPayload(payload) - const { stream_id } = payload - helpers.logger.info(`process_video task has begun for stream_id=${stream_id}`) + const { vod_id } = payload + helpers.logger.info(`process_video task has begun for vod_id=${vod_id}`) - const stream = await getStreamFromDatabase(stream_id, helpers) - if (!stream) throw new Error(`failed to get stream from database.`); - if (!stream.segments) throw new Error(`stream ${stream_id} fetched from database lacked any segments.`); + const vod = await getVod(vod_id, helpers) + if (!vod) throw new Error(`failed to get vod from database.`); + if (!vod.segments) throw new Error(`vod ${vod_id} fetched from database lacked any segments.`); - const isVodPresent: boolean = !!(stream?.vods && stream.vods.length > 0) - - if (!isVodPresent) { - const vod = await createVod(stream) - if (!vod) throw new Error('failed to create vod') - const vod_id = vod.id - const isCombinationNeeded = (stream.segments.length > 1) - if (isCombinationNeeded) { - const s3_manifest = stream.segments.map((segment) => ({ key: segment.s3_key })) - helpers.addJob('combine_video_segments', { s3_manifest, vod_id, stream_id }) - } + const isCombinationNeeded = (vod.segments.length > 1) + if (isCombinationNeeded) { + const s3_manifest = vod.segments.map((segment) => ({ key: segment.s3_key })) + helpers.addJob('combine_video_segments', { s3_manifest, vod_id }) } + } export default process_video; diff --git a/services/migrations/migrations/00041_add-is_recording_aborted-to-vods.sql b/services/migrations/migrations/00041_add-is_recording_aborted-to-vods.sql new file mode 100644 index 0000000..22ad593 --- /dev/null +++ b/services/migrations/migrations/00041_add-is_recording_aborted-to-vods.sql @@ -0,0 +1,3 @@ + +ALTER TABLE api.vods + ADD COLUMN is_recording_aborted BOOLEAN; \ No newline at end of file diff --git a/services/migrations/migrations/00042_segments-vods-fk.sql b/services/migrations/migrations/00042_segments-vods-fk.sql new file mode 100644 index 0000000..fc1d528 --- /dev/null +++ b/services/migrations/migrations/00042_segments-vods-fk.sql @@ -0,0 +1,6 @@ +ALTER TABLE api.segments + DROP COLUMN vod_id; + + +ALTER TABLE api.segments + ADD COLUMN vod_id UUID REFERENCES api.vods(id); diff --git a/services/migrations/migrations/00043_update_vod_on_segment_update.sql b/services/migrations/migrations/00043_update_vod_on_segment_update.sql new file mode 100644 index 0000000..9027379 --- /dev/null +++ b/services/migrations/migrations/00043_update_vod_on_segment_update.sql @@ -0,0 +1,23 @@ +DROP FUNCTION update_stream_on_segment_update CASCADE; + + +CREATE OR REPLACE FUNCTION update_vod_on_segment_update() +RETURNS TRIGGER AS $$ +BEGIN + UPDATE api.vods + SET updated_at = NOW() + WHERE id IN ( + SELECT vod_id + FROM segments_vod_links + WHERE segment_id = NEW.id + ); + + RETURN NEW; +END; +$$ LANGUAGE plpgsql; + + +CREATE TRIGGER trigger_update_vod + AFTER UPDATE ON api.segments + FOR EACH ROW + EXECUTE FUNCTION update_vod_on_segment_update(); diff --git a/services/migrations/migrations/00044_segments-uuid.sql b/services/migrations/migrations/00044_segments-uuid.sql new file mode 100644 index 0000000..dcae3d4 --- /dev/null +++ b/services/migrations/migrations/00044_segments-uuid.sql @@ -0,0 +1,6 @@ + +ALTER TABLE api.segments + DROP COLUMN id; + +ALTER TABLE api.segments + ADD COLUMN id uuid PRIMARY KEY DEFAULT gen_random_uuid(); diff --git a/services/migrations/migrations/00045_recreate-segments_vod_links.sql b/services/migrations/migrations/00045_recreate-segments_vod_links.sql new file mode 100644 index 0000000..f69346b --- /dev/null +++ b/services/migrations/migrations/00045_recreate-segments_vod_links.sql @@ -0,0 +1,10 @@ +-- we need vod_id and segment_id to be uuid, not text + +DROP TABLE api.segments_vod_links; + +CREATE TABLE api.segments_vod_links ( + id UUID DEFAULT gen_random_uuid(), + vod_id UUID NOT NULL REFERENCES api.vods(id), + segment_id UUID NOT NULL REFERENCES api.segments(id), + PRIMARY KEY(id, vod_id, segment_id) +); diff --git a/services/migrations/migrations/00046_segments_vod_links-many-to-one.sql b/services/migrations/migrations/00046_segments_vod_links-many-to-one.sql new file mode 100644 index 0000000..eb0bceb --- /dev/null +++ b/services/migrations/migrations/00046_segments_vod_links-many-to-one.sql @@ -0,0 +1,10 @@ +-- In the last migration, I accidentally created a many-to-many relationship. +-- What I actually need is a many-to-one relationship. + +DROP TABLE api.segments_vod_links; + +CREATE TABLE api.segments_vod_links ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + vod_id UUID NOT NULL REFERENCES api.vods(id), + segment_id UUID NOT NULL REFERENCES api.segments(id) +); diff --git a/services/migrations/migrations/00047_add-is_recording_aborted-to-vods-default-false.sql b/services/migrations/migrations/00047_add-is_recording_aborted-to-vods-default-false.sql new file mode 100644 index 0000000..a5b8b25 --- /dev/null +++ b/services/migrations/migrations/00047_add-is_recording_aborted-to-vods-default-false.sql @@ -0,0 +1,5 @@ +ALTER TABLE api.vods + DROP COLUMN is_recording_aborted; + +ALTER TABLE api.vods + ADD COLUMN is_recording_aborted BOOLEAN DEFAULT FALSE; diff --git a/services/migrations/migrations/00048_segments-vod-links-perms.sql b/services/migrations/migrations/00048_segments-vod-links-perms.sql new file mode 100644 index 0000000..c9cce2f --- /dev/null +++ b/services/migrations/migrations/00048_segments-vod-links-perms.sql @@ -0,0 +1,3 @@ +-- roles & permissions +GRANT all ON api.segments_vod_links TO automation; +GRANT SELECT ON api.segments_vod_links TO web_anon; diff --git a/services/migrations/migrations/00049_add-discord_message_id-on-vods.sql b/services/migrations/migrations/00049_add-discord_message_id-on-vods.sql new file mode 100644 index 0000000..7960a26 --- /dev/null +++ b/services/migrations/migrations/00049_add-discord_message_id-on-vods.sql @@ -0,0 +1,4 @@ +-- vods needs discord_message_id for chatops +ALTER TABLE api.vods + ADD COLUMN discord_message_id TEXT; + diff --git a/services/migrations/migrations/00050_add-status-to-vod.sql b/services/migrations/migrations/00050_add-status-to-vod.sql new file mode 100644 index 0000000..75d78b6 --- /dev/null +++ b/services/migrations/migrations/00050_add-status-to-vod.sql @@ -0,0 +1,3 @@ + +ALTER TABLE api.vods + ADD COLUMN status TEXT; \ No newline at end of file diff --git a/services/migrations/migrations/00051_set-vod-status.sql b/services/migrations/migrations/00051_set-vod-status.sql new file mode 100644 index 0000000..4336e45 --- /dev/null +++ b/services/migrations/migrations/00051_set-vod-status.sql @@ -0,0 +1,21 @@ +-- 'NEW' in this context is a segment row. +-- we update this function to also set the vod status to 'recording' if applicable. + +CREATE OR REPLACE FUNCTION update_vod_on_segment_update() +RETURNS TRIGGER AS $$ +BEGIN + UPDATE api.vods + SET + updated_at = NOW(), + status = CASE + WHEN NEW.filesize > OLD.filesize THEN 'recording' + ELSE status + END + WHERE id IN ( + SELECT vod_id + FROM segments_vod_links + WHERE segment_id = NEW.id + ); + RETURN NEW; +END; +$$ LANGUAGE plpgsql; \ No newline at end of file diff --git a/services/migrations/migrations/00052_correct-typo-filesize-to-bytes.sql b/services/migrations/migrations/00052_correct-typo-filesize-to-bytes.sql new file mode 100644 index 0000000..419df34 --- /dev/null +++ b/services/migrations/migrations/00052_correct-typo-filesize-to-bytes.sql @@ -0,0 +1,21 @@ +-- 'NEW' in this context is a segment row. +-- we update this function to also set the vod status to 'recording' if applicable. + +CREATE OR REPLACE FUNCTION update_vod_on_segment_update() +RETURNS TRIGGER AS $$ +BEGIN + UPDATE api.vods + SET + updated_at = NOW(), + status = CASE + WHEN NEW.bytes > OLD.bytes THEN 'recording' + ELSE status + END + WHERE id IN ( + SELECT vod_id + FROM segments_vod_links + WHERE segment_id = NEW.id + ); + RETURN NEW; +END; +$$ LANGUAGE plpgsql; \ No newline at end of file diff --git a/services/migrations/migrations/00053_add-default-status-on-vods.sql b/services/migrations/migrations/00053_add-default-status-on-vods.sql new file mode 100644 index 0000000..3fd37a8 --- /dev/null +++ b/services/migrations/migrations/00053_add-default-status-on-vods.sql @@ -0,0 +1,5 @@ +ALTER TABLE api.vods + DROP COLUMN status; + +ALTER TABLE api.vods + ADD COLUMN status TEXT DEFAULT 'pending_recording'; \ No newline at end of file diff --git a/services/scout/package.json b/services/scout/package.json index 26d04d1..254fa95 100644 --- a/services/scout/package.json +++ b/services/scout/package.json @@ -58,7 +58,9 @@ "slugify": "^1.6.6", "swagger-editor-dist": "^4.13.1", "swagger-ui-dist": "^5.17.14", + "ts-json-schema-generator": "^2.3.0", "tsx": "^4.18.0", + "typescript-json-schema": "^0.65.1", "xpath": "^0.0.34" }, "packageManager": "pnpm@9.6.0", @@ -66,16 +68,13 @@ "@babel/preset-env": "^7.25.4", "@babel/preset-typescript": "^7.24.7", "@futureporn/utils": "workspace:^", - "@jest/globals": "^29.7.0", "@types/chai": "^4.3.18", "@types/cheerio": "^0.22.35", - "@types/jest": "^29.5.12", "@types/mailparser": "^3.4.4", "@types/mocha": "^10.0.7", "@types/sinon": "^17.0.3", "chai": "^5.1.1", "esmock": "^2.6.7", - "jest": "^29.7.0", "mocha": "^10.7.3", "nodemon": "^3.1.4", "sinon": "^15.2.0", diff --git a/services/scout/pnpm-lock.yaml b/services/scout/pnpm-lock.yaml index f739e6f..a52c519 100644 --- a/services/scout/pnpm-lock.yaml +++ b/services/scout/pnpm-lock.yaml @@ -49,10 +49,10 @@ importers: version: 1.0.19 '@types/node': specifier: ^22.5.0 - version: 22.5.0 + version: 22.5.1 '@types/pg': specifier: ^8.11.6 - version: 8.11.6 + version: 8.11.8 cheerio: specifier: 1.0.0-rc.12 version: 1.0.0-rc.12 @@ -122,9 +122,15 @@ importers: swagger-ui-dist: specifier: ^5.17.14 version: 5.17.14 + ts-json-schema-generator: + specifier: ^2.3.0 + version: 2.3.0 tsx: specifier: ^4.18.0 - version: 4.18.0 + version: 4.19.0 + typescript-json-schema: + specifier: ^0.65.1 + version: 0.65.1(@swc/core@1.7.21) xpath: specifier: ^0.0.34 version: 0.0.34 @@ -138,18 +144,12 @@ importers: '@futureporn/utils': specifier: workspace:^ version: link:../../packages/utils - '@jest/globals': - specifier: ^29.7.0 - version: 29.7.0 '@types/chai': specifier: ^4.3.18 - version: 4.3.18 + version: 4.3.19 '@types/cheerio': specifier: ^0.22.35 version: 0.22.35 - '@types/jest': - specifier: ^29.5.12 - version: 29.5.12 '@types/mailparser': specifier: ^3.4.4 version: 3.4.4 @@ -165,9 +165,6 @@ importers: esmock: specifier: ^2.6.7 version: 2.6.7 - jest: - specifier: ^29.7.0 - version: 29.7.0(@types/node@22.5.0)(ts-node@10.9.2(@swc/core@1.7.18)(@types/node@22.5.0)(typescript@5.5.4)) mocha: specifier: ^10.7.3 version: 10.7.3 @@ -179,10 +176,10 @@ importers: version: 15.2.0 ts-node: specifier: ^10.9.2 - version: 10.9.2(@swc/core@1.7.18)(@types/node@22.5.0)(typescript@5.5.4) + version: 10.9.2(@swc/core@1.7.21)(@types/node@22.5.1)(typescript@5.5.4) tsup: specifier: ^8.2.4 - version: 8.2.4(@swc/core@1.7.18)(tsx@4.18.0)(typescript@5.5.4)(yaml@2.5.0) + version: 8.2.4(@swc/core@1.7.21)(tsx@4.19.0)(typescript@5.5.4)(yaml@2.5.0) typescript: specifier: ^5.5.4 version: 5.5.4 @@ -531,11 +528,6 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-syntax-bigint@7.8.3': - resolution: {integrity: sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==} - peerDependencies: - '@babel/core': ^7.0.0-0 - '@babel/plugin-syntax-class-properties@7.12.13': resolution: {integrity: sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==} peerDependencies: @@ -975,9 +967,6 @@ packages: resolution: {integrity: sha512-zQ1ijeeCXVEh+aNL0RlmkPkG8HUiDcU2pzQQFjtbntgAczRASFzj4H+6+bV+dy1ntKR14I/DypeuRG1uma98iQ==} engines: {node: '>=6.9.0'} - '@bcoe/v8-coverage@0.2.3': - resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==} - '@cspotcode/source-map-support@0.8.1': resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==} engines: {node: '>=12'} @@ -1275,80 +1264,6 @@ packages: resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} engines: {node: '>=12'} - '@istanbuljs/load-nyc-config@1.1.0': - resolution: {integrity: sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==} - engines: {node: '>=8'} - - '@istanbuljs/schema@0.1.3': - resolution: {integrity: sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==} - engines: {node: '>=8'} - - '@jest/console@29.7.0': - resolution: {integrity: sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - '@jest/core@29.7.0': - resolution: {integrity: sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - peerDependencies: - node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 - peerDependenciesMeta: - node-notifier: - optional: true - - '@jest/environment@29.7.0': - resolution: {integrity: sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - '@jest/expect-utils@29.7.0': - resolution: {integrity: sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - '@jest/expect@29.7.0': - resolution: {integrity: sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - '@jest/fake-timers@29.7.0': - resolution: {integrity: sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - '@jest/globals@29.7.0': - resolution: {integrity: sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - '@jest/reporters@29.7.0': - resolution: {integrity: sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - peerDependencies: - node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 - peerDependenciesMeta: - node-notifier: - optional: true - - '@jest/schemas@29.6.3': - resolution: {integrity: sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - '@jest/source-map@29.6.3': - resolution: {integrity: sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - '@jest/test-result@29.7.0': - resolution: {integrity: sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - '@jest/test-sequencer@29.7.0': - resolution: {integrity: sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - '@jest/transform@29.7.0': - resolution: {integrity: sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - '@jest/types@29.6.3': - resolution: {integrity: sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - '@jridgewell/gen-mapping@0.3.5': resolution: {integrity: sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==} engines: {node: '>=6.0.0'} @@ -1537,9 +1452,6 @@ packages: '@selderee/plugin-htmlparser2@0.11.0': resolution: {integrity: sha512-P33hHGdldxGabLFjPPpaTxVolMrzrcegejx+0GxjrIb9Zv48D8yAIA/QTDR2dFl7Uz7urX8aX6+5bCZslr+gWQ==} - '@sinclair/typebox@0.27.8': - resolution: {integrity: sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==} - '@sinonjs/commons@2.0.0': resolution: {integrity: sha512-uLa0j859mMrg2slwQYdO/AkrOfmH+X6LTVmNTS9CqexuE2IvVORIkSpJLqePAbEnKJ77aMmCwr1NUZ57120Xcg==} @@ -1761,68 +1673,68 @@ packages: resolution: {integrity: sha512-4pP0EV3iTsexDx+8PPGAKCQpd/6hsQBaQhqWzU4hqKPHN5epPsxKbvUTIiYIHTxaKt6/kEaqPBpu/ufvfbrRzw==} engines: {node: '>=16.0.0'} - '@swc/core-darwin-arm64@1.7.18': - resolution: {integrity: sha512-MwLc5U+VGPMZm8MjlFBjEB2wyT1EK0NNJ3tn+ps9fmxdFP+PL8EpMiY1O1F2t1ydy2OzBtZz81sycjM9RieFBg==} + '@swc/core-darwin-arm64@1.7.21': + resolution: {integrity: sha512-hh5uOZ7jWF66z2TRMhhXtWMQkssuPCSIZPy9VHf5KvZ46cX+5UeECDthchYklEVZQyy4Qr6oxfh4qff/5spoMA==} engines: {node: '>=10'} cpu: [arm64] os: [darwin] - '@swc/core-darwin-x64@1.7.18': - resolution: {integrity: sha512-IkukOQUw7/14VkHp446OkYGCZEHqZg9pTmTdBawlUyz2JwZMSn2VodCl7aFSdGCsU4Cwni8zKA8CCgkCCAELhw==} + '@swc/core-darwin-x64@1.7.21': + resolution: {integrity: sha512-lTsPquqSierQ6jWiWM7NnYXXZGk9zx3NGkPLHjPbcH5BmyiauX0CC/YJYJx7YmS2InRLyALlGmidHkaF4JY28A==} engines: {node: '>=10'} cpu: [x64] os: [darwin] - '@swc/core-linux-arm-gnueabihf@1.7.18': - resolution: {integrity: sha512-ATnb6jJaBeXCqrTUawWdoOy7eP9SCI7UMcfXlYIMxX4otKKspLPAEuGA5RaNxlCcj9ObyO0J3YGbtZ6hhD2pjg==} + '@swc/core-linux-arm-gnueabihf@1.7.21': + resolution: {integrity: sha512-AgSd0fnSzAqCvWpzzZCq75z62JVGUkkXEOpfdi99jj/tryPy38KdXJtkVWJmufPXlRHokGTBitalk33WDJwsbA==} engines: {node: '>=10'} cpu: [arm] os: [linux] - '@swc/core-linux-arm64-gnu@1.7.18': - resolution: {integrity: sha512-poHtH7zL7lEp9K2inY90lGHJABWxURAOgWNeZqrcR5+jwIe7q5KBisysH09Zf/JNF9+6iNns+U0xgWTNJzBuGA==} + '@swc/core-linux-arm64-gnu@1.7.21': + resolution: {integrity: sha512-l+jw6RQ4Y43/8dIst0c73uQE+W3kCWrCFqMqC/xIuE/iqHOnvYK6YbA1ffOct2dImkHzNiKuoehGqtQAc6cNaQ==} engines: {node: '>=10'} cpu: [arm64] os: [linux] - '@swc/core-linux-arm64-musl@1.7.18': - resolution: {integrity: sha512-qnNI1WmcOV7Wz1ZDyK6WrOlzLvJ01rnni8ec950mMHWkLRMP53QvCvhF3S+7gFplWBwWJTOOPPUqJp/PlSxWyQ==} + '@swc/core-linux-arm64-musl@1.7.21': + resolution: {integrity: sha512-29KKZXrTo/c9F1JFL9WsNvCa6UCdIVhHP5EfuYhlKbn5/YmSsNFkuHdUtZFEd5U4+jiShXDmgGCtLW2d08LIwg==} engines: {node: '>=10'} cpu: [arm64] os: [linux] - '@swc/core-linux-x64-gnu@1.7.18': - resolution: {integrity: sha512-x9SCqCLzwtlqtD5At3I1a7Gco+EuXnzrJGoucmkpeQohshHuwa+cskqsXO6u1Dz0jXJEuHbBZB9va1wYYfjgFg==} + '@swc/core-linux-x64-gnu@1.7.21': + resolution: {integrity: sha512-HsP3JwddvQj5HvnjmOr+Bd5plEm6ccpfP5wUlm3hywzvdVkj+yR29bmD7UwpV/1zCQ60Ry35a7mXhKI6HQxFgw==} engines: {node: '>=10'} cpu: [x64] os: [linux] - '@swc/core-linux-x64-musl@1.7.18': - resolution: {integrity: sha512-qtj8iOpMMgKjzxTv+islmEY0JBsbd93nka0gzcTTmGZxKtL5jSUsYQvkxwNPZr5M9NU1fgaR3n1vE6lFmtY0IQ==} + '@swc/core-linux-x64-musl@1.7.21': + resolution: {integrity: sha512-hYKLVeUTHqvFK628DFJEwxoX6p42T3HaQ4QjNtf3oKhiJWFh9iTRUrN/oCB5YI3R9WMkFkKh+99gZ/Dd0T5lsg==} engines: {node: '>=10'} cpu: [x64] os: [linux] - '@swc/core-win32-arm64-msvc@1.7.18': - resolution: {integrity: sha512-ltX/Ol9+Qu4SXmISCeuwVgAjSa8nzHTymknpozzVMgjXUoZMoz6lcynfKL1nCh5XLgqh0XNHUKLti5YFF8LrrA==} + '@swc/core-win32-arm64-msvc@1.7.21': + resolution: {integrity: sha512-qyWAKW10aMBe6iUqeZ7NAJIswjfggVTUpDINpQGUJhz+pR71YZDidXgZXpaDB84YyDB2JAlRqd1YrLkl7CMiIw==} engines: {node: '>=10'} cpu: [arm64] os: [win32] - '@swc/core-win32-ia32-msvc@1.7.18': - resolution: {integrity: sha512-RgTcFP3wgyxnQbTCJrlgBJmgpeTXo8t807GU9GxApAXfpLZJ3swJ2GgFUmIJVdLWyffSHF5BEkF3FmF6mtH5AQ==} + '@swc/core-win32-ia32-msvc@1.7.21': + resolution: {integrity: sha512-cy61wS3wgH5mEwBiQ5w6/FnQrchBDAdPsSh0dKSzNmI+4K8hDxS8uzdBycWqJXO0cc+mA77SIlwZC3hP3Kum2g==} engines: {node: '>=10'} cpu: [ia32] os: [win32] - '@swc/core-win32-x64-msvc@1.7.18': - resolution: {integrity: sha512-XbZ0wAgzR757+DhQcnv60Y/bK9yuWPhDNRQVFFQVRsowvK3+c6EblyfUSytIidpXgyYFzlprq/9A9ZlO/wvDWw==} + '@swc/core-win32-x64-msvc@1.7.21': + resolution: {integrity: sha512-/rexGItJURNJOdae+a48M+loT74nsEU+PyRRVAkZMKNRtLoYFAr0cpDlS5FodIgGunp/nqM0bst4H2w6Y05IKA==} engines: {node: '>=10'} cpu: [x64] os: [win32] - '@swc/core@1.7.18': - resolution: {integrity: sha512-qL9v5N5S38ijmqiQRvCFUUx2vmxWT/JJ2rswElnyaHkOHuVoAFhBB90Ywj4RKjh3R0zOjhEcemENTyF3q3G6WQ==} + '@swc/core@1.7.21': + resolution: {integrity: sha512-7/cN0SZ+y2V6e0hsDD8koGR0QVh7Jl3r756bwaHLLSN+kReoUb/yVcLsA8iTn90JLME3DkQK4CPjxDCQiyMXNg==} engines: {node: '>=10'} peerDependencies: '@swc/helpers': '*' @@ -1873,20 +1785,8 @@ packages: '@tsconfig/node20@20.1.4': resolution: {integrity: sha512-sqgsT69YFeLWf5NtJ4Xq/xAF8p4ZQHlmGW74Nu2tD4+g5fAsposc4ZfaaPixVu4y01BEiDCWLRDCvDM5JOsRxg==} - '@types/babel__core@7.20.5': - resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==} - - '@types/babel__generator@7.6.8': - resolution: {integrity: sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw==} - - '@types/babel__template@7.4.4': - resolution: {integrity: sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==} - - '@types/babel__traverse@7.20.6': - resolution: {integrity: sha512-r1bzfrm0tomOI8g1SzvCaQHo6Lcv6zu0EA+W2kHrt8dyrHQxGzBBL4kdkzIS+jBMV+EYcMAEAqXqYaLJq5rOZg==} - - '@types/chai@4.3.18': - resolution: {integrity: sha512-2UfJzigyNa8kYTKn7o4hNMPphkxtu4WTJyobK3m4FBpyj7EK5xgtPcOtxLm7Dznk/Qxr0QXn+gQbkg7mCZKdfg==} + '@types/chai@4.3.19': + resolution: {integrity: sha512-2hHHvQBVE2FiSK4eN0Br6snX9MtolHaTo/batnLjlGRhoQzlCL61iVpxoqO7SfFyOw+P/pwv+0zNHzKoGWz9Cw==} '@types/cheerio@0.22.35': resolution: {integrity: sha512-yD57BchKRvTV+JD53UZ6PD8KWY5g5rvvMLRnZR3EQBCZXiDT/HR+pKpMzFGlWNhFrXlo7VPZXtKvIEwZkAWOIA==} @@ -1900,24 +1800,9 @@ packages: '@types/fluent-ffmpeg@2.1.26': resolution: {integrity: sha512-0JVF3wdQG+pN0ImwWD0bNgJiKF2OHg/7CDBHw5UIbRTvlnkgGHK6V5doE54ltvhud4o31/dEiHm23CAlxFiUQg==} - '@types/graceful-fs@4.1.9': - resolution: {integrity: sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==} - '@types/imapflow@1.0.19': resolution: {integrity: sha512-U48VZXe4XYhS3AFNI+4ougW8OXI4VaKjyF0nGXgVzIN8SPs9lh2LPNRM0HmIM+hUTw60l7MHgbQO8hsf+Q4U5w==} - '@types/istanbul-lib-coverage@2.0.6': - resolution: {integrity: sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==} - - '@types/istanbul-lib-report@3.0.3': - resolution: {integrity: sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==} - - '@types/istanbul-reports@3.0.4': - resolution: {integrity: sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==} - - '@types/jest@29.5.12': - resolution: {integrity: sha512-eDC8bTvT/QhYdxJAulQikueigY5AsdBRH2yDKW3yveW7svY3+DzN84/2NUgkw10RTiJbWqZrTtoGVdYlvFJdLw==} - '@types/json-schema@7.0.15': resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} @@ -1933,14 +1818,17 @@ packages: '@types/ms@0.7.34': resolution: {integrity: sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g==} - '@types/node@20.16.1': - resolution: {integrity: sha512-zJDo7wEadFtSyNz5QITDfRcrhqDvQI1xQNQ0VoizPjM/dVAODqqIUWbJPkvsxmTI0MYRGRikcdjMPhOssnPejQ==} + '@types/node@18.19.47': + resolution: {integrity: sha512-1f7dB3BL/bpd9tnDJrrHb66Y+cVrhxSOTGorRNdHwYTUlTay3HuTDPKo9a/4vX9pMQkhYBcAbL4jQdNlhCFP9A==} - '@types/node@22.5.0': - resolution: {integrity: sha512-DkFrJOe+rfdHTqqMg0bSNlGlQ85hSoh2TPzZyhHsXnMtligRWpxUySiyw8FY14ITt24HVCiQPWxS3KO/QlGmWg==} + '@types/node@20.16.2': + resolution: {integrity: sha512-91s/n4qUPV/wg8eE9KHYW1kouTfDk2FPGjXbBMfRWP/2vg1rCXNQL1OCabwGs0XSdukuK+MwCDXE30QpSeMUhQ==} - '@types/pg@8.11.6': - resolution: {integrity: sha512-/2WmmBXHLsfRqzfHW7BNZ8SbYzE8OSk7i3WjFYvfgRHj7S1xj+16Je5fUKv3lVdVzk/zn9TXOqf+avFCFIE0yQ==} + '@types/node@22.5.1': + resolution: {integrity: sha512-KkHsxej0j9IW1KKOOAA/XBA0z08UFSrRQHErzEfA3Vgq57eXIMYboIlHJuYIfd+lwCQjtKqUu3UnmKbtUc9yRw==} + + '@types/pg@8.11.8': + resolution: {integrity: sha512-IqpCf8/569txXN/HoP5i1LjXfKZWL76Yr2R77xgeIICUbAYHeoaEZFhYHo2uDftecLWrTJUq63JvQu8q3lnDyA==} '@types/retry@0.12.1': resolution: {integrity: sha512-xoDlM2S4ortawSWORYqsdU+2rxdh4LRW9ytc3zmT37RIKQh6IHyKwwtKhKis9ah8ol07DCkZxPt8BBvPjC6v4g==} @@ -1951,15 +1839,6 @@ packages: '@types/sinonjs__fake-timers@8.1.5': resolution: {integrity: sha512-mQkU2jY8jJEF7YHjHvsQO8+3ughTL1mcnn96igfhONmR+fUPSKIkefQYpSe8bsly2Ep7oQbn/6VG5/9/0qcArQ==} - '@types/stack-utils@2.0.3': - resolution: {integrity: sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==} - - '@types/yargs-parser@21.0.3': - resolution: {integrity: sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==} - - '@types/yargs@17.0.33': - resolution: {integrity: sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==} - '@webassemblyjs/ast@1.12.1': resolution: {integrity: sha512-EKfMUOPRRUTy5UII4qJDGPpqfwjOmZ5jeGFwid9mnoqIFK+e0vqoi1qH56JpmZSzEL53jKnNzScdmftJyG5xWg==} @@ -2063,10 +1942,6 @@ packages: resolution: {integrity: sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==} engines: {node: '>=6'} - ansi-escapes@4.3.2: - resolution: {integrity: sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==} - engines: {node: '>=8'} - ansi-regex@5.0.1: resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} engines: {node: '>=8'} @@ -2083,10 +1958,6 @@ packages: resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} engines: {node: '>=8'} - ansi-styles@5.2.0: - resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==} - engines: {node: '>=10'} - ansi-styles@6.2.1: resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==} engines: {node: '>=12'} @@ -2104,9 +1975,6 @@ packages: arg@5.0.2: resolution: {integrity: sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==} - argparse@1.0.10: - resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==} - argparse@2.0.1: resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} @@ -2131,20 +1999,6 @@ packages: avvio@8.4.0: resolution: {integrity: sha512-CDSwaxINFy59iNwhYnkvALBwZiTydGkOecZyPkqBpABYR1KqGEsET0VOOYDwtleZSUIdeY36DC2bSZ24CO1igA==} - babel-jest@29.7.0: - resolution: {integrity: sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - peerDependencies: - '@babel/core': ^7.8.0 - - babel-plugin-istanbul@6.1.1: - resolution: {integrity: sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==} - engines: {node: '>=8'} - - babel-plugin-jest-hoist@29.6.3: - resolution: {integrity: sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - babel-plugin-polyfill-corejs2@0.4.11: resolution: {integrity: sha512-sMEJ27L0gRHShOh5G54uAAPaiCOygY/5ratXuiyb2G46FmlSpc9eFCzYVyDiPxfNbwzA7mYahmjQc5q+CZQ09Q==} peerDependencies: @@ -2160,17 +2014,6 @@ packages: peerDependencies: '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 - babel-preset-current-node-syntax@1.1.0: - resolution: {integrity: sha512-ldYss8SbBlWva1bs28q78Ju5Zq1F+8BrqBZZ0VFhLBvhh6lCpC2o3gDJi/5DRLs9FgYZCnmPYIVFU4lRXCkyUw==} - peerDependencies: - '@babel/core': ^7.0.0 - - babel-preset-jest@29.6.3: - resolution: {integrity: sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - peerDependencies: - '@babel/core': ^7.0.0 - balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} @@ -2208,9 +2051,6 @@ packages: engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true - bser@2.1.1: - resolution: {integrity: sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==} - buffer-from@1.1.2: resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} @@ -2238,14 +2078,6 @@ packages: resolution: {integrity: sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==} engines: {node: '>= 0.4'} - callsites@3.1.0: - resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} - engines: {node: '>=6'} - - camelcase@5.3.1: - resolution: {integrity: sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==} - engines: {node: '>=6'} - camelcase@6.3.0: resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==} engines: {node: '>=10'} @@ -2269,10 +2101,6 @@ packages: resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} engines: {node: '>=10'} - char-regex@1.0.2: - resolution: {integrity: sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==} - engines: {node: '>=10'} - check-error@2.1.1: resolution: {integrity: sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==} engines: {node: '>= 16'} @@ -2292,13 +2120,6 @@ packages: resolution: {integrity: sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==} engines: {node: '>=6.0'} - ci-info@3.9.0: - resolution: {integrity: sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==} - engines: {node: '>=8'} - - cjs-module-lexer@1.4.0: - resolution: {integrity: sha512-N1NGmowPlGBLsOZLPvm48StN04V4YvQRL0i6b7ctrVY3epjP/ct7hFLOItz6pDIvRjwpfPxi52a2UWV2ziir8g==} - cliui@7.0.4: resolution: {integrity: sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==} @@ -2306,13 +2127,6 @@ packages: resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} engines: {node: '>=12'} - co@4.6.0: - resolution: {integrity: sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==} - engines: {iojs: '>= 1.0.0', node: '>= 0.12.0'} - - collect-v8-coverage@1.0.2: - resolution: {integrity: sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==} - color-convert@1.9.3: resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} @@ -2333,6 +2147,10 @@ packages: resolution: {integrity: sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==} engines: {node: '>=12.5.0'} + commander@12.1.0: + resolution: {integrity: sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==} + engines: {node: '>=18'} + commander@2.20.3: resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==} @@ -2370,11 +2188,6 @@ packages: core-js-compat@3.38.1: resolution: {integrity: sha512-JRH6gfXxGmrzF3tZ57lFx97YARxCXPaMzPo6jELZhv88pBH5VXpQ+y0znKGlFnzuaihqhLbefxSJxWJMPtfDzw==} - create-jest@29.7.0: - resolution: {integrity: sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - hasBin: true - create-require@1.1.1: resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==} @@ -2417,14 +2230,6 @@ packages: resolution: {integrity: sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==} engines: {node: '>=10'} - dedent@1.5.3: - resolution: {integrity: sha512-NHQtfOOW68WD8lgypbLA5oT+Bt0xXJhiYvoR6SmmNXZfpzOGXwdKWmcwG8N7PwVVWV3eF/68nmD9BaJSsTBhyQ==} - peerDependencies: - babel-plugin-macros: ^3.1.0 - peerDependenciesMeta: - babel-plugin-macros: - optional: true - deep-eql@5.0.2: resolution: {integrity: sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==} engines: {node: '>=6'} @@ -2448,14 +2253,6 @@ packages: resolution: {integrity: sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==} engines: {node: '>=8'} - detect-newline@3.1.0: - resolution: {integrity: sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==} - engines: {node: '>=8'} - - diff-sequences@29.6.3: - resolution: {integrity: sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - diff@4.0.2: resolution: {integrity: sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==} engines: {node: '>=0.3.1'} @@ -2491,10 +2288,6 @@ packages: electron-to-chromium@1.5.13: resolution: {integrity: sha512-lbBcvtIJ4J6sS4tb5TLp1b4LyfCdMkwStzXPyAgVgTRAsep4bvrAGaBOP7ZJtQMNJpSQ9SqG4brWOroNaQtm7Q==} - emittery@0.13.1: - resolution: {integrity: sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==} - engines: {node: '>=12'} - emoji-regex@8.0.0: resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} @@ -2521,9 +2314,6 @@ packages: resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} engines: {node: '>=0.12'} - error-ex@1.3.2: - resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==} - es-define-property@1.0.0: resolution: {integrity: sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==} engines: {node: '>= 0.4'} @@ -2551,10 +2341,6 @@ packages: resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} engines: {node: '>=0.8.0'} - escape-string-regexp@2.0.0: - resolution: {integrity: sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==} - engines: {node: '>=8'} - escape-string-regexp@4.0.0: resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} engines: {node: '>=10'} @@ -2567,11 +2353,6 @@ packages: resolution: {integrity: sha512-4DmjZ0qQIG+NQV1njHvWrua/cZEuJq56A3pSELT2BjNuol1aads7BluofCbLErdO41Ic1XCd2UMepVLpjL64YQ==} engines: {node: '>=14.16.0'} - esprima@4.0.1: - resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} - engines: {node: '>=4'} - hasBin: true - esrecurse@4.3.0: resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} engines: {node: '>=4.0'} @@ -2604,14 +2385,6 @@ packages: resolution: {integrity: sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==} engines: {node: '>=16.17'} - exit@0.1.2: - resolution: {integrity: sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==} - engines: {node: '>= 0.8.0'} - - expect@29.7.0: - resolution: {integrity: sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - fast-content-type-parse@1.1.0: resolution: {integrity: sha512-fBHHqSTFLVnR61C+gltJuE5GkVQMV0S2nqUO8TJ+5Z3qAKG8vAx4FKai1s5jq/inV1+sREynIWSuQ6HgoSXpDQ==} @@ -2665,9 +2438,6 @@ packages: resolution: {integrity: sha512-kRrIg4be8VNYhycS2PY//hpBJSzZPr/DBbcy9VWelhZMW3KhyLkQR0HL0k0MNpmVoNFF4EdfMFkNAWjTP65g6w==} engines: {node: '>=0.8.0'} - fb-watchman@2.0.2: - resolution: {integrity: sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==} - fetch-blob@3.2.0: resolution: {integrity: sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==} engines: {node: ^12.20 || >= 14.13} @@ -2680,10 +2450,6 @@ packages: resolution: {integrity: sha512-HdWXgFYc6b1BJcOBDBwjqWuHJj1WYiqrxSh25qtU4DabpMFdj/gSunNBQb83t+8Zt67D7CXEzJWTkxaShMTMOA==} engines: {node: '>=14'} - find-up@4.1.0: - resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==} - engines: {node: '>=8'} - find-up@5.0.0: resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} engines: {node: '>=10'} @@ -2737,10 +2503,6 @@ packages: resolution: {integrity: sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==} engines: {node: '>= 0.4'} - get-package-type@0.1.0: - resolution: {integrity: sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==} - engines: {node: '>=8.0.0'} - get-stream@6.0.1: resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==} engines: {node: '>=10'} @@ -2817,9 +2579,6 @@ packages: resolution: {integrity: sha512-kUGoI3p7u6B41z/dp33G6OaL7J4DRqRYwVmeIlwLClx7yaaAy7hoDExnuejTKtuDwfcatGmddHDEOjf6EyIxtQ==} engines: {node: '>=10.0.0'} - html-escaper@2.0.2: - resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==} - html-to-text@9.0.5: resolution: {integrity: sha512-qY60FjREgVZL03vJU6IfMV4GDjGBIoOyvuFdpBDIX9yTlDw0TjxVBQp+P8NvpdIXNJvfWBTNul7fsAQJq2FNpg==} engines: {node: '>=14'} @@ -2866,15 +2625,6 @@ packages: imapflow@1.0.164: resolution: {integrity: sha512-+KAmLrpqq2Q0Ts1imMP4svydfhYznlvlLHhgtTb8NiIcccZvdRNfdHVP8/RrGaw0hy0TOaluawsm/6q+TqdLPw==} - import-local@3.2.0: - resolution: {integrity: sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==} - engines: {node: '>=8'} - hasBin: true - - imurmurhash@0.1.4: - resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} - engines: {node: '>=0.8.19'} - inflight@1.0.6: resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful. @@ -2890,9 +2640,6 @@ packages: resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==} engines: {node: '>= 0.10'} - is-arrayish@0.2.1: - resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} - is-arrayish@0.3.2: resolution: {integrity: sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==} @@ -2912,10 +2659,6 @@ packages: resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} engines: {node: '>=8'} - is-generator-fn@2.1.0: - resolution: {integrity: sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==} - engines: {node: '>=6'} - is-glob@4.0.3: resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} engines: {node: '>=0.10.0'} @@ -2947,166 +2690,13 @@ packages: resolution: {integrity: sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==} engines: {node: '>=16'} - istanbul-lib-coverage@3.2.2: - resolution: {integrity: sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==} - engines: {node: '>=8'} - - istanbul-lib-instrument@5.2.1: - resolution: {integrity: sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==} - engines: {node: '>=8'} - - istanbul-lib-instrument@6.0.3: - resolution: {integrity: sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==} - engines: {node: '>=10'} - - istanbul-lib-report@3.0.1: - resolution: {integrity: sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==} - engines: {node: '>=10'} - - istanbul-lib-source-maps@4.0.1: - resolution: {integrity: sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==} - engines: {node: '>=10'} - - istanbul-reports@3.1.7: - resolution: {integrity: sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==} - engines: {node: '>=8'} - jackspeak@3.4.3: resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==} - jest-changed-files@29.7.0: - resolution: {integrity: sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - jest-circus@29.7.0: - resolution: {integrity: sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - jest-cli@29.7.0: - resolution: {integrity: sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - hasBin: true - peerDependencies: - node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 - peerDependenciesMeta: - node-notifier: - optional: true - - jest-config@29.7.0: - resolution: {integrity: sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - peerDependencies: - '@types/node': '*' - ts-node: '>=9.0.0' - peerDependenciesMeta: - '@types/node': - optional: true - ts-node: - optional: true - - jest-diff@29.7.0: - resolution: {integrity: sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - jest-docblock@29.7.0: - resolution: {integrity: sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - jest-each@29.7.0: - resolution: {integrity: sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - jest-environment-node@29.7.0: - resolution: {integrity: sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - jest-get-type@29.6.3: - resolution: {integrity: sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - jest-haste-map@29.7.0: - resolution: {integrity: sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - jest-leak-detector@29.7.0: - resolution: {integrity: sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - jest-matcher-utils@29.7.0: - resolution: {integrity: sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - jest-message-util@29.7.0: - resolution: {integrity: sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - jest-mock@29.7.0: - resolution: {integrity: sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - jest-pnp-resolver@1.2.3: - resolution: {integrity: sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==} - engines: {node: '>=6'} - peerDependencies: - jest-resolve: '*' - peerDependenciesMeta: - jest-resolve: - optional: true - - jest-regex-util@29.6.3: - resolution: {integrity: sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - jest-resolve-dependencies@29.7.0: - resolution: {integrity: sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - jest-resolve@29.7.0: - resolution: {integrity: sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - jest-runner@29.7.0: - resolution: {integrity: sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - jest-runtime@29.7.0: - resolution: {integrity: sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - jest-snapshot@29.7.0: - resolution: {integrity: sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - jest-util@29.7.0: - resolution: {integrity: sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - jest-validate@29.7.0: - resolution: {integrity: sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - jest-watcher@29.7.0: - resolution: {integrity: sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - jest-worker@27.5.1: resolution: {integrity: sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==} engines: {node: '>= 10.13.0'} - jest-worker@29.7.0: - resolution: {integrity: sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - jest@29.7.0: - resolution: {integrity: sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - hasBin: true - peerDependencies: - node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 - peerDependenciesMeta: - node-notifier: - optional: true - joycon@3.1.1: resolution: {integrity: sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==} engines: {node: '>=10'} @@ -3114,10 +2704,6 @@ packages: js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} - js-yaml@3.14.1: - resolution: {integrity: sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==} - hasBin: true - js-yaml@4.1.0: resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} hasBin: true @@ -3161,17 +2747,9 @@ packages: just-performance@4.2.0: resolution: {integrity: sha512-4TikKSf+Gb+Et5SnA4ppyrxLSf9qWFq+SqfdDdrgHE1KLwSch/Zi1AQB0TrE4ppYjZdUrHnwdx+6dyx0cx/HyA==} - kleur@3.0.3: - resolution: {integrity: sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==} - engines: {node: '>=6'} - leac@0.6.0: resolution: {integrity: sha512-y+SqErxb8h7nE/fiEX07jsbuhrpO9lL8eca7/Y1nuWV2moNlXhyd59iDGcRf6moVyDMbmTNzL40SUyrFU/yDpg==} - leven@3.1.0: - resolution: {integrity: sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==} - engines: {node: '>=6'} - libbase64@1.2.1: resolution: {integrity: sha512-l+nePcPbIG1fNlqMzrh68MLkX/gTxk/+vdvAb388Ssi7UuUN31MI44w4Yf33mM3Cm4xDfw48mdf3rkdHszLNew==} @@ -3214,10 +2792,6 @@ packages: resolution: {integrity: sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==} engines: {node: '>=6.11.5'} - locate-path@5.0.0: - resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==} - engines: {node: '>=8'} - locate-path@6.0.0: resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} engines: {node: '>=10'} @@ -3265,16 +2839,9 @@ packages: mailsplit@5.4.0: resolution: {integrity: sha512-wnYxX5D5qymGIPYLwnp6h8n1+6P6vz/MJn5AzGjZ8pwICWssL+CCQjWBIToOVHASmATot4ktvlLo6CyLfOXWYA==} - make-dir@4.0.0: - resolution: {integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==} - engines: {node: '>=10'} - make-error@1.3.6: resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==} - makeerror@1.0.12: - resolution: {integrity: sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==} - memfs@4.11.1: resolution: {integrity: sha512-LZcMTBAgqUUKNXZagcZxvXXfgF1bHX7Y7nQ0QyEiNbRJgE29GhgPd8Yna1VQcLlPiHt/5RFJMWYN9Uv/VPNvjQ==} engines: {node: '>= 4.0.0'} @@ -3347,9 +2914,6 @@ packages: mz@2.7.0: resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} - natural-compare@1.4.0: - resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} - neo-async@2.6.2: resolution: {integrity: sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==} @@ -3364,9 +2928,6 @@ packages: resolution: {integrity: sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - node-int64@0.4.0: - resolution: {integrity: sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==} - node-releases@2.0.18: resolution: {integrity: sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==} @@ -3434,18 +2995,10 @@ packages: openapi-types@12.1.3: resolution: {integrity: sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw==} - p-limit@2.3.0: - resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==} - engines: {node: '>=6'} - p-limit@3.1.0: resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} engines: {node: '>=10'} - p-locate@4.1.0: - resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==} - engines: {node: '>=8'} - p-locate@5.0.0: resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} engines: {node: '>=10'} @@ -3454,20 +3007,12 @@ packages: resolution: {integrity: sha512-couX95waDu98NfNZV+i/iLt+fdVxmI7CbrrdC2uDWfPdUAApyxT4wmDlyOtR5KtTDmkDO0zDScDjDou9YHhd9g==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - p-try@2.2.0: - resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} - engines: {node: '>=6'} - package-json-from-dist@1.0.0: resolution: {integrity: sha512-dATvCeZN/8wQsGywez1mzHtTlP22H8OEfPrVMLNr4/eGa+ijtLn/6M5f0dY8UKNrC2O9UCU6SSoG3qRKnt7STw==} packet-reader@1.0.0: resolution: {integrity: sha512-HAKu/fG3HpHFO0AA8WE8q2g+gBJaZ9MG7fcKk+IJPLTGAD6Psw4443l+9DGRbOIh3/aXr7Phy0TjilYivJo5XQ==} - parse-json@5.2.0: - resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} - engines: {node: '>=8'} - parse5-htmlparser2-tree-adapter@7.0.0: resolution: {integrity: sha512-B77tOZrqqfUfnVcOrUvfdLbz4pu4RopLD/4vmu3HUPswwTA8OH0EMW9BlWR2B0RCoiZRAHEUu7IxeP1Pd1UU+g==} @@ -3477,6 +3022,9 @@ packages: parseley@0.12.1: resolution: {integrity: sha512-e6qHKe3a9HWr0oMRVDTRhKce+bRO8VGQR3NyVwcjwrbhMmFCX9KszEV35+rn4AdilFAq9VPxP/Fe1wC9Qjd2lw==} + path-equal@1.2.5: + resolution: {integrity: sha512-i73IctDr3F2W+bsOWDyyVm/lqsXO47aY9nsFZUjTT/aljSbkxHxxCoyZ9UUrM8jK0JVod+An+rl48RCsvWM+9g==} + path-exists@4.0.0: resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} engines: {node: '>=8'} @@ -3578,10 +3126,6 @@ packages: resolution: {integrity: sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==} engines: {node: '>= 6'} - pkg-dir@4.2.0: - resolution: {integrity: sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==} - engines: {node: '>=8'} - postcss-load-config@6.0.1: resolution: {integrity: sha512-oPtTM4oerL+UXmx+93ytZVN82RrlY/wPUV8IeDxFrzIjXOLF1pN+EmKPLbubvKHT2HC20xXsCAH2Z+CKV6Oz/g==} engines: {node: '>= 18'} @@ -3635,10 +3179,6 @@ packages: postgres-range@1.1.4: resolution: {integrity: sha512-i/hbxIE9803Alj/6ytL7UHQxRvZkI9O4Sy+J3HGc4F4oo/2eQAjTSNJ0bfxyse3bH0nuVesCk+3IRLaMtG3H6w==} - pretty-format@29.7.0: - resolution: {integrity: sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - prevvy@7.5.0: resolution: {integrity: sha512-4aKAl2fBhD7rkn2xfxApUP4Il6ILBBJCNMk1TURdXAfXfakSynhjNmnpgb/bj1xSRW0lJYXjULdZFEy8bQdCNw==} @@ -3652,10 +3192,6 @@ packages: resolution: {integrity: sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==} engines: {node: '>= 0.6.0'} - prompts@2.4.2: - resolution: {integrity: sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==} - engines: {node: '>= 6'} - proto3-json-serializer@2.0.2: resolution: {integrity: sha512-SAzp/O4Yh02jGdRc+uIrGoe87dkN/XtwxfZ4ZyafJHymd79ozp5VG5nyZ7ygqPM5+cpLDjjGnYFUkngonyDPOQ==} engines: {node: '>=14.0.0'} @@ -3682,9 +3218,6 @@ packages: resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} engines: {node: '>=6'} - pure-rand@6.1.0: - resolution: {integrity: sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==} - qs@6.13.0: resolution: {integrity: sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==} engines: {node: '>=0.6'} @@ -3704,9 +3237,6 @@ packages: rate-limiter-flexible@5.0.3: resolution: {integrity: sha512-lWx2y8NBVlTOLPyqs+6y7dxfEpT6YFqKy3MzWbCy95sTTOhOuxufP2QvRyOHpfXpB9OUJPbVLybw3z3AVAS5fA==} - react-is@18.3.1: - resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==} - readable-stream@3.6.2: resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} engines: {node: '>= 6'} @@ -3755,10 +3285,6 @@ packages: requires-port@1.0.0: resolution: {integrity: sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==} - resolve-cwd@3.0.0: - resolution: {integrity: sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==} - engines: {node: '>=8'} - resolve-from@5.0.0: resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} engines: {node: '>=8'} @@ -3766,10 +3292,6 @@ packages: resolve-pkg-maps@1.0.0: resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} - resolve.exports@2.0.2: - resolution: {integrity: sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg==} - engines: {node: '>=10'} - resolve@1.22.8: resolution: {integrity: sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==} hasBin: true @@ -3886,9 +3408,6 @@ packages: resolution: {integrity: sha512-nPS85arNqwBXaIsFCkolHjGIkFo+Oxu9vbgmBJizLAhqe6P2o3Qmj3KCUoRkfhHtvgDhZdWD3risLHAUJ8npjw==} deprecated: 16.1.1 - sisteransi@1.0.5: - resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==} - slash@3.0.0: resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} engines: {node: '>=8'} @@ -3918,9 +3437,6 @@ packages: peerDependencies: webpack: ^5.72.1 - source-map-support@0.5.13: - resolution: {integrity: sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==} - source-map-support@0.5.21: resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==} @@ -3943,16 +3459,9 @@ packages: resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==} engines: {node: '>= 10.x'} - sprintf-js@1.0.3: - resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} - sprintf-js@1.1.3: resolution: {integrity: sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==} - stack-utils@2.0.6: - resolution: {integrity: sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==} - engines: {node: '>=10'} - statuses@2.0.1: resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==} engines: {node: '>= 0.8'} @@ -3960,10 +3469,6 @@ packages: stream-browserify@3.0.0: resolution: {integrity: sha512-H73RAHsVBapbim0tU2JwwOiXUj+fikfiaoYAKHF3VJfA0pe2BCzkhAHBlLG6REzE+2WNZcxOXjK7lkso+9euLA==} - string-length@4.0.2: - resolution: {integrity: sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==} - engines: {node: '>=10'} - string-width@4.2.3: resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} engines: {node: '>=8'} @@ -3983,10 +3488,6 @@ packages: resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==} engines: {node: '>=12'} - strip-bom@4.0.0: - resolution: {integrity: sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==} - engines: {node: '>=8'} - strip-final-newline@2.0.0: resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==} engines: {node: '>=6'} @@ -4060,10 +3561,6 @@ packages: engines: {node: '>=10'} hasBin: true - test-exclude@6.0.0: - resolution: {integrity: sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==} - engines: {node: '>=8'} - thenify-all@1.6.0: resolution: {integrity: sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==} engines: {node: '>=0.8'} @@ -4084,9 +3581,6 @@ packages: resolution: {integrity: sha512-GA16+8HXvqtfEnw/DTcwB0UU354QE1n3+wh08oFjr6Znl7ZLAeUgYzCcK+/CCrOyE0vnHR8/pu3XXG3vDijXpQ==} hasBin: true - tmpl@1.0.5: - resolution: {integrity: sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==} - to-fast-properties@2.0.0: resolution: {integrity: sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==} engines: {node: '>=4'} @@ -4127,6 +3621,11 @@ packages: ts-interface-checker@0.1.13: resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==} + ts-json-schema-generator@2.3.0: + resolution: {integrity: sha512-t4lBQAwZc0sOJq9LJt3NgbznIcslVnm0JeEMFq8qIRklpMRY8jlYD0YmnRWbqBKANxkby91P1XanSSlSOFpUmg==} + engines: {node: '>=18.0.0'} + hasBin: true + ts-node@10.9.2: resolution: {integrity: sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==} hasBin: true @@ -4163,8 +3662,8 @@ packages: typescript: optional: true - tsx@4.18.0: - resolution: {integrity: sha512-a1jaKBSVQkd6yEc1/NI7G6yHFfefIcuf3QJST7ZEyn4oQnxLYrZR5uZAM8UrwUa3Ge8suiZHcNS1gNrEvmobqg==} + tsx@4.19.0: + resolution: {integrity: sha512-bV30kM7bsLZKZIOCHeMNVMJ32/LuJzLVajkQI/qf92J2Qr08ueLQvW00PUZGiuLPP760UINwupgUj8qrSCPUKg==} engines: {node: '>=18.0.0'} hasBin: true @@ -4179,9 +3678,9 @@ packages: resolution: {integrity: sha512-Acylog8/luQ8L7il+geoSxhEkazvkslg7PSNKOX59mbB9cOveP5aq9h74Y7YU8yDpJwetzQQrfIwtf4Wp4LKcw==} engines: {node: '>=4'} - type-fest@0.21.3: - resolution: {integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==} - engines: {node: '>=10'} + typescript-json-schema@0.65.1: + resolution: {integrity: sha512-tuGH7ff2jPaUYi6as3lHyHcKpSmXIqN7/mu50x3HlYn0EHzLpmt3nplZ7EuhUkO0eqDRc9GqWNkfjgBPIS9kxg==} + hasBin: true typescript@5.5.4: resolution: {integrity: sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q==} @@ -4194,6 +3693,9 @@ packages: undefsafe@2.0.5: resolution: {integrity: sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==} + undici-types@5.26.5: + resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==} + undici-types@6.19.8: resolution: {integrity: sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==} @@ -4242,13 +3744,6 @@ packages: v8-compile-cache-lib@3.0.1: resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==} - v8-to-istanbul@9.3.0: - resolution: {integrity: sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==} - engines: {node: '>=10.12.0'} - - walker@1.0.8: - resolution: {integrity: sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==} - watchpack@2.4.2: resolution: {integrity: sha512-TnbFSbcOCcDgjZ4piURLCbJ3nJhznVh9kw6F6iokjiFPl8ONxe9A6nMDVXDiNbrSfLILs6vB07F7wLBrwPYzJw==} engines: {node: '>=10.13.0'} @@ -4313,10 +3808,6 @@ packages: wrappy@1.0.2: resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} - write-file-atomic@4.0.2: - resolution: {integrity: sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==} - engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} - xpath@0.0.34: resolution: {integrity: sha512-FxF6+rkr1rNSQrhUNYrAFJpRXNzlDoMxeXN5qI84939ylEv3qqPFKa85Oxr6tDaJKqwW6KKyo2v26TSv3k6LeA==} engines: {node: '>=0.6.0'} @@ -5119,11 +4610,6 @@ snapshots: '@babel/core': 7.25.2 '@babel/helper-plugin-utils': 7.24.8 - '@babel/plugin-syntax-bigint@7.8.3(@babel/core@7.25.2)': - dependencies: - '@babel/core': 7.25.2 - '@babel/helper-plugin-utils': 7.24.8 - '@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.25.2)': dependencies: '@babel/core': 7.25.2 @@ -5697,8 +5183,6 @@ snapshots: '@babel/helper-validator-identifier': 7.24.7 to-fast-properties: 2.0.0 - '@bcoe/v8-coverage@0.2.3': {} - '@cspotcode/source-map-support@0.8.1': dependencies: '@jridgewell/trace-mapping': 0.3.9 @@ -5929,178 +5413,6 @@ snapshots: wrap-ansi: 8.1.0 wrap-ansi-cjs: wrap-ansi@7.0.0 - '@istanbuljs/load-nyc-config@1.1.0': - dependencies: - camelcase: 5.3.1 - find-up: 4.1.0 - get-package-type: 0.1.0 - js-yaml: 3.14.1 - resolve-from: 5.0.0 - - '@istanbuljs/schema@0.1.3': {} - - '@jest/console@29.7.0': - dependencies: - '@jest/types': 29.6.3 - '@types/node': 22.5.0 - chalk: 4.1.2 - jest-message-util: 29.7.0 - jest-util: 29.7.0 - slash: 3.0.0 - - '@jest/core@29.7.0(ts-node@10.9.2(@swc/core@1.7.18)(@types/node@22.5.0)(typescript@5.5.4))': - dependencies: - '@jest/console': 29.7.0 - '@jest/reporters': 29.7.0 - '@jest/test-result': 29.7.0 - '@jest/transform': 29.7.0 - '@jest/types': 29.6.3 - '@types/node': 22.5.0 - ansi-escapes: 4.3.2 - chalk: 4.1.2 - ci-info: 3.9.0 - exit: 0.1.2 - graceful-fs: 4.2.11 - jest-changed-files: 29.7.0 - jest-config: 29.7.0(@types/node@22.5.0)(ts-node@10.9.2(@swc/core@1.7.18)(@types/node@22.5.0)(typescript@5.5.4)) - jest-haste-map: 29.7.0 - jest-message-util: 29.7.0 - jest-regex-util: 29.6.3 - jest-resolve: 29.7.0 - jest-resolve-dependencies: 29.7.0 - jest-runner: 29.7.0 - jest-runtime: 29.7.0 - jest-snapshot: 29.7.0 - jest-util: 29.7.0 - jest-validate: 29.7.0 - jest-watcher: 29.7.0 - micromatch: 4.0.8 - pretty-format: 29.7.0 - slash: 3.0.0 - strip-ansi: 6.0.1 - transitivePeerDependencies: - - babel-plugin-macros - - supports-color - - ts-node - - '@jest/environment@29.7.0': - dependencies: - '@jest/fake-timers': 29.7.0 - '@jest/types': 29.6.3 - '@types/node': 22.5.0 - jest-mock: 29.7.0 - - '@jest/expect-utils@29.7.0': - dependencies: - jest-get-type: 29.6.3 - - '@jest/expect@29.7.0': - dependencies: - expect: 29.7.0 - jest-snapshot: 29.7.0 - transitivePeerDependencies: - - supports-color - - '@jest/fake-timers@29.7.0': - dependencies: - '@jest/types': 29.6.3 - '@sinonjs/fake-timers': 10.3.0 - '@types/node': 22.5.0 - jest-message-util: 29.7.0 - jest-mock: 29.7.0 - jest-util: 29.7.0 - - '@jest/globals@29.7.0': - dependencies: - '@jest/environment': 29.7.0 - '@jest/expect': 29.7.0 - '@jest/types': 29.6.3 - jest-mock: 29.7.0 - transitivePeerDependencies: - - supports-color - - '@jest/reporters@29.7.0': - dependencies: - '@bcoe/v8-coverage': 0.2.3 - '@jest/console': 29.7.0 - '@jest/test-result': 29.7.0 - '@jest/transform': 29.7.0 - '@jest/types': 29.6.3 - '@jridgewell/trace-mapping': 0.3.25 - '@types/node': 22.5.0 - chalk: 4.1.2 - collect-v8-coverage: 1.0.2 - exit: 0.1.2 - glob: 7.2.3 - graceful-fs: 4.2.11 - istanbul-lib-coverage: 3.2.2 - istanbul-lib-instrument: 6.0.3 - istanbul-lib-report: 3.0.1 - istanbul-lib-source-maps: 4.0.1 - istanbul-reports: 3.1.7 - jest-message-util: 29.7.0 - jest-util: 29.7.0 - jest-worker: 29.7.0 - slash: 3.0.0 - string-length: 4.0.2 - strip-ansi: 6.0.1 - v8-to-istanbul: 9.3.0 - transitivePeerDependencies: - - supports-color - - '@jest/schemas@29.6.3': - dependencies: - '@sinclair/typebox': 0.27.8 - - '@jest/source-map@29.6.3': - dependencies: - '@jridgewell/trace-mapping': 0.3.25 - callsites: 3.1.0 - graceful-fs: 4.2.11 - - '@jest/test-result@29.7.0': - dependencies: - '@jest/console': 29.7.0 - '@jest/types': 29.6.3 - '@types/istanbul-lib-coverage': 2.0.6 - collect-v8-coverage: 1.0.2 - - '@jest/test-sequencer@29.7.0': - dependencies: - '@jest/test-result': 29.7.0 - graceful-fs: 4.2.11 - jest-haste-map: 29.7.0 - slash: 3.0.0 - - '@jest/transform@29.7.0': - dependencies: - '@babel/core': 7.25.2 - '@jest/types': 29.6.3 - '@jridgewell/trace-mapping': 0.3.25 - babel-plugin-istanbul: 6.1.1 - chalk: 4.1.2 - convert-source-map: 2.0.0 - fast-json-stable-stringify: 2.1.0 - graceful-fs: 4.2.11 - jest-haste-map: 29.7.0 - jest-regex-util: 29.6.3 - jest-util: 29.7.0 - micromatch: 4.0.8 - pirates: 4.0.6 - slash: 3.0.0 - write-file-atomic: 4.0.2 - transitivePeerDependencies: - - supports-color - - '@jest/types@29.6.3': - dependencies: - '@jest/schemas': 29.6.3 - '@types/istanbul-lib-coverage': 2.0.6 - '@types/istanbul-reports': 3.0.4 - '@types/node': 22.5.0 - '@types/yargs': 17.0.33 - chalk: 4.1.2 - '@jridgewell/gen-mapping@0.3.5': dependencies: '@jridgewell/set-array': 1.2.1 @@ -6247,8 +5559,6 @@ snapshots: domhandler: 5.0.3 selderee: 0.11.0 - '@sinclair/typebox@0.27.8': {} - '@sinonjs/commons@2.0.0': dependencies: type-detect: 4.0.8 @@ -6604,51 +5914,51 @@ snapshots: '@smithy/types': 3.3.0 tslib: 2.7.0 - '@swc/core-darwin-arm64@1.7.18': + '@swc/core-darwin-arm64@1.7.21': optional: true - '@swc/core-darwin-x64@1.7.18': + '@swc/core-darwin-x64@1.7.21': optional: true - '@swc/core-linux-arm-gnueabihf@1.7.18': + '@swc/core-linux-arm-gnueabihf@1.7.21': optional: true - '@swc/core-linux-arm64-gnu@1.7.18': + '@swc/core-linux-arm64-gnu@1.7.21': optional: true - '@swc/core-linux-arm64-musl@1.7.18': + '@swc/core-linux-arm64-musl@1.7.21': optional: true - '@swc/core-linux-x64-gnu@1.7.18': + '@swc/core-linux-x64-gnu@1.7.21': optional: true - '@swc/core-linux-x64-musl@1.7.18': + '@swc/core-linux-x64-musl@1.7.21': optional: true - '@swc/core-win32-arm64-msvc@1.7.18': + '@swc/core-win32-arm64-msvc@1.7.21': optional: true - '@swc/core-win32-ia32-msvc@1.7.18': + '@swc/core-win32-ia32-msvc@1.7.21': optional: true - '@swc/core-win32-x64-msvc@1.7.18': + '@swc/core-win32-x64-msvc@1.7.21': optional: true - '@swc/core@1.7.18': + '@swc/core@1.7.21': dependencies: '@swc/counter': 0.1.3 '@swc/types': 0.1.12 optionalDependencies: - '@swc/core-darwin-arm64': 1.7.18 - '@swc/core-darwin-x64': 1.7.18 - '@swc/core-linux-arm-gnueabihf': 1.7.18 - '@swc/core-linux-arm64-gnu': 1.7.18 - '@swc/core-linux-arm64-musl': 1.7.18 - '@swc/core-linux-x64-gnu': 1.7.18 - '@swc/core-linux-x64-musl': 1.7.18 - '@swc/core-win32-arm64-msvc': 1.7.18 - '@swc/core-win32-ia32-msvc': 1.7.18 - '@swc/core-win32-x64-msvc': 1.7.18 + '@swc/core-darwin-arm64': 1.7.21 + '@swc/core-darwin-x64': 1.7.21 + '@swc/core-linux-arm-gnueabihf': 1.7.21 + '@swc/core-linux-arm64-gnu': 1.7.21 + '@swc/core-linux-arm64-musl': 1.7.21 + '@swc/core-linux-x64-gnu': 1.7.21 + '@swc/core-linux-x64-musl': 1.7.21 + '@swc/core-win32-arm64-msvc': 1.7.21 + '@swc/core-win32-ia32-msvc': 1.7.21 + '@swc/core-win32-x64-msvc': 1.7.21 '@swc/counter@0.1.3': {} @@ -6691,7 +6001,7 @@ snapshots: '@temporalio/worker@1.11.1(esbuild@0.23.1)': dependencies: - '@swc/core': 1.7.18 + '@swc/core': 1.7.21 '@temporalio/activity': 1.11.1 '@temporalio/client': 1.11.1 '@temporalio/common': 1.11.1 @@ -6703,11 +6013,11 @@ snapshots: memfs: 4.11.1 rxjs: 7.8.1 source-map: 0.7.4 - source-map-loader: 4.0.2(webpack@5.94.0(@swc/core@1.7.18)(esbuild@0.23.1)) + source-map-loader: 4.0.2(webpack@5.94.0(@swc/core@1.7.21)(esbuild@0.23.1)) supports-color: 8.1.1 - swc-loader: 0.2.6(@swc/core@1.7.18)(webpack@5.94.0(@swc/core@1.7.18)(esbuild@0.23.1)) + swc-loader: 0.2.6(@swc/core@1.7.21)(webpack@5.94.0(@swc/core@1.7.21)(esbuild@0.23.1)) unionfs: 4.5.4 - webpack: 5.94.0(@swc/core@1.7.18)(esbuild@0.23.1) + webpack: 5.94.0(@swc/core@1.7.21)(esbuild@0.23.1) transitivePeerDependencies: - '@swc/helpers' - esbuild @@ -6729,32 +6039,11 @@ snapshots: '@tsconfig/node20@20.1.4': {} - '@types/babel__core@7.20.5': - dependencies: - '@babel/parser': 7.25.4 - '@babel/types': 7.25.4 - '@types/babel__generator': 7.6.8 - '@types/babel__template': 7.4.4 - '@types/babel__traverse': 7.20.6 - - '@types/babel__generator@7.6.8': - dependencies: - '@babel/types': 7.25.4 - - '@types/babel__template@7.4.4': - dependencies: - '@babel/parser': 7.25.4 - '@babel/types': 7.25.4 - - '@types/babel__traverse@7.20.6': - dependencies: - '@babel/types': 7.25.4 - - '@types/chai@4.3.18': {} + '@types/chai@4.3.19': {} '@types/cheerio@0.22.35': dependencies: - '@types/node': 22.5.0 + '@types/node': 22.5.1 '@types/debug@4.1.12': dependencies: @@ -6764,30 +6053,11 @@ snapshots: '@types/fluent-ffmpeg@2.1.26': dependencies: - '@types/node': 22.5.0 - - '@types/graceful-fs@4.1.9': - dependencies: - '@types/node': 22.5.0 + '@types/node': 22.5.1 '@types/imapflow@1.0.19': dependencies: - '@types/node': 22.5.0 - - '@types/istanbul-lib-coverage@2.0.6': {} - - '@types/istanbul-lib-report@3.0.3': - dependencies: - '@types/istanbul-lib-coverage': 2.0.6 - - '@types/istanbul-reports@3.0.4': - dependencies: - '@types/istanbul-lib-report': 3.0.3 - - '@types/jest@29.5.12': - dependencies: - expect: 29.7.0 - pretty-format: 29.7.0 + '@types/node': 22.5.1 '@types/json-schema@7.0.15': {} @@ -6795,24 +6065,28 @@ snapshots: '@types/mailparser@3.4.4': dependencies: - '@types/node': 22.5.0 + '@types/node': 22.5.1 iconv-lite: 0.6.3 '@types/mocha@10.0.7': {} '@types/ms@0.7.34': {} - '@types/node@20.16.1': + '@types/node@18.19.47': + dependencies: + undici-types: 5.26.5 + + '@types/node@20.16.2': dependencies: undici-types: 6.19.8 - '@types/node@22.5.0': + '@types/node@22.5.1': dependencies: undici-types: 6.19.8 - '@types/pg@8.11.6': + '@types/pg@8.11.8': dependencies: - '@types/node': 22.5.0 + '@types/node': 22.5.1 pg-protocol: 1.6.1 pg-types: 4.0.2 @@ -6824,14 +6098,6 @@ snapshots: '@types/sinonjs__fake-timers@8.1.5': {} - '@types/stack-utils@2.0.3': {} - - '@types/yargs-parser@21.0.3': {} - - '@types/yargs@17.0.33': - dependencies: - '@types/yargs-parser': 21.0.3 - '@webassemblyjs/ast@1.12.1': dependencies: '@webassemblyjs/helper-numbers': 1.11.6 @@ -6956,10 +6222,6 @@ snapshots: ansi-colors@4.1.3: {} - ansi-escapes@4.3.2: - dependencies: - type-fest: 0.21.3 - ansi-regex@5.0.1: {} ansi-regex@6.0.1: {} @@ -6972,8 +6234,6 @@ snapshots: dependencies: color-convert: 2.0.1 - ansi-styles@5.2.0: {} - ansi-styles@6.2.1: {} any-promise@1.3.0: {} @@ -6987,10 +6247,6 @@ snapshots: arg@5.0.2: {} - argparse@1.0.10: - dependencies: - sprintf-js: 1.0.3 - argparse@2.0.1: {} array-union@2.1.0: {} @@ -7008,36 +6264,6 @@ snapshots: '@fastify/error': 3.4.1 fastq: 1.17.1 - babel-jest@29.7.0(@babel/core@7.25.2): - dependencies: - '@babel/core': 7.25.2 - '@jest/transform': 29.7.0 - '@types/babel__core': 7.20.5 - babel-plugin-istanbul: 6.1.1 - babel-preset-jest: 29.6.3(@babel/core@7.25.2) - chalk: 4.1.2 - graceful-fs: 4.2.11 - slash: 3.0.0 - transitivePeerDependencies: - - supports-color - - babel-plugin-istanbul@6.1.1: - dependencies: - '@babel/helper-plugin-utils': 7.24.8 - '@istanbuljs/load-nyc-config': 1.1.0 - '@istanbuljs/schema': 0.1.3 - istanbul-lib-instrument: 5.2.1 - test-exclude: 6.0.0 - transitivePeerDependencies: - - supports-color - - babel-plugin-jest-hoist@29.6.3: - dependencies: - '@babel/template': 7.25.0 - '@babel/types': 7.25.4 - '@types/babel__core': 7.20.5 - '@types/babel__traverse': 7.20.6 - babel-plugin-polyfill-corejs2@0.4.11(@babel/core@7.25.2): dependencies: '@babel/compat-data': 7.25.4 @@ -7062,31 +6288,6 @@ snapshots: transitivePeerDependencies: - supports-color - babel-preset-current-node-syntax@1.1.0(@babel/core@7.25.2): - dependencies: - '@babel/core': 7.25.2 - '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.25.2) - '@babel/plugin-syntax-bigint': 7.8.3(@babel/core@7.25.2) - '@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.25.2) - '@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.25.2) - '@babel/plugin-syntax-import-attributes': 7.24.7(@babel/core@7.25.2) - '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.25.2) - '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.25.2) - '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.25.2) - '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.25.2) - '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.25.2) - '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.25.2) - '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.25.2) - '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.25.2) - '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.25.2) - '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.25.2) - - babel-preset-jest@29.6.3(@babel/core@7.25.2): - dependencies: - '@babel/core': 7.25.2 - babel-plugin-jest-hoist: 29.6.3 - babel-preset-current-node-syntax: 1.1.0(@babel/core@7.25.2) - balanced-match@1.0.2: {} base64-js@1.5.1: {} @@ -7121,10 +6322,6 @@ snapshots: node-releases: 2.0.18 update-browserslist-db: 1.1.0(browserslist@4.23.3) - bser@2.1.1: - dependencies: - node-int64: 0.4.0 - buffer-from@1.1.2: {} buffer-writer@2.0.0: {} @@ -7154,10 +6351,6 @@ snapshots: get-intrinsic: 1.2.4 set-function-length: 1.2.2 - callsites@3.1.0: {} - - camelcase@5.3.1: {} - camelcase@6.3.0: {} caniuse-lite@1.0.30001653: {} @@ -7183,8 +6376,6 @@ snapshots: ansi-styles: 4.3.0 supports-color: 7.2.0 - char-regex@1.0.2: {} - check-error@2.1.1: {} cheerio-select@2.1.0: @@ -7220,10 +6411,6 @@ snapshots: chrome-trace-event@1.0.4: {} - ci-info@3.9.0: {} - - cjs-module-lexer@1.4.0: {} - cliui@7.0.4: dependencies: string-width: 4.2.3 @@ -7236,10 +6423,6 @@ snapshots: strip-ansi: 6.0.1 wrap-ansi: 7.0.0 - co@4.6.0: {} - - collect-v8-coverage@1.0.2: {} - color-convert@1.9.3: dependencies: color-name: 1.1.3 @@ -7262,6 +6445,8 @@ snapshots: color-convert: 2.0.1 color-string: 1.9.1 + commander@12.1.0: {} + commander@2.20.3: {} commander@4.1.1: {} @@ -7296,21 +6481,6 @@ snapshots: dependencies: browserslist: 4.23.3 - create-jest@29.7.0(@types/node@22.5.0)(ts-node@10.9.2(@swc/core@1.7.18)(@types/node@22.5.0)(typescript@5.5.4)): - dependencies: - '@jest/types': 29.6.3 - chalk: 4.1.2 - exit: 0.1.2 - graceful-fs: 4.2.11 - jest-config: 29.7.0(@types/node@22.5.0)(ts-node@10.9.2(@swc/core@1.7.18)(@types/node@22.5.0)(typescript@5.5.4)) - jest-util: 29.7.0 - prompts: 2.4.2 - transitivePeerDependencies: - - '@types/node' - - babel-plugin-macros - - supports-color - - ts-node - create-require@1.1.1: {} cross-spawn@7.0.3: @@ -7359,8 +6529,6 @@ snapshots: decamelize@4.0.0: {} - dedent@1.5.3: {} - deep-eql@5.0.2: {} deepmerge@4.3.1: {} @@ -7377,10 +6545,6 @@ snapshots: detect-libc@2.0.3: {} - detect-newline@3.1.0: {} - - diff-sequences@29.6.3: {} - diff@4.0.2: {} diff@5.2.0: {} @@ -7413,8 +6577,6 @@ snapshots: electron-to-chromium@1.5.13: {} - emittery@0.13.1: {} - emoji-regex@8.0.0: {} emoji-regex@9.2.2: {} @@ -7432,10 +6594,6 @@ snapshots: entities@4.5.0: {} - error-ex@1.3.2: - dependencies: - is-arrayish: 0.2.1 - es-define-property@1.0.0: dependencies: get-intrinsic: 1.2.4 @@ -7477,8 +6635,6 @@ snapshots: escape-string-regexp@1.0.5: {} - escape-string-regexp@2.0.0: {} - escape-string-regexp@4.0.0: {} eslint-scope@5.1.1: @@ -7488,8 +6644,6 @@ snapshots: esmock@2.6.7: {} - esprima@4.0.1: {} - esrecurse@4.3.0: dependencies: estraverse: 5.3.0 @@ -7528,16 +6682,6 @@ snapshots: signal-exit: 4.1.0 strip-final-newline: 3.0.0 - exit@0.1.2: {} - - expect@29.7.0: - dependencies: - '@jest/expect-utils': 29.7.0 - jest-get-type: 29.6.3 - jest-matcher-utils: 29.7.0 - jest-message-util: 29.7.0 - jest-util: 29.7.0 - fast-content-type-parse@1.1.0: {} fast-decode-uri-component@1.0.1: {} @@ -7616,10 +6760,6 @@ snapshots: tough-cookie: 4.1.4 tunnel-agent: 0.6.0 - fb-watchman@2.0.2: - dependencies: - bser: 2.1.1 - fetch-blob@3.2.0: dependencies: node-domexception: 1.0.0 @@ -7635,11 +6775,6 @@ snapshots: fast-querystring: 1.1.2 safe-regex2: 3.1.0 - find-up@4.1.0: - dependencies: - locate-path: 5.0.0 - path-exists: 4.0.0 - find-up@5.0.0: dependencies: locate-path: 6.0.0 @@ -7686,8 +6821,6 @@ snapshots: has-symbols: 1.0.3 hasown: 2.0.2 - get-package-type@0.1.0: {} - get-stream@6.0.1: {} get-stream@8.0.1: {} @@ -7765,8 +6898,6 @@ snapshots: heap-js@2.5.0: {} - html-escaper@2.0.2: {} - html-to-text@9.0.5: dependencies: '@selderee/plugin-htmlparser2': 0.11.0 @@ -7827,13 +6958,6 @@ snapshots: pino: 9.2.0 socks: 2.8.3 - import-local@3.2.0: - dependencies: - pkg-dir: 4.2.0 - resolve-cwd: 3.0.0 - - imurmurhash@0.1.4: {} - inflight@1.0.6: dependencies: once: 1.4.0 @@ -7848,8 +6972,6 @@ snapshots: ipaddr.js@1.9.1: {} - is-arrayish@0.2.1: {} - is-arrayish@0.3.2: {} is-binary-path@2.1.0: @@ -7864,8 +6986,6 @@ snapshots: is-fullwidth-code-point@3.0.0: {} - is-generator-fn@2.1.0: {} - is-glob@4.0.3: dependencies: is-extglob: 2.1.1 @@ -7884,377 +7004,22 @@ snapshots: isexe@3.1.1: {} - istanbul-lib-coverage@3.2.2: {} - - istanbul-lib-instrument@5.2.1: - dependencies: - '@babel/core': 7.25.2 - '@babel/parser': 7.25.4 - '@istanbuljs/schema': 0.1.3 - istanbul-lib-coverage: 3.2.2 - semver: 6.3.1 - transitivePeerDependencies: - - supports-color - - istanbul-lib-instrument@6.0.3: - dependencies: - '@babel/core': 7.25.2 - '@babel/parser': 7.25.4 - '@istanbuljs/schema': 0.1.3 - istanbul-lib-coverage: 3.2.2 - semver: 7.6.3 - transitivePeerDependencies: - - supports-color - - istanbul-lib-report@3.0.1: - dependencies: - istanbul-lib-coverage: 3.2.2 - make-dir: 4.0.0 - supports-color: 7.2.0 - - istanbul-lib-source-maps@4.0.1: - dependencies: - debug: 4.3.6 - istanbul-lib-coverage: 3.2.2 - source-map: 0.6.1 - transitivePeerDependencies: - - supports-color - - istanbul-reports@3.1.7: - dependencies: - html-escaper: 2.0.2 - istanbul-lib-report: 3.0.1 - jackspeak@3.4.3: dependencies: '@isaacs/cliui': 8.0.2 optionalDependencies: '@pkgjs/parseargs': 0.11.0 - jest-changed-files@29.7.0: - dependencies: - execa: 5.1.1 - jest-util: 29.7.0 - p-limit: 3.1.0 - - jest-circus@29.7.0: - dependencies: - '@jest/environment': 29.7.0 - '@jest/expect': 29.7.0 - '@jest/test-result': 29.7.0 - '@jest/types': 29.6.3 - '@types/node': 22.5.0 - chalk: 4.1.2 - co: 4.6.0 - dedent: 1.5.3 - is-generator-fn: 2.1.0 - jest-each: 29.7.0 - jest-matcher-utils: 29.7.0 - jest-message-util: 29.7.0 - jest-runtime: 29.7.0 - jest-snapshot: 29.7.0 - jest-util: 29.7.0 - p-limit: 3.1.0 - pretty-format: 29.7.0 - pure-rand: 6.1.0 - slash: 3.0.0 - stack-utils: 2.0.6 - transitivePeerDependencies: - - babel-plugin-macros - - supports-color - - jest-cli@29.7.0(@types/node@22.5.0)(ts-node@10.9.2(@swc/core@1.7.18)(@types/node@22.5.0)(typescript@5.5.4)): - dependencies: - '@jest/core': 29.7.0(ts-node@10.9.2(@swc/core@1.7.18)(@types/node@22.5.0)(typescript@5.5.4)) - '@jest/test-result': 29.7.0 - '@jest/types': 29.6.3 - chalk: 4.1.2 - create-jest: 29.7.0(@types/node@22.5.0)(ts-node@10.9.2(@swc/core@1.7.18)(@types/node@22.5.0)(typescript@5.5.4)) - exit: 0.1.2 - import-local: 3.2.0 - jest-config: 29.7.0(@types/node@22.5.0)(ts-node@10.9.2(@swc/core@1.7.18)(@types/node@22.5.0)(typescript@5.5.4)) - jest-util: 29.7.0 - jest-validate: 29.7.0 - yargs: 17.7.2 - transitivePeerDependencies: - - '@types/node' - - babel-plugin-macros - - supports-color - - ts-node - - jest-config@29.7.0(@types/node@22.5.0)(ts-node@10.9.2(@swc/core@1.7.18)(@types/node@22.5.0)(typescript@5.5.4)): - dependencies: - '@babel/core': 7.25.2 - '@jest/test-sequencer': 29.7.0 - '@jest/types': 29.6.3 - babel-jest: 29.7.0(@babel/core@7.25.2) - chalk: 4.1.2 - ci-info: 3.9.0 - deepmerge: 4.3.1 - glob: 7.2.3 - graceful-fs: 4.2.11 - jest-circus: 29.7.0 - jest-environment-node: 29.7.0 - jest-get-type: 29.6.3 - jest-regex-util: 29.6.3 - jest-resolve: 29.7.0 - jest-runner: 29.7.0 - jest-util: 29.7.0 - jest-validate: 29.7.0 - micromatch: 4.0.8 - parse-json: 5.2.0 - pretty-format: 29.7.0 - slash: 3.0.0 - strip-json-comments: 3.1.1 - optionalDependencies: - '@types/node': 22.5.0 - ts-node: 10.9.2(@swc/core@1.7.18)(@types/node@22.5.0)(typescript@5.5.4) - transitivePeerDependencies: - - babel-plugin-macros - - supports-color - - jest-diff@29.7.0: - dependencies: - chalk: 4.1.2 - diff-sequences: 29.6.3 - jest-get-type: 29.6.3 - pretty-format: 29.7.0 - - jest-docblock@29.7.0: - dependencies: - detect-newline: 3.1.0 - - jest-each@29.7.0: - dependencies: - '@jest/types': 29.6.3 - chalk: 4.1.2 - jest-get-type: 29.6.3 - jest-util: 29.7.0 - pretty-format: 29.7.0 - - jest-environment-node@29.7.0: - dependencies: - '@jest/environment': 29.7.0 - '@jest/fake-timers': 29.7.0 - '@jest/types': 29.6.3 - '@types/node': 22.5.0 - jest-mock: 29.7.0 - jest-util: 29.7.0 - - jest-get-type@29.6.3: {} - - jest-haste-map@29.7.0: - dependencies: - '@jest/types': 29.6.3 - '@types/graceful-fs': 4.1.9 - '@types/node': 22.5.0 - anymatch: 3.1.3 - fb-watchman: 2.0.2 - graceful-fs: 4.2.11 - jest-regex-util: 29.6.3 - jest-util: 29.7.0 - jest-worker: 29.7.0 - micromatch: 4.0.8 - walker: 1.0.8 - optionalDependencies: - fsevents: 2.3.3 - - jest-leak-detector@29.7.0: - dependencies: - jest-get-type: 29.6.3 - pretty-format: 29.7.0 - - jest-matcher-utils@29.7.0: - dependencies: - chalk: 4.1.2 - jest-diff: 29.7.0 - jest-get-type: 29.6.3 - pretty-format: 29.7.0 - - jest-message-util@29.7.0: - dependencies: - '@babel/code-frame': 7.24.7 - '@jest/types': 29.6.3 - '@types/stack-utils': 2.0.3 - chalk: 4.1.2 - graceful-fs: 4.2.11 - micromatch: 4.0.8 - pretty-format: 29.7.0 - slash: 3.0.0 - stack-utils: 2.0.6 - - jest-mock@29.7.0: - dependencies: - '@jest/types': 29.6.3 - '@types/node': 22.5.0 - jest-util: 29.7.0 - - jest-pnp-resolver@1.2.3(jest-resolve@29.7.0): - optionalDependencies: - jest-resolve: 29.7.0 - - jest-regex-util@29.6.3: {} - - jest-resolve-dependencies@29.7.0: - dependencies: - jest-regex-util: 29.6.3 - jest-snapshot: 29.7.0 - transitivePeerDependencies: - - supports-color - - jest-resolve@29.7.0: - dependencies: - chalk: 4.1.2 - graceful-fs: 4.2.11 - jest-haste-map: 29.7.0 - jest-pnp-resolver: 1.2.3(jest-resolve@29.7.0) - jest-util: 29.7.0 - jest-validate: 29.7.0 - resolve: 1.22.8 - resolve.exports: 2.0.2 - slash: 3.0.0 - - jest-runner@29.7.0: - dependencies: - '@jest/console': 29.7.0 - '@jest/environment': 29.7.0 - '@jest/test-result': 29.7.0 - '@jest/transform': 29.7.0 - '@jest/types': 29.6.3 - '@types/node': 22.5.0 - chalk: 4.1.2 - emittery: 0.13.1 - graceful-fs: 4.2.11 - jest-docblock: 29.7.0 - jest-environment-node: 29.7.0 - jest-haste-map: 29.7.0 - jest-leak-detector: 29.7.0 - jest-message-util: 29.7.0 - jest-resolve: 29.7.0 - jest-runtime: 29.7.0 - jest-util: 29.7.0 - jest-watcher: 29.7.0 - jest-worker: 29.7.0 - p-limit: 3.1.0 - source-map-support: 0.5.13 - transitivePeerDependencies: - - supports-color - - jest-runtime@29.7.0: - dependencies: - '@jest/environment': 29.7.0 - '@jest/fake-timers': 29.7.0 - '@jest/globals': 29.7.0 - '@jest/source-map': 29.6.3 - '@jest/test-result': 29.7.0 - '@jest/transform': 29.7.0 - '@jest/types': 29.6.3 - '@types/node': 22.5.0 - chalk: 4.1.2 - cjs-module-lexer: 1.4.0 - collect-v8-coverage: 1.0.2 - glob: 7.2.3 - graceful-fs: 4.2.11 - jest-haste-map: 29.7.0 - jest-message-util: 29.7.0 - jest-mock: 29.7.0 - jest-regex-util: 29.6.3 - jest-resolve: 29.7.0 - jest-snapshot: 29.7.0 - jest-util: 29.7.0 - slash: 3.0.0 - strip-bom: 4.0.0 - transitivePeerDependencies: - - supports-color - - jest-snapshot@29.7.0: - dependencies: - '@babel/core': 7.25.2 - '@babel/generator': 7.25.5 - '@babel/plugin-syntax-jsx': 7.24.7(@babel/core@7.25.2) - '@babel/plugin-syntax-typescript': 7.25.4(@babel/core@7.25.2) - '@babel/types': 7.25.4 - '@jest/expect-utils': 29.7.0 - '@jest/transform': 29.7.0 - '@jest/types': 29.6.3 - babel-preset-current-node-syntax: 1.1.0(@babel/core@7.25.2) - chalk: 4.1.2 - expect: 29.7.0 - graceful-fs: 4.2.11 - jest-diff: 29.7.0 - jest-get-type: 29.6.3 - jest-matcher-utils: 29.7.0 - jest-message-util: 29.7.0 - jest-util: 29.7.0 - natural-compare: 1.4.0 - pretty-format: 29.7.0 - semver: 7.6.3 - transitivePeerDependencies: - - supports-color - - jest-util@29.7.0: - dependencies: - '@jest/types': 29.6.3 - '@types/node': 22.5.0 - chalk: 4.1.2 - ci-info: 3.9.0 - graceful-fs: 4.2.11 - picomatch: 2.3.1 - - jest-validate@29.7.0: - dependencies: - '@jest/types': 29.6.3 - camelcase: 6.3.0 - chalk: 4.1.2 - jest-get-type: 29.6.3 - leven: 3.1.0 - pretty-format: 29.7.0 - - jest-watcher@29.7.0: - dependencies: - '@jest/test-result': 29.7.0 - '@jest/types': 29.6.3 - '@types/node': 22.5.0 - ansi-escapes: 4.3.2 - chalk: 4.1.2 - emittery: 0.13.1 - jest-util: 29.7.0 - string-length: 4.0.2 - jest-worker@27.5.1: dependencies: - '@types/node': 22.5.0 + '@types/node': 22.5.1 merge-stream: 2.0.0 supports-color: 8.1.1 - jest-worker@29.7.0: - dependencies: - '@types/node': 22.5.0 - jest-util: 29.7.0 - merge-stream: 2.0.0 - supports-color: 8.1.1 - - jest@29.7.0(@types/node@22.5.0)(ts-node@10.9.2(@swc/core@1.7.18)(@types/node@22.5.0)(typescript@5.5.4)): - dependencies: - '@jest/core': 29.7.0(ts-node@10.9.2(@swc/core@1.7.18)(@types/node@22.5.0)(typescript@5.5.4)) - '@jest/types': 29.6.3 - import-local: 3.2.0 - jest-cli: 29.7.0(@types/node@22.5.0)(ts-node@10.9.2(@swc/core@1.7.18)(@types/node@22.5.0)(typescript@5.5.4)) - transitivePeerDependencies: - - '@types/node' - - babel-plugin-macros - - supports-color - - ts-node - joycon@3.1.1: {} js-tokens@4.0.0: {} - js-yaml@3.14.1: - dependencies: - argparse: 1.0.10 - esprima: 4.0.1 - js-yaml@4.1.0: dependencies: argparse: 2.0.1 @@ -8289,12 +7054,8 @@ snapshots: just-performance@4.2.0: {} - kleur@3.0.3: {} - leac@0.6.0: {} - leven@3.1.0: {} - libbase64@1.2.1: {} libbase64@1.3.0: {} @@ -8339,10 +7100,6 @@ snapshots: loader-runner@4.3.0: {} - locate-path@5.0.0: - dependencies: - p-locate: 4.1.0 - locate-path@6.0.0: dependencies: p-locate: 5.0.0 @@ -8397,16 +7154,8 @@ snapshots: libmime: 5.2.0 libqp: 2.0.1 - make-dir@4.0.0: - dependencies: - semver: 7.6.3 - make-error@1.3.6: {} - makeerror@1.0.12: - dependencies: - tmpl: 1.0.5 - memfs@4.11.1: dependencies: '@jsonjoy.com/json-pack': 1.1.0(tslib@2.7.0) @@ -8488,8 +7237,6 @@ snapshots: object-assign: 4.1.1 thenify-all: 1.6.0 - natural-compare@1.4.0: {} - neo-async@2.6.2: {} nise@5.1.9: @@ -8508,8 +7255,6 @@ snapshots: fetch-blob: 3.2.0 formdata-polyfill: 4.0.10 - node-int64@0.4.0: {} - node-releases@2.0.18: {} nodemailer@6.9.13: {} @@ -8585,18 +7330,10 @@ snapshots: openapi-types@12.1.3: {} - p-limit@2.3.0: - dependencies: - p-try: 2.2.0 - p-limit@3.1.0: dependencies: yocto-queue: 0.1.0 - p-locate@4.1.0: - dependencies: - p-limit: 2.3.0 - p-locate@5.0.0: dependencies: p-limit: 3.1.0 @@ -8606,19 +7343,10 @@ snapshots: '@types/retry': 0.12.1 retry: 0.13.1 - p-try@2.2.0: {} - package-json-from-dist@1.0.0: {} packet-reader@1.0.0: {} - parse-json@5.2.0: - dependencies: - '@babel/code-frame': 7.24.7 - error-ex: 1.3.2 - json-parse-even-better-errors: 2.3.1 - lines-and-columns: 1.2.4 - parse5-htmlparser2-tree-adapter@7.0.0: dependencies: domhandler: 5.0.3 @@ -8633,6 +7361,8 @@ snapshots: leac: 0.6.0 peberminta: 0.9.0 + path-equal@1.2.5: {} + path-exists@4.0.0: {} path-is-absolute@1.0.1: {} @@ -8741,15 +7471,11 @@ snapshots: pirates@4.0.6: {} - pkg-dir@4.2.0: - dependencies: - find-up: 4.1.0 - - postcss-load-config@6.0.1(tsx@4.18.0)(yaml@2.5.0): + postcss-load-config@6.0.1(tsx@4.19.0)(yaml@2.5.0): dependencies: lilconfig: 3.1.2 optionalDependencies: - tsx: 4.18.0 + tsx: 4.19.0 yaml: 2.5.0 postgres-array@2.0.0: {} @@ -8774,18 +7500,12 @@ snapshots: postgres-range@1.1.4: {} - pretty-format@29.7.0: - dependencies: - '@jest/schemas': 29.6.3 - ansi-styles: 5.2.0 - react-is: 18.3.1 - prevvy@7.5.0: dependencies: '@types/debug': 4.1.12 '@types/fluent-ffmpeg': 2.1.26 '@types/luxon': 3.4.2 - '@types/node': 20.16.1 + '@types/node': 20.16.2 debug: 4.3.6 execa: 8.0.1 fluent-ffmpeg: 2.1.3 @@ -8800,11 +7520,6 @@ snapshots: process@0.11.10: {} - prompts@2.4.2: - dependencies: - kleur: 3.0.3 - sisteransi: 1.0.5 - proto3-json-serializer@2.0.2: dependencies: protobufjs: 7.4.0 @@ -8821,7 +7536,7 @@ snapshots: '@protobufjs/path': 1.1.2 '@protobufjs/pool': 1.1.0 '@protobufjs/utf8': 1.1.0 - '@types/node': 22.5.0 + '@types/node': 22.5.1 long: 5.2.3 proxy-addr@2.0.7: @@ -8837,8 +7552,6 @@ snapshots: punycode@2.3.1: {} - pure-rand@6.1.0: {} - qs@6.13.0: dependencies: side-channel: 1.0.6 @@ -8855,8 +7568,6 @@ snapshots: rate-limiter-flexible@5.0.3: {} - react-is@18.3.1: {} - readable-stream@3.6.2: dependencies: inherits: 2.0.4 @@ -8908,16 +7619,10 @@ snapshots: requires-port@1.0.0: {} - resolve-cwd@3.0.0: - dependencies: - resolve-from: 5.0.0 - resolve-from@5.0.0: {} resolve-pkg-maps@1.0.0: {} - resolve.exports@2.0.2: {} - resolve@1.22.8: dependencies: is-core-module: 2.15.1 @@ -9069,8 +7774,6 @@ snapshots: nise: 5.1.9 supports-color: 7.2.0 - sisteransi@1.0.5: {} - slash@3.0.0: {} slugify@1.6.6: {} @@ -9088,16 +7791,11 @@ snapshots: source-map-js@1.2.0: {} - source-map-loader@4.0.2(webpack@5.94.0(@swc/core@1.7.18)(esbuild@0.23.1)): + source-map-loader@4.0.2(webpack@5.94.0(@swc/core@1.7.21)(esbuild@0.23.1)): dependencies: iconv-lite: 0.6.3 source-map-js: 1.2.0 - webpack: 5.94.0(@swc/core@1.7.18)(esbuild@0.23.1) - - source-map-support@0.5.13: - dependencies: - buffer-from: 1.1.2 - source-map: 0.6.1 + webpack: 5.94.0(@swc/core@1.7.21)(esbuild@0.23.1) source-map-support@0.5.21: dependencies: @@ -9116,14 +7814,8 @@ snapshots: split2@4.2.0: {} - sprintf-js@1.0.3: {} - sprintf-js@1.1.3: {} - stack-utils@2.0.6: - dependencies: - escape-string-regexp: 2.0.0 - statuses@2.0.1: {} stream-browserify@3.0.0: @@ -9131,11 +7823,6 @@ snapshots: inherits: 2.0.4 readable-stream: 3.6.2 - string-length@4.0.2: - dependencies: - char-regex: 1.0.2 - strip-ansi: 6.0.1 - string-width@4.2.3: dependencies: emoji-regex: 8.0.0 @@ -9160,8 +7847,6 @@ snapshots: dependencies: ansi-regex: 6.0.1 - strip-bom@4.0.0: {} - strip-final-newline@2.0.0: {} strip-final-newline@3.0.0: {} @@ -9198,24 +7883,24 @@ snapshots: swagger-ui-dist@5.17.14: {} - swc-loader@0.2.6(@swc/core@1.7.18)(webpack@5.94.0(@swc/core@1.7.18)(esbuild@0.23.1)): + swc-loader@0.2.6(@swc/core@1.7.21)(webpack@5.94.0(@swc/core@1.7.21)(esbuild@0.23.1)): dependencies: - '@swc/core': 1.7.18 + '@swc/core': 1.7.21 '@swc/counter': 0.1.3 - webpack: 5.94.0(@swc/core@1.7.18)(esbuild@0.23.1) + webpack: 5.94.0(@swc/core@1.7.21)(esbuild@0.23.1) tapable@2.2.1: {} - terser-webpack-plugin@5.3.10(@swc/core@1.7.18)(esbuild@0.23.1)(webpack@5.94.0(@swc/core@1.7.18)(esbuild@0.23.1)): + terser-webpack-plugin@5.3.10(@swc/core@1.7.21)(esbuild@0.23.1)(webpack@5.94.0(@swc/core@1.7.21)(esbuild@0.23.1)): dependencies: '@jridgewell/trace-mapping': 0.3.25 jest-worker: 27.5.1 schema-utils: 3.3.0 serialize-javascript: 6.0.2 terser: 5.31.6 - webpack: 5.94.0(@swc/core@1.7.18)(esbuild@0.23.1) + webpack: 5.94.0(@swc/core@1.7.21)(esbuild@0.23.1) optionalDependencies: - '@swc/core': 1.7.18 + '@swc/core': 1.7.21 esbuild: 0.23.1 terser@5.31.6: @@ -9225,12 +7910,6 @@ snapshots: commander: 2.20.3 source-map-support: 0.5.21 - test-exclude@6.0.0: - dependencies: - '@istanbuljs/schema': 0.1.3 - glob: 7.2.3 - minimatch: 3.1.2 - thenify-all@1.6.0: dependencies: thenify: 3.3.1 @@ -9249,8 +7928,6 @@ snapshots: tlds@1.252.0: {} - tmpl@1.0.5: {} - to-fast-properties@2.0.0: {} to-regex-range@5.0.1: @@ -9282,14 +7959,25 @@ snapshots: ts-interface-checker@0.1.13: {} - ts-node@10.9.2(@swc/core@1.7.18)(@types/node@22.5.0)(typescript@5.5.4): + ts-json-schema-generator@2.3.0: + dependencies: + '@types/json-schema': 7.0.15 + commander: 12.1.0 + glob: 10.4.5 + json5: 2.2.3 + normalize-path: 3.0.0 + safe-stable-stringify: 2.5.0 + tslib: 2.7.0 + typescript: 5.5.4 + + ts-node@10.9.2(@swc/core@1.7.21)(@types/node@18.19.47)(typescript@5.5.4): dependencies: '@cspotcode/source-map-support': 0.8.1 '@tsconfig/node10': 1.0.11 '@tsconfig/node12': 1.0.11 '@tsconfig/node14': 1.0.3 '@tsconfig/node16': 1.0.4 - '@types/node': 22.5.0 + '@types/node': 18.19.47 acorn: 8.12.1 acorn-walk: 8.3.3 arg: 4.1.3 @@ -9300,11 +7988,31 @@ snapshots: v8-compile-cache-lib: 3.0.1 yn: 3.1.1 optionalDependencies: - '@swc/core': 1.7.18 + '@swc/core': 1.7.21 + + ts-node@10.9.2(@swc/core@1.7.21)(@types/node@22.5.1)(typescript@5.5.4): + dependencies: + '@cspotcode/source-map-support': 0.8.1 + '@tsconfig/node10': 1.0.11 + '@tsconfig/node12': 1.0.11 + '@tsconfig/node14': 1.0.3 + '@tsconfig/node16': 1.0.4 + '@types/node': 22.5.1 + acorn: 8.12.1 + acorn-walk: 8.3.3 + arg: 4.1.3 + create-require: 1.1.1 + diff: 4.0.2 + make-error: 1.3.6 + typescript: 5.5.4 + v8-compile-cache-lib: 3.0.1 + yn: 3.1.1 + optionalDependencies: + '@swc/core': 1.7.21 tslib@2.7.0: {} - tsup@8.2.4(@swc/core@1.7.18)(tsx@4.18.0)(typescript@5.5.4)(yaml@2.5.0): + tsup@8.2.4(@swc/core@1.7.21)(tsx@4.19.0)(typescript@5.5.4)(yaml@2.5.0): dependencies: bundle-require: 5.0.0(esbuild@0.23.1) cac: 6.7.14 @@ -9316,14 +8024,14 @@ snapshots: globby: 11.1.0 joycon: 3.1.1 picocolors: 1.0.1 - postcss-load-config: 6.0.1(tsx@4.18.0)(yaml@2.5.0) + postcss-load-config: 6.0.1(tsx@4.19.0)(yaml@2.5.0) resolve-from: 5.0.0 rollup: 4.21.1 source-map: 0.8.0-beta.0 sucrase: 3.35.0 tree-kill: 1.2.2 optionalDependencies: - '@swc/core': 1.7.18 + '@swc/core': 1.7.21 typescript: 5.5.4 transitivePeerDependencies: - jiti @@ -9331,7 +8039,7 @@ snapshots: - tsx - yaml - tsx@4.18.0: + tsx@4.19.0: dependencies: esbuild: 0.23.1 get-tsconfig: 4.7.6 @@ -9346,7 +8054,19 @@ snapshots: type-detect@4.1.0: {} - type-fest@0.21.3: {} + typescript-json-schema@0.65.1(@swc/core@1.7.21): + dependencies: + '@types/json-schema': 7.0.15 + '@types/node': 18.19.47 + glob: 7.2.3 + path-equal: 1.2.5 + safe-stable-stringify: 2.5.0 + ts-node: 10.9.2(@swc/core@1.7.21)(@types/node@18.19.47)(typescript@5.5.4) + typescript: 5.5.4 + yargs: 17.7.2 + transitivePeerDependencies: + - '@swc/core' + - '@swc/wasm' typescript@5.5.4: {} @@ -9354,6 +8074,8 @@ snapshots: undefsafe@2.0.5: {} + undici-types@5.26.5: {} + undici-types@6.19.8: {} unicode-canonical-property-names-ecmascript@2.0.0: {} @@ -9394,16 +8116,6 @@ snapshots: v8-compile-cache-lib@3.0.1: {} - v8-to-istanbul@9.3.0: - dependencies: - '@jridgewell/trace-mapping': 0.3.25 - '@types/istanbul-lib-coverage': 2.0.6 - convert-source-map: 2.0.0 - - walker@1.0.8: - dependencies: - makeerror: 1.0.12 - watchpack@2.4.2: dependencies: glob-to-regexp: 0.4.1 @@ -9415,7 +8127,7 @@ snapshots: webpack-sources@3.2.3: {} - webpack@5.94.0(@swc/core@1.7.18)(esbuild@0.23.1): + webpack@5.94.0(@swc/core@1.7.21)(esbuild@0.23.1): dependencies: '@types/estree': 1.0.5 '@webassemblyjs/ast': 1.12.1 @@ -9437,7 +8149,7 @@ snapshots: neo-async: 2.6.2 schema-utils: 3.3.0 tapable: 2.2.1 - terser-webpack-plugin: 5.3.10(@swc/core@1.7.18)(esbuild@0.23.1)(webpack@5.94.0(@swc/core@1.7.18)(esbuild@0.23.1)) + terser-webpack-plugin: 5.3.10(@swc/core@1.7.21)(esbuild@0.23.1)(webpack@5.94.0(@swc/core@1.7.21)(esbuild@0.23.1)) watchpack: 2.4.2 webpack-sources: 3.2.3 transitivePeerDependencies: @@ -9487,11 +8199,6 @@ snapshots: wrappy@1.0.2: {} - write-file-atomic@4.0.2: - dependencies: - imurmurhash: 0.1.4 - signal-exit: 3.0.7 - xpath@0.0.34: {} xtend@4.0.2: {} diff --git a/services/scout/src/fastify.js b/services/scout/src/fastify.js deleted file mode 100644 index 5b03994..0000000 --- a/services/scout/src/fastify.js +++ /dev/null @@ -1,106 +0,0 @@ - - -import Fastify from 'fastify' -import fastifySwagger from '@fastify/swagger' -import fastifySwaggerUi from '@fastify/swagger-ui' -import { readFileSync } from 'node:fs' -import { fileURLToPath } from 'url' -import { dirname, join } from 'node:path' - -const __dirname = dirname(fileURLToPath(import.meta.url)); - - -const swaggerDarkCss = readFileSync(join(__dirname, './css/SwaggerDark.css'), { encoding: 'utf-8' }) - - -async function fastifySetup(configs) { - - const fastify = Fastify({ - logger: true - }) - - - await fastify.register(fastifySwagger) - await fastify.register(fastifySwaggerUi, { - theme: { - title: '@futureporn/scout', - css: [ - { filename: 'SwaggerDark.css', content: swaggerDarkCss } - ] - }, - routePrefix: '/', - uiConfig: { - docExpansion: 'full', - deepLinking: false - }, - uiHooks: { - onRequest: function (request, reply, next) { next() }, - preHandler: function (request, reply, next) { next() } - }, - staticCSP: true, - transformStaticCSP: (header) => header, - transformSpecification: (swaggerObject, request, reply) => { return swaggerObject }, - transformSpecificationClone: true -}) - - -fastify.put('/some-route/:id', { - schema: { - description: 'post some data', - tags: ['vtuber'], - summary: 'qwerty', - params: { - type: 'object', - properties: { - id: { - type: 'string', - description: 'user id' - } - } - }, - body: { - type: 'object', - properties: { - hello: { type: 'string' }, - obj: { - type: 'object', - properties: { - some: { type: 'string' } - } - } - } - }, - response: { - 201: { - description: 'Successful response', - type: 'object', - properties: { - hello: { type: 'string' } - } - }, - default: { - description: 'Default response', - type: 'object', - properties: { - foo: { type: 'string' } - } - } - }, - } -}, (req, reply) => { - reply.send('HAHAHAHAHAH') -}) - - fastify.listen({ port: configs.port }, function (err, address) { - console.log(`@futureporn/scout listening on ${address}`) - if (err) { - fastify.log.error(err) - process.exit(1) - } - }) - - -} - - -export default fastifySetup diff --git a/services/scout/src/fastify.ts b/services/scout/src/fastify.ts new file mode 100644 index 0000000..6e09be4 --- /dev/null +++ b/services/scout/src/fastify.ts @@ -0,0 +1,159 @@ + + +import Fastify, { FastifyPluginCallback, FastifyReply, FastifyRequest, FastifyReplyContext, FastifyPluginAsync, FastifyServerOptions } from 'fastify' +import fastifySwagger from '@fastify/swagger' +import fastifySwaggerUi from '@fastify/swagger-ui' +import { readFileSync } from 'node:fs' +import { fileURLToPath } from 'url' +import { dirname, join } from 'node:path' +import { Config } from './config' +import { VtuberRecord, VtuberResponse, VtuberDataScrape } from './schemas.ts' +import scrapeVtuberData from './scrapeVtuberData.ts' +import { getPlaylistUrl } from './ytdlp.ts' +import { getRandomRoom } from './cb.ts' +import { getPackageVersion } from '@futureporn/utils' + + +type VtuberDataRequest = FastifyRequest<{ + Querystring: { url: string } +}> + + +const __dirname = dirname(fileURLToPath(import.meta.url)); +const swaggerDarkCss = readFileSync(join(__dirname, './css/SwaggerDark.css'), { encoding: 'utf-8' }) +const version = getPackageVersion(join(__dirname, '../package.json')) + + +async function fastifySetup(configs: Config) { + + const fastify = Fastify({ + logger: true + }) + + + await fastify.register(fastifySwagger, { + openapi: { + info: { + title: '@futureporn/scout', + description: 'Vtuber data acquisition API', + version + }, + } + }) + await fastify.register(fastifySwaggerUi, { + theme: { + title: '@fp/scout', + css: [ + { filename: 'SwaggerDark.css', content: swaggerDarkCss } + ] + }, + routePrefix: '/', + uiConfig: { + docExpansion: 'list', + deepLinking: true + }, + uiHooks: { + onRequest: function (request: FastifyRequest, reply: FastifyReply, next: any) { next() }, + preHandler: function (request: FastifyRequest, reply: FastifyReply, next: any) { next() } + }, + staticCSP: true, + transformStaticCSP: (header: any) => header, + transformSpecification: (swaggerObject: any, request: FastifyRequest, reply: FastifyReply) => { return swaggerObject }, + transformSpecificationClone: true +}) + +fastify.addSchema(VtuberResponse) +fastify.addSchema(VtuberRecord) +fastify.addSchema(VtuberDataScrape) + + + +fastify.get('/chaturbate/random-room', { + schema: { + response: { + '2xx': { + type: 'object' + } + }, + tags: ['chaturbate'] + } +}, async (req, reply) => { + const room = await getRandomRoom() + console.log(room) + reply.type('application/json').send(JSON.stringify(room)) +}) + + + +fastify.get('/ytdlp/playlist-url', { + schema: { + querystring: { + type: 'object', + properties: { + url: { + type: 'string' + } + } + }, + response: { + '2xx': { + error: { type: 'boolean' }, + message: { type: 'string' }, + data: { type: 'object', properties: { + url: { type: 'string' } + }} + } + }, + tags: ['yt-dlp'] + } +}, async (req: VtuberDataRequest, reply) => { + try { + const playlistUrl = await getPlaylistUrl(req.query.url) + console.log(`playlistUrl=${playlistUrl}`) + reply.type('application/json').send(JSON.stringify({ data: { url: playlistUrl } })) + } catch (e) { + reply.type('application/json').send(JSON.stringify({ data: null, error: e })) + } +}) + + + +fastify.get('/vtuber/data', { + schema: { + querystring: { + type: 'object', + properties: { + url: { + type: 'string', + description: 'URL of a vtuber profile on Chaturbate or Fansly. ex: https://chaturbate.com/projektmelody' + } + } + }, + response: { + '2xx': { $ref: 'VtuberDataScrape' } + }, + tags: [ + 'vtuber' + ] + } +}, async (req: VtuberDataRequest, reply) => { + console.log(`we received a request with url=${req.query.url}`) + const data = await scrapeVtuberData(req.query.url) + reply.type('application/json').send(data) +}) + + + +fastify.listen({ host: '0.0.0.0', port: configs.port }, function (err, address) { + console.log(`@futureporn/scout listening on ${address}`) + if (err) { + fastify.log.error(err) + process.exit(1) + } +}) + + +} + + +export default fastifySetup diff --git a/services/scout/src/index.ts b/services/scout/src/index.ts index 9a37836..479c565 100644 --- a/services/scout/src/index.ts +++ b/services/scout/src/index.ts @@ -2,4 +2,5 @@ import fastify from './fastify.js' import { configs } from './config.js' -fastify(configs) \ No newline at end of file +fastify(configs) + diff --git a/services/scout/src/schemas.ts b/services/scout/src/schemas.ts new file mode 100644 index 0000000..af03515 --- /dev/null +++ b/services/scout/src/schemas.ts @@ -0,0 +1,28 @@ +/** + * schemas.ts + * + * uses ts-json-schema-generator to generate json schema from typescript declarations. + * + */ +import tsj, { Config } from 'ts-json-schema-generator' +import { createRequire } from 'node:module' + + + +const require = createRequire(import.meta.url); +const typesIndex = require.resolve('@futureporn/types/src/index.ts'); +const typesTsConfig = require.resolve('@futureporn/types/tsconfig.json'); + +const config: Config = { + path: typesIndex, + tsconfig: typesTsConfig, + type: '*', + skipTypeCheck: true, + encodeRefs: true, + topRef: false, +} + +export const VtuberResponse = tsj.createGenerator(Object.assign(config, { schemaId: 'VtuberResponse' })).createSchema('VtuberResponse') +export const VtuberRecord = tsj.createGenerator(Object.assign(config, { schemaId: 'VtuberRecord' })).createSchema('VtuberRecord') +export const VtuberDataScrape = tsj.createGenerator(Object.assign(config, { schemaId: 'VtuberDataScrape' })).createSchema('VtuberDataScrape') +// export const VtuberDataScrape = tsj.createGenerator(Object.assign(config, { schemaId: 'VtuberDataScrape' })).createSchema('VtuberDataScrape') diff --git a/services/scout/src/scrapeVtuberData.ts b/services/scout/src/scrapeVtuberData.ts index adb9647..abb17fe 100644 --- a/services/scout/src/scrapeVtuberData.ts +++ b/services/scout/src/scrapeVtuberData.ts @@ -1,4 +1,4 @@ -import type { VtuberRecord } from "@futureporn/types" +import type { VtuberDataScrape } from "@futureporn/types" import { fetchHtml, getBroadcasterDisplayName, getInitialRoomDossier } from './cb.ts' import { getAccountData, usernameRegex } from "./fansly.ts" import { fpSlugify } from "@futureporn/utils" @@ -10,9 +10,9 @@ import { fpSlugify } from "@futureporn/utils" * * The purpose of this function is to retrieve enough data about a vtuber in order for us to create a database record about them. */ -export default async function scrapeVtuberData(url: string): Promise> { +export default async function scrapeVtuberData(url: string): Promise { - let display_name, chaturbate_id, slug, fansly_id + let display_name, chaturbate_id, slug, fansly_id, chaturbate, fansly if (url.match(/chaturbate/)) { console.log('url matches chaturbate') let html = await fetchHtml(url) @@ -22,6 +22,7 @@ export default async function scrapeVtuberData(url: string): Promise { +export async function getPlaylistUrl (roomUrl: string, proxy = false, retries = 0): Promise { console.log(`getPlaylistUrl roomUrl=${roomUrl}, proxy=${false}, retries=${retries}`) let args = ['-g', roomUrl] if (proxy) {