progress
ci / build (push) Has been cancelled Details

This commit is contained in:
CJ_Clippy 2024-07-10 14:11:18 -08:00
parent 358e484a12
commit 93b90c5586
93 changed files with 518 additions and 415 deletions

View File

@ -19,9 +19,6 @@
**/charts/**/charts **/charts/**/charts
**/.mocharc.json **/.mocharc.json
**/.env
**/node_modules
packages/strapi/.tmp/ packages/strapi/.tmp/
packages/strapi/.cache/ packages/strapi/.cache/
packages/strapi/.git/ packages/strapi/.git/

View File

@ -48,19 +48,6 @@ dotenv(fn='.env.development')
# ) # )
## this method results in the following error. Build Failed: Internal error occurred: failed calling webhook "webhook.cert-manager.io": failed to call webhook: Post "https://cert-manager-webhook.cert-manager.svc:443/validate?timeout=30s": service "cert-manager-webhook" not found
# helm_remote(
# 'cert-manager',
# repo_url='https://charts.jetstack.io',
# repo_name='cert-manager',
# namespace='cert-manager',
# version='1.15.1',
# set=[
# 'crds.enabled=true'
# ]
# )
helm_remote( helm_remote(
'traefik', 'traefik',
@ -125,8 +112,8 @@ k8s_yaml(helm(
docker_build( docker_build(
'fp/strapi', 'fp/strapi',
'.', '.',
only=['./packages/strapi', './packages/types'], dockerfile='./d.packages.dockerfile',
dockerfile='./d.strapi.dev.dockerfile', target='strapi',
live_update=[ live_update=[
sync('./packages/strapi', '/app') sync('./packages/strapi', '/app')
] ]
@ -135,7 +122,6 @@ docker_build(
docker_build( docker_build(
'fp/bot', 'fp/bot',
'.', '.',
only=['./pnpm-lock.yaml', './package.json', './packages/types', './packages/bot'],
dockerfile='./d.packages.dockerfile', dockerfile='./d.packages.dockerfile',
target='bot-dev', target='bot-dev',
live_update=[ live_update=[
@ -193,8 +179,7 @@ cmd_button('temporal-web:namespace',
docker_build( docker_build(
'fp/next', 'fp/next',
'.', '.',
# only=['./pnpm-lock.yaml', './package.json', './packages/next', './packages/types', './ca/letsencrypt-stg-root-x1.pem'], dockerfile='d.packages.dockerfile',
dockerfile='d.next.dockerfile',
target='next', target='next',
build_args={ build_args={
'NEXT_PUBLIC_STRAPI_URL': 'https://strapi.fp.sbtp.xyz' 'NEXT_PUBLIC_STRAPI_URL': 'https://strapi.fp.sbtp.xyz'
@ -208,7 +193,6 @@ docker_build(
docker_build( docker_build(
'fp/scout-manager', 'fp/scout-manager',
'.', '.',
only=['./pnpm-lock.yaml', './package.json', './packages/scout', './packages/types', './ca/letsencrypt-stg-root-x1.pem'],
dockerfile='d.packages.dockerfile', dockerfile='d.packages.dockerfile',
target='scout-manager', target='scout-manager',
live_update=[ live_update=[
@ -223,7 +207,6 @@ docker_build(
docker_build( docker_build(
'fp/boop', 'fp/boop',
'.', '.',
# only=['./pnpm-lock.yaml', './package.json', './packages/scout', './packages/types', './ca/letsencrypt-stg-root-x1.pem'],
dockerfile='d.packages.dockerfile', dockerfile='d.packages.dockerfile',
target='boop', target='boop',
live_update=[ live_update=[
@ -239,7 +222,6 @@ docker_build(
docker_build( docker_build(
'fp/scout-worker', 'fp/scout-worker',
'.', '.',
only=['./pnpm-lock.yaml', './package.json', './packages/scout', './packages/types', './ca/letsencrypt-stg-root-x1.pem'],
# ignore=['./packages/next'], # I wish I could use this ignore to ignore file changes in this dir, but that's not how it works # ignore=['./packages/next'], # I wish I could use this ignore to ignore file changes in this dir, but that's not how it works
dockerfile='d.packages.dockerfile', dockerfile='d.packages.dockerfile',
target='scout-worker', target='scout-worker',

View File

@ -1,45 +0,0 @@
## Important! Build context is the ROOT of the project.
## this keeps the door open for future possibility of shared code between pnpm workspace packages
FROM node:20-slim AS base
FROM base AS deps
ENV PNPM_HOME="/pnpm"
ENV PATH="$PNPM_HOME:$PATH"
RUN corepack enable
WORKDIR /app
FROM deps AS install
ARG NEXT_PUBLIC_SITE_URL=https://futureporn.net
ARG NEXT_PUBLIC_STRAPI_URL=https://portal.futureporn.net
ARG NEXT_PUBLIC_UPPY_COMPANION_URL=https://uppy.futureporn.net
ENV NEXT_PUBLIC_SITE_URL ${NEXT_PUBLIC_SITE_URL}
ENV NEXT_PUBLIC_STRAPI_URL ${NEXT_PUBLIC_STRAPI_URL}
ENV NEXT_PUBLIC_UPPY_COMPANION_URL ${NEXT_PUBLIC_UPPY_COMPANION_URL}
ENV NEXT_TELEMETRY_DISABLED 1
ENV NODE_EXTRA_CA_CERTS "/app/letsencrypt-stg-root-x1.pem"
COPY pnpm-lock.yaml ./
RUN pnpm fetch
COPY ./packages/next /app
RUN --mount=type=cache,id=pnpm-store,target=/pnpm/store pnpm install
FROM install AS dev
CMD ["pnpm", "run", "dev"]
FROM install AS build
RUN pnpm run build
FROM deps AS next
RUN apt-get update && apt-get install -y -qq --no-install-recommends dumb-init
COPY --chown=node:node --from=build /app/package.json /app/pnpm-lock.yaml ./
RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm install --prod --frozen-lockfile
COPY --chown=node:node --from=build /app/public ./public
COPY --chown=node:node --from=build /app/.next/standalone ./
COPY --chown=node:node --from=build /app/.next/static ./.next/static
ENV TZ=UTC
ENV NODE_ENV=production
ENV HOSTNAME="0.0.0.0"
CMD [ "dumb-init", "node", "server.js" ]

View File

@ -21,19 +21,37 @@ ENTRYPOINT ["pnpm"]
FROM base AS build FROM base AS build
ENV NODE_ENV=production ARG NEXT_PUBLIC_SITE_URL=https://futureporn.net
ARG NEXT_PUBLIC_STRAPI_URL=https://portal.futureporn.net
ARG NEXT_PUBLIC_UPPY_COMPANION_URL=https://uppy.futureporn.net
ENV NEXT_PUBLIC_SITE_URL ${NEXT_PUBLIC_SITE_URL}
ENV NEXT_PUBLIC_STRAPI_URL ${NEXT_PUBLIC_STRAPI_URL}
ENV NEXT_PUBLIC_UPPY_COMPANION_URL ${NEXT_PUBLIC_UPPY_COMPANION_URL}
ENV NEXT_TELEMETRY_DISABLED 1
COPY . /usr/src/app COPY . /usr/src/app
WORKDIR /usr/src/app WORKDIR /usr/src/app
RUN mkdir -p /prod/scout RUN mkdir -p /prod
RUN pnpm fetch RUN pnpm fetch
RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm install --frozen-lockfile --offline ## strapi needs node-gyp
RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm install -g node-gyp
RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm install --frozen-lockfile
ENV NODE_ENV production
RUN pnpm --recursive build RUN pnpm --recursive build
RUN pnpm deploy --filter=boop /prod/boop ## we use pnpm deploy to create bundled, isolated node packages for docker containers. See https://pnpm.io/cli/deploy
# RUN pnpm deploy --filter=scout --prod /prod/scout ## we can deploy most packages this way, but with some projects such as next it's not necessary as next's build step packages all dependencies.
# RUN pnpm deploy --filter=bot --prod /prod/bot RUN pnpm deploy --filter=boop --prod /prod/boop
# RUN pnpm deploy --filter=temporal-worker --prod /prod/temporal-worker RUN pnpm deploy --filter=scout --prod /prod/scout
# RUN pnpm deploy --filter=next /prod/next
# RUN pnpm deploy --filter=next /prod/next-dev
FROM base AS strapi
COPY --from=build /usr/src/app/packages/strapi .
CMD ["run", "develop"]
# NODE_ENV=development is important for the fp/next build.
## @todo remove the --filter
FROM base AS boop FROM base AS boop
COPY --from=build /prod/boop /app COPY --from=build /prod/boop /app
@ -60,19 +78,25 @@ CMD ["start"]
# RUN pnpm fetch # RUN pnpm fetch
# RUN --mount=type=cache,id=pnpm-store,target=/pnpm/store pnpm install # RUN --mount=type=cache,id=pnpm-store,target=/pnpm/store pnpm install
FROM base AS next-build # FROM base AS next-build
COPY --from=build /prod/next /app # COPY --from=build /prod/next /app
# RUN --mount=type=cache,id=pnpm-store,target=/pnpm/store pnpm install # # RUN --mount=type=cache,id=pnpm-store,target=/pnpm/store pnpm install
# RUN ls -la # # RUN ls -la
RUN pnpm run build # RUN pnpm run build
FROM base as next FROM base as next
COPY --from=next-build /app /app RUN apt-get update && apt-get install -y -qq --no-install-recommends dumb-init
COPY --chown=node:node --from=build /usr/src/app/packages/next/public ./public
COPY --chown=node:node --from=build /usr/src/app/packages/next/.next/standalone ./
COPY --chown=node:node --from=build /usr/src/app/packages/next/.next/static ./.next/static
ENV TZ=UTC ENV TZ=UTC
ENV NODE_ENV=production ENV NODE_ENV=production
ENV HOSTNAME="0.0.0.0" ENV HOSTNAME="0.0.0.0"
ENTRYPOINT [ "dumb-init", "node", "server.js" ] ENTRYPOINT [ "dumb-init", "node", "server.js" ]
# FROM base AS next-pre # FROM base AS next-pre
# COPY --from=build /prod/next /app # COPY --from=build /prod/next /app
# ENV NODE_EXTRA_CA_CERTS "/app/letsencrypt-stg-root-x1.pem" # ENV NODE_EXTRA_CA_CERTS "/app/letsencrypt-stg-root-x1.pem"

View File

@ -1,10 +0,0 @@
FROM node:18 AS base
WORKDIR /usr/src/app/
RUN corepack enable
ENV PNPM_HOME="/pnpm"
ENV PATH="$PNPM_HOME:$PATH"
COPY ./packages/strapi/package.json ./packages/strapi/pnpm-lock.yaml .
RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm install --frozen-lockfile
COPY ./packages/strapi/ .
RUN ["pnpm", "run", "build"]
CMD ["pnpm", "run", "develop"]

View File

@ -15,7 +15,7 @@
"noImplicitOverride": true, "noImplicitOverride": true,
// Transpile our TypeScript code to JavaScript // Transpile our TypeScript code to JavaScript
"module": "NodeNext", "module": "NodeNext",
"outDir": "lib", "outDir": "dist",
"lib": [ "lib": [
"es2022" "es2022"
] ]

View File

@ -6,7 +6,8 @@
"main": "index.js", "main": "index.js",
"scripts": { "scripts": {
"test": "echo \"Error: no test specified\" && exit 1", "test": "echo \"Error: no test specified\" && exit 1",
"start": "node index.js" "start": "node index.js",
"build": "tsc --build"
}, },
"dependencies": { "dependencies": {
"taco": "workspace:*", "taco": "workspace:*",
@ -14,5 +15,8 @@
}, },
"keywords": [], "keywords": [],
"author": "", "author": "",
"license": "ISC" "license": "Unlicense",
"devDependencies": {
"typescript": "^5.5.3"
}
} }

View File

@ -14,3 +14,18 @@ importers:
types: types:
specifier: workspace:* specifier: workspace:*
version: link:../types version: link:../types
devDependencies:
typescript:
specifier: ^5.5.3
version: 5.5.3
packages:
typescript@5.5.3:
resolution: {integrity: sha512-/hreyEujaB0w76zKo6717l3L0o/qEUtRgdvUBvlkhoWeOVMjMuHNHk0BRBzikzuGDqNmPQbg5ifMEqsHLiIUcQ==}
engines: {node: '>=14.17'}
hasBin: true
snapshots:
typescript@5.5.3: {}

View File

@ -0,0 +1,31 @@
{
"compilerOptions": {
// Base Options recommended for all projects
"esModuleInterop": true,
"skipLibCheck": true,
"target": "es2022",
"allowJs": true,
"resolveJsonModule": true,
"moduleDetection": "force",
"isolatedModules": true,
// Enable strict type checking so you can catch bugs early
"strict": true,
"noUncheckedIndexedAccess": true,
"noImplicitOverride": true,
// Transpile our TypeScript code to JavaScript
"module": "NodeNext",
"outDir": "dist",
"lib": [
"es2022",
"dom"
]
},
// Include the necessary files for your project
"include": [
"**/*.ts",
"**/*.tsx"
],
"exclude": [
"node_modules"
]
}

View File

@ -14,7 +14,7 @@
"noImplicitOverride": true, "noImplicitOverride": true,
// Transpile our TypeScript code to JavaScript // Transpile our TypeScript code to JavaScript
"module": "NodeNext", "module": "NodeNext",
"outDir": "lib", "outDir": "dist",
"lib": [ "lib": [
"es2022" "es2022"
] ]

View File

@ -1,2 +1,3 @@
# next # next
HTML5 frontend for https://futureporn.net

View File

@ -1,7 +1,7 @@
import { getVodTitle } from '@/components/vod-page'; import { getVodTitle } from '@/app/components/vod-page';
import { getUrl, getAllVods } from "@/lib/vods" import { getUrl, getAllVods } from "@/app/lib/vods"
import { IVod } from "@/lib/vods" import { IVod } from "@/app/lib/vods"
/* /*

View File

@ -1,6 +1,6 @@
import StreamPage from '@/components/stream-page'; import StreamPage from '@/app/components/stream-page';
import { getStreamByCuid } from '@/lib/streams'; import { getStreamByCuid } from '@/app/lib/streams';
interface IPageParams { interface IPageParams {

View File

@ -1,8 +1,8 @@
import Pager from "@/components/pager"; import Pager from "@/app/components/pager";
import StreamsList from "@/components/streams-list"; import StreamsList from "@/app/components/streams-list";
import StreamsTable from '@/components/streams-table'; import StreamsTable from '@/app/components/streams-table';
import { getAllStreams, getStreamsForVtuber } from "@/lib/streams"; import { getAllStreams, getStreamsForVtuber } from "@/app/lib/streams";
// import { getAllVtubers } from "@/lib/vtubers"; // import { getAllVtubers } from "@/app/lib/vtubers";
import { notFound } from "next/navigation"; import { notFound } from "next/navigation";

View File

@ -1,6 +1,6 @@
import { getAllStreamsForVtuber, getStreamCountForVtuber } from "@/lib/streams"; import { getAllStreamsForVtuber, getStreamCountForVtuber } from "@/app/lib/streams";
import { getVodsForVtuber } from "@/lib/vods"; import { getVodsForVtuber } from "@/app/lib/vods";
import { IVtuber } from "@/lib/vtubers"; import { IVtuber } from "types";
export interface IArchiveProgressProps { export interface IArchiveProgressProps {
vtuber: IVtuber; vtuber: IVtuber;

View File

@ -7,7 +7,7 @@ import { faPatreon } from '@fortawesome/free-brands-svg-icons';
import { useLocalStorageValue } from '@react-hookz/web'; import { useLocalStorageValue } from '@react-hookz/web';
import { faRightFromBracket } from '@fortawesome/free-solid-svg-icons'; import { faRightFromBracket } from '@fortawesome/free-solid-svg-icons';
import Skeleton from 'react-loading-skeleton'; import Skeleton from 'react-loading-skeleton';
import { strapiUrl } from '@/lib/constants'; import { strapiUrl } from '@/app/lib/constants';
// import NextAuth from 'next-auth'; // this is (pipedream) wishlist // import NextAuth from 'next-auth'; // this is (pipedream) wishlist
// import Providers from 'next-auth/providers'; // import Providers from 'next-auth/providers';

View File

@ -1,6 +1,6 @@
import { getCampaign } from "@/lib/patreon"; import { getCampaign } from "@/app/lib/patreon";
import { getGoals, IGoals } from '@/lib/pm' import { getGoals, IGoals } from '@/app/lib/pm'
import Image from "next/legacy/image"; import Image from "next/legacy/image";
import React from 'react'; import React from 'react';
import Link from 'next/link' import Link from 'next/link'

View File

@ -5,7 +5,7 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faExternalLinkAlt } from "@fortawesome/free-solid-svg-icons"; import { faExternalLinkAlt } from "@fortawesome/free-solid-svg-icons";
import { faUser, faUpload } from "@fortawesome/free-solid-svg-icons"; import { faUser, faUpload } from "@fortawesome/free-solid-svg-icons";
import Link from 'next/link' import Link from 'next/link'
import { LoginButton, useAuth } from '@/components/auth' import { LoginButton, useAuth } from '@/app/components/auth'
export default function Navbar() { export default function Navbar() {

View File

@ -1,4 +1,4 @@
import { IStream } from "@/lib/streams"; import { IStream } from "types";
import Link from "next/link" import Link from "next/link"
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faCalendar } from "@fortawesome/free-solid-svg-icons"; import { faCalendar } from "@fortawesome/free-solid-svg-icons";

View File

@ -1,8 +1,8 @@
'use client'; 'use client';
import { IStream } from "@/lib/streams"; import { IStream } from "types";
// import NotFound from "app/streams/[cuid]/not-found"; // import NotFound from "app/streams/[cuid]/not-found";
import { IVod } from "@/lib/vods"; import { IVod } from "@/app/lib/vods";
import Link from "next/link"; import Link from "next/link";
import Image from "next/legacy/image"; import Image from "next/legacy/image";
import { LocalizedDate } from "./localized-date"; import { LocalizedDate } from "./localized-date";
@ -37,7 +37,7 @@ function determineStatus(stream: IStream): Status {
if (stream.attributes.vods.data.length < 1) { if (stream.attributes.vods.data.length < 1) {
return 'missing' return 'missing'
} else { } else {
if (stream.attributes.vods.data.some(vod => !hasNote(vod))) { if (stream.attributes.vods.data.some((vod: IVod) => !hasNote(vod))) {
return 'good'; return 'good';
} else { } else {
return 'issue'; return 'issue';

View File

@ -1,9 +1,9 @@
import { IStream } from "@/lib/streams"; import { IStream } from "types";
import NotFound from "app/vt/[slug]/not-found"; import NotFound from "@/app/vt/[slug]/not-found";
import { LocalizedDate } from "./localized-date"; import { LocalizedDate } from "./localized-date";
import Link from "next/link"; import Link from "next/link";
import ChaturbateIcon from "@/components/icons/chaturbate"; import ChaturbateIcon from "@/app/components/icons/chaturbate";
import FanslyIcon from "@/components/icons/fansly"; import FanslyIcon from "@/app/components/icons/fansly";
import Image from "next/legacy/image"; import Image from "next/legacy/image";
export interface IStreamProps { export interface IStreamProps {

View File

@ -1,9 +1,10 @@
import React from 'react' import React from 'react'
import Link from 'next/link'; import Link from 'next/link';
import { IVtuber } from '@/lib/vtubers'; import { IVtuber } from 'types';
import { notFound } from 'next/navigation'; import { notFound } from 'next/navigation';
import { IStream, getAllStreams } from '@/lib/streams'; import { getAllStreams } from '@/app/lib/streams';
import { StreamSummary } from '@/components/stream'; import { IStream } from 'types';
import { StreamSummary } from '@/app/components/stream';
interface IStreamsListProps { interface IStreamsListProps {
vtubers: IVtuber[]; vtubers: IVtuber[];

View File

@ -19,7 +19,8 @@ import {
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { faSpinner } from '@fortawesome/free-solid-svg-icons' import { faSpinner } from '@fortawesome/free-solid-svg-icons'
import { fetchStreamData, IStream } from '@/lib/streams' import { fetchStreamData } from '@/app/lib/streams'
import { IStream } from 'types'
const queryClient = new QueryClient() const queryClient = new QueryClient()

View File

@ -1,13 +1,13 @@
'use client'; 'use client';
import { ITagVodRelation, ITagVodRelationsResponse } from "@/lib/tag-vod-relations" import { ITagVodRelation, ITagVodRelationsResponse } from "@/app/lib/tag-vod-relations"
import { isWithinInterval, subHours } from "date-fns"; import { isWithinInterval, subHours } from "date-fns";
import { faTrash } from "@fortawesome/free-solid-svg-icons"; import { faTrash } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { AuthContext, IUseAuth } from "./auth"; import { AuthContext, IUseAuth } from "./auth";
import { useContext, useEffect, useState } from "react"; import { useContext, useEffect, useState } from "react";
import { useRouter } from 'next/navigation'; import { useRouter } from 'next/navigation';
import { strapiUrl } from "@/lib/constants"; import { strapiUrl } from "@/app/lib/constants";
export interface ITagParams { export interface ITagParams {
tvr: ITagVodRelation; tvr: ITagVodRelation;

View File

@ -1,18 +1,18 @@
'use client'; 'use client';
import { useState, useCallback, useEffect, useContext } from 'react'; import { useState, useCallback, useEffect, useContext } from 'react';
import { IVod } from '@/lib/vods'; import { IVod } from '@/app/lib/vods';
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faPlus, faX, faTags } from "@fortawesome/free-solid-svg-icons"; import { faPlus, faX, faTags } from "@fortawesome/free-solid-svg-icons";
import { formatTimestamp } from '@/lib/dates'; import { formatTimestamp } from '@/app/lib/dates';
import { readOrCreateTagVodRelation } from '@/lib/tag-vod-relations'; import { readOrCreateTagVodRelation } from '@/app/lib/tag-vod-relations';
import { readOrCreateTag } from '@/lib/tags'; import { readOrCreateTag } from '@/app/lib/tags';
import { useAuth } from './auth'; import { useAuth } from './auth';
import { debounce } from 'lodash'; import { debounce } from 'lodash';
import { strapiUrl } from '@/lib/constants'; import { strapiUrl } from '@/app/lib/constants';
import { VideoContext } from './video-context'; import { VideoContext } from './video-context';
import { useForm } from "react-hook-form"; import { useForm } from "react-hook-form";
import { ITimestamp, createTimestamp } from '@/lib/timestamps'; import { ITimestamp, createTimestamp } from '@/app/lib/timestamps';
import { useRouter } from 'next/navigation'; import { useRouter } from 'next/navigation';
import styles from '@/assets/styles/fp.module.css' import styles from '@/assets/styles/fp.module.css'
import qs from 'qs'; import qs from 'qs';

View File

@ -1,13 +1,13 @@
import React, { useContext, useState, useEffect } from "react"; import React, { useContext, useState, useEffect } from "react";
import { IVod } from "@/lib/vods"; import { IVod } from "@/app/lib/vods";
import { import {
ITimestamp, ITimestamp,
deleteTimestamp deleteTimestamp
} from "@/lib/timestamps"; } from "@/app/lib/timestamps";
import { import {
formatTimestamp, formatTimestamp,
formatUrlTimestamp, formatUrlTimestamp,
} from "@/lib/dates"; } from "@/app/lib/dates";
import Link from 'next/link'; import Link from 'next/link';
import { faClock, faLink, faTrash } from "@fortawesome/free-solid-svg-icons"; import { faClock, faLink, faTrash } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";

View File

@ -1,6 +1,6 @@
import React from 'react'; import React from 'react';
import { IToy, IToysResponse } from '@/lib/toys'; import { IToy, IToysResponse } from '@/app/lib/toys';
import { IVtuber } from '@/lib/vtubers'; import { IVtuber } from 'types';
import Link from 'next/link'; import Link from 'next/link';
import Image from "next/legacy/image"; import Image from "next/legacy/image";

View File

@ -1,28 +1,26 @@
'use client'; 'use client';
import { IVtuber } from "@/lib/vtubers";
import { IStream } from "@/lib/streams"; import { IVtuber } from "types";
import { useSearchParams } from 'next/navigation'; import { useSearchParams } from 'next/navigation';
import React, { useContext, useState, useEffect } from 'react'; import React from 'react';
import { UppyContext } from 'app/uppy';
import AwsS3 from '@uppy/aws-s3'; import AwsS3 from '@uppy/aws-s3';
import RemoteSources from '@uppy/remote-sources'; import RemoteSources from '@uppy/remote-sources';
import { LoginButton, useAuth } from '@/components/auth'; import { LoginButton, useAuth } from '@/app/components/auth';
import { Dashboard } from '@uppy/react'; import { Dashboard } from '@uppy/react';
import styles from '@/assets/styles/fp.module.css' import styles from '@/assets/styles/fp.module.css'
import { projektMelodyEpoch } from "@/lib/constants"; import { projektMelodyEpoch } from "@/app/lib/constants";
import add from "date-fns/add"; import add from "date-fns/add";
import sub from "date-fns/sub"; import sub from "date-fns/sub";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faCheckCircle, faEraser, faPaperPlane, faSpinner, faX, faXmark } from "@fortawesome/free-solid-svg-icons"; import { faEraser, faPaperPlane, faSpinner, faX, faXmark } from "@fortawesome/free-solid-svg-icons";
import { useForm, useFieldArray, ValidationMode } from 'react-hook-form'; import { useForm, ValidationMode } from 'react-hook-form';
import { yupResolver } from '@hookform/resolvers/yup'; import { yupResolver } from '@hookform/resolvers/yup';
import * as Yup from 'yup'; import * as Yup from 'yup';
import qs from 'qs';
import { toast } from "react-toastify"; import { toast } from "react-toastify";
import { ErrorMessage } from "@hookform/error-message" import { ErrorMessage } from "@hookform/error-message"
import Uppy from '@uppy/core'; import Uppy from '@uppy/core';
import { companionUrl } from '@/lib/constants'; import { companionUrl } from '@/app/lib/constants';

View File

@ -1,7 +1,6 @@
import VideoApiElement from "@mux/mux-player/dist/types/video-api"; import { createContext } from "react";
import { MutableRefObject, createContext, useState } from "react"; import { ITagVodRelation } from "@/app/lib/tag-vod-relations";
import { ITagVodRelation } from "@/lib/tag-vod-relations";
export interface IVideoContextValue { export interface IVideoContextValue {
timeStamp: number; timeStamp: number;

View File

@ -1,17 +1,17 @@
'use client'; 'use client';
import { IVod } from "@/lib/vods"; import { IVod } from "@/app/lib/vods";
import { useRef, useState, useEffect, useCallback } from "react"; import { useRef, useState, useEffect, useCallback } from "react";
import { VideoPlayer } from "./video-player"; import { VideoPlayer } from "./video-player";
import { Tagger } from './tagger'; import { Tagger } from './tagger';
import { ITimestamp, getTimestampsForVod } from "@/lib/timestamps"; import { ITimestamp, getTimestampsForVod } from "@/app/lib/timestamps";
import { TimestampsList } from "./timestamps-list"; import { TimestampsList } from "./timestamps-list";
import { ITagVodRelation } from "@/lib/tag-vod-relations"; import { ITagVodRelation } from "@/app/lib/tag-vod-relations";
import { VideoContext } from "./video-context"; import { VideoContext } from "./video-context";
import { getVodTitle } from "./vod-page"; import { getVodTitle } from "./vod-page";
import { useSearchParams } from 'next/navigation'; import { useSearchParams } from 'next/navigation';
import VideoApiElement from "@mux/mux-player/dist/types/video-api"; import type VideoApiElement from "@mux/mux-player";
import { parseUrlTimestamp } from "@/lib/dates"; import { parseUrlTimestamp } from "@/app/lib/dates";
import { faTags, faNoteSticky, faClock } from "@fortawesome/free-solid-svg-icons"; import { faTags, faNoteSticky, faClock } from "@fortawesome/free-solid-svg-icons";
import { Tag } from './tag'; import { Tag } from './tag';
import VodNav from './vod-nav'; import VodNav from './vod-nav';

View File

@ -1,16 +1,16 @@
'use client'; 'use client';
import { useEffect, useState, forwardRef, useContext, Ref } from 'react'; import { useEffect, useState, forwardRef, useContext, Ref } from 'react';
import { IVod } from '@/lib/vods'; import { IVod } from '@/app/lib/vods';
import { useAuth } from '@/components/auth'; import { useAuth } from '@/app/components/auth';
import { getVodTitle } from './vod-page'; import { getVodTitle } from './vod-page';
import { VideoSourceSelector } from '@/components/video-source-selector' import { VideoSourceSelector } from '@/app/components/video-source-selector'
import { buildIpfsUrl } from '@/lib/ipfs'; import { buildIpfsUrl } from '@/app/lib/ipfs';
import { strapiUrl } from '@/lib/constants'; import { strapiUrl } from '@/app/lib/constants';
import MuxPlayer from '@mux/mux-player-react/lazy'; import MuxPlayer from '@mux/mux-player-react/lazy';
import { VideoContext } from './video-context'; import { VideoContext } from './video-context';
import MuxPlayerElement from '@mux/mux-player'; import MuxPlayerElement from '@mux/mux-player';
import VideoApiElement from "@mux/mux-player/dist/types/video-api"; import type VideoApiElement from "@mux/mux-player";
interface IPlayerProps { interface IPlayerProps {
vod: IVod; vod: IVod;

View File

@ -1,8 +1,8 @@
import Link from "next/link"; import Link from "next/link";
import { getSafeDate } from '@/lib/dates'; import { getSafeDate } from '@/app/lib/dates';
import { IVtuber } from '@/lib/vtubers'; import { IVtuber } from 'types';
import Image from "next/legacy/image" import Image from "next/legacy/image"
import { LocalizedDate } from '@/components/localized-date' import { LocalizedDate } from '@/app/components/localized-date'
interface IVodCardProps { interface IVodCardProps {
id: number; id: number;

View File

@ -5,10 +5,10 @@ import { faXTwitter } from '@fortawesome/free-brands-svg-icons';
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import Image from "next/legacy/image"; import Image from "next/legacy/image";
import Link from 'next/link'; import Link from 'next/link';
import { IVod } from '@/lib/vods'; import { IVod } from '@/app/lib/vods';
import { buildIpfsUrl } from '@/lib/ipfs'; import { buildIpfsUrl } from '@/app/lib/ipfs';
import { getSafeDate } from "@/lib/dates"; import { getSafeDate } from "@/app/lib/dates";
import { StreamButton } from '@/components/stream-button'; import { StreamButton } from '@/app/components/stream-button';
import VtuberButton from "./vtuber-button"; import VtuberButton from "./vtuber-button";
import { LocalizedDate } from "./localized-date"; import { LocalizedDate } from "./localized-date";

View File

@ -1,5 +1,5 @@
import { getUrl, getNextVod, getPreviousVod, getLocalizedDate } from '@/lib/vods'; import { getUrl, getNextVod, getPreviousVod, getLocalizedDate } from '@/app/lib/vods';
import { IVod } from '@/lib/vods'; import { IVod } from '@/app/lib/vods';
import Link from 'next/link'; import Link from 'next/link';
import { VideoInteractive } from './video-interactive'; import { VideoInteractive } from './video-interactive';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';

View File

@ -1,8 +1,8 @@
import React from 'react' import React from 'react'
import Link from 'next/link'; import Link from 'next/link';
import VodCard from './vod-card'; import VodCard from './vod-card';
import { IVtuber, IVtuberResponse } from '@/lib/vtubers'; import { IVtuber } from 'types';
import { IVodsResponse, IVod } from '@/lib/vods'; import { IVod } from '@/app/lib/vods';
import { getVodTitle } from './vod-page'; import { getVodTitle } from './vod-page';
import { notFound } from 'next/navigation'; import { notFound } from 'next/navigation';

View File

@ -1,8 +1,8 @@
import Link from "next/link"; import Link from "next/link";
import type { IVtuber } from '@/lib/vtubers'; import type { IVtuber } from 'types';
import { getVodsForVtuber } from "@/lib/vods"; import { getVodsForVtuber } from "@/app/lib/vods";
import Image from "next/legacy/image" import Image from "next/legacy/image"
import NotFound from "app/vt/[slug]/not-found"; import NotFound from "@/app/vt/[slug]/not-found";
import ArchiveProgress from "./archive-progress"; import ArchiveProgress from "./archive-progress";
export default async function VTuberCard(vtuber: IVtuber) { export default async function VTuberCard(vtuber: IVtuber) {

View File

@ -3,9 +3,9 @@
import { useSearchParams, useRouter } from 'next/navigation' import { useSearchParams, useRouter } from 'next/navigation'
import Link from 'next/link' import Link from 'next/link'
import { useEffect, useState } from 'react' import { useEffect, useState } from 'react'
import { strapiUrl } from '@/lib/constants' import { strapiUrl } from '@/app/lib/constants'
import { useAuth, IAuthData, IUser, IJWT } from '@/components/auth' import { useAuth, IAuthData, IUser, IJWT } from '@/app/components/auth'
import { DangerNotification } from '@/components/notifications' import { DangerNotification } from '@/app/components/notifications'
export type AccessToken = string | null; export type AccessToken = string | null;

View File

@ -3,8 +3,8 @@ import { getVtuberBySlug } from '../lib/vtubers'
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faExternalLinkAlt } from "@fortawesome/free-solid-svg-icons"; import { faExternalLinkAlt } from "@fortawesome/free-solid-svg-icons";
import { faLink } from '@fortawesome/free-solid-svg-icons'; import { faLink } from '@fortawesome/free-solid-svg-icons';
import { projektMelodyEpoch } from '@/lib/constants'; import { projektMelodyEpoch } from '@/app/lib/constants';
import LinkableHeading from '@/components/linkable-heading'; import LinkableHeading from '@/app/components/linkable-heading';
export default async function Page() { export default async function Page() {
return ( return (

View File

@ -1,4 +1,4 @@
import { generateFeeds } from "@/lib/rss" import { generateFeeds } from "@/app/lib/rss"
export async function GET() { export async function GET() {
const feeds = await generateFeeds() const feeds = await generateFeeds()

View File

@ -1,4 +1,4 @@
import { generateFeeds } from "@/lib/rss" import { generateFeeds } from "@/app/lib/rss"
export async function GET() { export async function GET() {
const { atom1 } = await generateFeeds() const { atom1 } = await generateFeeds()

View File

@ -1,4 +1,4 @@
import { generateFeeds } from "@/lib/rss" import { generateFeeds } from "@/app/lib/rss"
export async function GET() { export async function GET() {
const { rss2 } = await generateFeeds() const { rss2 } = await generateFeeds()

View File

@ -1,5 +1,5 @@
import { getGoals } from "@/lib/pm"; import { getGoals } from "@/app/lib/pm";
import { getCampaign } from "@/lib/patreon"; import { getCampaign } from "@/app/lib/patreon";
interface IFundingStatusBadgeProps { interface IFundingStatusBadgeProps {
completedPercentage: number; completedPercentage: number;

View File

@ -1,6 +1,6 @@
import VodsList from '@/components/vods-list'; import VodsList from '@/app/components/vods-list';
import { getVods } from '@/lib/vods'; import { getVods } from '@/app/lib/vods';
import Pager from '@/components/pager'; import Pager from '@/app/components/pager';
interface IPageParams { interface IPageParams {
params: { params: {

View File

@ -1,8 +1,8 @@
import VodsList from '@/components/vods-list'; import VodsList from '@/app/components/vods-list';
import { IVodsResponse } from '@/lib/vods'; import { IVodsResponse } from '@/app/lib/vods';
import Pager from '@/components/pager'; import Pager from '@/app/components/pager';
import { getVods } from '@/lib/vods'; import { getVods } from '@/app/lib/vods';
interface IPageParams { interface IPageParams {

View File

@ -1,5 +1,5 @@
import { strapiUrl, patreonVideoAccessBenefitId, giteaUrl } from './constants' import { strapiUrl, patreonVideoAccessBenefitId, giteaUrl } from './constants'
import { IAuthData } from '@/components/auth'; import { IAuthData } from '@/app/components/auth';
export interface IPatron { export interface IPatron {
username: string; username: string;

View File

@ -1,7 +1,7 @@
import { authorName, authorEmail, siteUrl, title, description, siteImage, favicon, authorLink } from './constants' import { authorName, authorEmail, siteUrl, title, description, siteImage, favicon, authorLink } from './constants'
import { Feed } from "feed"; import { Feed } from "feed";
import { getVods, getUrl, IVod } from '@/lib/vods' import { getVods, getUrl, IVod } from '@/app/lib/vods'
import { ITagVodRelation } from '@/lib/tag-vod-relations'; import { ITagVodRelation } from '@/app/lib/tag-vod-relations';
export async function generateFeeds() { export async function generateFeeds() {
const feedOptions = { const feedOptions = {

View File

@ -2,8 +2,8 @@
import { siteUrl, strapiUrl } from './constants'; import { siteUrl, strapiUrl } from './constants';
import { getSafeDate } from './dates'; import { getSafeDate } from './dates';
import qs from 'qs'; import qs from 'qs';
import { IStream } from 'types';
import { IStreamsResponse } from 'types';

View File

@ -10,7 +10,7 @@ import qs from 'qs';
import { strapiUrl } from './constants' import { strapiUrl } from './constants'
import { ITagResponse, IToyTagResponse } from './tags'; import { ITagResponse, IToyTagResponse } from './tags';
import { IVod, IVodResponse } from './vods'; import { IVod, IVodResponse } from './vods';
import { IAuthData } from '@/components/auth'; import { IAuthData } from '@/app/components/auth';
import { IMeta } from 'types'; import { IMeta } from 'types';
export interface ITagVodRelation { export interface ITagVodRelation {

View File

@ -3,7 +3,7 @@ import { fetchPaginatedData } from './fetchers';
import { IVod } from './vods'; import { IVod } from './vods';
import slugify from 'slugify'; import slugify from 'slugify';
import { IToy } from './toys'; import { IToy } from './toys';
import { IAuthData } from '@/components/auth'; import { IAuthData } from '@/app/components/auth';
import qs from 'qs'; import qs from 'qs';
import { IMeta } from 'types'; import { IMeta } from 'types';

View File

@ -2,7 +2,7 @@
import qs from 'qs'; import qs from 'qs';
import { strapiUrl } from './constants' import { strapiUrl } from './constants'
import { IAuthData } from '@/components/auth'; import { IAuthData } from '@/app/components/auth';
import { ITagsResponse, ITag, ITagResponse } from './tags'; import { ITagsResponse, ITag, ITagResponse } from './tags';
import { IMeta } from 'types'; import { IMeta } from 'types';

View File

@ -1,5 +1,5 @@
import { ITag, ITagResponse, ITagsResponse } from '@/lib/tags' import { ITag, ITagResponse, ITagsResponse } from '@/app/lib/tags'
import { IMeta } from 'types'; import { IMeta } from 'types';

View File

@ -1,4 +1,4 @@
import { IVtuberResponse } from "./vtubers"; import { IVtuberResponse } from "types";
import { IMeta } from "types"; import { IMeta } from "types";
export interface ITweet { export interface ITweet {

View File

@ -1,13 +1,12 @@
import { strapiUrl, siteUrl } from './constants'; import { strapiUrl, siteUrl } from './constants';
import { getDateFromSafeDate, getSafeDate } from './dates'; import { getDateFromSafeDate, getSafeDate } from './dates';
import { IVtuber, IVtuberResponse } from './vtubers'; import { IVtuber, IVtuberResponse, IStream, IStreamResponse } from 'types';
import { IStream, IStreamResponse } from './streams';
import qs from 'qs'; import qs from 'qs';
import { ITagVodRelationsResponse } from './tag-vod-relations'; import { ITagVodRelationsResponse } from './tag-vod-relations';
import { ITimestampsResponse } from './timestamps'; import { ITimestampsResponse } from './timestamps';
import { IMeta, IMuxAsset, IMuxAssetResponse } from 'types'; import { IMeta, IMuxAsset, IMuxAssetResponse } from 'types';
import { IB2File, IB2FileResponse } from '@/lib/b2File'; import { IB2File, IB2FileResponse } from '@/app/lib/b2File';
import fetchAPI from './fetch-api'; import fetchAPI from './fetch-api';
import { IUserResponse } from './users'; import { IUserResponse } from './users';

View File

@ -3,7 +3,7 @@
import { IVod } from './vods' import { IVod } from './vods'
import { strapiUrl, siteUrl } from './constants'; import { strapiUrl, siteUrl } from './constants';
import qs from 'qs'; import qs from 'qs';
import { IMeta } from 'types'; import { IMeta, IVtuber, IVtubersResponse } from 'types';
const fetchVtubersOptions = { const fetchVtubersOptions = {

View File

@ -1,24 +1,25 @@
import FundingGoal from "@/components/funding-goal"; import FundingGoal from "@/app/components/funding-goal";
import VodCard from "@/components/vod-card"; import VodCard from "@/app/components/vod-card";
import { getVodTitle } from "@/components/vod-page"; import { getVodTitle } from "@/app/components/vod-page";
import { getVods } from '@/lib/vods'; import { getVods } from '@/app/lib/vods';
import { IVod } from "@/lib/vods"; import { IVod } from "@/app/lib/vods";
import { getVtubers, IVtuber } from "./lib/vtubers"; import { IVtuber } from 'types';
import { getVtubers } from "./lib/vtubers";
import VTuberCard from "./components/vtuber-card"; import VTuberCard from "./components/vtuber-card";
import Link from 'next/link'; import Link from 'next/link';
import { notFound } from "next/navigation"; import { notFound } from "next/navigation";
export default async function Page() { export default async function Page() {
const vods = await getVods(1, 9, true); const vods = await getVods(1, 9, true);
console.log('vods as follows') // console.log('vods as follows')
console.log(JSON.stringify(vods, null, 2)) // console.log(JSON.stringify(vods, null, 2))
const vtubers = await getVtubers(); const vtubers = await getVtubers();
if (!vtubers) notFound(); if (!vtubers) notFound();
console.log(`vtubers as follows`) // console.log(`vtubers as follows`)
console.log(JSON.stringify(vtubers, null, 2)) // console.log(JSON.stringify(vtubers, null, 2))

View File

@ -1,7 +1,7 @@
import { getVodsForTag, IVod } from '@/lib/vods' import { getVodsForTag, IVod } from '@/app/lib/vods'
import VodCard from '@/components/vod-card' import VodCard from '@/app/components/vod-card'
import Link from 'next/link' import Link from 'next/link'
import { getVodTitle } from '@/components/vod-page' import { getVodTitle } from '@/app/components/vod-page'
import { notFound } from 'next/navigation' import { notFound } from 'next/navigation'
export default async function Page({ params }: { params: { slug: string }}) { export default async function Page({ params }: { params: { slug: string }}) {

View File

@ -1,11 +1,11 @@
import { getAllVtubers } from '@/lib/vtubers'; import { getAllVtubers } from '@/app/lib/vtubers';
import UploadForm from '@/components/upload-form'; import UploadForm from '@/app/components/upload-form';
import { Suspense } from 'react'; import { Suspense } from 'react';
import '@uppy/core/dist/style.min.css'; import '@uppy/core/dist/style.min.css';
import '@uppy/dashboard/dist/style.min.css'; import '@uppy/dashboard/dist/style.min.css';
import { getStreamByCuid } from '@/lib/streams'; import { getStreamByCuid } from '@/app/lib/streams';
export default async function Page() { export default async function Page() {

View File

@ -5,7 +5,7 @@ import Uppy from '@uppy/core';
import AwsS3 from '@uppy/aws-s3'; import AwsS3 from '@uppy/aws-s3';
import RemoteSources from '@uppy/remote-sources'; import RemoteSources from '@uppy/remote-sources';
import { useAuth } from './components/auth'; import { useAuth } from './components/auth';
import { companionUrl } from '@/lib/constants'; import { companionUrl } from '@/app/lib/constants';
// Uppy is a challenging react integration. Following are some references // Uppy is a challenging react integration. Following are some references

View File

@ -1,6 +1,6 @@
import VodPage from '@/components/vod-page'; import VodPage from '@/app/components/vod-page';
import { IVodPageProps, getVodFromSafeDateOrCuid } from '@/lib/vods'; import { IVodPageProps, getVodFromSafeDateOrCuid } from '@/app/lib/vods';
import { notFound } from 'next/navigation'; import { notFound } from 'next/navigation';

View File

@ -1,24 +1,24 @@
import VodsList from '@/components/vods-list'; import VodsList from '@/app/components/vods-list';
import Link from 'next/link'; import Link from 'next/link';
import { getVtuberBySlug } from '@/lib/vtubers' import { getVtuberBySlug } from '@/app/lib/vtubers'
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faExternalLinkAlt, faBagShopping } from "@fortawesome/free-solid-svg-icons"; import { faExternalLinkAlt, faBagShopping } from "@fortawesome/free-solid-svg-icons";
import { faFacebook, faInstagram, faPatreon, faYoutube, faTwitch, faTiktok, faXTwitter, faReddit, faDiscord } from "@fortawesome/free-brands-svg-icons"; import { faFacebook, faInstagram, faPatreon, faYoutube, faTwitch, faTiktok, faXTwitter, faReddit, faDiscord } from "@fortawesome/free-brands-svg-icons";
import Image from "next/legacy/image"; import Image from "next/legacy/image";
import OnlyfansIcon from "@/components/icons/onlyfans"; import OnlyfansIcon from "@/app/components/icons/onlyfans";
import PornhubIcon from '@/components/icons/pornhub'; import PornhubIcon from '@/app/components/icons/pornhub';
import ThroneIcon from '@/components/icons/throne'; import ThroneIcon from '@/app/components/icons/throne';
import LinktreeIcon from '@/components/icons/linktree'; import LinktreeIcon from '@/app/components/icons/linktree';
import FanslyIcon from '@/components/icons/fansly'; import FanslyIcon from '@/app/components/icons/fansly';
import ChaturbateIcon from '@/components/icons/chaturbate'; import ChaturbateIcon from '@/app/components/icons/chaturbate';
import CarrdIcon from '@/components/icons/carrd'; import CarrdIcon from '@/app/components/icons/carrd';
import styles from '@/assets/styles/icon.module.css'; import styles from '@/assets/styles/icon.module.css';
import { getVodsForVtuber } from '@/lib/vods'; import { getVodsForVtuber } from '@/app/lib/vods';
import { notFound } from 'next/navigation'; import { notFound } from 'next/navigation';
import ArchiveProgress from '@/components/archive-progress'; import ArchiveProgress from '@/app/components/archive-progress';
import { getAllStreamsForVtuber, getStreamsForVtuber } from '@/lib/streams'; import { getAllStreamsForVtuber, getStreamsForVtuber } from '@/app/lib/streams';
import LinkableHeading from '@/components/linkable-heading'; import LinkableHeading from '@/app/components/linkable-heading';

View File

@ -1,7 +1,7 @@
import { Stream } from '@/components/stream'; import { Stream } from '@/app/components/stream';
import { IStream, getStreamForVtuber } from '@/lib/streams'; import { getStreamForVtuber } from '@/app/lib/streams';
import { getVtuberBySlug } from '@/lib/vtubers'; import { getVtuberBySlug } from '@/app/lib/vtubers';
import NotFound from '../../not-found'; import NotFound from '../../not-found';
interface IPageProps { interface IPageProps {

View File

@ -1,7 +1,7 @@
import { getVtuberBySlug } from '@/lib/vtubers'; import { getVtuberBySlug } from '@/app/lib/vtubers';
import { getStreamsForVtuber } from '@/lib/streams'; import { getStreamsForVtuber } from '@/app/lib/streams';
import Pager from '@/components/pager'; import Pager from '@/app/components/pager';
import { notFound } from 'next/navigation'; import { notFound } from 'next/navigation';
interface IPageParams { interface IPageParams {

View File

@ -1,9 +1,9 @@
// import VodsList, { VodsListHeading } from '@/components/vods-list' // import VodsList, { VodsListHeading } from '@/app/components/vods-list'
// import { getVtuberBySlug } from '@/lib/vtubers' // import { getVtuberBySlug } from '@/app/lib/vtubers'
// // import { IToys, getToysForVtuber } from '@/lib/toys' // // import { IToys, getToysForVtuber } from '@/app/lib/toys'
// import { ToysList, ToysListHeading } from '@/components/toys' // import { ToysList, ToysListHeading } from '@/app/components/toys'
// import Pager from '@/components/pager' // import Pager from '@/app/components/pager'
// interface IPageParams { // interface IPageParams {
// params: { // params: {

View File

@ -1,9 +1,9 @@
// import VodsList, { VodsListHeading } from '@/components/vods-list' // import VodsList, { VodsListHeading } from '@/app/components/vods-list'
// import { getVtuberBySlug } from '@/lib/vtubers' // import { getVtuberBySlug } from '@/app/lib/vtubers'
// // import { IToys, getToysForVtuber } from '@/lib/toys' // // import { IToys, getToysForVtuber } from '@/app/lib/toys'
// import { ToysList } from '@/components/toys' // import { ToysList } from '@/app/components/toys'
// import Pager from '@/components/pager' // import Pager from '@/app/components/pager'
interface IPageParams { interface IPageParams {
params: { params: {

View File

@ -1,6 +1,6 @@
import VodPage from '@/components/vod-page' import VodPage from '@/app/components/vod-page'
import { IVodPageProps, getVodFromSafeDateOrCuid } from '@/lib/vods' import { IVodPageProps, getVodFromSafeDateOrCuid } from '@/app/lib/vods'
import { notFound } from 'next/navigation'; import { notFound } from 'next/navigation';

View File

@ -1,7 +1,7 @@
import VodsList, { VodsListHeading } from '@/components/vods-list'; import VodsList, { VodsListHeading } from '@/app/components/vods-list';
import { getVtuberBySlug, getUrl } from '@/lib/vtubers'; import { getVtuberBySlug, getUrl } from '@/app/lib/vtubers';
import { IVodsResponse, getVodsForVtuber } from '@/lib/vods'; import { IVodsResponse, getVodsForVtuber } from '@/app/lib/vods';
import Pager from '@/components/pager'; import Pager from '@/app/components/pager';
import { notFound } from 'next/navigation'; import { notFound } from 'next/navigation';

View File

@ -1,8 +1,8 @@
import VodsList, { VodsListHeading } from '@/components/vods-list' import VodsList, { VodsListHeading } from '@/app/components/vods-list'
import { getVtuberBySlug, getUrl } from '@/lib/vtubers' import { getVtuberBySlug, getUrl } from '@/app/lib/vtubers'
import { IVodsResponse, getVodsForVtuber, getPaginatedUrl } from '@/lib/vods' import { IVodsResponse, getVodsForVtuber, getPaginatedUrl } from '@/app/lib/vods'
import Pager from '@/components/pager' import Pager from '@/app/components/pager'
import { notFound } from 'next/navigation' import { notFound } from 'next/navigation'
interface IPageParams { interface IPageParams {

View File

@ -1,6 +1,7 @@
import { notFound } from 'next/navigation' import { notFound } from 'next/navigation'
import VTuberCard from '../components/vtuber-card' import VTuberCard from '../components/vtuber-card'
import { getVtubers, IVtuber } from '../lib/vtubers' import { getVtubers } from '../lib/vtubers'
import { IVtuber } from 'types'
export default async function Page() { export default async function Page() {

View File

@ -1,49 +1,27 @@
{ {
"compilerOptions": { "compilerOptions": {
"baseUrl": ".", "lib": ["dom", "dom.iterable", "esnext"],
"paths": { "allowJs": true,
"@/components/*": [ "skipLibCheck": true,
"app/components/*" "strict": true,
"noEmit": true,
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "bundler",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve",
"incremental": true,
"plugins": [
{
"name": "next"
}
], ],
"@/lib/*": [ "paths": {
"app/lib/*" "@/*": ["./*"]
],
"@/assets/*": [
"assets/*"
]
},
"target": "es5",
"lib": [
"dom",
"dom.iterable",
"esnext"
],
"allowJs": true,
"skipLibCheck": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"noEmit": true,
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve",
"incremental": true,
"plugins": [
{
"name": "next"
} }
] },
}, "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
"include": [ "exclude": ["node_modules"]
"next-env.d.ts", }
"**/*.ts",
"**/*.tsx",
"dist/types/**/*.ts",
".next/types/**/*.ts"
],
"exclude": [
"node_modules"
]
}

View File

@ -0,0 +1,51 @@
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/components/*": [
"app/components/*"
],
"@/lib/*": [
"app/lib/*"
],
"@/assets/*": [
"assets/*"
]
},
"target": "es5",
"lib": [
"dom",
"dom.iterable",
"esnext"
],
"allowJs": true,
"skipLibCheck": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"noEmit": true,
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve",
"incremental": true,
"plugins": [
{
"name": "next"
}
]
},
"include": [
"next-env.d.ts",
"**/*.ts",
"**/*.tsx",
"dist/types/**/*.ts",
".next/types/**/*.ts",
"./app",
"./assets"
],
"exclude": [
"node_modules"
]
}

View File

@ -47,6 +47,7 @@
"slugify": "^1.6.6", "slugify": "^1.6.6",
"ts-node": "^10.9.2", "ts-node": "^10.9.2",
"tsx": "^4.7.2", "tsx": "^4.7.2",
"types": "workspace:^",
"typescript": "^5.4.5", "typescript": "^5.4.5",
"xpath": "^0.0.34" "xpath": "^0.0.34"
}, },

View File

@ -95,6 +95,9 @@ importers:
tsx: tsx:
specifier: ^4.7.2 specifier: ^4.7.2
version: 4.15.1 version: 4.15.1
types:
specifier: workspace:^
version: link:../types
typescript: typescript:
specifier: ^5.4.5 specifier: ^5.4.5
version: 5.4.5 version: 5.4.5

View File

@ -1,43 +0,0 @@
import dotenv from 'dotenv'
dotenv.config({
path: '../../.env'
})
import twitter from './twitter.js'
import fansly from './fansly.js'
/**
* Acquire a vtuber image from the www
*
* Sources preference
* 1. Twitter
* 2. Fansly
*
* Our task is to download an avatar image of the vtuber.
* A slug is good for pulling a record from the database. From there, we can see any social medias such as Twitter or Fansly.
* Twitter is preferred.
*
* We depend on one of these social media URLs. If there is neither Twitter or fansly listed, we throw an error.
*
* @param {Object} vtuber -- vtuber instance from strapi
* @returns {String} filePath -- path on disk where the image was saved
*/
export async function getImage(vtuber) {
if (!vtuber) throw new Error('first arg must be vtuber instance');
const { twitter: twitterUrl, fanslyId: fanslyId } = vtuber.attributes
const twitterUsername = twitterUrl && twitter.regex.username.exec(twitterUrl).at(1)
let img;
if (twitterUrl) {
img = await twitter.data.image(twitterUsername)
} else if (fanslyId) {
img = await fansly.data.image(fanslyId)
} else {
const msg = ` while attempting to get vtuber image, there was neither twitterUrl nor fanslyId listed. One of these must exist for us to download an image. \nvtuber=${JSON.stringify(vtuber, null, 2)}`
console.error(msg)
throw new Error(msg)
}
return img
}

View File

@ -0,0 +1,43 @@
import dotenv from 'dotenv';
dotenv.config({
path: '../../.env',
});
import { type IVtuber } from 'types';
import twitter from './twitter.js';
import fansly from './fansly.js';
/**
* Acquire a vtuber image from the www
*
* Sources preference
* 1. Twitter
* 2. Fansly
*
* Our task is to download an avatar image of the vtuber.
* A slug is good for pulling a record from the database. From there, we can see any social medias such as Twitter or Fansly.
* Twitter is preferred.
*
* We depend on one of these social media URLs. If there is neither Twitter nor Fansly listed, we throw an error.
*
* @param {IVtuber} vtuber -- vtuber instance from Strapi
* @returns {string} filePath -- path on disk where the image was saved
*/
export async function getImage(vtuber: IVtuber): Promise<string> {
if (!vtuber) throw new Error('first arg must be vtuber instance');
const { twitter: twitterUrl, fanslyId: fanslyId } = vtuber.attributes;
const twitterUsername = twitterUrl && twitterUrl.match(/@(\w+)/)?.[1];
let img: string;
if (twitterUrl) {
img = await twitter.data.image(twitterUsername);
} else if (fanslyId) {
img = await fansly.data.image(fanslyId);
} else {
const msg = `while attempting to get vtuber image, there was neither twitterUrl nor fanslyId listed. One of these must exist for us to download an image.\nvtuber=${JSON.stringify(vtuber, null, 2)}`;
console.error(msg);
throw new Error(msg);
}
return img;
}

View File

@ -1,30 +1,31 @@
// {
// "compilerOptions": {
// "outDir": "./built",
// "allowJs": true,
// "target": "ES2017",
// "module": "NodeNext"
// },
// "include": ["./src/**/*"]
// }
{ {
"extends": "@tsconfig/node20/tsconfig.json",
"version": "4.4.2",
"compilerOptions": { "compilerOptions": {
"target": "es2017", // Base Options recommended for all projects
"module": "esnext", "esModuleInterop": true,
"moduleResolution": "node", "skipLibCheck": true,
"declaration": true, "target": "es2022",
"declarationMap": true, "allowJs": true,
"sourceMap": true, "resolveJsonModule": true,
"rootDir": "./src", "moduleDetection": "force",
"outDir": "./dist" "isolatedModules": true,
}, "verbatimModuleSyntax": true,
"include": ["**/*.ts"], // Enable strict type checking so you can catch bugs early
"ts-node": { "strict": true,
"experimentalSpecifierResolution": "node", "noUncheckedIndexedAccess": true,
"transpileOnly": true, "noImplicitOverride": true,
"esm": true, // Transpile our TypeScript code to JavaScript
"module": "NodeNext",
"outDir": "dist",
"lib": [
"es2022"
]
}, },
// Include the necessary files for your project
"include": [
"**/*.ts",
"**/*.tsx"
],
"exclude": [
"node_modules"
]
} }

View File

@ -14,7 +14,7 @@
"noImplicitOverride": true, "noImplicitOverride": true,
// Transpile our TypeScript code to JavaScript // Transpile our TypeScript code to JavaScript
"module": "NodeNext", "module": "NodeNext",
"outDir": "lib", "outDir": "dist",
"lib": [ "lib": [
"es2022" "es2022"
] ]

View File

@ -1,5 +1,6 @@
{ {
"name": "temporal-worker", "name": "temporal-worker",
"type": "module",
"version": "0.0.0", "version": "0.0.0",
"private": true, "private": true,
"scripts": { "scripts": {

View File

@ -5,7 +5,7 @@
"declaration": true, "declaration": true,
"declarationMap": true, "declarationMap": true,
"sourceMap": true, "sourceMap": true,
"outDir": "./lib" "outDir": "dist"
}, },
"include": ["**/*.ts"] "include": ["**/*.ts"]
} }

View File

@ -1,5 +1,5 @@
import { Worker } from '@temporalio/worker'; import { Worker } from '@temporalio/worker';
import * as activities from '../temporal-workflows/src/all-activities'; import * as activities from '../temporal-workflows/src/all-activities.js';
async function run() { async function run() {
// Step 1: Register Workflows and Activities with the Worker and connect to // Step 1: Register Workflows and Activities with the Worker and connect to

View File

@ -11,6 +11,7 @@
"dependencies": { "dependencies": {
"@temporalio/activity": "^1.9.0", "@temporalio/activity": "^1.9.0",
"@temporalio/workflow": "^1.9.0", "@temporalio/workflow": "^1.9.0",
"@types/qs": "^6.9.15",
"date-fns": "^3.6.0", "date-fns": "^3.6.0",
"image": "workspace:*", "image": "workspace:*",
"qs": "^6.12.3", "qs": "^6.12.3",

View File

@ -14,6 +14,9 @@ importers:
'@temporalio/workflow': '@temporalio/workflow':
specifier: ^1.9.0 specifier: ^1.9.0
version: 1.10.1 version: 1.10.1
'@types/qs':
specifier: ^6.9.15
version: 6.9.15
date-fns: date-fns:
specifier: ^3.6.0 specifier: ^3.6.0
version: 3.6.0 version: 3.6.0
@ -151,6 +154,9 @@ packages:
'@types/node@20.14.10': '@types/node@20.14.10':
resolution: {integrity: sha512-MdiXf+nDuMvY0gJKxyfZ7/6UFsETO7mGKF54MVD/ekJS6HdFtpZFBgrh6Pseu64XTb2MLyFPlbW6hj8HYRQNOQ==} resolution: {integrity: sha512-MdiXf+nDuMvY0gJKxyfZ7/6UFsETO7mGKF54MVD/ekJS6HdFtpZFBgrh6Pseu64XTb2MLyFPlbW6hj8HYRQNOQ==}
'@types/qs@6.9.15':
resolution: {integrity: sha512-uXHQKES6DQKKCLh441Xv/dwxOq1TVS3JPUMlEqoEglvlhR6Mxnlew/Xq/LRVHpLyk7iK3zODe1qYHIMltO7XGg==}
'@types/semver@7.5.8': '@types/semver@7.5.8':
resolution: {integrity: sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==} resolution: {integrity: sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==}
@ -959,6 +965,8 @@ snapshots:
dependencies: dependencies:
undici-types: 5.26.5 undici-types: 5.26.5
'@types/qs@6.9.15': {}
'@types/semver@7.5.8': {} '@types/semver@7.5.8': {}
'@typescript-eslint/eslint-plugin@5.62.0(@typescript-eslint/parser@5.62.0(eslint@7.32.0)(typescript@4.9.5))(eslint@7.32.0)(typescript@4.9.5)': '@typescript-eslint/eslint-plugin@5.62.0(@typescript-eslint/parser@5.62.0(eslint@7.32.0)(typescript@4.9.5))(eslint@7.32.0)(typescript@4.9.5)':

View File

@ -1,4 +1,4 @@
import { IStreamResponse } from 'types'; import { IStreamResponse, IStreamsResponse } from 'types';
import { subMinutes, addMinutes } from 'date-fns'; import { subMinutes, addMinutes } from 'date-fns';
import qs from 'qs' import qs from 'qs'
@ -53,22 +53,24 @@ export async function upsertStream({
'Content-Type': 'application/json' 'Content-Type': 'application/json'
} }
}) })
const findStreamData = await findStreamRes.json() as IStreamResponse const findStreamData = await findStreamRes.json() as IStreamsResponse
if (findStreamData?.data && findStreamData.data.length > 0) { if (findStreamData?.data && findStreamData.data.length > 0) {
console.log('>> we found a findStreamData json. (there is an existing stream for this e-mail/notification)') console.log('>> we found a findStreamData json. (there is an existing stream for this e-mail/notification)')
console.log(JSON.stringify(findStreamData, null, 2)) console.log(JSON.stringify(findStreamData, null, 2))
streamId = findStreamData.data[0].id const stream = findStreamData.data[0]
if (!stream) throw new Error('stream was undefined');
streamId = stream.id
// Before we're done here, we need to do something extra. We need to populate isChaturbateStream and/or isFanslyStream. // Before we're done here, we need to do something extra. We need to populate isChaturbateStream and/or isFanslyStream.
// We know which of these booleans to set based on the stream's related platformNotifications // We know which of these booleans to set based on the stream's related platformNotifications
// We go through each pNotif and look at it's platform // We go through each pNotif and look at it's platform
let isFanslyStream = false let isFanslyStream = false
let isChaturbateStream = false let isChaturbateStream = false
if (findStreamData.data[0].attributes.platformNotifications) { if (stream.attributes.platformNotifications) {
for (const pn of findStreamData.data[0].attributes.platformNotifications) { for (const pn of stream.attributes.platformNotifications) {
if (pn.platform === 'fansly') { if (pn.attributes.platform === 'fansly') {
isFanslyStream = true isFanslyStream = true
} else if (pn.platform === 'chaturbate') { } else if (pn.attributes.platform === 'chaturbate') {
isChaturbateStream = true isChaturbateStream = true
} }
} }
@ -131,7 +133,7 @@ export async function upsertStream({
console.error(JSON.stringify(createStreamJson.error, null, 2)) console.error(JSON.stringify(createStreamJson.error, null, 2))
throw new Error('Failed to create stream in DB due to an error. (see above)') throw new Error('Failed to create stream in DB due to an error. (see above)')
} }
streamId = createStreamJson.id streamId = createStreamJson.data.id
} }
if (!streamId) throw new Error('failed to get streamId') if (!streamId) throw new Error('failed to get streamId')

View File

@ -1,4 +1,4 @@
import { NotificationData, IVtubersResponse, IVtuberResponse } from 'types'; import { NotificationData, IVtubersResponse, IVtuberResponse, IVtuber } from 'types';
import { fpSlugify } from 'utils'; import { fpSlugify } from 'utils';
import qs from 'qs'; import qs from 'qs';
import { getProminentColor } from 'image'; import { getProminentColor } from 'image';
@ -8,7 +8,7 @@ import { uploadFile } from 'storage/s3.js';
/** /**
* find or create vtuber in Strapi * find or create vtuber in Strapi
*/ */
export async function upsertVtuber({ platform, userId, url, channel }: NotificationData): Promise<number> { export async function upsertVtuber({ platform, userId, url, channel }: { platform: string, userId: string | null, url: string, channel: string }): Promise<number> {
let vtuberId let vtuberId
console.log('>> # Step 1, upsertVtuber') console.log('>> # Step 1, upsertVtuber')
@ -74,10 +74,16 @@ export async function upsertVtuber({ platform, userId, url, channel }: Notificat
// download image from platform // download image from platform
// vtuber.getImage expects a vtuber object, which we don't have yet, so we create a dummy one // vtuber.getImage expects a vtuber object, which we don't have yet, so we create a dummy one
const dummyVtuber = { const dummyVtuber: IVtuber = {
id: 69,
attributes: { attributes: {
slug: fpSlugify(channel), slug: fpSlugify(channel),
fanslyId: (platform === 'fansly') ? userId : null displayName: 'example',
vods: [],
description1: ' ',
image: ' ',
themeColor: ' ',
fanslyId: (platform === 'fansly') ? (userId ? userId : undefined) : undefined
} }
} }
const imageFile = await getImage(dummyVtuber) const imageFile = await getImage(dummyVtuber)

View File

@ -1,8 +1,9 @@
import { proxyActivities, sleep, log } from '@temporalio/workflow'; import { proxyActivities, sleep, log } from '@temporalio/workflow';
import { NotificationData } from 'types';
// Only import the activity types // Only import the activity types
import type * as upsertVtuberType from './activities/upsertVtuber'; import type * as upsertVtuberType from './activities/upsertVtuber.js';
import type * as upsertStreamType from './activities/upsertStream'; import type * as upsertStreamType from './activities/upsertStream.js';
import type * as upsertPlatformNotificationType from './activities/upsertPlatformNotification'; import type * as upsertPlatformNotificationType from './activities/upsertPlatformNotification.js';
const { upsertVtuber } = proxyActivities<typeof upsertVtuberType>({ const { upsertVtuber } = proxyActivities<typeof upsertVtuberType>({
startToCloseTimeout: '1 minute', startToCloseTimeout: '1 minute',
@ -13,11 +14,11 @@ const { upsertStream } = proxyActivities<typeof upsertStreamType>({
const { upsertPlatformNotification } = proxyActivities<typeof upsertPlatformNotificationType>({ const { upsertPlatformNotification } = proxyActivities<typeof upsertPlatformNotificationType>({
startToCloseTimeout: '1 minute', startToCloseTimeout: '1 minute',
}); });
export async function WorkflowA(name: string): Promise<string> { export async function processNotifEmail({ url, platform, channel, displayName, date, userId, avatar }: NotificationData): Promise<string> {
log.info('Hello from processNotifEmail'); log.info('Hello from processNotifEmail workflow');
const res1 = await upsertVtuber(name); const vtuberId = await upsertVtuber({ platform, userId, url, channel });
const res2 = await upsertStream(name); const pNotifId = await upsertPlatformNotification({ source: 'email', date, platform, vtuberId });
const res3 = await upsertPlatformNotification(name); const streamId = await upsertStream({ date, vtuberId, platform, pNotifId });
return `A: ${res1} | B: ${res2} | C: ${res3}`; return `vtuberId: ${vtuberId} | pNotifId: ${pNotifId} | streamId: ${streamId}`;
} }

View File

@ -1,7 +1,7 @@
import { proxyActivities, sleep, log } from '@temporalio/workflow'; import { proxyActivities, sleep, log } from '@temporalio/workflow';
// Only import the activity types // Only import the activity types
import type * as activitiesA from './activities/activitiesA'; import type * as activitiesA from './activities/activitiesA.js';
import type * as activitiesB from './activities/activitiesB'; import type * as activitiesB from './activities/activitiesB.js';
const { activityA } = proxyActivities<typeof activitiesA>({ const { activityA } = proxyActivities<typeof activitiesA>({
startToCloseTimeout: '1 minute', startToCloseTimeout: '1 minute',

View File

@ -14,7 +14,7 @@
"noImplicitOverride": true, "noImplicitOverride": true,
// Transpile our TypeScript code to JavaScript // Transpile our TypeScript code to JavaScript
"module": "NodeNext", "module": "NodeNext",
"outDir": "lib", "outDir": "dist",
"lib": [ "lib": [
"es2022" "es2022"
] ]

View File

@ -57,6 +57,7 @@ export interface IStream {
tweet: ITweetResponse; tweet: ITweetResponse;
isChaturbateStream: boolean; isChaturbateStream: boolean;
isFanslyStream: boolean; isFanslyStream: boolean;
platformNotifications: IPlatformNotification[];
} }
} }

View File

@ -6,7 +6,7 @@
"declarationMap": true, "declarationMap": true,
"sourceMap": true, "sourceMap": true,
"rootDir": "./src", "rootDir": "./src",
"outDir": "./lib" "outDir": "dist"
}, },
"files": ["index.d.ts"] "files": ["index.d.ts"]
} }

View File

@ -1,4 +1,4 @@
import * as slugify from 'slugify'; import slugify from './src/slugifyFix.js';
import { basename, join } from 'path'; import { basename, join } from 'path';
import os from 'node:os'; import os from 'node:os';
import fs from 'node:fs'; import fs from 'node:fs';
@ -42,11 +42,12 @@ export async function download({ url, filePath }: { url: string; filePath?: stri
}); });
const { body } = response; const { body } = response;
if (!body) throw new Error('body was null');
await finished(Readable.fromWeb(body).pipe(stream)); await finished(Readable.fromWeb(body).pipe(stream));
// Abort retrying if the resource doesn't exist // Abort retrying if the resource doesn't exist
if (response.status === 404) { if (response.status === 404) {
throw new AbortError(response.statusText); throw new Error(response.statusText);
} }
return; return;

View File

@ -11,7 +11,9 @@
"author": "", "author": "",
"license": "Unlicense", "license": "Unlicense",
"dependencies": { "dependencies": {
"@paralleldrive/cuid2": "^2.2.2",
"@types/node": "^20.14.9", "@types/node": "^20.14.9",
"p-retry": "^5.1.2",
"scout": "workspace:^", "scout": "workspace:^",
"slugify": "^1.6.6" "slugify": "^1.6.6"
}, },

View File

@ -8,9 +8,15 @@ importers:
.: .:
dependencies: dependencies:
'@paralleldrive/cuid2':
specifier: ^2.2.2
version: 2.2.2
'@types/node': '@types/node':
specifier: ^20.14.9 specifier: ^20.14.9
version: 20.14.10 version: 20.14.10
p-retry:
specifier: ^5.1.2
version: 5.1.2
scout: scout:
specifier: workspace:^ specifier: workspace:^
version: link:../scout version: link:../scout
@ -50,6 +56,13 @@ packages:
'@jridgewell/trace-mapping@0.3.9': '@jridgewell/trace-mapping@0.3.9':
resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==} resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==}
'@noble/hashes@1.4.0':
resolution: {integrity: sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==}
engines: {node: '>= 16'}
'@paralleldrive/cuid2@2.2.2':
resolution: {integrity: sha512-ZOBkgDwEdoYVlSeRbYYXs0S9MejQofiVYoTbKzy/6GQa39/q5tQU2IX46+shYnUkpEl3wc+J6wRlar7r2EK2xA==}
'@tsconfig/node10@1.0.11': '@tsconfig/node10@1.0.11':
resolution: {integrity: sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==} resolution: {integrity: sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==}
@ -71,6 +84,9 @@ packages:
'@types/node@20.14.10': '@types/node@20.14.10':
resolution: {integrity: sha512-MdiXf+nDuMvY0gJKxyfZ7/6UFsETO7mGKF54MVD/ekJS6HdFtpZFBgrh6Pseu64XTb2MLyFPlbW6hj8HYRQNOQ==} resolution: {integrity: sha512-MdiXf+nDuMvY0gJKxyfZ7/6UFsETO7mGKF54MVD/ekJS6HdFtpZFBgrh6Pseu64XTb2MLyFPlbW6hj8HYRQNOQ==}
'@types/retry@0.12.1':
resolution: {integrity: sha512-xoDlM2S4ortawSWORYqsdU+2rxdh4LRW9ytc3zmT37RIKQh6IHyKwwtKhKis9ah8ol07DCkZxPt8BBvPjC6v4g==}
acorn-walk@8.3.3: acorn-walk@8.3.3:
resolution: {integrity: sha512-MxXdReSRhGO7VlFe1bRG/oI7/mdLV9B9JJT0N8vZOhF7gFRR5l3M8W9G8JxmKV+JC5mGqJ0QvqfSOLsCPa4nUw==} resolution: {integrity: sha512-MxXdReSRhGO7VlFe1bRG/oI7/mdLV9B9JJT0N8vZOhF7gFRR5l3M8W9G8JxmKV+JC5mGqJ0QvqfSOLsCPa4nUw==}
engines: {node: '>=0.4.0'} engines: {node: '>=0.4.0'}
@ -319,6 +335,10 @@ packages:
resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==}
engines: {node: '>=10'} engines: {node: '>=10'}
p-retry@5.1.2:
resolution: {integrity: sha512-couX95waDu98NfNZV+i/iLt+fdVxmI7CbrrdC2uDWfPdUAApyxT4wmDlyOtR5KtTDmkDO0zDScDjDou9YHhd9g==}
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
path-exists@4.0.0: path-exists@4.0.0:
resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==}
engines: {node: '>=8'} engines: {node: '>=8'}
@ -342,6 +362,10 @@ packages:
resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==}
engines: {node: '>=0.10.0'} engines: {node: '>=0.10.0'}
retry@0.13.1:
resolution: {integrity: sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==}
engines: {node: '>= 4'}
safe-buffer@5.2.1: safe-buffer@5.2.1:
resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==}
@ -450,6 +474,12 @@ snapshots:
'@jridgewell/resolve-uri': 3.1.2 '@jridgewell/resolve-uri': 3.1.2
'@jridgewell/sourcemap-codec': 1.5.0 '@jridgewell/sourcemap-codec': 1.5.0
'@noble/hashes@1.4.0': {}
'@paralleldrive/cuid2@2.2.2':
dependencies:
'@noble/hashes': 1.4.0
'@tsconfig/node10@1.0.11': {} '@tsconfig/node10@1.0.11': {}
'@tsconfig/node12@1.0.11': {} '@tsconfig/node12@1.0.11': {}
@ -466,6 +496,8 @@ snapshots:
dependencies: dependencies:
undici-types: 5.26.5 undici-types: 5.26.5
'@types/retry@0.12.1': {}
acorn-walk@8.3.3: acorn-walk@8.3.3:
dependencies: dependencies:
acorn: 8.12.1 acorn: 8.12.1
@ -693,6 +725,11 @@ snapshots:
dependencies: dependencies:
p-limit: 3.1.0 p-limit: 3.1.0
p-retry@5.1.2:
dependencies:
'@types/retry': 0.12.1
retry: 0.13.1
path-exists@4.0.0: {} path-exists@4.0.0: {}
pathval@2.0.0: {} pathval@2.0.0: {}
@ -709,6 +746,8 @@ snapshots:
require-directory@2.1.1: {} require-directory@2.1.1: {}
retry@0.13.1: {}
safe-buffer@5.2.1: {} safe-buffer@5.2.1: {}
serialize-javascript@6.0.2: serialize-javascript@6.0.2:

View File

@ -0,0 +1,8 @@
/**
* Hack to make 'slugify' import work with "type": "module".
* @see https://github.com/simov/slugify/issues/173
*/
import slugify from 'slugify'
export default slugify as unknown as typeof slugify.default

View File

@ -14,7 +14,7 @@
"noImplicitOverride": true, "noImplicitOverride": true,
// Transpile our TypeScript code to JavaScript // Transpile our TypeScript code to JavaScript
"module": "NodeNext", "module": "NodeNext",
"outDir": "lib", "outDir": "dist",
"lib": [ "lib": [
"es2022" "es2022"
] ]