From a496556005ea7d20e7b3f1316bcec29a8566f3ed Mon Sep 17 00:00:00 2001 From: Marvin Willms Date: Wed, 5 Jul 2023 14:34:33 +0200 Subject: [PATCH 01/17] feat(example): added initial nextjs app to examples --- apps/examples/nextjs/.gitignore | 35 +++++ apps/examples/nextjs/README.md | 34 ++++ apps/examples/nextjs/next.config.js | 15 ++ apps/examples/nextjs/package.json | 27 ++++ apps/examples/nextjs/postcss.config.js | 6 + apps/examples/nextjs/public/next.svg | 1 + apps/examples/nextjs/public/vercel.svg | 1 + .../[id]/images/[imageId]/confirm/route.ts | 12 ++ .../api/users/[id]/images/[imageId]/route.ts | 22 +++ .../src/app/api/users/[id]/images/route.ts | 19 +++ apps/examples/nextjs/src/app/favicon.ico | Bin 0 -> 39535 bytes apps/examples/nextjs/src/app/globals.css | 27 ++++ apps/examples/nextjs/src/app/layout.tsx | 21 +++ apps/examples/nextjs/src/app/page.tsx | 148 ++++++++++++++++++ apps/examples/nextjs/src/app/upload-wizard.ts | 37 +++++ apps/examples/nextjs/tailwind.config.js | 18 +++ apps/examples/nextjs/tsconfig.json | 28 ++++ 17 files changed, 451 insertions(+) create mode 100644 apps/examples/nextjs/.gitignore create mode 100644 apps/examples/nextjs/README.md create mode 100644 apps/examples/nextjs/next.config.js create mode 100644 apps/examples/nextjs/package.json create mode 100644 apps/examples/nextjs/postcss.config.js create mode 100644 apps/examples/nextjs/public/next.svg create mode 100644 apps/examples/nextjs/public/vercel.svg create mode 100644 apps/examples/nextjs/src/app/api/users/[id]/images/[imageId]/confirm/route.ts create mode 100644 apps/examples/nextjs/src/app/api/users/[id]/images/[imageId]/route.ts create mode 100644 apps/examples/nextjs/src/app/api/users/[id]/images/route.ts create mode 100644 apps/examples/nextjs/src/app/favicon.ico create mode 100644 apps/examples/nextjs/src/app/globals.css create mode 100644 apps/examples/nextjs/src/app/layout.tsx create mode 100644 apps/examples/nextjs/src/app/page.tsx create mode 100644 apps/examples/nextjs/src/app/upload-wizard.ts create mode 100644 apps/examples/nextjs/tailwind.config.js create mode 100644 apps/examples/nextjs/tsconfig.json diff --git a/apps/examples/nextjs/.gitignore b/apps/examples/nextjs/.gitignore new file mode 100644 index 0000000..8f322f0 --- /dev/null +++ b/apps/examples/nextjs/.gitignore @@ -0,0 +1,35 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules +/.pnp +.pnp.js + +# testing +/coverage + +# next.js +/.next/ +/out/ + +# production +/build + +# misc +.DS_Store +*.pem + +# debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# local env files +.env*.local + +# vercel +.vercel + +# typescript +*.tsbuildinfo +next-env.d.ts diff --git a/apps/examples/nextjs/README.md b/apps/examples/nextjs/README.md new file mode 100644 index 0000000..f4da3c4 --- /dev/null +++ b/apps/examples/nextjs/README.md @@ -0,0 +1,34 @@ +This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app). + +## Getting Started + +First, run the development server: + +```bash +npm run dev +# or +yarn dev +# or +pnpm dev +``` + +Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. + +You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file. + +This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font. + +## Learn More + +To learn more about Next.js, take a look at the following resources: + +- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. +- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. + +You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome! + +## Deploy on Vercel + +The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js. + +Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details. diff --git a/apps/examples/nextjs/next.config.js b/apps/examples/nextjs/next.config.js new file mode 100644 index 0000000..fb5cc8b --- /dev/null +++ b/apps/examples/nextjs/next.config.js @@ -0,0 +1,15 @@ +/** @type {import('next').NextConfig} */ +const webpack = require("webpack"); + +const nextConfig = { + webpack: (config, { isServer, nextRuntime }) => { + // Avoid AWS SDK Node.js require issue + if (isServer && nextRuntime === "nodejs") + config.plugins.push( + new webpack.IgnorePlugin({ resourceRegExp: /^aws-crt|signature-v4-crt$/ }) + ); + return config; + }, +} + +module.exports = nextConfig diff --git a/apps/examples/nextjs/package.json b/apps/examples/nextjs/package.json new file mode 100644 index 0000000..1f23326 --- /dev/null +++ b/apps/examples/nextjs/package.json @@ -0,0 +1,27 @@ +{ + "name": "nextjs-example", + "version": "0.1.0", + "private": true, + "scripts": { + "dev": "next dev", + "build": "next build", + "start": "next start", + "lint": "next lint" + }, + "dependencies": { + "@types/node": "20.3.3", + "@types/react": "18.2.14", + "@types/react-dom": "18.2.6", + "autoprefixer": "10.4.14", + "next": "13.4.8", + "postcss": "8.4.24", + "react": "18.2.0", + "react-dom": "18.2.0", + "tailwindcss": "3.3.2", + "typescript": "^5.1.6", + "@server/core": "workspace:*", + "@providers/s3": "workspace:*", + "@adapters/interface": "workspace:*", + "shared-types": "workspace:*" + } +} diff --git a/apps/examples/nextjs/postcss.config.js b/apps/examples/nextjs/postcss.config.js new file mode 100644 index 0000000..33ad091 --- /dev/null +++ b/apps/examples/nextjs/postcss.config.js @@ -0,0 +1,6 @@ +module.exports = { + plugins: { + tailwindcss: {}, + autoprefixer: {}, + }, +} diff --git a/apps/examples/nextjs/public/next.svg b/apps/examples/nextjs/public/next.svg new file mode 100644 index 0000000..5174b28 --- /dev/null +++ b/apps/examples/nextjs/public/next.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/apps/examples/nextjs/public/vercel.svg b/apps/examples/nextjs/public/vercel.svg new file mode 100644 index 0000000..d2f8422 --- /dev/null +++ b/apps/examples/nextjs/public/vercel.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/apps/examples/nextjs/src/app/api/users/[id]/images/[imageId]/confirm/route.ts b/apps/examples/nextjs/src/app/api/users/[id]/images/[imageId]/confirm/route.ts new file mode 100644 index 0000000..969f8d7 --- /dev/null +++ b/apps/examples/nextjs/src/app/api/users/[id]/images/[imageId]/confirm/route.ts @@ -0,0 +1,12 @@ +import { NextRequest, NextResponse } from "next/server"; +import { uploadWizard } from "../../../../../../upload-wizard"; + +export async function POST( + request: NextRequest, + { params }: { params: { id: string, imageId: string } } +) { + // TODO: validate confirm Token + await uploadWizard.confirmUpload(params.imageId, 'confirmToken') + + return NextResponse.json(undefined) +} diff --git a/apps/examples/nextjs/src/app/api/users/[id]/images/[imageId]/route.ts b/apps/examples/nextjs/src/app/api/users/[id]/images/[imageId]/route.ts new file mode 100644 index 0000000..9da65f0 --- /dev/null +++ b/apps/examples/nextjs/src/app/api/users/[id]/images/[imageId]/route.ts @@ -0,0 +1,22 @@ +import { NextRequest, NextResponse } from "next/server"; +import { uploadWizard } from "../../../../../upload-wizard"; + +export async function DELETE( + request: NextRequest, + { params }: { params: { id: string, imageId: string } } +) { + // TODO: delete image + await uploadWizard.delete(params.imageId) + + return NextResponse.json(undefined) +} + +export async function GET( + request: NextRequest, + { params }: { params: { id: string, imageId: string } } +) { + // TODO: get image + await uploadWizard.getData(params.imageId) + + return NextResponse.json(undefined) +} diff --git a/apps/examples/nextjs/src/app/api/users/[id]/images/route.ts b/apps/examples/nextjs/src/app/api/users/[id]/images/route.ts new file mode 100644 index 0000000..0bb7815 --- /dev/null +++ b/apps/examples/nextjs/src/app/api/users/[id]/images/route.ts @@ -0,0 +1,19 @@ +import { NextRequest, NextResponse } from 'next/server' +import * as crypto from "crypto"; +import { uploadWizard } from "../../../../upload-wizard"; + +export async function POST( + request: NextRequest, + { params }: { params: { id: string } } +) { + console.log(process.env.AWS_ACCESS_KEY_ID) + const {id, url, confirmToken, expiry} = await uploadWizard.signedUploadUrl() + + + return NextResponse.json({ + id, + confirmToken, + url, + expiry: expiry, + }) +} diff --git a/apps/examples/nextjs/src/app/favicon.ico b/apps/examples/nextjs/src/app/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..4570eb8d9269ad58b17fecbec6d630cded56f507 GIT binary patch literal 39535 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 literal 0 HcmV?d00001 diff --git a/apps/examples/nextjs/src/app/globals.css b/apps/examples/nextjs/src/app/globals.css new file mode 100644 index 0000000..fd81e88 --- /dev/null +++ b/apps/examples/nextjs/src/app/globals.css @@ -0,0 +1,27 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; + +:root { + --foreground-rgb: 0, 0, 0; + --background-start-rgb: 214, 219, 220; + --background-end-rgb: 255, 255, 255; +} + +@media (prefers-color-scheme: dark) { + :root { + --foreground-rgb: 255, 255, 255; + --background-start-rgb: 0, 0, 0; + --background-end-rgb: 0, 0, 0; + } +} + +body { + color: rgb(var(--foreground-rgb)); + background: linear-gradient( + to bottom, + transparent, + rgb(var(--background-end-rgb)) + ) + rgb(var(--background-start-rgb)); +} diff --git a/apps/examples/nextjs/src/app/layout.tsx b/apps/examples/nextjs/src/app/layout.tsx new file mode 100644 index 0000000..7ed8f04 --- /dev/null +++ b/apps/examples/nextjs/src/app/layout.tsx @@ -0,0 +1,21 @@ +import './globals.css' +import { Inter } from 'next/font/google' + +const inter = Inter({ subsets: ['latin'] }) + +export const metadata = { + title: 'Upload Wizard - Next.js Example', + description: 'Generated by create next app', +} + +export default function RootLayout({ + children, +}: { + children: React.ReactNode +}) { + return ( + + {children} + + ) +} diff --git a/apps/examples/nextjs/src/app/page.tsx b/apps/examples/nextjs/src/app/page.tsx new file mode 100644 index 0000000..6a92026 --- /dev/null +++ b/apps/examples/nextjs/src/app/page.tsx @@ -0,0 +1,148 @@ +'use client' + +import { useEffect, useMemo, useState } from 'react' +import { MediaFile, FileStatus } from 'shared-types' +import { SignedUploadUrl } from '@server/core' +import { z } from 'zod' + +class UploadHandler { + constructor(private controller: AbortController) { + this.controller = controller + } + + abort = () => { + this.controller.abort() + } + + upload = async (file: File) => { + const { url, confirmToken, id } = await this.requestSignedURL() + + await this.uploadImage(file, url) + + await this.confirmUpload(id, confirmToken) + + return await this.pollFileUntilReady(id) + } + + private requestSignedURL = async (): Promise> => { + const res = await fetch('/api/users/1/images', { + method: 'POST', + signal: this.controller.signal, + }) + + if (!res.ok) { + throw new Error('Failed to request signed URL') + } + + const schema = z.object({ + url: z.string(), + confirmToken: z.string(), + id: z.string(), + expiry: z.number(), + }) + + const parsed = schema.parse(await res.json()) + + return parsed satisfies SignedUploadUrl + } + + private uploadImage = async (file: File, signedURL: string) => { + console.log(file) + + const res = await fetch(signedURL, { + method: 'POST', + signal: this.controller.signal, + body: file, + }) + + if (!res.ok) { + throw new Error('Failed to upload file') + } + } + + private confirmUpload = async (id: string, confirmToken: string) => { + const res = await fetch(`/api/users/1/images/${id}/confirm`, { + method: 'POST', + signal: this.controller.signal, + body: JSON.stringify({ confirmToken }), + }) + + if (!res.ok) { + throw new Error('Failed to confirm upload') + } + } + + private getFile = async (id: string): Promise> => { + const res = await fetch(`/api/users/1/images/${id}`, { + method: 'GET', + signal: this.controller.signal, + }) + + if (!res.ok) { + throw new Error('Failed to get file') + } + + const schema = z.object({ + id: z.string(), + status: z.enum([FileStatus.UPLOADED, FileStatus.PROCESSED]), + variants: z.union([z.array(z.string()), z.string(), z.undefined()]), + }) + + const parsed = schema.parse(await res.json()) + + return parsed satisfies MediaFile + } + + private pollFileUntilReady = async ( + id: string + ): Promise> => { + let file = await this.getFile(id) + + while (file.status === FileStatus.PROCESSED) { + await new Promise((resolve) => setTimeout(resolve, 1000)) + + file = await this.getFile(id) + } + + return file + } +} + +export default function Home() { + const [data, setData] = useState<{ message: string } | null>() + const controller = useMemo(() => new AbortController(), []) + + useEffect(() => { + const controller = new AbortController() + + const uploadHandler = new UploadHandler(controller) + + const uploadFile = async () => { + const file = new File(['test'], 'test.txt', { type: 'text/plain' }) + + try { + try { + const mediaFile = await uploadHandler.upload(file) + setData({ message: `File uploaded: ${mediaFile.variants}` }) + } catch (err) { + setData({ message: `Upload aborted: ${err}` }) + } + } catch (err) { + console.error(err) + } + } + + uploadFile() + + return () => { + uploadHandler.abort() + } + }, []) + + return ( +
+

Upload Wizard - Next.js Example

+

Data: {data?.message}

+
+ ) +} diff --git a/apps/examples/nextjs/src/app/upload-wizard.ts b/apps/examples/nextjs/src/app/upload-wizard.ts new file mode 100644 index 0000000..0482aa1 --- /dev/null +++ b/apps/examples/nextjs/src/app/upload-wizard.ts @@ -0,0 +1,37 @@ +import { UploadWizard } from '@server/core' +import { S3Provider } from '@providers/s3' +import { DBFileProvider } from "@adapters/interface"; + +class DBProvider extends DBFileProvider { + createEntry(input: any): Promise { + return Promise.resolve(undefined) + } + + deleteEntry(fileId: string): Promise { + return Promise.resolve(undefined) + } + + updateStatus(fileId: string, status: any): Promise { + return Promise.resolve(undefined) + } + + validateConfirmToken( + fileId: string, + confirmToken: string + ): Promise { + return Promise.resolve(true) + } + + exists(fileId: string): Promise { + return Promise.resolve(true) + } +} + +export const uploadWizard = new UploadWizard({ + dbFileProvider: new DBProvider(), + storageServiceProvider: new S3Provider({ + bucketPath: 'uploads', + acl: 'public-read', + optimisticFileDataResponse: true, + }), +}) diff --git a/apps/examples/nextjs/tailwind.config.js b/apps/examples/nextjs/tailwind.config.js new file mode 100644 index 0000000..d53b2ea --- /dev/null +++ b/apps/examples/nextjs/tailwind.config.js @@ -0,0 +1,18 @@ +/** @type {import('tailwindcss').Config} */ +module.exports = { + content: [ + './src/pages/**/*.{js,ts,jsx,tsx,mdx}', + './src/components/**/*.{js,ts,jsx,tsx,mdx}', + './src/app/**/*.{js,ts,jsx,tsx,mdx}', + ], + theme: { + extend: { + backgroundImage: { + 'gradient-radial': 'radial-gradient(var(--tw-gradient-stops))', + 'gradient-conic': + 'conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))', + }, + }, + }, + plugins: [], +} diff --git a/apps/examples/nextjs/tsconfig.json b/apps/examples/nextjs/tsconfig.json new file mode 100644 index 0000000..417ce6b --- /dev/null +++ b/apps/examples/nextjs/tsconfig.json @@ -0,0 +1,28 @@ +{ + "compilerOptions": { + "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" + } + ], + "paths": { + "@components/*": ["./src/components/*"] + } + }, + "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], + "exclude": ["node_modules"] +} From 2af21d6f6883090e1dfb1d3b4e20a97eda496c3e Mon Sep 17 00:00:00 2001 From: Marvin Willms Date: Wed, 5 Jul 2023 14:42:46 +0200 Subject: [PATCH 02/17] fix(example): change file upload method to `PUT` --- apps/examples/nextjs/src/app/page.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/examples/nextjs/src/app/page.tsx b/apps/examples/nextjs/src/app/page.tsx index 6a92026..edac079 100644 --- a/apps/examples/nextjs/src/app/page.tsx +++ b/apps/examples/nextjs/src/app/page.tsx @@ -50,7 +50,7 @@ class UploadHandler { console.log(file) const res = await fetch(signedURL, { - method: 'POST', + method: 'PUT', signal: this.controller.signal, body: file, }) From 2f306a693ad80b0c18b8060b0b4e51126b618f4a Mon Sep 17 00:00:00 2001 From: Marvin Willms Date: Fri, 7 Jul 2023 20:35:52 +0200 Subject: [PATCH 03/17] fix(example): change acl to "bucket-owner-full-control" --- apps/examples/nextjs/src/app/upload-wizard.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/examples/nextjs/src/app/upload-wizard.ts b/apps/examples/nextjs/src/app/upload-wizard.ts index 0482aa1..2e38382 100644 --- a/apps/examples/nextjs/src/app/upload-wizard.ts +++ b/apps/examples/nextjs/src/app/upload-wizard.ts @@ -31,7 +31,7 @@ export const uploadWizard = new UploadWizard({ dbFileProvider: new DBProvider(), storageServiceProvider: new S3Provider({ bucketPath: 'uploads', - acl: 'public-read', + acl: 'bucket-owner-full-control', optimisticFileDataResponse: true, }), }) From 34e25e3292b99ca00f6dd23cbd0fbb13033e7343 Mon Sep 17 00:00:00 2001 From: Marvin Willms Date: Fri, 7 Jul 2023 20:49:39 +0200 Subject: [PATCH 04/17] feat(example): add basic ui/ux --- apps/examples/nextjs/src/app/page.tsx | 207 ++++++++++++++++++++++---- 1 file changed, 175 insertions(+), 32 deletions(-) diff --git a/apps/examples/nextjs/src/app/page.tsx b/apps/examples/nextjs/src/app/page.tsx index edac079..0b25e72 100644 --- a/apps/examples/nextjs/src/app/page.tsx +++ b/apps/examples/nextjs/src/app/page.tsx @@ -1,10 +1,18 @@ 'use client' -import { useEffect, useMemo, useState } from 'react' +import { ReactNode, useMemo, useState } from 'react' import { MediaFile, FileStatus } from 'shared-types' import { SignedUploadUrl } from '@server/core' import { z } from 'zod' +enum Step { + CHOOSE_FILE, + REQUEST_UPLOAD_URL, + UPLOAD_FILE, + CONFIRM_UPLOAD, + POLL_FILE, +} + class UploadHandler { constructor(private controller: AbortController) { this.controller = controller @@ -14,6 +22,9 @@ class UploadHandler { this.controller.abort() } + /** + * @deprecated This "all-in-one" method is deprecated in favor of the more granular methods below. + * */ upload = async (file: File) => { const { url, confirmToken, id } = await this.requestSignedURL() @@ -24,7 +35,7 @@ class UploadHandler { return await this.pollFileUntilReady(id) } - private requestSignedURL = async (): Promise> => { + requestSignedURL = async (): Promise> => { const res = await fetch('/api/users/1/images', { method: 'POST', signal: this.controller.signal, @@ -46,7 +57,7 @@ class UploadHandler { return parsed satisfies SignedUploadUrl } - private uploadImage = async (file: File, signedURL: string) => { + uploadImage = async (file: File, signedURL: string) => { console.log(file) const res = await fetch(signedURL, { @@ -60,7 +71,7 @@ class UploadHandler { } } - private confirmUpload = async (id: string, confirmToken: string) => { + confirmUpload = async (id: string, confirmToken: string) => { const res = await fetch(`/api/users/1/images/${id}/confirm`, { method: 'POST', signal: this.controller.signal, @@ -72,7 +83,7 @@ class UploadHandler { } } - private getFile = async (id: string): Promise> => { + getFile = async (id: string): Promise> => { const res = await fetch(`/api/users/1/images/${id}`, { method: 'GET', signal: this.controller.signal, @@ -93,9 +104,7 @@ class UploadHandler { return parsed satisfies MediaFile } - private pollFileUntilReady = async ( - id: string - ): Promise> => { + pollFileUntilReady = async (id: string): Promise> => { let file = await this.getFile(id) while (file.status === FileStatus.PROCESSED) { @@ -111,38 +120,172 @@ class UploadHandler { export default function Home() { const [data, setData] = useState<{ message: string } | null>() const controller = useMemo(() => new AbortController(), []) + const uploadHandler = useMemo( + () => new UploadHandler(controller), + [controller] + ) + const [step, setStep] = useState(Step.CHOOSE_FILE) + + // useEffect(() => { + // const controller = new AbortController() + // + // const uploadHandler = new UploadHandler(controller) + // + // const uploadFile = async () => { + // const file = new File(['test'], 'test.txt', { type: 'text/plain' }) + // + // try { + // try { + // const mediaFile = await uploadHandler.upload(file) + // setData({ message: `File uploaded: ${mediaFile.variants}` }) + // } catch (err) { + // setData({ message: `Upload aborted: ${err}` }) + // } + // } catch (err) { + // console.error(err) + // } + // } + // + // uploadFile() + // + // return () => { + // uploadHandler.abort() + // } + // }, []) + + const [done, setDone] = useState(false) - useEffect(() => { - const controller = new AbortController() + return ( +
+
+

Upload Wizard - Next.js Example

+
+
+
+

{ + setDone((prevState) => !prevState) + }} + > + Upload +

+ +
+ {(disabled) => ( +
+ + +
+ )} +
+ +
+ {(disabled) => ( + + )} +
+ +
+ {(disabled) => ( + + )} +
+ +
+ {(disabled) => ( + + )} +
+ +
+ {(disabled) => ( + + )} +
+
+
+ ) +} - const uploadHandler = new UploadHandler(controller) +interface SectionProps { + number: number + active: boolean + done?: boolean + children: (disabled: boolean) => ReactNode +} - const uploadFile = async () => { - const file = new File(['test'], 'test.txt', { type: 'text/plain' }) +const Section = ({ children, number, active, done }: SectionProps) => { + const disabled = useMemo(() => { + return !active + }, [active]) - try { - try { - const mediaFile = await uploadHandler.upload(file) - setData({ message: `File uploaded: ${mediaFile.variants}` }) - } catch (err) { - setData({ message: `Upload aborted: ${err}` }) + return ( +
+
+ +
+
+ {children(disabled)} +
+
+ ) +} - uploadFile() +interface NumberComponentProps { + number: number + done?: boolean +} - return () => { - uploadHandler.abort() - } - }, []) +const NumberComponent = ({ number, done }: NumberComponentProps) => { + return ( +
+ {done ? '✓' : number} +
+ ) +} +interface ButtonProps { + disabled?: boolean + children: ReactNode +} + +const Button = ({ children, disabled }: ButtonProps) => { return ( -
-

Upload Wizard - Next.js Example

-

Data: {data?.message}

-
+ ) } From 25e61da61cd1ee147d6e8b24986f233adda9634c Mon Sep 17 00:00:00 2001 From: gio-shara-code Date: Mon, 24 Jul 2023 14:41:18 +0200 Subject: [PATCH 05/17] fix: fixed the issue `Failed to add workspace ''` - excluding `.next` and `dist` in every subdirectory - filtering packages when by package names when executing `pnpm build` --- package.json | 4 +- pnpm-lock.yaml | 499 +++++++++++++++++++++++++++++++++++++++++--- pnpm-workspace.yaml | 6 +- 3 files changed, 473 insertions(+), 36 deletions(-) diff --git a/package.json b/package.json index 5780ac2..3c4233b 100644 --- a/package.json +++ b/package.json @@ -1,8 +1,8 @@ { "private": true, "scripts": { - "build": "turbo run build", - "dev": "turbo run dev", + "build": "turbo run build --filter=@adapters/* --filter=@providers/* --filter=@server/* --filter=shared-types", + "dev": "turbo run dev --filter=nextjs-example", "test": "turbo run test", "lint": "turbo run lint", "format": "prettier --write \"**/*.{ts,tsx,md}\"", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c58f5a0..aab2fd2 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -70,6 +70,51 @@ importers: specifier: ^5.1.6 version: 5.1.6 + apps/examples/nextjs: + dependencies: + '@adapters/interface': + specifier: workspace:* + version: link:../../../packages/adapters/interface + '@providers/s3': + specifier: workspace:* + version: link:../../../packages/providers/s3 + '@server/core': + specifier: workspace:* + version: link:../../../packages/server/core + '@types/node': + specifier: 20.3.3 + version: 20.3.3 + '@types/react': + specifier: 18.2.14 + version: 18.2.14 + '@types/react-dom': + specifier: 18.2.6 + version: 18.2.6 + autoprefixer: + specifier: 10.4.14 + version: 10.4.14(postcss@8.4.24) + next: + specifier: 13.4.8 + version: 13.4.8(@babel/core@7.22.6)(react-dom@18.2.0)(react@18.2.0) + postcss: + specifier: 8.4.24 + version: 8.4.24 + react: + specifier: 18.2.0 + version: 18.2.0 + react-dom: + specifier: 18.2.0 + version: 18.2.0(react@18.2.0) + shared-types: + specifier: workspace:* + version: link:../../../packages/types + tailwindcss: + specifier: 3.3.2 + version: 3.3.2(ts-node@10.9.1) + typescript: + specifier: ^5.1.6 + version: 5.1.6 + packages/adapters/interface: dependencies: shared-types: @@ -203,6 +248,11 @@ packages: engines: {node: '>=0.10.0'} dev: true + /@alloc/quick-lru@5.2.0: + resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==} + engines: {node: '>=10'} + dev: false + /@ampproject/remapping@2.2.1: resolution: {integrity: sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==} engines: {node: '>=6.0.0'} @@ -2054,6 +2104,91 @@ packages: read-yaml-file: 1.1.0 dev: true + /@next/env@13.4.8: + resolution: {integrity: sha512-twuSf1klb3k9wXI7IZhbZGtFCWvGD4wXTY2rmvzIgVhXhs7ISThrbNyutBx3jWIL8Y/Hk9+woytFz5QsgtcRKQ==} + dev: false + + /@next/swc-darwin-arm64@13.4.8: + resolution: {integrity: sha512-MSFplVM4dTWOuKAUv0XR9gY7AWtMSBu9os9f+kp+s5rWhM1I2CdR3obFttd6366nS/W/VZxbPM5oEIdlIa46zA==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [darwin] + requiresBuild: true + dev: false + optional: true + + /@next/swc-darwin-x64@13.4.8: + resolution: {integrity: sha512-Reox+UXgonon9P0WNDE6w85DGtyBqGitl/ryznOvn6TvfxEaZIpTgeu3ZrJLU9dHSMhiK7YAM793mE/Zii2/Qw==} + engines: {node: '>= 10'} + cpu: [x64] + os: [darwin] + requiresBuild: true + dev: false + optional: true + + /@next/swc-linux-arm64-gnu@13.4.8: + resolution: {integrity: sha512-kdyzYvAYtqQVgzIKNN7e1rLU8aZv86FDSRqPlOkKZlvqudvTO0iohuTPmnEEDlECeBM6qRPShNffotDcU/R2KA==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: false + optional: true + + /@next/swc-linux-arm64-musl@13.4.8: + resolution: {integrity: sha512-oWxx4yRkUGcR81XwbI+T0zhZ3bDF6V1aVLpG+C7hSG50ULpV8gC39UxVO22/bv93ZlcfMY4zl8xkz9Klct6dpQ==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: false + optional: true + + /@next/swc-linux-x64-gnu@13.4.8: + resolution: {integrity: sha512-anhtvuO6eE9YRhYnaEGTfbpH3L5gT/9qPFcNoi6xS432r/4DAtpJY8kNktqkTVevVIC/pVumqO8tV59PR3zbNg==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: false + optional: true + + /@next/swc-linux-x64-musl@13.4.8: + resolution: {integrity: sha512-aR+J4wWfNgH1DwCCBNjan7Iumx0lLtn+2/rEYuhIrYLY4vnxqSVGz9u3fXcgUwo6Q9LT8NFkaqK1vPprdq+BXg==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: false + optional: true + + /@next/swc-win32-arm64-msvc@13.4.8: + resolution: {integrity: sha512-OWBKIrJwQBTqrat0xhxEB/jcsjJR3+diD9nc/Y8F1mRdQzsn4bPsomgJyuqPVZs6Lz3K18qdIkvywmfSq75SsQ==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [win32] + requiresBuild: true + dev: false + optional: true + + /@next/swc-win32-ia32-msvc@13.4.8: + resolution: {integrity: sha512-agiPWGjUndXGTOn4ChbKipQXRA6/UPkywAWIkx7BhgGv48TiJfHTK6MGfBoL9tS6B4mtW39++uy0wFPnfD0JWg==} + engines: {node: '>= 10'} + cpu: [ia32] + os: [win32] + requiresBuild: true + dev: false + optional: true + + /@next/swc-win32-x64-msvc@13.4.8: + resolution: {integrity: sha512-UIRKoByVKbuR6SnFG4JM8EMFlJrfEGuUQ1ihxzEleWcNwRMMiVaCj1KyqfTOW8VTQhJ0u8P1Ngg6q1RwnIBTtw==} + engines: {node: '>= 10'} + cpu: [x64] + os: [win32] + requiresBuild: true + dev: false + optional: true + /@nicolo-ribaudo/semver-v6@6.3.3: resolution: {integrity: sha512-3Yc1fUTs69MG/uZbJlLSI3JISMn2UV2rg+1D/vROUqZyh3l6iYHCs7GMp+M40ZD7yOdDbYjJcU1oTJhrc+dGKg==} @@ -2063,12 +2198,10 @@ packages: dependencies: '@nodelib/fs.stat': 2.0.5 run-parallel: 1.2.0 - dev: true /@nodelib/fs.stat@2.0.5: resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} engines: {node: '>= 8'} - dev: true /@nodelib/fs.walk@1.2.8: resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} @@ -2076,7 +2209,6 @@ packages: dependencies: '@nodelib/fs.scandir': 2.1.5 fastq: 1.15.0 - dev: true /@sinclair/typebox@0.25.24: resolution: {integrity: sha512-XJfwUVUKDHF5ugKwIcxEgc9k8b7HbznCp6eUfWgu710hMPNIO4aw4/zB5RogDQz8nd6gyCDpU9O/m6qYEWY6yQ==} @@ -2571,6 +2703,12 @@ packages: '@swc/core-win32-ia32-msvc': 1.3.67 '@swc/core-win32-x64-msvc': 1.3.67 + /@swc/helpers@0.5.1: + resolution: {integrity: sha512-sJ902EfIzn1Fa+qYmjdQqh8tPsoxyBz+8yBKC2HKUxyezKJFwPGOn7pv4WY6QuQW//ySQi5lJjA/ZT9sNWWNTg==} + dependencies: + tslib: 2.6.0 + dev: false + /@swc/jest@0.2.26(@swc/core@1.3.67): resolution: {integrity: sha512-7lAi7q7ShTO3E5Gt1Xqf3pIhRbERxR1DUxvtVa9WKzIB+HGQ7wZP5sYx86zqnaEoKKGhmOoZ7gyW0IRu8Br5+A==} engines: {npm: '>= 7.0.0'} @@ -2669,6 +2807,10 @@ packages: /@types/node@18.16.19: resolution: {integrity: sha512-IXl7o+R9iti9eBW4Wg2hx1xQDig183jj7YLn8F7udNceyfkbn1ZxmzZXuak20gR40D7pIkIY1kYGx5VIGbaHKA==} + /@types/node@20.3.3: + resolution: {integrity: sha512-wheIYdr4NYML61AjC8MKj/2jrR/kDQri/CIpVoZwldwhnIrD/j9jIU5bJ8yBKuB2VhpFV7Ab6G2XkBjv9r9Zzw==} + dev: false + /@types/normalize-package-data@2.4.1: resolution: {integrity: sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==} dev: true @@ -2676,6 +2818,28 @@ packages: /@types/prettier@2.7.3: resolution: {integrity: sha512-+68kP9yzs4LMp7VNh8gdzMSPZFL44MLGqiHWvttYJe+6qnuVr4Ek9wSBQoveqY/r+LwjCcU29kNVkidwim+kYA==} + /@types/prop-types@15.7.5: + resolution: {integrity: sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==} + dev: false + + /@types/react-dom@18.2.6: + resolution: {integrity: sha512-2et4PDvg6PVCyS7fuTc4gPoksV58bW0RwSxWKcPRcHZf0PRUGq03TKcD/rUHe3azfV6/5/biUBJw+HhCQjaP0A==} + dependencies: + '@types/react': 18.2.14 + dev: false + + /@types/react@18.2.14: + resolution: {integrity: sha512-A0zjq+QN/O0Kpe30hA1GidzyFjatVvrpIvWLxD+xv67Vt91TWWgco9IvrJBkeyHm1trGaFS/FSGqPlhyeZRm0g==} + dependencies: + '@types/prop-types': 15.7.5 + '@types/scheduler': 0.16.3 + csstype: 3.1.2 + dev: false + + /@types/scheduler@0.16.3: + resolution: {integrity: sha512-5cJ8CB4yAx7BH1oMvdU0Jh9lrEXyPkar6F9G/ERswkCuvP4KQZfZkSjcMbAICCpQTN4OuZn8tz0HiKv9TGZgrQ==} + dev: false + /@types/semver@7.5.0: resolution: {integrity: sha512-G8hZ6XJiHnuhQKR7ZmysCeJWE08o8T0AXtk5darsCaTVsYZhhgUrq53jizaR2FvsoeCwJhlmwTjkXBY5Pn/ZHw==} dev: true @@ -2905,7 +3069,6 @@ packages: /any-promise@1.3.0: resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==} - dev: true /anymatch@3.1.3: resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} @@ -2917,6 +3080,10 @@ packages: /arg@4.1.3: resolution: {integrity: sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==} + /arg@5.0.2: + resolution: {integrity: sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==} + dev: false + /argparse@1.0.10: resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==} dependencies: @@ -2957,6 +3124,22 @@ packages: engines: {node: '>=0.10.0'} dev: true + /autoprefixer@10.4.14(postcss@8.4.24): + resolution: {integrity: sha512-FQzyfOsTlwVzjHxKEqRIAdJx9niO6VCBCoEwax/VLSoQF29ggECcPuBqUMZ+u8jCZOPSy8b8/8KnuFbp0SaFZQ==} + engines: {node: ^10 || ^12 || >=14} + hasBin: true + peerDependencies: + postcss: ^8.1.0 + dependencies: + browserslist: 4.21.9 + caniuse-lite: 1.0.30001512 + fraction.js: 4.2.0 + normalize-range: 0.1.2 + picocolors: 1.0.0 + postcss: 8.4.24 + postcss-value-parser: 4.2.0 + dev: false + /available-typed-arrays@1.0.5: resolution: {integrity: sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==} engines: {node: '>= 0.4'} @@ -3042,7 +3225,6 @@ packages: /binary-extensions@2.2.0: resolution: {integrity: sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==} engines: {node: '>=8'} - dev: true /bowser@2.11.0: resolution: {integrity: sha512-AlcaJBi/pqqJBIQ8U9Mcpc9i8Aqxn88Skv5d+xBX006BY5u8N3mGLHa5Lgppa7L/HfwgwLgZ6NYs+Ag6uUmJRA==} @@ -3069,6 +3251,7 @@ packages: /browserslist@4.21.9: resolution: {integrity: sha512-M0MFoZzbUrRU4KNfCrDLnvyE7gub+peetoTid3TBIqtunaDJyXlwhakT+/VkvSXcfIzFfK/nkCs4nmyTmxdNSg==} engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true dependencies: caniuse-lite: 1.0.30001512 electron-to-chromium: 1.4.449 @@ -3093,6 +3276,13 @@ packages: load-tsconfig: 0.2.5 dev: true + /busboy@1.6.0: + resolution: {integrity: sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==} + engines: {node: '>=10.16.0'} + dependencies: + streamsearch: 1.1.0 + dev: false + /cac@6.7.14: resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} engines: {node: '>=8'} @@ -3109,6 +3299,11 @@ packages: resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} engines: {node: '>=6'} + /camelcase-css@2.0.1: + resolution: {integrity: sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==} + engines: {node: '>= 6'} + dev: false + /camelcase-keys@6.2.2: resolution: {integrity: sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg==} engines: {node: '>=8'} @@ -3165,7 +3360,6 @@ packages: readdirp: 3.6.0 optionalDependencies: fsevents: 2.3.2 - dev: true /ci-info@3.8.0: resolution: {integrity: sha512-eXTggHWSooYhq49F2opQhuHWgzucfF2YgODK4e1566GQs5BIfP30B0oenwBJHfWxAs2fyPB1s7Mg949zLf61Yw==} @@ -3174,6 +3368,10 @@ packages: /cjs-module-lexer@1.2.3: resolution: {integrity: sha512-0TNiGstbQmCFwt4akjjBg5pLRTSyj/PkWQ1ZoO2zntmg9yLqSRxwEa4iCfQLGjqhiqBfOJa7W/E8wfGrTDmlZQ==} + /client-only@0.0.1: + resolution: {integrity: sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==} + dev: false + /cliui@6.0.0: resolution: {integrity: sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==} dependencies: @@ -3226,7 +3424,6 @@ packages: /commander@4.1.1: resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==} engines: {node: '>= 6'} - dev: true /compare-func@2.0.0: resolution: {integrity: sha512-zHig5N+tPWARooBnb0Zx1MFcdfpyJrfTJ3Y5L+IFvUm8rM74hHz66z0gw0x4tijh5CorKkKUCnW82R2vmpeCRA==} @@ -3332,6 +3529,16 @@ packages: shebang-command: 2.0.0 which: 2.0.2 + /cssesc@3.0.0: + resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==} + engines: {node: '>=4'} + hasBin: true + dev: false + + /csstype@3.1.2: + resolution: {integrity: sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==} + dev: false + /csv-generate@3.4.3: resolution: {integrity: sha512-w/T+rqR0vwvHqWs/1ZyMDWtHHSJaN06klRqJXBEpDJaM/+dZkso0OKh1VcuuYvK3XM53KysVNq8Ko/epCK8wOw==} dev: true @@ -3436,6 +3643,10 @@ packages: resolution: {integrity: sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==} engines: {node: '>=8'} + /didyoumean@1.2.2: + resolution: {integrity: sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==} + dev: false + /diff-sequences@29.4.3: resolution: {integrity: sha512-ofrBgwpPhCD85kMKtE9RYFFq6OC1A89oW2vvgWZNCwxrUpRUILopY7lsYyMDSjc8g6U6aiO0Qubg6r4Wgt5ZnA==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -3451,6 +3662,10 @@ packages: path-type: 4.0.0 dev: true + /dlv@1.1.3: + resolution: {integrity: sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==} + dev: false + /doctrine@3.0.0: resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==} engines: {node: '>=6.0.0'} @@ -3766,7 +3981,6 @@ packages: glob-parent: 5.1.2 merge2: 1.4.1 micromatch: 4.0.5 - dev: true /fast-json-stable-stringify@2.1.0: resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} @@ -3785,7 +3999,6 @@ packages: resolution: {integrity: sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==} dependencies: reusify: 1.0.4 - dev: true /fb-watchman@2.0.2: resolution: {integrity: sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==} @@ -3845,6 +4058,10 @@ packages: is-callable: 1.2.7 dev: true + /fraction.js@4.2.0: + resolution: {integrity: sha512-MhLuK+2gUcnZe8ZHlaaINnQLl0xRIGRfcGk2yl8xoQAfHrSsL3rYu6FCmBdkdbhc9EPlwyGHewaRsvwRMJtAlA==} + dev: false + /fs-extra@11.1.1: resolution: {integrity: sha512-MGIE4HOvQCeUCzmlHs0vXpih4ysz4wg9qiSAu6cd42lVwPbTM1TjV7RusoyQqMmk/95gdQZX72u+YW+c3eEpFQ==} engines: {node: '>=14.14'} @@ -3948,14 +4165,16 @@ packages: engines: {node: '>= 6'} dependencies: is-glob: 4.0.3 - dev: true /glob-parent@6.0.2: resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} engines: {node: '>=10.13.0'} dependencies: is-glob: 4.0.3 - dev: true + + /glob-to-regexp@0.4.1: + resolution: {integrity: sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==} + dev: false /glob@7.1.6: resolution: {integrity: sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==} @@ -3966,7 +4185,6 @@ packages: minimatch: 3.1.2 once: 1.4.0 path-is-absolute: 1.0.1 - dev: true /glob@7.2.3: resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} @@ -4189,7 +4407,6 @@ packages: engines: {node: '>=8'} dependencies: binary-extensions: 2.2.0 - dev: true /is-boolean-object@1.1.2: resolution: {integrity: sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==} @@ -4225,7 +4442,6 @@ packages: /is-extglob@2.1.1: resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} engines: {node: '>=0.10.0'} - dev: true /is-fullwidth-code-point@3.0.0: resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} @@ -4240,7 +4456,6 @@ packages: engines: {node: '>=0.10.0'} dependencies: is-extglob: 2.1.1 - dev: true /is-negative-zero@2.0.2: resolution: {integrity: sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==} @@ -4769,6 +4984,11 @@ packages: - supports-color - ts-node + /jiti@1.19.1: + resolution: {integrity: sha512-oVhqoRDaBXf7sjkll95LHVS6Myyyb1zaunVwk4Z0+WPSW4gjS0pl01zYKHScTuyEhQsFxV5L4DR5r+YqSyqyyg==} + hasBin: true + dev: false + /joycon@3.1.1: resolution: {integrity: sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==} engines: {node: '>=10'} @@ -4864,7 +5084,6 @@ packages: /lilconfig@2.1.0: resolution: {integrity: sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==} engines: {node: '>=10'} - dev: true /lines-and-columns@1.2.4: resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} @@ -4945,6 +5164,13 @@ packages: resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} dev: true + /loose-envify@1.4.0: + resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} + hasBin: true + dependencies: + js-tokens: 4.0.0 + dev: false + /lru-cache@4.1.5: resolution: {integrity: sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==} dependencies: @@ -5027,7 +5253,6 @@ packages: /merge2@1.4.1: resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} engines: {node: '>= 8'} - dev: true /micromatch@4.0.5: resolution: {integrity: sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==} @@ -5081,7 +5306,11 @@ packages: any-promise: 1.3.0 object-assign: 4.1.1 thenify-all: 1.6.0 - dev: true + + /nanoid@3.3.6: + resolution: {integrity: sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true /natural-compare-lite@1.4.0: resolution: {integrity: sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==} @@ -5090,6 +5319,49 @@ packages: /natural-compare@1.4.0: resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} + /next@13.4.8(@babel/core@7.22.6)(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-lxUjndYKjZHGK3CWeN2RI+/6ni6EUvjiqGWXAYPxUfGIdFGQ5XoisrqAJ/dF74aP27buAfs8MKIbIMMdxjqSBg==} + engines: {node: '>=16.8.0'} + hasBin: true + peerDependencies: + '@opentelemetry/api': ^1.1.0 + fibers: '>= 3.1.0' + react: ^18.2.0 + react-dom: ^18.2.0 + sass: ^1.3.0 + peerDependenciesMeta: + '@opentelemetry/api': + optional: true + fibers: + optional: true + sass: + optional: true + dependencies: + '@next/env': 13.4.8 + '@swc/helpers': 0.5.1 + busboy: 1.6.0 + caniuse-lite: 1.0.30001512 + postcss: 8.4.14 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + styled-jsx: 5.1.1(@babel/core@7.22.6)(react@18.2.0) + watchpack: 2.4.0 + zod: 3.21.4 + optionalDependencies: + '@next/swc-darwin-arm64': 13.4.8 + '@next/swc-darwin-x64': 13.4.8 + '@next/swc-linux-arm64-gnu': 13.4.8 + '@next/swc-linux-arm64-musl': 13.4.8 + '@next/swc-linux-x64-gnu': 13.4.8 + '@next/swc-linux-x64-musl': 13.4.8 + '@next/swc-win32-arm64-msvc': 13.4.8 + '@next/swc-win32-ia32-msvc': 13.4.8 + '@next/swc-win32-x64-msvc': 13.4.8 + transitivePeerDependencies: + - '@babel/core' + - babel-plugin-macros + dev: false + /node-int64@0.4.0: resolution: {integrity: sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==} @@ -5141,6 +5413,11 @@ packages: resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} engines: {node: '>=0.10.0'} + /normalize-range@0.1.2: + resolution: {integrity: sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==} + engines: {node: '>=0.10.0'} + dev: false + /npm-run-path@4.0.1: resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==} engines: {node: '>=8'} @@ -5150,7 +5427,11 @@ packages: /object-assign@4.1.1: resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} engines: {node: '>=0.10.0'} - dev: true + + /object-hash@3.0.0: + resolution: {integrity: sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==} + engines: {node: '>= 6'} + dev: false /object-inspect@1.12.3: resolution: {integrity: sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==} @@ -5287,6 +5568,11 @@ packages: resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} engines: {node: '>=8.6'} + /pify@2.3.0: + resolution: {integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==} + engines: {node: '>=0.10.0'} + dev: false + /pify@4.0.1: resolution: {integrity: sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==} engines: {node: '>=6'} @@ -5302,7 +5588,29 @@ packages: dependencies: find-up: 4.1.0 - /postcss-load-config@4.0.1(ts-node@10.9.1): + /postcss-import@15.1.0(postcss@8.4.24): + resolution: {integrity: sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==} + engines: {node: '>=14.0.0'} + peerDependencies: + postcss: ^8.0.0 + dependencies: + postcss: 8.4.24 + postcss-value-parser: 4.2.0 + read-cache: 1.0.0 + resolve: 1.22.2 + dev: false + + /postcss-js@4.0.1(postcss@8.4.24): + resolution: {integrity: sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==} + engines: {node: ^12 || ^14 || >= 16} + peerDependencies: + postcss: ^8.4.21 + dependencies: + camelcase-css: 2.0.1 + postcss: 8.4.24 + dev: false + + /postcss-load-config@4.0.1(postcss@8.4.24)(ts-node@10.9.1): resolution: {integrity: sha512-vEJIc8RdiBRu3oRAI0ymerOn+7rPuMvRXslTvZUKZonDHFIczxztIyJ1urxM1x9JXEikvpWWTUUqal5j/8QgvA==} engines: {node: '>= 14'} peerDependencies: @@ -5315,9 +5623,48 @@ packages: optional: true dependencies: lilconfig: 2.1.0 + postcss: 8.4.24 ts-node: 10.9.1(@swc/core@1.3.67)(@types/node@18.16.19)(typescript@5.1.6) yaml: 2.3.1 - dev: true + + /postcss-nested@6.0.1(postcss@8.4.24): + resolution: {integrity: sha512-mEp4xPMi5bSWiMbsgoPfcP74lsWLHkQbZc3sY+jWYd65CUwXrUaTp0fmNpa01ZcETKlIgUdFN/MpS2xZtqL9dQ==} + engines: {node: '>=12.0'} + peerDependencies: + postcss: ^8.2.14 + dependencies: + postcss: 8.4.24 + postcss-selector-parser: 6.0.13 + dev: false + + /postcss-selector-parser@6.0.13: + resolution: {integrity: sha512-EaV1Gl4mUEV4ddhDnv/xtj7sxwrwxdetHdWUGnT4VJQf+4d05v6lHYZr8N573k5Z0BViss7BDhfWtKS3+sfAqQ==} + engines: {node: '>=4'} + dependencies: + cssesc: 3.0.0 + util-deprecate: 1.0.2 + dev: false + + /postcss-value-parser@4.2.0: + resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==} + dev: false + + /postcss@8.4.14: + resolution: {integrity: sha512-E398TUmfAYFPBSdzgeieK2Y1+1cpdxJx8yXbK/m57nRhKSmk1GB2tO4lbLBtlkfPQTDKfe4Xqv1ASWPpayPEig==} + engines: {node: ^10 || ^12 || >=14} + dependencies: + nanoid: 3.3.6 + picocolors: 1.0.0 + source-map-js: 1.0.2 + dev: false + + /postcss@8.4.24: + resolution: {integrity: sha512-M0RzbcI0sO/XJNucsGjvWU9ERWxb/ytp1w6dKtxTKgixdtQDq4rmx/g8W1hnaheq9jgwL/oyEdH5Bc4WwJKMqg==} + engines: {node: ^10 || ^12 || >=14} + dependencies: + nanoid: 3.3.6 + picocolors: 1.0.0 + source-map-js: 1.0.2 /preferred-pm@3.0.3: resolution: {integrity: sha512-+wZgbxNES/KlJs9q40F/1sfOd/j7f1O9JaHcW5Dsn3aUUOZg3L2bjpVUcKV2jvtElYfoTuQiNeMfQJ4kwUAhCQ==} @@ -5377,16 +5724,38 @@ packages: /queue-microtask@1.2.3: resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} - dev: true /quick-lru@4.0.1: resolution: {integrity: sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==} engines: {node: '>=8'} dev: true + /react-dom@18.2.0(react@18.2.0): + resolution: {integrity: sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==} + peerDependencies: + react: ^18.2.0 + dependencies: + loose-envify: 1.4.0 + react: 18.2.0 + scheduler: 0.23.0 + dev: false + /react-is@18.2.0: resolution: {integrity: sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==} + /react@18.2.0: + resolution: {integrity: sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==} + engines: {node: '>=0.10.0'} + dependencies: + loose-envify: 1.4.0 + dev: false + + /read-cache@1.0.0: + resolution: {integrity: sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==} + dependencies: + pify: 2.3.0 + dev: false + /read-pkg-up@7.0.1: resolution: {integrity: sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==} engines: {node: '>=8'} @@ -5430,7 +5799,6 @@ packages: engines: {node: '>=8.10.0'} dependencies: picomatch: 2.3.1 - dev: true /redent@3.0.0: resolution: {integrity: sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==} @@ -5494,6 +5862,7 @@ packages: /resolve@1.22.2: resolution: {integrity: sha512-Sb+mjNHOULsBv818T40qSPeRiuWLyaGMa5ewydRLFimneixmVy2zdivRl+AF6jaYPC8ERxGDmFSiqui6SfPd+g==} + hasBin: true dependencies: is-core-module: 2.12.1 path-parse: 1.0.7 @@ -5502,7 +5871,6 @@ packages: /reusify@1.0.4: resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} engines: {iojs: '>=1.0.0', node: '>=0.10.0'} - dev: true /rimraf@3.0.2: resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} @@ -5521,7 +5889,6 @@ packages: resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} dependencies: queue-microtask: 1.2.3 - dev: true /rxjs@7.8.1: resolution: {integrity: sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==} @@ -5545,6 +5912,12 @@ packages: resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} dev: true + /scheduler@0.23.0: + resolution: {integrity: sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==} + dependencies: + loose-envify: 1.4.0 + dev: false + /semver@5.7.1: resolution: {integrity: sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==} dev: true @@ -5636,6 +6009,10 @@ packages: yargs: 15.4.1 dev: true + /source-map-js@1.0.2: + resolution: {integrity: sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==} + engines: {node: '>=0.10.0'} + /source-map-support@0.5.13: resolution: {integrity: sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==} dependencies: @@ -5707,6 +6084,11 @@ packages: mixme: 0.5.9 dev: true + /streamsearch@1.1.0: + resolution: {integrity: sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==} + engines: {node: '>=10.0.0'} + dev: false + /string-length@4.0.2: resolution: {integrity: sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==} engines: {node: '>=10'} @@ -5787,6 +6169,24 @@ packages: resolution: {integrity: sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==} dev: false + /styled-jsx@5.1.1(@babel/core@7.22.6)(react@18.2.0): + resolution: {integrity: sha512-pW7uC1l4mBZ8ugbiZrcIsiIvVx1UmTfw7UkC3Um2tmfUq9Bhk8IiyEIPl6F8agHgjzku6j0xQEZbfA5uSgSaCw==} + engines: {node: '>= 12.0.0'} + peerDependencies: + '@babel/core': '*' + babel-plugin-macros: '*' + react: '>= 16.8.0 || 17.x.x || ^18.0.0-0' + peerDependenciesMeta: + '@babel/core': + optional: true + babel-plugin-macros: + optional: true + dependencies: + '@babel/core': 7.22.6 + client-only: 0.0.1 + react: 18.2.0 + dev: false + /sucrase@3.32.0: resolution: {integrity: sha512-ydQOU34rpSyj2TGyz4D2p8rbktIOZ8QY9s+DGLvFU1i5pWJE8vkpruCjGCMHsdXwnD7JDcS+noSwM/a7zyNFDQ==} engines: {node: '>=8'} @@ -5798,7 +6198,6 @@ packages: mz: 2.7.0 pirates: 4.0.6 ts-interface-checker: 0.1.13 - dev: true /supports-color@5.5.0: resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==} @@ -5822,6 +6221,38 @@ packages: resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} engines: {node: '>= 0.4'} + /tailwindcss@3.3.2(ts-node@10.9.1): + resolution: {integrity: sha512-9jPkMiIBXvPc2KywkraqsUfbfj+dHDb+JPWtSJa9MLFdrPyazI7q6WX2sUrm7R9eVR7qqv3Pas7EvQFzxKnI6w==} + engines: {node: '>=14.0.0'} + hasBin: true + dependencies: + '@alloc/quick-lru': 5.2.0 + arg: 5.0.2 + chokidar: 3.5.3 + didyoumean: 1.2.2 + dlv: 1.1.3 + fast-glob: 3.3.0 + glob-parent: 6.0.2 + is-glob: 4.0.3 + jiti: 1.19.1 + lilconfig: 2.1.0 + micromatch: 4.0.5 + normalize-path: 3.0.0 + object-hash: 3.0.0 + picocolors: 1.0.0 + postcss: 8.4.24 + postcss-import: 15.1.0(postcss@8.4.24) + postcss-js: 4.0.1(postcss@8.4.24) + postcss-load-config: 4.0.1(postcss@8.4.24)(ts-node@10.9.1) + postcss-nested: 6.0.1(postcss@8.4.24) + postcss-selector-parser: 6.0.13 + postcss-value-parser: 4.2.0 + resolve: 1.22.2 + sucrase: 3.32.0 + transitivePeerDependencies: + - ts-node + dev: false + /term-size@2.2.1: resolution: {integrity: sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg==} engines: {node: '>=8'} @@ -5849,13 +6280,11 @@ packages: engines: {node: '>=0.8'} dependencies: thenify: 3.3.1 - dev: true /thenify@3.3.1: resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==} dependencies: any-promise: 1.3.0 - dev: true /through2@4.0.2: resolution: {integrity: sha512-iOqSav00cVxEEICeD7TjLB1sueEL+81Wpzp2bY17uZjZN0pWZPuo4suZ/61VujxmqSGFfgOcNuTZ85QJwNZQpw==} @@ -5910,7 +6339,6 @@ packages: /ts-interface-checker@0.1.13: resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==} - dev: true /ts-node@10.9.1(@swc/core@1.3.67)(@types/node@18.16.19)(typescript@5.1.6): resolution: {integrity: sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==} @@ -5974,7 +6402,7 @@ packages: execa: 5.1.1 globby: 11.1.0 joycon: 3.1.1 - postcss-load-config: 4.0.1(ts-node@10.9.1) + postcss-load-config: 4.0.1(postcss@8.4.24)(ts-node@10.9.1) resolve-from: 5.0.0 rollup: 3.26.0 source-map: 0.8.0-beta.0 @@ -6120,6 +6548,7 @@ packages: /typescript@5.1.6: resolution: {integrity: sha512-zaWCozRZ6DLEWAWFrVDz1H6FVXzUSfTy5FUMWsQlU8Ym5JP9eO4xkTIROFCQvhQf61z6O/G6ugw3SgAnvvm+HA==} engines: {node: '>=14.17'} + hasBin: true /unbox-primitive@1.0.2: resolution: {integrity: sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==} @@ -6162,7 +6591,6 @@ packages: /util-deprecate@1.0.2: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} - dev: true /uuid@8.3.2: resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==} @@ -6191,6 +6619,14 @@ packages: dependencies: makeerror: 1.0.12 + /watchpack@2.4.0: + resolution: {integrity: sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==} + engines: {node: '>=10.13.0'} + dependencies: + glob-to-regexp: 0.4.1 + graceful-fs: 4.2.11 + dev: false + /wcwidth@1.0.1: resolution: {integrity: sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==} dependencies: @@ -6303,7 +6739,6 @@ packages: /yaml@2.3.1: resolution: {integrity: sha512-2eHWfjaoXgTBC2jNM1LRef62VQa0umtvRiDSk6HSzW7RvS5YtkabJrwYLLEKWBc8a5U2PTSCs+dJjUTJdlHsWQ==} engines: {node: '>= 14'} - dev: true /yargs-parser@18.1.3: resolution: {integrity: sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==} diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 84935dc..32aae9d 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -1,3 +1,5 @@ packages: - - "apps/*" - - "packages/**/*" + - "apps/**" + - "packages/**" + - "!**/.next/**" + - "!**/dist/**" From f2c7ff72c0b8ffb116f21a6aa8b9c671d034c980 Mon Sep 17 00:00:00 2001 From: gio-shara-code Date: Mon, 24 Jul 2023 16:07:22 +0200 Subject: [PATCH 06/17] refactor: abstracted components and put them in a separate files + renamed component - Section -> StepSection - Abstracted primary button in a separate file - created generic type file --- apps/examples/nextjs/src/app/StepSection.tsx | 51 ++++++++++ apps/examples/nextjs/src/app/page.tsx | 95 ++++--------------- .../src/components/atoms/buttons/Primary.tsx | 15 +++ .../nextjs/src/components/types/generic.ts | 7 ++ 4 files changed, 92 insertions(+), 76 deletions(-) create mode 100644 apps/examples/nextjs/src/app/StepSection.tsx create mode 100644 apps/examples/nextjs/src/components/atoms/buttons/Primary.tsx create mode 100644 apps/examples/nextjs/src/components/types/generic.ts diff --git a/apps/examples/nextjs/src/app/StepSection.tsx b/apps/examples/nextjs/src/app/StepSection.tsx new file mode 100644 index 0000000..a80fafa --- /dev/null +++ b/apps/examples/nextjs/src/app/StepSection.tsx @@ -0,0 +1,51 @@ + import {ReactNode, useMemo} from "react"; + + +interface NumberComponentProps { + number: number + done?: boolean +} + +const NumberComponent = ({ number, done }: NumberComponentProps) => { + return ( +
+ {done ? '✓' : number} +
+ ) +} + +interface Props { + number: number + active: boolean + done?: boolean + children: (disabled: boolean) => ReactNode +} + +export default function StepSection ({ children, number, active, done }: Props){ + const disabled = useMemo(() => { + return !active + }, [active]) + + return ( +
+
+ +
+
+ {children(disabled)} +
+
+ ) +} diff --git a/apps/examples/nextjs/src/app/page.tsx b/apps/examples/nextjs/src/app/page.tsx index 0b25e72..87df25e 100644 --- a/apps/examples/nextjs/src/app/page.tsx +++ b/apps/examples/nextjs/src/app/page.tsx @@ -1,9 +1,11 @@ 'use client' -import { ReactNode, useMemo, useState } from 'react' +import { useMemo, useState } from 'react' import { MediaFile, FileStatus } from 'shared-types' import { SignedUploadUrl } from '@server/core' import { z } from 'zod' +import PrimaryButton from "@components/atoms/buttons/Primary"; +import StepSection from "./StepSection"; enum Step { CHOOSE_FILE, @@ -117,6 +119,8 @@ class UploadHandler { } } + + export default function Home() { const [data, setData] = useState<{ message: string } | null>() const controller = useMemo(() => new AbortController(), []) @@ -175,7 +179,7 @@ export default function Home() { Upload -
)} -
+ -
+ {(disabled) => ( - + Request Upload URL )} -
+ -
+ {(disabled) => ( - + Upload File )} -
+ -
+ {(disabled) => ( - + Confirm Upload )} -
+ -
+ {(disabled) => ( - + Poll File )} -
+ ) } -interface SectionProps { - number: number - active: boolean - done?: boolean - children: (disabled: boolean) => ReactNode -} -const Section = ({ children, number, active, done }: SectionProps) => { - const disabled = useMemo(() => { - return !active - }, [active]) - - return ( -
-
- -
-
- {children(disabled)} -
-
- ) -} -interface NumberComponentProps { - number: number - done?: boolean -} -const NumberComponent = ({ number, done }: NumberComponentProps) => { - return ( -
- {done ? '✓' : number} -
- ) -} - -interface ButtonProps { - disabled?: boolean - children: ReactNode -} - -const Button = ({ children, disabled }: ButtonProps) => { - return ( - - ) -} diff --git a/apps/examples/nextjs/src/components/atoms/buttons/Primary.tsx b/apps/examples/nextjs/src/components/atoms/buttons/Primary.tsx new file mode 100644 index 0000000..a8c5736 --- /dev/null +++ b/apps/examples/nextjs/src/components/atoms/buttons/Primary.tsx @@ -0,0 +1,15 @@ +import React from 'react'; +import {ButtonProps} from "@components/types/generic"; + +export default function PrimaryButton ({ children, disabled }: ButtonProps) { + return ( + + ) +} + + diff --git a/apps/examples/nextjs/src/components/types/generic.ts b/apps/examples/nextjs/src/components/types/generic.ts new file mode 100644 index 0000000..4a1e69c --- /dev/null +++ b/apps/examples/nextjs/src/components/types/generic.ts @@ -0,0 +1,7 @@ +import {ReactNode} from "react"; + +export interface ButtonProps { + disabled?: boolean + children: ReactNode +} + From 23b74cf348cf5bef88e8c6d8e31ca96d0a9cc568 Mon Sep 17 00:00:00 2001 From: gio-shara-code Date: Mon, 24 Jul 2023 16:10:32 +0200 Subject: [PATCH 07/17] refactor: abstract enum `Step` and `UploadHandler` class in a separate file --- apps/examples/nextjs/src/app/page.tsx | 122 +----------------- apps/examples/nextjs/src/types/step.ts | 7 + .../nextjs/src/utils/upload_handler.ts | 108 ++++++++++++++++ 3 files changed, 119 insertions(+), 118 deletions(-) create mode 100644 apps/examples/nextjs/src/types/step.ts create mode 100644 apps/examples/nextjs/src/utils/upload_handler.ts diff --git a/apps/examples/nextjs/src/app/page.tsx b/apps/examples/nextjs/src/app/page.tsx index 87df25e..fbd88c7 100644 --- a/apps/examples/nextjs/src/app/page.tsx +++ b/apps/examples/nextjs/src/app/page.tsx @@ -1,123 +1,9 @@ 'use client' - import { useMemo, useState } from 'react' -import { MediaFile, FileStatus } from 'shared-types' -import { SignedUploadUrl } from '@server/core' -import { z } from 'zod' import PrimaryButton from "@components/atoms/buttons/Primary"; import StepSection from "./StepSection"; - -enum Step { - CHOOSE_FILE, - REQUEST_UPLOAD_URL, - UPLOAD_FILE, - CONFIRM_UPLOAD, - POLL_FILE, -} - -class UploadHandler { - constructor(private controller: AbortController) { - this.controller = controller - } - - abort = () => { - this.controller.abort() - } - - /** - * @deprecated This "all-in-one" method is deprecated in favor of the more granular methods below. - * */ - upload = async (file: File) => { - const { url, confirmToken, id } = await this.requestSignedURL() - - await this.uploadImage(file, url) - - await this.confirmUpload(id, confirmToken) - - return await this.pollFileUntilReady(id) - } - - requestSignedURL = async (): Promise> => { - const res = await fetch('/api/users/1/images', { - method: 'POST', - signal: this.controller.signal, - }) - - if (!res.ok) { - throw new Error('Failed to request signed URL') - } - - const schema = z.object({ - url: z.string(), - confirmToken: z.string(), - id: z.string(), - expiry: z.number(), - }) - - const parsed = schema.parse(await res.json()) - - return parsed satisfies SignedUploadUrl - } - - uploadImage = async (file: File, signedURL: string) => { - console.log(file) - - const res = await fetch(signedURL, { - method: 'PUT', - signal: this.controller.signal, - body: file, - }) - - if (!res.ok) { - throw new Error('Failed to upload file') - } - } - - confirmUpload = async (id: string, confirmToken: string) => { - const res = await fetch(`/api/users/1/images/${id}/confirm`, { - method: 'POST', - signal: this.controller.signal, - body: JSON.stringify({ confirmToken }), - }) - - if (!res.ok) { - throw new Error('Failed to confirm upload') - } - } - - getFile = async (id: string): Promise> => { - const res = await fetch(`/api/users/1/images/${id}`, { - method: 'GET', - signal: this.controller.signal, - }) - - if (!res.ok) { - throw new Error('Failed to get file') - } - - const schema = z.object({ - id: z.string(), - status: z.enum([FileStatus.UPLOADED, FileStatus.PROCESSED]), - variants: z.union([z.array(z.string()), z.string(), z.undefined()]), - }) - - const parsed = schema.parse(await res.json()) - - return parsed satisfies MediaFile - } - - pollFileUntilReady = async (id: string): Promise> => { - let file = await this.getFile(id) - - while (file.status === FileStatus.PROCESSED) { - await new Promise((resolve) => setTimeout(resolve, 1000)) - - file = await this.getFile(id) - } - - return file - } -} +import {UploadHandler} from "../utils/upload_handler"; +import {Step} from "../types/step"; @@ -172,9 +58,9 @@ export default function Home() {

{ + onClick={() => setDone((prevState) => !prevState) - }} + } > Upload

diff --git a/apps/examples/nextjs/src/types/step.ts b/apps/examples/nextjs/src/types/step.ts new file mode 100644 index 0000000..cc4cc3a --- /dev/null +++ b/apps/examples/nextjs/src/types/step.ts @@ -0,0 +1,7 @@ +export enum Step { + CHOOSE_FILE, + REQUEST_UPLOAD_URL, + UPLOAD_FILE, + CONFIRM_UPLOAD, + POLL_FILE, +} diff --git a/apps/examples/nextjs/src/utils/upload_handler.ts b/apps/examples/nextjs/src/utils/upload_handler.ts new file mode 100644 index 0000000..4454f41 --- /dev/null +++ b/apps/examples/nextjs/src/utils/upload_handler.ts @@ -0,0 +1,108 @@ +import {SignedUploadUrl} from "@server/core"; +import {z} from "zod"; +import {FileStatus, MediaFile} from "shared-types"; + +export class UploadHandler { + constructor(private controller: AbortController) { + this.controller = controller + } + + abort = () => { + this.controller.abort() + } + + /** + * @deprecated This "all-in-one" method is deprecated in favor of the more granular methods below. + * */ + upload = async (file: File) => { + const { url, confirmToken, id } = await this.requestSignedURL() + + await this.uploadImage(file, url) + + await this.confirmUpload(id, confirmToken) + + return await this.pollFileUntilReady(id) + } + + requestSignedURL = async (): Promise> => { + const res = await fetch('/api/users/1/images', { + method: 'POST', + signal: this.controller.signal, + }) + + if (!res.ok) { + throw new Error('Failed to request signed URL') + } + + const schema = z.object({ + url: z.string(), + confirmToken: z.string(), + id: z.string(), + expiry: z.number(), + }) + + const parsed = schema.parse(await res.json()) + + return parsed satisfies SignedUploadUrl + } + + uploadImage = async (file: File, signedURL: string) => { + console.log(file) + + const res = await fetch(signedURL, { + method: 'PUT', + signal: this.controller.signal, + body: file, + }) + + if (!res.ok) { + throw new Error('Failed to upload file') + } + } + + confirmUpload = async (id: string, confirmToken: string) => { + const res = await fetch(`/api/users/1/images/${id}/confirm`, { + method: 'POST', + signal: this.controller.signal, + body: JSON.stringify({ confirmToken }), + }) + + if (!res.ok) { + throw new Error('Failed to confirm upload') + } + } + + getFile = async (id: string): Promise> => { + const res = await fetch(`/api/users/1/images/${id}`, { + method: 'GET', + signal: this.controller.signal, + }) + + if (!res.ok) { + throw new Error('Failed to get file') + } + + const schema = z.object({ + id: z.string(), + status: z.enum([FileStatus.UPLOADED, FileStatus.PROCESSED]), + variants: z.union([z.array(z.string()), z.string(), z.undefined()]), + }) + + const parsed = schema.parse(await res.json()) + + return parsed satisfies MediaFile + } + + pollFileUntilReady = async (id: string): Promise> => { + let file = await this.getFile(id) + + while (file.status === FileStatus.PROCESSED) { + await new Promise((resolve) => setTimeout(resolve, 1000)) + + file = await this.getFile(id) + } + + return file + } +} + From 0bd4849344bbf522faddfc088540e03e54db0b0d Mon Sep 17 00:00:00 2001 From: gio-shara-code Date: Mon, 7 Aug 2023 12:19:23 +0200 Subject: [PATCH 08/17] fix(aws): fixed parsing env variables in aws provider --- packages/providers/s3/src/config/aws.ts | 27 +++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/packages/providers/s3/src/config/aws.ts b/packages/providers/s3/src/config/aws.ts index d8e09be..d94ddfc 100644 --- a/packages/providers/s3/src/config/aws.ts +++ b/packages/providers/s3/src/config/aws.ts @@ -1,20 +1,29 @@ import { parseEnv } from 'znv' import { z } from 'zod' +const schemas = { + AWS_S3_BUCKET_NAME: { + schema: z.string(), + }, + AWS_S3_BUCKET_REGION: { + schema: z.string(), + }, + AWS_ACCESS_KEY_ID: { + schema: z.string(), + }, + AWS_SECRET_ACCESS_KEY: { + schema: z.string(), + }, +} + const { AWS_S3_BUCKET_NAME, AWS_S3_BUCKET_REGION, AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, -} = parseEnv(process.env, { - AWS_S3_BUCKET_NAME: z.string().optional(), - AWS_S3_BUCKET_REGION: z.string().optional(), - - AWS_ACCESS_KEY_ID: z.string(), - AWS_SECRET_ACCESS_KEY: z.string(), -}) +} = parseEnv(process.env, schemas) -export default Object.freeze({ +const config = Object.freeze({ s3: { region: AWS_S3_BUCKET_REGION, name: AWS_S3_BUCKET_NAME, @@ -22,3 +31,5 @@ export default Object.freeze({ secretAccessKey: AWS_SECRET_ACCESS_KEY, accessKeyId: AWS_ACCESS_KEY_ID, }) + +export default config From bb908d60aa7643490f9d9b11bcfb33d5bd6c677a Mon Sep 17 00:00:00 2001 From: gio-shara-code Date: Mon, 7 Aug 2023 12:19:57 +0200 Subject: [PATCH 09/17] feat(type): extended button props properly - refactor Primary button --- .../src/components/atoms/buttons/Primary.tsx | 14 +++++--------- .../nextjs/src/components/types/generic.ts | 9 +++------ 2 files changed, 8 insertions(+), 15 deletions(-) diff --git a/apps/examples/nextjs/src/components/atoms/buttons/Primary.tsx b/apps/examples/nextjs/src/components/atoms/buttons/Primary.tsx index a8c5736..a7196a3 100644 --- a/apps/examples/nextjs/src/components/atoms/buttons/Primary.tsx +++ b/apps/examples/nextjs/src/components/atoms/buttons/Primary.tsx @@ -1,15 +1,11 @@ -import React from 'react'; -import {ButtonProps} from "@components/types/generic"; +import React from 'react' +import { ButtonProps } from '@components/types/generic' -export default function PrimaryButton ({ children, disabled }: ButtonProps) { +export default function PrimaryButton({ className, ...rest }: ButtonProps) { return ( + /> ) } - - diff --git a/apps/examples/nextjs/src/components/types/generic.ts b/apps/examples/nextjs/src/components/types/generic.ts index 4a1e69c..22d771d 100644 --- a/apps/examples/nextjs/src/components/types/generic.ts +++ b/apps/examples/nextjs/src/components/types/generic.ts @@ -1,7 +1,4 @@ -import {ReactNode} from "react"; - -export interface ButtonProps { - disabled?: boolean - children: ReactNode -} +import { ReactNode } from 'react' +export interface ButtonProps + extends React.ButtonHTMLAttributes {} From 8545fa100fa53be8378b6a13a89e6d2310718d36 Mon Sep 17 00:00:00 2001 From: gio-shara-code Date: Mon, 7 Aug 2023 12:21:23 +0200 Subject: [PATCH 10/17] refactor(package.json): removed unstructured eslint config + eslintIgnore - eslint needs to be configured globally --- package.json | 176 +------------------------ pnpm-lock.yaml | 347 ++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 347 insertions(+), 176 deletions(-) diff --git a/package.json b/package.json index 3c4233b..c2ded5a 100644 --- a/package.json +++ b/package.json @@ -25,187 +25,21 @@ "concurrently": "^8.2.0", "dotenv": "^16.3.1", "eslint": "^8.44.0", + "eslint-config-prettier": "^8.8.0", + "eslint-config-standard-with-typescript": "^37.0.0", + "eslint-plugin-react": "^7.33.0", "husky": "^8.0.3", "jest": "^29.5.0", "nodemon": "^2.0.22", "prettier": "^2.8.8", "tsup": "^7.1.0", - "typescript": "^5.1.6", - "turbo": "1.10.7" + "turbo": "1.10.7", + "typescript": "^5.1.6" }, "dependencies": { "znv": "^0.3.2", "zod": "^3.21.4" }, - "eslintIgnore": [ - ".eslintrc.js", - ".cache-loader", - ".DS_Store", - ".pnpm-debug.log", - ".turbo", - ".vscode/generated*", - "/_work", - "/actions-runner", - "node_modules", - "patches", - "pnpm-lock.yaml", - ".github/actions/issue-validator/index.mjs", - "*.cjs", - "*.js", - "*.d.ts", - "*.d.ts.map", - ".svelte-kit", - ".next", - ".nuxt", - ".docusaurus", - "build", - "docs/docs/reference/core", - "docs/docs/reference/sveltekit", - "static", - "coverage", - "dist", - "packages/core/src/providers/oauth-types.ts", - "packages/core/src/lib/pages/styles.ts", - "packages/frameworks-sveltekit/package", - "packages/frameworks-sveltekit/vite.config.{js,ts}.timestamp-*", - "packages/next-auth/src/providers/oauth-types.ts", - "packages/next-auth/css/index.css", - ".branches", - "db.sqlite", - "dev.db", - "dynamodblocal-bin", - "firebase-debug.log", - "firestore-debug.log", - "migrations", - "test.schema.gql", - "apps/example-sveltekit", - "apps", - "packages/**/*test*" - ], - "eslintConfig": { - "env": { - "browser": true, - "es2021": true, - "node": true - }, - "extends": [ - "eslint:recommended", - "prettier" - ], - "overrides": [ - { - "files": [ - "*.ts", - "*.tsx" - ], - "parser": "@typescript-eslint/parser", - "parserOptions": { - "project": [ - "./packages/**/tsconfig.json", - "./apps/**/tsconfig.json" - ] - }, - "settings": { - "react": { - "version": "18" - } - }, - "extends": [ - "plugin:react/recommended", - "plugin:react/jsx-runtime", - "standard-with-typescript", - "prettier" - ], - "rules": { - "@typescript-eslint/explicit-function-return-type": "off", - "@typescript-eslint/method-signature-style": "off", - "@typescript-eslint/naming-convention": "off", - "@typescript-eslint/no-non-null-assertion": "off", - "@typescript-eslint/restrict-template-expressions": "off", - "@typescript-eslint/strict-boolean-expressions": "off", - "react/prop-types": "off", - "react/no-unescaped-entities": "off" - } - }, - { - "files": [ - "*.test.ts", - "*.test.js" - ], - "extends": [ - "plugin:jest/recommended" - ], - "env": { - "jest": true - } - }, - { - "files": [ - "docs/**" - ], - "plugins": [ - "@docusaurus" - ], - "extends": [ - "plugin:@docusaurus/recommended" - ] - }, - { - "files": [ - "packages/{core,sveltekit}/*.ts" - ], - "plugins": [ - "jsdoc" - ], - "extends": [ - "plugin:jsdoc/recommended" - ], - "rules": { - "jsdoc/require-param": "off", - "jsdoc/require-returns": "off", - "jsdoc/require-jsdoc": [ - "warn", - { - "publicOnly": true, - "enableFixer": false - } - ], - "jsdoc/no-multi-asterisks": [ - "warn", - { - "allowWhitespace": true - } - ], - "jsdoc/tag-lines": "off" - } - }, - { - "files": [ - "packages/frameworks-sveltekit" - ], - "plugins": [ - "svelte3" - ], - "parserOptions": { - "sourceType": "module", - "ecmaVersion": 2020 - }, - "env": { - "browser": true, - "es2017": true, - "node": true - } - } - ], - "parserOptions": { - "sourceType": "module", - "ecmaVersion": "latest", - "ecmaFeatures": { - "jsx": true - } - }, - "root": true - }, "engines": { "node": "^16.13.0 || ^18.12.0" }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index aab2fd2..77f0672 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -48,6 +48,15 @@ importers: eslint: specifier: ^8.44.0 version: 8.44.0 + eslint-config-prettier: + specifier: ^8.8.0 + version: 8.8.0(eslint@8.44.0) + eslint-config-standard-with-typescript: + specifier: ^37.0.0 + version: 37.0.0(@typescript-eslint/eslint-plugin@5.60.1)(eslint-plugin-import@2.27.5)(eslint-plugin-n@16.0.1)(eslint-plugin-promise@6.1.1)(eslint@8.44.0)(typescript@5.1.6) + eslint-plugin-react: + specifier: ^7.33.0 + version: 7.33.0(eslint@8.44.0) husky: specifier: ^8.0.3 version: 8.0.3 @@ -1777,6 +1786,11 @@ packages: engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} dev: true + /@eslint-community/regexpp@4.6.1: + resolution: {integrity: sha512-O7x6dMstWLn2ktjcoiNLDkAGG2EjveHL+Vvc+n0fXumkJYAcSqcVYKtwDU+hDZ0uDUsnUagSYaZrOLAYE8un1A==} + engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} + dev: true + /@eslint/eslintrc@2.1.0: resolution: {integrity: sha512-Lj7DECXqIVCqnqjjHMPna4vn6GJcMgul/wuS0je9OZ9gsL0zzDpKPVtcG1HaDVc+9y+qgXneTeUMbCqXJNpH1A==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -2796,6 +2810,10 @@ packages: resolution: {integrity: sha512-Hr5Jfhc9eYOQNPYO5WLDq/n4jqijdHNlDXjuAQkkt+mWdQR+XJToOHrsD4cPaMXpn6KO7y2+wM8AZEs8VpBLVA==} dev: true + /@types/json5@0.0.29: + resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==} + dev: true + /@types/minimist@1.2.2: resolution: {integrity: sha512-jhuKLIRrhvCPLqwPcx6INqmKeiA5EWrsCOPhrlFSrbrmU4ZMPjj5Ul/oLCMDO98XRUIwVm78xICz4EPCektzeQ==} dev: true @@ -3104,6 +3122,17 @@ packages: resolution: {integrity: sha512-c5AMf34bKdvPhQ7tBGhqkgKNUzMr4WUs+WDtC2ZUGOUncbxKMTvqxYctiseW3+L4bA8ec+GcZ6/A/FW4m8ukng==} dev: true + /array-includes@3.1.6: + resolution: {integrity: sha512-sgTbLvL6cNnw24FnbaDyjmvddQ2ML8arZsgaJhoABMoplz/4QRhtrYS+alr1BUM1Bwp6dhx8vVCBSLG+StwOFw==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + define-properties: 1.2.0 + es-abstract: 1.21.2 + get-intrinsic: 1.2.1 + is-string: 1.0.7 + dev: true + /array-union@2.1.0: resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} engines: {node: '>=8'} @@ -3119,6 +3148,26 @@ packages: es-shim-unscopables: 1.0.0 dev: true + /array.prototype.flatmap@1.3.1: + resolution: {integrity: sha512-8UGn9O1FDVvMNB0UlLv4voxRMze7+FpHyF5mSMRjWHUMlpoDViniy05870VlxhfgTnLbpuwTzvD76MTtWxB/mQ==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + define-properties: 1.2.0 + es-abstract: 1.21.2 + es-shim-unscopables: 1.0.0 + dev: true + + /array.prototype.tosorted@1.1.1: + resolution: {integrity: sha512-pZYPXPRl2PqWcsUs6LOMn+1f1532nEoPTYowBtqLwAW+W8vSVhkIGnmOX1t/UQjD6YGI0vcD2B1U7ZFGQH9jnQ==} + dependencies: + call-bind: 1.0.2 + define-properties: 1.2.0 + es-abstract: 1.21.2 + es-shim-unscopables: 1.0.0 + get-intrinsic: 1.2.1 + dev: true + /arrify@1.0.1: resolution: {integrity: sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==} engines: {node: '>=0.10.0'} @@ -3266,6 +3315,12 @@ packages: /buffer-from@1.1.2: resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} + /builtins@5.0.1: + resolution: {integrity: sha512-qwVpFEHNfhYJIzNRBvd2C1kyo6jz3ZSMPyyuR47OPdiKWlbYnZNyDWuyR175qDnAJLiCo5fBBqPb3RiXgWlkOQ==} + dependencies: + semver: 7.5.3 + dev: true + /bundle-require@4.0.1(esbuild@0.18.11): resolution: {integrity: sha512-9NQkRHlNdNpDBGmLpngF3EFDcwodhMUuLz9PaWYciVcQF9SE4LFjM2DB/xV1Li5JiuDMv7ZUWuC3rGbqR0MAXQ==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} @@ -3666,6 +3721,13 @@ packages: resolution: {integrity: sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==} dev: false + /doctrine@2.1.0: + resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==} + engines: {node: '>=0.10.0'} + dependencies: + esutils: 2.0.3 + dev: true + /doctrine@3.0.0: resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==} engines: {node: '>=6.0.0'} @@ -3817,6 +3879,185 @@ packages: engines: {node: '>=10'} dev: true + /eslint-config-prettier@8.8.0(eslint@8.44.0): + resolution: {integrity: sha512-wLbQiFre3tdGgpDv67NQKnJuTlcUVYHas3k+DZCc2U2BadthoEY4B7hLPvAxaqdyOGCzuLfii2fqGph10va7oA==} + hasBin: true + peerDependencies: + eslint: '>=7.0.0' + dependencies: + eslint: 8.44.0 + dev: true + + /eslint-config-standard-with-typescript@37.0.0(@typescript-eslint/eslint-plugin@5.60.1)(eslint-plugin-import@2.27.5)(eslint-plugin-n@16.0.1)(eslint-plugin-promise@6.1.1)(eslint@8.44.0)(typescript@5.1.6): + resolution: {integrity: sha512-V8I/Q1eFf9tiOuFHkbksUdWO3p1crFmewecfBtRxXdnvb71BCJx+1xAknlIRZMwZioMX3/bPtMVCZsf1+AjjOw==} + peerDependencies: + '@typescript-eslint/eslint-plugin': ^5.52.0 + eslint: ^8.0.1 + eslint-plugin-import: ^2.25.2 + eslint-plugin-n: '^15.0.0 || ^16.0.0 ' + eslint-plugin-promise: ^6.0.0 + typescript: '*' + dependencies: + '@typescript-eslint/eslint-plugin': 5.60.1(@typescript-eslint/parser@5.60.1)(eslint@8.44.0)(typescript@5.1.6) + '@typescript-eslint/parser': 5.60.1(eslint@8.44.0)(typescript@5.1.6) + eslint: 8.44.0 + eslint-config-standard: 17.1.0(eslint-plugin-import@2.27.5)(eslint-plugin-n@16.0.1)(eslint-plugin-promise@6.1.1)(eslint@8.44.0) + eslint-plugin-import: 2.27.5(@typescript-eslint/parser@5.60.1)(eslint@8.44.0) + eslint-plugin-n: 16.0.1(eslint@8.44.0) + eslint-plugin-promise: 6.1.1(eslint@8.44.0) + typescript: 5.1.6 + transitivePeerDependencies: + - supports-color + dev: true + + /eslint-config-standard@17.1.0(eslint-plugin-import@2.27.5)(eslint-plugin-n@16.0.1)(eslint-plugin-promise@6.1.1)(eslint@8.44.0): + resolution: {integrity: sha512-IwHwmaBNtDK4zDHQukFDW5u/aTb8+meQWZvNFWkiGmbWjD6bqyuSSBxxXKkCftCUzc1zwCH2m/baCNDLGmuO5Q==} + engines: {node: '>=12.0.0'} + peerDependencies: + eslint: ^8.0.1 + eslint-plugin-import: ^2.25.2 + eslint-plugin-n: '^15.0.0 || ^16.0.0 ' + eslint-plugin-promise: ^6.0.0 + dependencies: + eslint: 8.44.0 + eslint-plugin-import: 2.27.5(@typescript-eslint/parser@5.60.1)(eslint@8.44.0) + eslint-plugin-n: 16.0.1(eslint@8.44.0) + eslint-plugin-promise: 6.1.1(eslint@8.44.0) + dev: true + + /eslint-import-resolver-node@0.3.7: + resolution: {integrity: sha512-gozW2blMLJCeFpBwugLTGyvVjNoeo1knonXAcatC6bjPBZitotxdWf7Gimr25N4c0AAOo4eOUfaG82IJPDpqCA==} + dependencies: + debug: 3.2.7(supports-color@5.5.0) + is-core-module: 2.12.1 + resolve: 1.22.2 + transitivePeerDependencies: + - supports-color + dev: true + + /eslint-module-utils@2.8.0(@typescript-eslint/parser@5.60.1)(eslint-import-resolver-node@0.3.7)(eslint@8.44.0): + resolution: {integrity: sha512-aWajIYfsqCKRDgUfjEXNN/JlrzauMuSEy5sbd7WXbtW3EH6A6MpwEh42c7qD+MqQo9QMJ6fWLAeIJynx0g6OAw==} + engines: {node: '>=4'} + peerDependencies: + '@typescript-eslint/parser': '*' + eslint: '*' + eslint-import-resolver-node: '*' + eslint-import-resolver-typescript: '*' + eslint-import-resolver-webpack: '*' + peerDependenciesMeta: + '@typescript-eslint/parser': + optional: true + eslint: + optional: true + eslint-import-resolver-node: + optional: true + eslint-import-resolver-typescript: + optional: true + eslint-import-resolver-webpack: + optional: true + dependencies: + '@typescript-eslint/parser': 5.60.1(eslint@8.44.0)(typescript@5.1.6) + debug: 3.2.7(supports-color@5.5.0) + eslint: 8.44.0 + eslint-import-resolver-node: 0.3.7 + transitivePeerDependencies: + - supports-color + dev: true + + /eslint-plugin-es-x@7.2.0(eslint@8.44.0): + resolution: {integrity: sha512-9dvv5CcvNjSJPqnS5uZkqb3xmbeqRLnvXKK7iI5+oK/yTusyc46zbBZKENGsOfojm/mKfszyZb+wNqNPAPeGXA==} + engines: {node: ^14.18.0 || >=16.0.0} + peerDependencies: + eslint: '>=8' + dependencies: + '@eslint-community/eslint-utils': 4.4.0(eslint@8.44.0) + '@eslint-community/regexpp': 4.6.1 + eslint: 8.44.0 + dev: true + + /eslint-plugin-import@2.27.5(@typescript-eslint/parser@5.60.1)(eslint@8.44.0): + resolution: {integrity: sha512-LmEt3GVofgiGuiE+ORpnvP+kAm3h6MLZJ4Q5HCyHADofsb4VzXFsRiWj3c0OFiV+3DWFh0qg3v9gcPlfc3zRow==} + engines: {node: '>=4'} + peerDependencies: + '@typescript-eslint/parser': '*' + eslint: ^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 + peerDependenciesMeta: + '@typescript-eslint/parser': + optional: true + dependencies: + '@typescript-eslint/parser': 5.60.1(eslint@8.44.0)(typescript@5.1.6) + array-includes: 3.1.6 + array.prototype.flat: 1.3.1 + array.prototype.flatmap: 1.3.1 + debug: 3.2.7(supports-color@5.5.0) + doctrine: 2.1.0 + eslint: 8.44.0 + eslint-import-resolver-node: 0.3.7 + eslint-module-utils: 2.8.0(@typescript-eslint/parser@5.60.1)(eslint-import-resolver-node@0.3.7)(eslint@8.44.0) + has: 1.0.3 + is-core-module: 2.12.1 + is-glob: 4.0.3 + minimatch: 3.1.2 + object.values: 1.1.6 + resolve: 1.22.2 + semver: 6.3.1 + tsconfig-paths: 3.14.2 + transitivePeerDependencies: + - eslint-import-resolver-typescript + - eslint-import-resolver-webpack + - supports-color + dev: true + + /eslint-plugin-n@16.0.1(eslint@8.44.0): + resolution: {integrity: sha512-CDmHegJN0OF3L5cz5tATH84RPQm9kG+Yx39wIqIwPR2C0uhBGMWfbbOtetR83PQjjidA5aXMu+LEFw1jaSwvTA==} + engines: {node: '>=16.0.0'} + peerDependencies: + eslint: '>=7.0.0' + dependencies: + '@eslint-community/eslint-utils': 4.4.0(eslint@8.44.0) + builtins: 5.0.1 + eslint: 8.44.0 + eslint-plugin-es-x: 7.2.0(eslint@8.44.0) + ignore: 5.2.4 + is-core-module: 2.12.1 + minimatch: 3.1.2 + resolve: 1.22.2 + semver: 7.5.3 + dev: true + + /eslint-plugin-promise@6.1.1(eslint@8.44.0): + resolution: {integrity: sha512-tjqWDwVZQo7UIPMeDReOpUgHCmCiH+ePnVT+5zVapL0uuHnegBUs2smM13CzOs2Xb5+MHMRFTs9v24yjba4Oig==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^7.0.0 || ^8.0.0 + dependencies: + eslint: 8.44.0 + dev: true + + /eslint-plugin-react@7.33.0(eslint@8.44.0): + resolution: {integrity: sha512-qewL/8P34WkY8jAqdQxsiL82pDUeT7nhs8IsuXgfgnsEloKCT4miAV9N9kGtx7/KM9NH/NCGUE7Edt9iGxLXFw==} + engines: {node: '>=4'} + peerDependencies: + eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 + dependencies: + array-includes: 3.1.6 + array.prototype.flatmap: 1.3.1 + array.prototype.tosorted: 1.1.1 + doctrine: 2.1.0 + eslint: 8.44.0 + estraverse: 5.3.0 + jsx-ast-utils: 3.3.4 + minimatch: 3.1.2 + object.entries: 1.1.6 + object.fromentries: 2.0.6 + object.hasown: 1.1.2 + object.values: 1.1.6 + prop-types: 15.8.1 + resolve: 2.0.0-next.4 + semver: 6.3.1 + string.prototype.matchall: 4.0.8 + dev: true + /eslint-scope@5.1.1: resolution: {integrity: sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==} engines: {node: '>=8.0.0'} @@ -4571,7 +4812,7 @@ packages: '@babel/parser': 7.22.6 '@istanbuljs/schema': 0.1.3 istanbul-lib-coverage: 3.2.0 - semver: 6.3.0 + semver: 6.3.1 transitivePeerDependencies: - supports-color @@ -5028,6 +5269,13 @@ packages: resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} dev: true + /json5@1.0.2: + resolution: {integrity: sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==} + hasBin: true + dependencies: + minimist: 1.2.8 + dev: true + /json5@2.2.3: resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} engines: {node: '>=6'} @@ -5055,6 +5303,16 @@ packages: engines: {'0': node >= 0.2.0} dev: true + /jsx-ast-utils@3.3.4: + resolution: {integrity: sha512-fX2TVdCViod6HwKEtSWGHs57oFhVfCMwieb9PuRDgjDPh5XeqJiHFFFJCHxU5cnTc3Bu/GRL+kPiFmw8XWOfKw==} + engines: {node: '>=4.0'} + dependencies: + array-includes: 3.1.6 + array.prototype.flat: 1.3.1 + object.assign: 4.1.4 + object.values: 1.1.6 + dev: true + /kind-of@6.0.3: resolution: {integrity: sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==} engines: {node: '>=0.10.0'} @@ -5169,7 +5427,6 @@ packages: hasBin: true dependencies: js-tokens: 4.0.0 - dev: false /lru-cache@4.1.5: resolution: {integrity: sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==} @@ -5193,7 +5450,7 @@ packages: resolution: {integrity: sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==} engines: {node: '>=8'} dependencies: - semver: 6.3.0 + semver: 6.3.1 /make-error@1.3.6: resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==} @@ -5452,6 +5709,40 @@ packages: object-keys: 1.1.1 dev: true + /object.entries@1.1.6: + resolution: {integrity: sha512-leTPzo4Zvg3pmbQ3rDK69Rl8GQvIqMWubrkxONG9/ojtFE2rD9fjMKfSI5BxW3osRH1m6VdzmqK8oAY9aT4x5w==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + define-properties: 1.2.0 + es-abstract: 1.21.2 + dev: true + + /object.fromentries@2.0.6: + resolution: {integrity: sha512-VciD13dswC4j1Xt5394WR4MzmAQmlgN72phd/riNp9vtD7tp4QQWJ0R4wvclXcafgcYK8veHRed2W6XeGBvcfg==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + define-properties: 1.2.0 + es-abstract: 1.21.2 + dev: true + + /object.hasown@1.1.2: + resolution: {integrity: sha512-B5UIT3J1W+WuWIU55h0mjlwaqxiE5vYENJXIXZ4VFe05pNYrkKuK0U/6aFcb0pKywYJh7IhfoqUfKVmrJJHZHw==} + dependencies: + define-properties: 1.2.0 + es-abstract: 1.21.2 + dev: true + + /object.values@1.1.6: + resolution: {integrity: sha512-FVVTkD1vENCsAcwNs9k6jea2uHC/X0+JcjG8YA60FN5CMaJmG95wT9jek/xX9nornqGRrBkKtzuAu2wuHpKqvw==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + define-properties: 1.2.0 + es-abstract: 1.21.2 + dev: true + /once@1.4.0: resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} dependencies: @@ -5701,6 +5992,14 @@ packages: kleur: 3.0.3 sisteransi: 1.0.5 + /prop-types@15.8.1: + resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==} + dependencies: + loose-envify: 1.4.0 + object-assign: 4.1.1 + react-is: 16.13.1 + dev: true + /pseudomap@1.0.2: resolution: {integrity: sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ==} dev: true @@ -5740,6 +6039,10 @@ packages: scheduler: 0.23.0 dev: false + /react-is@16.13.1: + resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} + dev: true + /react-is@18.2.0: resolution: {integrity: sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==} @@ -5868,6 +6171,15 @@ packages: path-parse: 1.0.7 supports-preserve-symlinks-flag: 1.0.0 + /resolve@2.0.0-next.4: + resolution: {integrity: sha512-iMDbmAWtfU+MHpxt/I5iWI7cY6YVEZUQ3MBgPQ++XD1PELuJHIl82xBmObyP2KyQmkNB2dsqF7seoQQiAn5yDQ==} + hasBin: true + dependencies: + is-core-module: 2.12.1 + path-parse: 1.0.7 + supports-preserve-symlinks-flag: 1.0.0 + dev: true + /reusify@1.0.4: resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} engines: {iojs: '>=1.0.0', node: '>=0.10.0'} @@ -5922,16 +6234,19 @@ packages: resolution: {integrity: sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==} dev: true - /semver@6.3.0: - resolution: {integrity: sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==} + /semver@6.3.1: + resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} + hasBin: true /semver@7.0.0: resolution: {integrity: sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==} + hasBin: true dev: true /semver@7.5.2: resolution: {integrity: sha512-SoftuTROv/cRjCze/scjGyiDtcUyxw1rgYQSZY7XTmtR5hX+dm76iDbTH8TkLPHCQmlbQVSSbNZCPM2hb0knnQ==} engines: {node: '>=10'} + hasBin: true dependencies: lru-cache: 6.0.0 dev: true @@ -6104,6 +6419,19 @@ packages: is-fullwidth-code-point: 3.0.0 strip-ansi: 6.0.1 + /string.prototype.matchall@4.0.8: + resolution: {integrity: sha512-6zOCOcJ+RJAQshcTvXPHoxoQGONa3e/Lqx90wUA+wEzX78sg5Bo+1tQo4N0pohS0erG9qtCqJDjNCQBjeWVxyg==} + dependencies: + call-bind: 1.0.2 + define-properties: 1.2.0 + es-abstract: 1.21.2 + get-intrinsic: 1.2.1 + has-symbols: 1.0.3 + internal-slot: 1.0.5 + regexp.prototype.flags: 1.5.0 + side-channel: 1.0.4 + dev: true + /string.prototype.trim@1.2.7: resolution: {integrity: sha512-p6TmeT1T3411M8Cgg9wBTMRtY2q9+PNy9EV1i2lIXUN/btt763oIfxwN3RR8VU6wHX8j/1CFy0L+YuThm6bgOg==} engines: {node: '>= 0.4'} @@ -6371,6 +6699,15 @@ packages: v8-compile-cache-lib: 3.0.1 yn: 3.1.1 + /tsconfig-paths@3.14.2: + resolution: {integrity: sha512-o/9iXgCYc5L/JxCHPe3Hvh8Q/2xm5Z+p18PESBU6Ff33695QnCHBEjcytY2q19ua7Mbl/DavtBOLq+oG0RCL+g==} + dependencies: + '@types/json5': 0.0.29 + json5: 1.0.2 + minimist: 1.2.8 + strip-bom: 3.0.0 + dev: true + /tslib@1.14.1: resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==} From 77525d58a06c567559d8f39974918edcb347c9a1 Mon Sep 17 00:00:00 2001 From: gio-shara-code Date: Mon, 7 Aug 2023 12:23:08 +0200 Subject: [PATCH 11/17] fix(route): instead of returning undefined from route they return an empty object --- .../[id]/images/[imageId]/confirm/route.ts | 14 +++++------ .../api/users/[id]/images/[imageId]/route.ts | 24 +++++++++---------- 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/apps/examples/nextjs/src/app/api/users/[id]/images/[imageId]/confirm/route.ts b/apps/examples/nextjs/src/app/api/users/[id]/images/[imageId]/confirm/route.ts index 969f8d7..533b17c 100644 --- a/apps/examples/nextjs/src/app/api/users/[id]/images/[imageId]/confirm/route.ts +++ b/apps/examples/nextjs/src/app/api/users/[id]/images/[imageId]/confirm/route.ts @@ -1,12 +1,12 @@ -import { NextRequest, NextResponse } from "next/server"; -import { uploadWizard } from "../../../../../../upload-wizard"; +import { NextRequest, NextResponse } from 'next/server' +import { uploadWizard } from '../../../../../../upload-wizard' export async function POST( - request: NextRequest, - { params }: { params: { id: string, imageId: string } } + request: NextRequest, + { params }: { params: { id: string; imageId: string } } ) { - // TODO: validate confirm Token - await uploadWizard.confirmUpload(params.imageId, 'confirmToken') + // TODO: validate confirm Token + await uploadWizard.confirmUpload(params.imageId, 'confirmToken') - return NextResponse.json(undefined) + return NextResponse.json({}) } diff --git a/apps/examples/nextjs/src/app/api/users/[id]/images/[imageId]/route.ts b/apps/examples/nextjs/src/app/api/users/[id]/images/[imageId]/route.ts index 9da65f0..d41f71a 100644 --- a/apps/examples/nextjs/src/app/api/users/[id]/images/[imageId]/route.ts +++ b/apps/examples/nextjs/src/app/api/users/[id]/images/[imageId]/route.ts @@ -1,22 +1,22 @@ -import { NextRequest, NextResponse } from "next/server"; -import { uploadWizard } from "../../../../../upload-wizard"; +import { NextRequest, NextResponse } from 'next/server' +import { uploadWizard } from '../../../../../upload-wizard' export async function DELETE( - request: NextRequest, - { params }: { params: { id: string, imageId: string } } + request: NextRequest, + { params }: { params: { id: string; imageId: string } } ) { - // TODO: delete image - await uploadWizard.delete(params.imageId) + // TODO: delete image + await uploadWizard.delete(params.imageId) - return NextResponse.json(undefined) + return NextResponse.json({}) } export async function GET( - request: NextRequest, - { params }: { params: { id: string, imageId: string } } + request: NextRequest, + { params }: { params: { id: string; imageId: string } } ) { - // TODO: get image - await uploadWizard.getData(params.imageId) + // TODO: get image + await uploadWizard.getData(params.imageId) - return NextResponse.json(undefined) + return NextResponse.json({}) } From df7e98c15a5e0ff0d6bd7991b33d354b38ebc097 Mon Sep 17 00:00:00 2001 From: gio-shara-code Date: Mon, 7 Aug 2023 12:23:57 +0200 Subject: [PATCH 12/17] feat(api/users/[id]/images/route): added error handling --- .../src/app/api/users/[id]/images/route.ts | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/apps/examples/nextjs/src/app/api/users/[id]/images/route.ts b/apps/examples/nextjs/src/app/api/users/[id]/images/route.ts index 0bb7815..15d8d31 100644 --- a/apps/examples/nextjs/src/app/api/users/[id]/images/route.ts +++ b/apps/examples/nextjs/src/app/api/users/[id]/images/route.ts @@ -1,19 +1,19 @@ import { NextRequest, NextResponse } from 'next/server' -import * as crypto from "crypto"; -import { uploadWizard } from "../../../../upload-wizard"; +import { uploadWizard } from '../../../../upload-wizard' export async function POST( request: NextRequest, { params }: { params: { id: string } } ) { - console.log(process.env.AWS_ACCESS_KEY_ID) - const {id, url, confirmToken, expiry} = await uploadWizard.signedUploadUrl() + let response + try { + response = await uploadWizard.signedUploadUrl() + } catch (e) { + console.log('e', e) + return new Response('Internal server error', { + status: 500, + }) + } - - return NextResponse.json({ - id, - confirmToken, - url, - expiry: expiry, - }) + return NextResponse.json(response) } From 8a986f0a3697dc1f2a2ba89cbb575229ddd37e1b Mon Sep 17 00:00:00 2001 From: gio-shara-code Date: Mon, 7 Aug 2023 12:27:17 +0200 Subject: [PATCH 13/17] refactor(upload_handler): added comment notes + removed unnecessary return type + abstracted zod object from the class method --- .../nextjs/src/utils/upload_handler.ts | 64 +++++++++---------- 1 file changed, 29 insertions(+), 35 deletions(-) diff --git a/apps/examples/nextjs/src/utils/upload_handler.ts b/apps/examples/nextjs/src/utils/upload_handler.ts index 4454f41..dcbe8d5 100644 --- a/apps/examples/nextjs/src/utils/upload_handler.ts +++ b/apps/examples/nextjs/src/utils/upload_handler.ts @@ -1,6 +1,20 @@ -import {SignedUploadUrl} from "@server/core"; -import {z} from "zod"; -import {FileStatus, MediaFile} from "shared-types"; +import { z } from 'zod' +import { FileStatus, MediaFile } from 'shared-types' + +export const RequestSignedURLReturnObject = z.object({ + id: z.string(), // QUESTION is it a uuid? If yes we can add .uuid() + url: z.string().url(), + confirmToken: z.string(), + expiry: z.number(), // +}) + +export const GetFileReturnObject = z.object({ + id: z.string().optional(), + status: z.enum([FileStatus.UPLOADED, FileStatus.PROCESSED]).optional(), + variants: z + .union([z.array(z.string()), z.string(), z.undefined()]) + .optional(), +}) export class UploadHandler { constructor(private controller: AbortController) { @@ -16,7 +30,7 @@ export class UploadHandler { * */ upload = async (file: File) => { const { url, confirmToken, id } = await this.requestSignedURL() - + console.log('url', url) await this.uploadImage(file, url) await this.confirmUpload(id, confirmToken) @@ -24,7 +38,7 @@ export class UploadHandler { return await this.pollFileUntilReady(id) } - requestSignedURL = async (): Promise> => { + requestSignedURL = async () => { const res = await fetch('/api/users/1/images', { method: 'POST', signal: this.controller.signal, @@ -34,21 +48,10 @@ export class UploadHandler { throw new Error('Failed to request signed URL') } - const schema = z.object({ - url: z.string(), - confirmToken: z.string(), - id: z.string(), - expiry: z.number(), - }) - - const parsed = schema.parse(await res.json()) - - return parsed satisfies SignedUploadUrl + return RequestSignedURLReturnObject.parse(await res.json()) } uploadImage = async (file: File, signedURL: string) => { - console.log(file) - const res = await fetch(signedURL, { method: 'PUT', signal: this.controller.signal, @@ -67,36 +70,28 @@ export class UploadHandler { body: JSON.stringify({ confirmToken }), }) - if (!res.ok) { - throw new Error('Failed to confirm upload') - } + if (!res.ok) throw new Error('Failed to confirm upload') } - getFile = async (id: string): Promise> => { + getFile = async (id: string) => { const res = await fetch(`/api/users/1/images/${id}`, { method: 'GET', signal: this.controller.signal, }) - if (!res.ok) { - throw new Error('Failed to get file') - } - - const schema = z.object({ - id: z.string(), - status: z.enum([FileStatus.UPLOADED, FileStatus.PROCESSED]), - variants: z.union([z.array(z.string()), z.string(), z.undefined()]), - }) + if (!res.ok) throw new Error('Failed to get file') - const parsed = schema.parse(await res.json()) + const bodyJson = await res.json() - return parsed satisfies MediaFile + console.log('getFile:res', bodyJson) + return GetFileReturnObject.parse(bodyJson) } - pollFileUntilReady = async (id: string): Promise> => { + pollFileUntilReady = async (id: string) => { let file = await this.getFile(id) - while (file.status === FileStatus.PROCESSED) { + // NOTE this constantly pulls an empty object, why is it happening? + while (file.status !== FileStatus.UPLOADED) { await new Promise((resolve) => setTimeout(resolve, 1000)) file = await this.getFile(id) @@ -105,4 +100,3 @@ export class UploadHandler { return file } } - From 7973367f4190cf3e203a626c57e536995399a30e Mon Sep 17 00:00:00 2001 From: gio-shara-code Date: Mon, 7 Aug 2023 12:28:41 +0200 Subject: [PATCH 14/17] feat(home): implemented separate functionalities for each step + instead of having on upload method you can now go step by step and experiment with the upload flow --- apps/examples/nextjs/src/app/page.tsx | 179 +++++++++++++++++++------- 1 file changed, 131 insertions(+), 48 deletions(-) diff --git a/apps/examples/nextjs/src/app/page.tsx b/apps/examples/nextjs/src/app/page.tsx index fbd88c7..5a6c0e1 100644 --- a/apps/examples/nextjs/src/app/page.tsx +++ b/apps/examples/nextjs/src/app/page.tsx @@ -1,47 +1,111 @@ 'use client' -import { useMemo, useState } from 'react' -import PrimaryButton from "@components/atoms/buttons/Primary"; -import StepSection from "./StepSection"; -import {UploadHandler} from "../utils/upload_handler"; -import {Step} from "../types/step"; +import { useEffect, useRef, useState } from 'react' +import PrimaryButton from '@components/atoms/buttons/Primary' +import StepSection from './StepSection' +import { + UploadHandler, + RequestSignedURLReturnObject, +} from '../utils/upload_handler' +import { Step } from '../types/step' +import { z } from 'zod' +import { Simulate } from 'react-dom/test-utils' +const controller = new AbortController() +const uploadHandler = new UploadHandler(controller) export default function Home() { const [data, setData] = useState<{ message: string } | null>() - const controller = useMemo(() => new AbortController(), []) - const uploadHandler = useMemo( - () => new UploadHandler(controller), - [controller] - ) + const [step, setStep] = useState(Step.CHOOSE_FILE) - // useEffect(() => { - // const controller = new AbortController() - // - // const uploadHandler = new UploadHandler(controller) - // - // const uploadFile = async () => { - // const file = new File(['test'], 'test.txt', { type: 'text/plain' }) - // - // try { - // try { - // const mediaFile = await uploadHandler.upload(file) - // setData({ message: `File uploaded: ${mediaFile.variants}` }) - // } catch (err) { - // setData({ message: `Upload aborted: ${err}` }) - // } - // } catch (err) { - // console.error(err) - // } - // } - // - // uploadFile() - // - // return () => { - // uploadHandler.abort() - // } - // }, []) + const signedURL = useRef | null>(null) + + const pickedFile = useRef(null) + + useEffect(() => () => controller.abort(), []) + + const onFilePicked = async (file: File) => { + console.log('onFilePicked', file.name) + + pickedFile.current = file + console.log('picked file done', pickedFile.current) + setStep(Step.REQUEST_UPLOAD_URL) + + try { + // const mediaFile = await uploadHandler.upload(file) + // const { url, confirmToken, id } = + // await uploadHandler.requestSignedURL() + // await uploadHandler.uploadImage(file, url) + // + // await uploadHandler.confirmUpload(id, confirmToken) + // + // const mediaFile = await uploadHandler.pollFileUntilReady(id) + // setData({ message: `File uploaded: ${mediaFile.variants}` }) + } catch (err) { + // setData({ message: `Upload aborted: ${err}` }) + } + } + + const onRequestUploadURL = async () => { + try { + signedURL.current = await uploadHandler.requestSignedURL() + console.log('signed url done', signedURL.current) + setStep(Step.UPLOAD_FILE) + } catch (e) { + console.error('Failed to request signed URL', e) + } + } + + const checkFileExists = () => { + if (pickedFile.current === null) throw new Error('No file picked') + return pickedFile.current + } + + const checkSignedURLExists = () => { + if (signedURL.current === null) throw new Error('No signed url') + return signedURL.current + } + const onUploadFile = async () => { + try { + const file = checkFileExists() + const signedUrl = checkSignedURLExists() + await uploadHandler.uploadImage(file, signedUrl.url) + console.log('uploaded file done') + setStep(Step.CONFIRM_UPLOAD) + } catch (e) { + console.error('Failed to upload file', e) + } + } + + const onConfirmUpload = async () => { + try { + const signedURL = checkSignedURLExists() + await uploadHandler.confirmUpload( + signedURL.id, + signedURL.confirmToken + ) + console.log('confirmed upload done') + setStep(Step.POLL_FILE) + } catch (e) { + console.error('Failed to confirm upload', e) + } + } + + const onPollFile = async () => { + try { + const signedURL = checkSignedURLExists() + const mediaFile = await uploadHandler.pollFileUntilReady( + signedURL.id + ) + console.log('poll file done', mediaFile) + setData({ message: `File uploaded: ${mediaFile.variants}` }) + } catch (e) { + console.error('Failed to poll file', e) + } + } const [done, setDone] = useState(false) @@ -58,9 +122,7 @@ export default function Home() {

- setDone((prevState) => !prevState) - } + onClick={() => setDone((prevState) => !prevState)} > Upload

@@ -80,41 +142,62 @@ export default function Home() { { + const file = e.target.files?.[0] + if (file) onFilePicked(file) + }} /> )} - + {(disabled) => ( - Request Upload URL + + Request Upload URL + )} {(disabled) => ( - Upload File + + Upload File + )} {(disabled) => ( - Confirm Upload + + Confirm Upload + )} {(disabled) => ( - Poll File + + Poll File + )}
) } - - - - From ada07803c29b9370e7b17a9884c1b15e0cc3cfa3 Mon Sep 17 00:00:00 2001 From: gio-shara-code Date: Mon, 7 Aug 2023 12:29:02 +0200 Subject: [PATCH 15/17] doc(shared-types): small note --- packages/types/src/types/common.types.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/types/src/types/common.types.ts b/packages/types/src/types/common.types.ts index 0bd3145..4797468 100644 --- a/packages/types/src/types/common.types.ts +++ b/packages/types/src/types/common.types.ts @@ -14,6 +14,7 @@ export const FileStatus = { export type FileStatusType = keyof typeof FileStatus +// NOTE: Maybe we firstly defined zod object and out of the object generate type. The goal here is to have the highest level of type safety and binding between out ts types and zod objects. Just a thought interface File { id: ID status: FileStatusType From 3e2719bfcc81e4f5098dd05326bbb08327b7d5d5 Mon Sep 17 00:00:00 2001 From: gio-shara-code Date: Mon, 14 Aug 2023 21:10:02 +0300 Subject: [PATCH 16/17] doc(api): added README.md file in the /api folder to keep track of the api routes and their functionalities --- apps/examples/nextjs/src/app/api/README.md | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 apps/examples/nextjs/src/app/api/README.md diff --git a/apps/examples/nextjs/src/app/api/README.md b/apps/examples/nextjs/src/app/api/README.md new file mode 100644 index 0000000..e9ac9d6 --- /dev/null +++ b/apps/examples/nextjs/src/app/api/README.md @@ -0,0 +1,9 @@ +### API Routes List +- POST /api/users/[id]/images + - signs upload url and returns a **signed url** + **confirmation token** +- GET /api/users/[id]/images/[imageId] + - gets image url **status** and **image variants** if they exist +- DELETE /api/users/[id]/images/[imageId] + - checks if file exists in db, if yes it deletes the file from db and storage provider as well. +- POST /api/users/[id]/images/[imageId]/confirm + - validates **confirm token**, checks if image **exists** in the storage provider and finally updates the file status to **uploaded**. \ No newline at end of file From 9dff079e31f593cc1c78a06e7b3225ad2bf0338a Mon Sep 17 00:00:00 2001 From: gio-shara-code Date: Mon, 14 Aug 2023 21:22:10 +0300 Subject: [PATCH 17/17] fix(/api/users/[id]/images/[imageId]): instead of sending back an empty object the returned file data is set from `getData` method --- .../nextjs/src/app/api/users/[id]/images/[imageId]/route.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/examples/nextjs/src/app/api/users/[id]/images/[imageId]/route.ts b/apps/examples/nextjs/src/app/api/users/[id]/images/[imageId]/route.ts index d41f71a..27cb620 100644 --- a/apps/examples/nextjs/src/app/api/users/[id]/images/[imageId]/route.ts +++ b/apps/examples/nextjs/src/app/api/users/[id]/images/[imageId]/route.ts @@ -16,7 +16,7 @@ export async function GET( { params }: { params: { id: string; imageId: string } } ) { // TODO: get image - await uploadWizard.getData(params.imageId) + const data = await uploadWizard.getData(params.imageId) - return NextResponse.json({}) + return NextResponse.json(data) }