diff --git a/apps/cs/app/(with-base-layout)/image/page.tsx b/apps/cs/app/(with-base-layout)/image/page.tsx new file mode 100644 index 000000000..89ca9eeb1 --- /dev/null +++ b/apps/cs/app/(with-base-layout)/image/page.tsx @@ -0,0 +1,2 @@ +import { ImagePage } from "@/pages/image"; +export default ImagePage; diff --git a/apps/cs/middleware.ts b/apps/cs/middleware.ts index c9284d69b..81039090b 100644 --- a/apps/cs/middleware.ts +++ b/apps/cs/middleware.ts @@ -12,7 +12,11 @@ export function middleware(req: NextRequest) { const newUrl = `${serverApiUrl}${pathname.replace("/server", "")}${ queryParams ? `?${queryParams}` : "" }`; - console.log("Rewriting to: ", newUrl); + return NextResponse.rewrite(new URL(newUrl)); + } + + if (pathname.startsWith("/attach-service")) { + const newUrl = `${serverApiUrl}${pathname}`; return NextResponse.rewrite(new URL(newUrl)); } @@ -20,5 +24,5 @@ export function middleware(req: NextRequest) { } export const config = { - matcher: "/server/:path*", + matcher: ["/server/:path*", "/attach-service/:path*"], }; diff --git a/apps/cs/next.config.ts b/apps/cs/next.config.ts index 7c1a12675..cb651cdc0 100644 --- a/apps/cs/next.config.ts +++ b/apps/cs/next.config.ts @@ -1,37 +1,5 @@ import type { NextConfig } from "next"; -import webpack from "webpack"; -const nextConfig: NextConfig = { - webpack: (config) => { - config.plugins.push( - new webpack.DefinePlugin({ - "import.meta.env.VITE_PORT": JSON.stringify( - process.env.NEXT_PUBLIC_PORT, - ), - "import.meta.env.VITE_PROXY_HOST": JSON.stringify( - process.env.NEXT_PUBLIC_PROXY_HOST, - ), - "import.meta.env.VITE_SERVER_API_URL": JSON.stringify( - process.env.NEXT_PUBLIC_SERVER_API_URL, - ), - "import.meta.env.VITE_SITE_ID": JSON.stringify(process.env.SITE_ID), - "import.meta.env.VITE_CLAIM_NAME": JSON.stringify( - process.env.NEXT_PUBLIC_CLAIM_NAME, - ), - "import.meta.env.VITE_ACCESS_TOKEN": JSON.stringify( - process.env.NEXT_PUBLIC_ACCESS_TOKEN, - ), - "import.meta.env.VITE_REFRESH_TOKEN": JSON.stringify( - process.env.NEXT_PUBLIC_REFRESH_TOKEN, - ), - "import.meta.env.VITE_AUTH_USER_ID": JSON.stringify( - process.env.NEXT_PUBLIC_AUTH_USER_ID, - ), - }), - ); - - return config; - }, -}; +const nextConfig: NextConfig = {}; export default nextConfig; diff --git a/apps/cs/package.json b/apps/cs/package.json index 4394eaf17..38d378299 100644 --- a/apps/cs/package.json +++ b/apps/cs/package.json @@ -27,6 +27,7 @@ "react-dnd": "^16.0.1", "react-hook-form": "^7.54.2", "tailwindcss-animate": "^1.0.7", + "tus-js-client": "^4.3.1", "zod": "^3.24.2", "zustand": "^5.0.3" }, diff --git a/apps/cs/src/pages/image/index.ts b/apps/cs/src/pages/image/index.ts new file mode 100644 index 000000000..554a47695 --- /dev/null +++ b/apps/cs/src/pages/image/index.ts @@ -0,0 +1 @@ +export { ImagePage } from "./ui/image-page"; diff --git a/apps/cs/src/pages/image/ui/image-page.tsx b/apps/cs/src/pages/image/ui/image-page.tsx new file mode 100644 index 000000000..9c82acf0c --- /dev/null +++ b/apps/cs/src/pages/image/ui/image-page.tsx @@ -0,0 +1,61 @@ +"use client"; + +import { Upload } from "tus-js-client"; +import { FileUploader } from "@common/components/ui"; +import { PageLayout } from "@/shared/ui/page-layout"; +import { AUTH_USER_ID, CLAIM_NAME, ENDPOINT } from "@/shared/config"; +import { getCookie } from "@/shared/lib"; + +function ImagePage() { + const handleUpload = (e: React.ChangeEvent) => { + const file = e.target.files?.item(0); + if (!file) return; + + const upload = new Upload(file, { + endpoint: `${ENDPOINT.ATTACH_SERVICE.TUS_UPLOAD}`, + headers: { + [CLAIM_NAME]: getCookie(CLAIM_NAME) ?? "", + [AUTH_USER_ID]: getCookie(AUTH_USER_ID) ?? "", + }, + retryDelays: [0, 3000, 5000, 10000, 20000], + metadata: { + filename: file.name, + filetype: file.type, + }, + onError: function (error) { + console.log("Failed because: " + error); + }, + onProgress: function (bytesUploaded, bytesTotal) { + const percentage = ((bytesUploaded / bytesTotal) * 100).toFixed(2); + console.log(bytesUploaded, bytesTotal, percentage + "%"); + }, + onSuccess: function () { + console.log( + "Download %s from %s", + (upload.file as File).name, + upload.url, + ); + }, + }); + + upload.findPreviousUploads().then(function (previousUploads) { + if (previousUploads.length) { + upload.resumeFromPreviousUpload(previousUploads[0]!); + } + upload.start(); + }); + }; + + return ( + +
+
+

이미지 목록

+
+ +
+
+ ); +} + +export { ImagePage }; diff --git a/apps/cs/src/shared/config/api.ts b/apps/cs/src/shared/config/api.ts index 5ef3383df..a4f861c1a 100644 --- a/apps/cs/src/shared/config/api.ts +++ b/apps/cs/src/shared/config/api.ts @@ -3,6 +3,9 @@ import { PROXY_HOST } from "./env"; export const BASE_URL = `${PROXY_HOST}/server`; export const ENDPOINT = { + ATTACH_SERVICE: { + TUS_UPLOAD: "/attach-service/api/v1/tus/upload", + }, USER_SERVICE: { LOGIN: "/user-service/login", USERS: "/user-service/api/v1/users", diff --git a/packages/common/components/.storybook/main.ts b/packages/common/components/.storybook/main.ts index 87a748dfd..4b70a4aed 100644 --- a/packages/common/components/.storybook/main.ts +++ b/packages/common/components/.storybook/main.ts @@ -16,15 +16,38 @@ const config: StorybookConfig = { }, viteFinal: async (config) => { config.define = { - "process.env.BASE_ASSETS_PATH": JSON.stringify( - process.env.BASE_ASSETS_PATH, - ), + "process.env": { + BASE_ASSETS_PATH: JSON.stringify(process.env.BASE_ASSETS_PATH), + PORT: JSON.stringify(process.env.PORT || "3000"), + PROXY_HOST: JSON.stringify( + process.env.PROXY_HOST || "http://localhost:3000", + ), + SERVER_API_URL: JSON.stringify(process.env.SERVER_API_URL), + SITE_ID: JSON.stringify(process.env.SITE_ID || "CS"), + CLAIM_NAME: JSON.stringify(process.env.CLAIM_NAME || "Authorization"), + ACCESS_TOKEN: JSON.stringify( + process.env.ACCESS_TOKEN || "access-token", + ), + REFRESH_TOKEN: JSON.stringify( + process.env.REFRESH_TOKEN || "refresh-token", + ), + AUTH_USER_ID: JSON.stringify(process.env.AUTH_USER_ID || "token-id"), + }, }; config.plugins?.push( tsconfigPaths({ projects: [path.resolve(path.dirname(__dirname), "tsconfig.json")], }), ); + config.server = { + proxy: { + "/server": { + target: process.env.SERVER_API_URL, + changeOrigin: true, + rewrite: (path) => path.replace(/^\/server/, ""), + }, + }, + }; return config; }, diff --git a/packages/common/components/src/config/env.ts b/packages/common/components/src/config/env.ts index d75d108a3..dfe49efdb 100644 --- a/packages/common/components/src/config/env.ts +++ b/packages/common/components/src/config/env.ts @@ -1,12 +1,10 @@ -export const PORT = import.meta.env.VITE_PORT || "3000"; -export const PROXY_HOST = - import.meta.env.VITE_PROXY_HOST || `http://localhost:${PORT}`; +export const PORT = process.env.PORT || "3000"; +export const PROXY_HOST = process.env.PROXY_HOST || `http://localhost:${PORT}`; -export const SERVER_API_URL = import.meta.env.VITE_SERVER_API_URL; -export const SITE_ID = import.meta.env.VITE_SITE_ID || "CS"; +export const SERVER_API_URL = process.env.SERVER_API_URL; +export const SITE_ID = process.env.SITE_ID || "CS"; -export const CLAIM_NAME = import.meta.env.VITE_CLAIM_NAME || "Authorization"; -export const ACCESS_TOKEN = import.meta.env.VITE_ACCESS_TOKEN || "access-token"; -export const REFRESH_TOKEN = - import.meta.env.VITE_REFRESH_TOKEN || "refresh-token"; -export const AUTH_USER_ID = import.meta.env.VITE_AUTH_USER_ID || "token-id"; +export const CLAIM_NAME = process.env.CLAIM_NAME || "Authorization"; +export const ACCESS_TOKEN = process.env.ACCESS_TOKEN || "access-token"; +export const REFRESH_TOKEN = process.env.REFRESH_TOKEN || "refresh-token"; +export const AUTH_USER_ID = process.env.AUTH_USER_ID || "token-id"; diff --git a/packages/common/components/src/ui/file-search/file-search.stories.tsx b/packages/common/components/src/ui/file-search/file-search.stories.tsx deleted file mode 100644 index fdef8da99..000000000 --- a/packages/common/components/src/ui/file-search/file-search.stories.tsx +++ /dev/null @@ -1,23 +0,0 @@ -import type { Meta, StoryObj } from "@storybook/react"; -import { FileSearch } from "."; - -const meta: Meta = { - title: "UI/FileSearch", - component: FileSearch, - parameters: { - docs: { - description: { - component: ` -- [Docs](https://ui.shadcn.com/docs/components/input#file) -`, - }, - }, - }, -}; - -export default meta; -type Story = StoryObj; - -export const Default: Story = { - render: () => , -}; diff --git a/packages/common/components/src/ui/file-search/index.tsx b/packages/common/components/src/ui/file-search/index.tsx deleted file mode 100644 index 9c9043b60..000000000 --- a/packages/common/components/src/ui/file-search/index.tsx +++ /dev/null @@ -1,29 +0,0 @@ -import "./file-search.css"; - -import { Input } from "../input"; -import { cn } from "../../lib/utils"; -import { combineFileName, validFileType } from "./utils"; - -type FileSearchProps = React.InputHTMLAttributes & { - id: string; - files?: ReadonlyArray; -}; - -export function FileSearch({ - id, - className, - files, - ...props -}: FileSearchProps) { - return ( -
- -
- {files?.filter(validFileType).map(combineFileName).join(", ")} -
- -
- ); -} diff --git a/packages/common/components/src/ui/file-search/file-search.css b/packages/common/components/src/ui/file-uploader/file-uploader.css similarity index 82% rename from packages/common/components/src/ui/file-search/file-search.css rename to packages/common/components/src/ui/file-uploader/file-uploader.css index c8a1e3975..16a92099c 100644 --- a/packages/common/components/src/ui/file-search/file-search.css +++ b/packages/common/components/src/ui/file-uploader/file-uploader.css @@ -1,13 +1,13 @@ @tailwind components; @layer components { - .file-search-container { + .file-uploader-container { @apply flex w-full max-w-sm items-center gap-2; } - .file-search-preview { + .file-uploader-preview { @apply h-12 w-full min-w-56 overflow-x-auto whitespace-nowrap rounded-md border border-input bg-background px-3 py-2 text-[1.2rem]; } - .file-search-label { + .file-uploader-label { @apply flex-shrink-0 cursor-pointer rounded-sm border border-input bg-muted px-4 py-2 text-[1.2rem] font-medium text-muted-foreground; } } diff --git a/packages/common/components/src/ui/file-uploader/file-uploader.stories.tsx b/packages/common/components/src/ui/file-uploader/file-uploader.stories.tsx new file mode 100644 index 000000000..a849f2b14 --- /dev/null +++ b/packages/common/components/src/ui/file-uploader/file-uploader.stories.tsx @@ -0,0 +1,14 @@ +import type { Meta, StoryObj } from "@storybook/react"; +import { FileUploader } from "."; + +const meta: Meta = { + title: "UI/FileUploader", + component: FileUploader, +}; + +export default meta; +type Story = StoryObj; + +export const Default: Story = { + render: () => {}} />, +}; diff --git a/packages/common/components/src/ui/file-uploader/index.tsx b/packages/common/components/src/ui/file-uploader/index.tsx new file mode 100644 index 000000000..956c99ceb --- /dev/null +++ b/packages/common/components/src/ui/file-uploader/index.tsx @@ -0,0 +1,37 @@ +import "./file-uploader.css"; + +import { cn } from "../../lib/utils"; +import { Input } from "../input"; +import { combineFileName, validFileType } from "./utils"; + +type FileUploaderProps = React.InputHTMLAttributes & { + id: string; + files?: ReadonlyArray; + onUpload: (e: React.ChangeEvent) => void; +}; + +export function FileUploader({ + id, + className, + files, + onUpload, + ...props +}: FileUploaderProps) { + return ( +
+ +
+ {files?.filter(validFileType).map(combineFileName).join(", ")} +
+ +
+ ); +} diff --git a/packages/common/components/src/ui/file-search/utils.ts b/packages/common/components/src/ui/file-uploader/utils.ts similarity index 100% rename from packages/common/components/src/ui/file-search/utils.ts rename to packages/common/components/src/ui/file-uploader/utils.ts diff --git a/packages/common/components/src/ui/index.ts b/packages/common/components/src/ui/index.ts index dde5f5879..007c8ab43 100644 --- a/packages/common/components/src/ui/index.ts +++ b/packages/common/components/src/ui/index.ts @@ -7,7 +7,7 @@ export * from "./data-table"; export * from "./date-picker"; export * from "./dialog"; export * from "./dropdown-menu"; -export * from "./file-search"; +export * from "./file-uploader"; export * from "./icon-finder"; export * from "./input"; export * from "./label"; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 82f59a6cd..553c3a9e2 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -93,6 +93,9 @@ importers: tailwindcss-animate: specifier: ^1.0.7 version: 1.0.7(tailwindcss@3.4.17) + tus-js-client: + specifier: ^4.3.1 + version: 4.3.1 zod: specifier: ^3.24.2 version: 3.24.2 @@ -4175,7 +4178,6 @@ packages: /buffer-from@1.1.2: resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} - dev: true /bundle-require@5.1.0(esbuild@0.24.2): resolution: {integrity: sha512-3WrrOuZiyaaZPWiEt4G3+IffISVC9HYlWueJEBWED4ZH4aIAC2PnkdnuRrR94M+w6yGWn4AglWtJtBI8YqvgoA==} @@ -4353,6 +4355,13 @@ packages: dev: false optional: true + /combine-errors@3.0.3: + resolution: {integrity: sha512-C8ikRNRMygCwaTx+Ek3Yr+OuZzgZjduCOfSQBjbM8V3MfgcjSTeto/GXP6PAwKvJz/v15b7GHZvx5rOlczFw/Q==} + dependencies: + custom-error-instance: 2.1.1 + lodash.uniqby: 4.5.0 + dev: false + /combined-stream@1.0.8: resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} engines: {node: '>= 0.8'} @@ -4422,6 +4431,10 @@ packages: /csstype@3.1.3: resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} + /custom-error-instance@2.1.1: + resolution: {integrity: sha512-p6JFxJc3M4OTD2li2qaHkDCw9SfMw82Ldr6OC9Je1aXiGfhx2W8p3GaoeaGrPJTUN9NirTM/KTxHWMUdR1rsUg==} + dev: false + /d3-array@3.2.4: resolution: {integrity: sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==} engines: {node: '>=12'} @@ -5715,7 +5728,6 @@ packages: /graceful-fs@4.2.11: resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} - dev: true /graphemer@1.4.0: resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} @@ -5993,6 +6005,11 @@ packages: call-bound: 1.0.3 dev: true + /is-stream@2.0.1: + resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} + engines: {node: '>=8'} + dev: false + /is-string@1.1.1: resolution: {integrity: sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==} engines: {node: '>= 0.4'} @@ -6097,6 +6114,10 @@ packages: engines: {node: '>=10'} dev: true + /js-base64@3.7.7: + resolution: {integrity: sha512-7rCnleh0z2CkXhH67J8K1Ytz0b2Y+yxTPL+/KOJoa20hfnVQ/3/T6W/KflYI4bRHRagNeXeU2bkNGI3v1oS/lw==} + dev: false + /js-cookie@3.0.5: resolution: {integrity: sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==} engines: {node: '>=14'} @@ -6219,6 +6240,37 @@ packages: resolution: {integrity: sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==} dev: true + /lodash._baseiteratee@4.7.0: + resolution: {integrity: sha512-nqB9M+wITz0BX/Q2xg6fQ8mLkyfF7MU7eE+MNBNjTHFKeKaZAPEzEg+E8LWxKWf1DQVflNEn9N49yAuqKh2mWQ==} + dependencies: + lodash._stringtopath: 4.8.0 + dev: false + + /lodash._basetostring@4.12.0: + resolution: {integrity: sha512-SwcRIbyxnN6CFEEK4K1y+zuApvWdpQdBHM/swxP962s8HIxPO3alBH5t3m/dl+f4CMUug6sJb7Pww8d13/9WSw==} + dev: false + + /lodash._baseuniq@4.6.0: + resolution: {integrity: sha512-Ja1YevpHZctlI5beLA7oc5KNDhGcPixFhcqSiORHNsp/1QTv7amAXzw+gu4YOvErqVlMVyIJGgtzeepCnnur0A==} + dependencies: + lodash._createset: 4.0.3 + lodash._root: 3.0.1 + dev: false + + /lodash._createset@4.0.3: + resolution: {integrity: sha512-GTkC6YMprrJZCYU3zcqZj+jkXkrXzq3IPBcF/fIPpNEAB4hZEtXU8zp/RwKOvZl43NUmwDbyRk3+ZTbeRdEBXA==} + dev: false + + /lodash._root@3.0.1: + resolution: {integrity: sha512-O0pWuFSK6x4EXhM1dhZ8gchNtG7JMqBtrHdoUFUWXD7dJnNSUze1GuyQr5sOs0aCvgGeI3o/OJW8f4ca7FDxmQ==} + dev: false + + /lodash._stringtopath@4.8.0: + resolution: {integrity: sha512-SXL66C731p0xPDC5LZg4wI5H+dJo/EO4KTqOMwLYCH3+FmmfAKJEZCm6ohGpI+T1xwsDsJCfL4OnhorllvlTPQ==} + dependencies: + lodash._basetostring: 4.12.0 + dev: false + /lodash.add@3.7.0: resolution: {integrity: sha512-/uxUZkB6vYiPoGSXADJJE+Ee4W2vU+bxZOFdvJdx+mDgv+qyAO76sWD84Q4FxsMgTjC1+gVFC9YNhdG243QRDw==} dev: false @@ -6248,6 +6300,17 @@ packages: resolution: {integrity: sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==} dev: true + /lodash.throttle@4.1.1: + resolution: {integrity: sha512-wIkUCfVKpVsWo3JSZlc+8MB5it+2AN5W8J7YVMST30UrvcQNZ1Okbj+rbVniijTWE6FGYy4XJq/rHkas8qJMLQ==} + dev: false + + /lodash.uniqby@4.5.0: + resolution: {integrity: sha512-IRt7cfTtHy6f1aRVA5n7kT8rgN3N1nH6MOWLcHfpWG2SH19E3JksLK38MktLxZDhlAjCP9jpIXkOnRXlu6oByQ==} + dependencies: + lodash._baseiteratee: 4.7.0 + lodash._baseuniq: 4.6.0 + dev: false + /lodash@4.17.21: resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} @@ -6905,6 +6968,14 @@ packages: object-assign: 4.1.1 react-is: 16.13.1 + /proper-lockfile@4.1.2: + resolution: {integrity: sha512-TjNPblN4BwAWMXU8s9AEz4JmQxnD1NNL7bNOY/AKUzyamc379FWASUhc/K1pL2noVb+XmZKLL68cjzLsiOAMaA==} + dependencies: + graceful-fs: 4.2.11 + retry: 0.12.0 + signal-exit: 3.0.7 + dev: false + /proxy-from-env@1.1.0: resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} dev: false @@ -6914,6 +6985,10 @@ packages: engines: {node: '>=6'} dev: true + /querystringify@2.2.0: + resolution: {integrity: sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==} + dev: false + /queue-microtask@1.2.3: resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} @@ -7272,6 +7347,10 @@ packages: engines: {node: '>=0.10.0'} dev: true + /requires-port@1.0.0: + resolution: {integrity: sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==} + dev: false + /resolve-from@4.0.0: resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} engines: {node: '>=4'} @@ -7304,6 +7383,11 @@ packages: supports-preserve-symlinks-flag: 1.0.0 dev: true + /retry@0.12.0: + resolution: {integrity: sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==} + engines: {node: '>= 4'} + dev: false + /reusify@1.1.0: resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} engines: {iojs: '>=1.0.0', node: '>=0.10.0'} @@ -7517,6 +7601,10 @@ packages: side-channel-weakmap: 1.0.2 dev: true + /signal-exit@3.0.7: + resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} + dev: false + /signal-exit@4.1.0: resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} engines: {node: '>=14'} @@ -8089,6 +8177,19 @@ packages: turbo-windows-arm64: 2.4.2 dev: true + /tus-js-client@4.3.1: + resolution: {integrity: sha512-ZLeYmjrkaU1fUsKbIi8JML52uAocjEZtBx4DKjRrqzrZa0O4MYwT6db+oqePlspV+FxXJAyFBc/L5gwUi2OFsg==} + engines: {node: '>=18'} + dependencies: + buffer-from: 1.1.2 + combine-errors: 3.0.3 + is-stream: 2.0.1 + js-base64: 3.7.7 + lodash.throttle: 4.1.1 + proper-lockfile: 4.1.2 + url-parse: 1.5.10 + dev: false + /type-check@0.4.0: resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} engines: {node: '>= 0.8.0'} @@ -8221,6 +8322,13 @@ packages: punycode: 2.3.1 dev: true + /url-parse@1.5.10: + resolution: {integrity: sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==} + dependencies: + querystringify: 2.2.0 + requires-port: 1.0.0 + dev: false + /use-callback-ref@1.3.3(@types/react@18.3.18)(react@18.3.1): resolution: {integrity: sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg==} engines: {node: '>=10'}