Merge branch 'main' into canvas-dnd-integration

This commit is contained in:
Roman Pominov authored and GitHub committed 2022-07-29 11:23:29 +03:00
commit 05e65a5906
52 files changed
+636 -244

No files matched your search

+1 -1
View File
@@ -1,5 +1,5 @@
module.exports = {
root: true,
// This tells ESLint to load the config from the package `eslint-config-custom`
extends: ["custom"],
extends: ["@webstudio-is/eslint-config-custom"],
};
+28
View File
@@ -0,0 +1,28 @@
name: 'Release NPM package'
description: 'Release NPM package'
inputs:
package:
description: 'Package.json location (relative)'
default: 'package.json'
required: true
token:
description: 'NPM Token'
default: 'null'
required: true
runs:
using: "composite"
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: 16
- name: Install Dependencies
run: yarn install
shell: bash
- uses: JS-DevTools/npm-publish@v1
with:
token: ${{ inputs.token }}
dry-run: false
check-version: true
access: 'public'
package: ${{ inputs.package }}
+1 -1
View File
@@ -14,7 +14,7 @@
## Before merging
- [ ] tested locally and on preview environment (preview dev login: 5de6e)
- [ ] tested locally and on preview environment (preview dev login: 5de6)
- [ ] updated [test cases](https://github.com/webstudio-is/webstudio-designer/blob/main/apps/designer/docs/test-cases.md) document
- [ ] added tests
- [ ] if any new env variables are added, added them to `.env.example` and the `designer/env-check.js` if mandatory
+16
View File
@@ -0,0 +1,16 @@
name: Design System Publish
on:
push:
paths:
- "packages/design-system/**/*"
- ".github/workflows/design-system.yml"
- ".github/actions/publish/action.yaml"
jobs:
publish:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: ./.github/actions/publish
with:
package: "./packages/design-system/package.json"
token: ${{ secrets.NPM_TOKEN }}
@@ -0,0 +1,16 @@
name: Eslint Config Publish
on:
push:
paths:
- "packages/eslint-config-custom/**/*"
- ".github/workflows/eslint-config-custom.yml"
- ".github/actions/publish/action.yaml"
jobs:
publish:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: ./.github/actions/publish
with:
package: "./packages/eslint-config-custom/package.json"
token: ${{ secrets.NPM_TOKEN }}
+16
View File
@@ -0,0 +1,16 @@
name: Icons Publish
on:
push:
paths:
- "packages/icons/**/*"
- ".github/workflows/icons-publish.yml"
- ".github/actions/publish/action.yaml"
jobs:
publish:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: ./.github/actions/publish
with:
package: "./packages/icons/package.json"
token: ${{ secrets.NPM_TOKEN }}
+16
View File
@@ -0,0 +1,16 @@
name: Jest Config Publish
on:
push:
paths:
- "packages/jest-config/**/*"
- ".github/workflows/jest-config.yml"
- ".github/actions/publish/action.yaml"
jobs:
publish:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: ./.github/actions/publish
with:
package: "./packages/jest-config/package.json"
token: ${{ secrets.NPM_TOKEN }}
-22
View File
@@ -1,22 +0,0 @@
name: SDK publish
on:
push:
paths:
- 'packages/react-sdk/**/*'
- '.github/workflows/react-sdk-publish.yml'
jobs:
publish:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: 16
- name: Install Dependencies
run: yarn install
- uses: JS-DevTools/npm-publish@v1
with:
token: ${{ secrets.NPM_TOKEN }}
dry-run: false
check-version: true
package: './packages/react-sdk/package.json'
+16
View File
@@ -0,0 +1,16 @@
name: SDK publish
on:
push:
paths:
- "packages/react-sdk/**/*"
- ".github/workflows/react-sdk.yml"
- ".github/actions/publish/action.yaml"
jobs:
publish:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: ./.github/actions/publish
with:
package: "./packages/react-sdk/package.json"
token: ${{ secrets.NPM_TOKEN }}
+16
View File
@@ -0,0 +1,16 @@
name: Tsconfig Publish
on:
push:
paths:
- "packages/tsconfig/**/*"
- ".github/workflows/tsconfig.yml"
- ".github/actions/publish/action.yaml"
jobs:
publish:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: ./.github/actions/publish
with:
package: "./packages/tsconfig/package.json"
token: ${{ secrets.NPM_TOKEN }}
@@ -28,9 +28,9 @@ export const Inspector = ({ publish }: InspectorProps) => {
if (selectedInstanceData === undefined) {
return (
<Box css={{ p: "$2" }}>
<Box css={{ p: "$2", flexBasis: "100%" }}>
{/* @todo: use this space for something more usefull: a-la figma's no instance selected sate, maybe create an issue with a more specific proposal? */}
<Card css={{ p: "$3", mt: "$3" }}>
<Card css={{ p: "$3", mt: "$3", width: "100%" }}>
<Paragraph>Select an instance on the canvas</Paragraph>
</Card>
</Box>
@@ -2,48 +2,53 @@ import { ImageIcon } from "@webstudio-is/icons";
import { Flex, Grid, Heading } from "@webstudio-is/design-system";
import { useEffect, useState } from "react";
import { useActionData } from "@remix-run/react";
import { Asset } from "@webstudio-is/prisma-client";
import { AssetManagerImage } from "./components/image";
import { AssetManagerThumbnail } from "./components/thumbnail";
import { AddAnAssetForm } from "./components/add-an-asset-form";
import { UploadingAsset } from "../types";
import { Asset } from "../types";
export const useAssetsState = (baseAssets: Array<Asset>) => {
const imageChanges = useActionData();
const [assets, setAssets] = useState<Asset[]>(baseAssets);
useEffect(() => {
if (imageChanges?.uploadedAssets?.length) {
setAssets((currentAssets) => [
...imageChanges.uploadedAssets,
...currentAssets.filter((asset) => asset.status !== "uploading"),
]);
}
if (imageChanges?.deletedAsset?.id) {
setAssets((currentAssets) => [
...currentAssets.filter(
(asset) => asset.id !== imageChanges.deletedAsset.id
),
]);
}
}, [imageChanges]);
const onUploadAsset = (uploadedAssets: Array<Asset>) =>
setAssets((assets) => [...uploadedAssets, ...assets]);
return { assets, onUploadAsset };
};
export const TabContent = ({
assets: baseAssets,
}: {
assets: Array<Asset | UploadingAsset>;
assets: Array<Asset>;
}) => {
const newImages = useActionData();
const [assets, setAssets] = useState(baseAssets);
useEffect(() => {
if (newImages?.length) {
setAssets((currentAssets) => [
...newImages,
...currentAssets.filter((asset) => asset.status !== "uploading"),
]);
}
}, [newImages]);
const { assets, onUploadAsset } = useAssetsState(baseAssets);
return (
<Flex gap="3" direction="column" css={{ padding: "$1" }}>
<Flex justify="between">
<Flex justify="between" align="center">
<Heading>Assets</Heading>
<AddAnAssetForm
onSubmit={(uploadedAssets: Array<UploadingAsset>) =>
setAssets((assets) => [...uploadedAssets, ...assets])
}
/>
<AddAnAssetForm onSubmit={onUploadAsset} />
</Flex>
<Grid columns={2} gap={2}>
{assets.map((asset) => (
<AssetManagerImage
key={asset.id}
path={asset.path}
alt={asset.alt || asset.name}
status={asset.status}
/>
<AssetManagerThumbnail key={asset.id} {...asset} />
))}
</Grid>
</Flex>
@@ -1,79 +0,0 @@
import { useEffect, useState } from "react";
import { Box, ProgressBar } from "@webstudio-is/design-system";
import { useInterval } from "react-use";
import placeholderImage from "~/shared/images/image-placeholder.svg";
import brokenImage from "~/shared/images/broken-image-placeholder.svg";
import { Asset } from "@webstudio-is/prisma-client";
const useImageWithFallback = ({ path }: { path: string }) => {
const [src, setSrc] = useState(placeholderImage);
useEffect(() => {
const newImage = new Image();
newImage.onload = () => setSrc(path);
newImage.onerror = () => setSrc(brokenImage);
newImage.src = path;
}, [path]);
return src;
};
export const AssetManagerImage = ({
path,
alt,
status,
}: {
path: string;
alt?: string;
status?: Asset["status"];
}) => {
const isUploading = status === "uploading";
const src = useImageWithFallback({ path });
const [progressBarPercentage, setProgressBarPercentage] = useState(0);
// @todo rewrite this fake indication to show real progress
useInterval(
() => {
setProgressBarPercentage((percentage) =>
percentage < 60 ? percentage + 1 : percentage
);
},
isUploading ? 100 : null
);
return (
<Box
title={alt || ""}
css={{
aspectRatio: "1/1",
display: "flex",
justifyContent: "center",
alignItems: "center",
padding: "0 $2",
position: "relative",
}}
>
<Box
css={{
backgroundImage: `url("${src}")`,
width: "100%",
height: "100%",
backgroundSize: "contain",
backgroundRepeat: "no-repeat",
backgroundPosition: "center",
position: "absolute",
left: 0,
top: 0,
...(isUploading ? { filter: "blur(1px)", opacity: 0.7 } : {}),
}}
></Box>
{isUploading && (
<ProgressBar
value={progressBarPercentage}
max={60}
css={{ width: "100%", height: "$1" }}
/>
)}
</Box>
);
};
@@ -0,0 +1,142 @@
import { useEffect, useState } from "react";
import {
Box,
Button,
Flex,
ProgressBar,
Tooltip,
} from "@webstudio-is/design-system";
import { useInterval } from "react-use";
import placeholderImage from "~/shared/images/image-placeholder.svg";
import brokenImage from "~/shared/images/broken-image-placeholder.svg";
import { useSubmit } from "@remix-run/react";
import { Asset } from "~/designer/features/sidebar-left/types";
import { Cross2Icon } from "@radix-ui/react-icons";
const useImageWithFallback = ({ path }: { path: string }) => {
const [src, setSrc] = useState(placeholderImage);
useEffect(() => {
const newImage = new Image();
newImage.onload = () => setSrc(path);
newImage.onerror = () => setSrc(brokenImage);
newImage.src = path;
}, [path]);
return src;
};
const useFakeProgress = ({
isUploading,
isDeleting,
}: {
isUploading: boolean;
isDeleting: boolean;
}) => {
const [progressBarPercentage, setProgressBarPercentage] = useState(0);
// @todo rewrite this fake indication to show real progress
useInterval(
() => {
setProgressBarPercentage((percentage) =>
percentage < 60 ? percentage + 1 : percentage
);
},
isUploading || isDeleting ? 100 : null
);
return progressBarPercentage;
};
export const AssetManagerThumbnail = ({
path,
alt,
status,
name,
id,
}: Asset) => {
const submit = useSubmit();
const [isDeleting, setIsDeleting] = useState(false);
const isUploading = status === "uploading";
const src = useImageWithFallback({ path });
const [isTooltipOpen, setTolltipOpen] = useState(false);
const progressBarPercentage = useFakeProgress({ isUploading, isDeleting });
const closeTooltip = () => setTolltipOpen(false);
const deleteAsset = () => {
const formData = new FormData();
formData.append("assetId", id);
formData.append("assetName", name);
closeTooltip();
setIsDeleting(true);
submit(formData, { method: "delete" });
};
return (
<Box
title={alt || name}
css={{
aspectRatio: "1/1",
display: "flex",
justifyContent: "center",
alignItems: "center",
padding: "0 $2",
position: "relative",
}}
>
<Box
css={{
backgroundImage: `url("${src}")`,
width: "100%",
height: "100%",
backgroundSize: "contain",
backgroundRepeat: "no-repeat",
backgroundPosition: "center",
position: "absolute",
left: 0,
top: 0,
...(isUploading ? { filter: "blur(1px)", opacity: 0.7 } : {}),
}}
></Box>
{!isUploading && (
<Tooltip
open={isTooltipOpen}
multiline
onEscapeKeyDown={closeTooltip}
onPointerDownOutside={closeTooltip}
content={
<Flex direction="column" gap={1} align="center" justify="center">
Are you sure you want to delete this asset?
<Button variant="red" onClick={deleteAsset}>
Delete
</Button>
</Flex>
}
>
<Button
variant="raw"
title="Delete asset"
onClick={() => setTolltipOpen(true)}
css={{
position: "absolute",
top: "$1",
right: "$1",
cursor: "pointer",
color: "$highContrast",
}}
>
<Cross2Icon />
</Button>
</Tooltip>
)}
{(isUploading || isDeleting) && (
<ProgressBar
value={progressBarPercentage}
max={60}
css={{ width: "100%", height: "$1" }}
/>
)}
</Box>
);
};
@@ -1,9 +1,10 @@
import { Asset } from "@webstudio-is/prisma-client";
import { Asset as BaseAsset } from "@webstudio-is/prisma-client";
import { panels } from "./panels";
export type TabName = keyof typeof panels | "none";
export type UploadingAsset = Pick<
Asset,
BaseAsset,
"id" | "status" | "name" | "path" | "alt"
>;
export type Asset = BaseAsset | UploadingAsset;
+4 -1
View File
@@ -1,4 +1,5 @@
// Our root outlet doesn't contain a layout because we have 2 types of documents: canvas and designer and we need to decide down the line which one to render, thre is no single root document.
import { TooltipProvider } from "@radix-ui/react-tooltip";
import { Outlet } from "@remix-run/react";
import { withSentryRouteTracing } from "@sentry/remix";
import { ErrorBoundary } from "@sentry/remix";
@@ -7,7 +8,9 @@ import { OutletProps } from "react-router-dom";
const RootWithErrorBoundary = (props: OutletProps) => (
// @ts-expect-error 'ErrorBoundary' cannot be used as a JSX component.
<ErrorBoundary>
<Outlet {...props} />
<TooltipProvider>
<Outlet {...props} />
</TooltipProvider>
</ErrorBoundary>
);
+7 -3
View File
@@ -10,12 +10,16 @@ import * as db from "~/shared/db";
import config from "~/config";
import { ensureUserCookie } from "~/shared/session";
import { authenticator } from "~/services/auth.server";
import { zfd } from "zod-form-data";
export { links };
const schema = zfd.formData({
project: zfd.text(),
});
export const action: ActionFunction = async ({ request }) => {
const formData = await request.formData();
const title = formData.get("project");
if (typeof title !== "string") return { errors: "Title required" };
const { project: title } = schema.parse(await request.formData());
const { userId, headers } = await ensureUserCookie(request);
const authenticatedUser = await authenticator.isAuthenticated(request);
try {
+35 -6
View File
@@ -9,7 +9,11 @@ import { uploadAssets } from "~/shared/db/misc.server";
import { ErrorMessage } from "~/shared/error";
// if this file does not end in .server remix will not build
// since it only allows node code in those files
import { loadByProject } from "@webstudio-is/asset-uploader/index.server";
import {
deleteAsset,
loadByProject,
} from "@webstudio-is/asset-uploader/index.server";
import { zfd } from "zod-form-data";
export { links };
@@ -33,19 +37,44 @@ type Error = {
errors: "string";
};
const deleteAssetSchema = zfd.formData({
assetId: zfd.text(),
assetName: zfd.text(),
});
export const action: ActionFunction = async ({ request, params }) => {
if (params.id === undefined) throw new Error("Project id undefined");
try {
if (request.method === "DELETE") {
const { assetId, assetName } = deleteAssetSchema.parse(
await request.formData()
);
const deletedAsset = await deleteAsset({
id: assetId,
name: assetName,
});
return { deletedAsset };
}
} catch (error) {
if (error instanceof Error) {
return {
errors: error.message,
};
}
}
if (request.method === "POST") {
try {
const assets = await uploadAssets({
request,
projectId: params.id,
dirname: __dirname,
});
return assets.map((asset: Asset) => ({
...asset,
status: "uploaded",
}));
return {
uploadedAssets: assets.map((asset: Asset) => ({
...asset,
status: "uploaded",
})),
};
} catch (error) {
if (error instanceof Error) {
return {
+7 -3
View File
@@ -1,10 +1,14 @@
import { type ActionFunction } from "@remix-run/node";
import { zfd } from "zod-form-data";
import * as db from "~/shared/db";
const schema = zfd.formData({
domain: zfd.text(),
projectId: zfd.text(),
});
export const action: ActionFunction = async ({ request }) => {
const formData = await request.formData();
const domain = formData.get("domain") as string | null;
const projectId = formData.get("projectId") as string | null;
const { domain, projectId } = schema.parse(await request.formData());
try {
const project = await db.misc.publish({ projectId, domain });
return { domain: project.domain };
+7
View File
@@ -74,3 +74,10 @@
- Add a Box component to the canvas
- Open Props panel
- See the default tag is provided and can be modified
1. Asset manager
- Open assets panel
- Upload an image
- Check it loads and shows a progress bar
- Delete an asset by clicking on the `x` icon and then the delete button in the tooltip
+5 -4
View File
@@ -26,8 +26,8 @@
"@stitches/react": "^1.2.7",
"@vercel/node": "^2.4.1",
"@webstudio-is/asset-uploader": "*",
"@webstudio-is/react-sdk": "*",
"@webstudio-is/prisma-client": "*",
"@webstudio-is/react-sdk": "*",
"@webstudio-is/design-system": "*",
"@webstudio-is/icons": "*",
"bson-objectid": "^2.0.2",
@@ -60,7 +60,8 @@
"remix-auth-github": "^1.1.1",
"remix-auth-google": "^1.1.0",
"sharp": "^0.30.7",
"slugify": "^1.6.5"
"slugify": "^1.6.5",
"zod-form-data": "^1.2.1"
},
"devDependencies": {
"@babel/core": "^7.18.5",
@@ -98,12 +99,12 @@
"esbuild-jest": "^0.5.0",
"esbuild-node-externals": "^1.4.1",
"esbuild-node-loader": "^0.8.0",
"eslint-config-custom": "*",
"jest": "^27.5.1",
"prettier": "^2.6.2",
"react-test-renderer": "^17.0.2",
"relative-deps": "^1.0.7",
"tsconfig": "*",
"@webstudio-is/eslint-config-custom": "*",
"@webstudio-is/tsconfig": "*",
"typescript": "4.6.4"
},
"sideEffects": false,
+1 -2
View File
@@ -1,6 +1,5 @@
{
// Extends can't use node's resolution rn https://github.com/dividab/tsconfig-paths/issues/209#issuecomment-1177261091
"extends": "../../node_modules/tsconfig/remix.json",
"extends": "../../node_modules/@webstudio-is/tsconfig/remix.json",
"include": ["remix.env.d.ts", "**/*.ts", "**/*.tsx"],
"compilerOptions": {
"paths": {
+1 -1
View File
@@ -17,7 +17,7 @@
"storybook": "turbo run storybook:run"
},
"devDependencies": {
"eslint-config-custom": "*",
"@webstudio-is/eslint-config-custom": "*",
"husky": ">=6",
"lint-staged": "^13.0.2",
"prettier": "latest",
+1 -1
View File
@@ -1,5 +1,5 @@
// eslint-disable-next-line
const base = require("jest-config/jest.config.js");
const base = require("@webstudio-is/jest-config");
module.exports = {
...base,
+5 -3
View File
@@ -15,13 +15,15 @@
"dependencies": {
"@aws-sdk/client-s3": "^3.131.0",
"@aws-sdk/lib-storage": "^3.131.0",
"sharp": "^0.30.7",
"@webstudio-is/prisma-client": "*"
"@webstudio-is/prisma-client": "*",
"app-root-path": "^3.0.0",
"sharp": "^0.30.7"
},
"devDependencies": {
"@types/app-root-path": "^1.2.4",
"@types/node": "^17.0.21",
"@types/sharp": "^0.30.4",
"jest-config": "*",
"@webstudio-is/jest-config": "*",
"tsup": "^6.1.3",
"typescript": "^4.6.2",
"zod": "^3.17.3"
+1 -1
View File
@@ -1 +1 @@
export const DEFAULT_UPLPOAD_PATH = "uploads";
export const DEFAULT_UPLOAD_PATH = "public/uploads";
+12
View File
@@ -0,0 +1,12 @@
import { prisma, Asset } from "@webstudio-is/prisma-client";
import { getAssetPath } from "../helpers/get-asset-path";
export const deleteAssetInDb = async (assetId: Asset["id"]): Promise<Asset> => {
if (typeof assetId !== "string") {
throw new Error("Asset ID required");
}
const removedAsset = await prisma.asset.delete({ where: { id: assetId } });
return { ...removedAsset, path: getAssetPath(removedAsset) };
};
+1
View File
@@ -1,2 +1,3 @@
export * from "./create";
export * from "./load";
export * from "./delete";
+1 -1
View File
@@ -5,7 +5,7 @@ export const loadByProject = async (
projectId?: Project["id"]
): Promise<Asset[]> => {
if (typeof projectId !== "string") {
throw new Error("Tree ID required");
throw new Error("Project ID required");
}
const assets = await prisma.asset.findMany({
+38
View File
@@ -0,0 +1,38 @@
import { DeleteObjectCommand } from "@aws-sdk/client-s3";
import { prisma, Asset } from "@webstudio-is/prisma-client";
import { unlink } from "fs/promises";
import path from "path";
import { deleteAssetInDb } from "./db";
import { imageFsDirectory } from "./helpers/image-fs-path";
import { s3EnvVariables } from "./schema";
import { getS3Client } from "./targets/s3/client";
export const deleteAsset = async ({
id,
name,
}: {
id: string;
name: string;
}): Promise<Asset> => {
const currentAsset = await prisma.asset.findUnique({
where: { id },
});
if (!currentAsset) throw new Error("Asset does not exist");
if (currentAsset.location === "REMOTE") {
const s3Envs = s3EnvVariables.parse(process.env);
await getS3Client().send(
new DeleteObjectCommand({
Bucket: s3Envs.S3_BUCKET,
Key: name,
})
);
return await deleteAssetInDb(id);
} else {
const directory = await imageFsDirectory();
await unlink(path.join(directory, name));
return await deleteAssetInDb(id);
}
};
@@ -7,7 +7,8 @@ const fsEnvs = fsEnvVariables.parse(process.env);
export const getAssetPath = (asset: DbAsset) => {
if (asset.location === Location.FS) {
return path.join("/", fsEnvs.FILE_UPLOAD_PATH, asset.name);
const splitPath = fsEnvs.FILE_UPLOAD_PATH.split("public");
return path.join("/", splitPath[splitPath.length - 1], asset.name);
}
if (asset.location === Location.REMOTE && s3Envs.success) {
@@ -0,0 +1,17 @@
import path from "path";
import appRoot from "app-root-path";
import { fsEnvVariables } from "../schema";
const fsUploadVars = fsEnvVariables.parse(process.env);
export const imageFsDirectory = () => {
const isProd = process.env.NODE_ENV === "production";
if (isProd) return path.join(appRoot.path, fsUploadVars.FILE_UPLOAD_PATH);
return path.join(
appRoot.path,
"apps",
"designer",
fsUploadVars.FILE_UPLOAD_PATH
);
};
+1
View File
@@ -1,2 +1,3 @@
export * from "./upload";
export * from "./delete";
export * from "./db";
+2 -2
View File
@@ -1,6 +1,6 @@
import { z } from "zod";
import { NodeOnDiskFile } from "@remix-run/node";
import { DEFAULT_UPLPOAD_PATH } from "./constants";
import { DEFAULT_UPLOAD_PATH } from "./constants";
const SingleImageInUpload = z.instanceof(NodeOnDiskFile);
@@ -25,7 +25,7 @@ export const s3EnvVariables = z.object({
});
export const fsEnvVariables = z.object({
FILE_UPLOAD_PATH: z.string().optional().default(DEFAULT_UPLPOAD_PATH),
FILE_UPLOAD_PATH: z.string().optional().default(DEFAULT_UPLOAD_PATH),
});
export const assetEnvVariables = z.object({
@@ -0,0 +1,14 @@
import { S3Client } from "@aws-sdk/client-s3";
import { s3EnvVariables } from "../../schema";
export const getS3Client = () => {
const s3Envs = s3EnvVariables.parse(process.env);
return new S3Client({
endpoint: s3Envs.S3_ENDPOINT,
region: s3Envs.S3_REGION,
credentials: {
accessKeyId: s3Envs.S3_ACCESS_KEY_ID,
secretAccessKey: s3Envs.S3_SECRET_ACCESS_KEY,
},
});
};
@@ -1,5 +1,5 @@
import { UploadHandlerPart } from "@remix-run/node";
import { PutObjectCommandInput, S3Client } from "@aws-sdk/client-s3";
import { PutObjectCommandInput } from "@aws-sdk/client-s3";
import { Upload } from "@aws-sdk/lib-storage";
import ObjectID from "bson-objectid";
@@ -14,6 +14,7 @@ import {
getFilenameAndExtension,
} from "../../helpers/array-buffer-helpers";
import { Location } from "@webstudio-is/prisma-client";
import { getS3Client } from "./client";
type S3UploadHandler = ({
file,
@@ -57,16 +58,7 @@ export const s3UploadHandler: S3UploadHandler = async ({
},
};
const client = new S3Client({
endpoint: s3Envs.S3_ENDPOINT,
region: s3Envs.S3_REGION,
credentials: {
accessKeyId: s3Envs.S3_ACCESS_KEY_ID,
secretAccessKey: s3Envs.S3_SECRET_ACCESS_KEY,
},
});
const upload = new Upload({ client, params });
const upload = new Upload({ client: getS3Client(), params });
ImagesUploadedSuccess.parse(await upload.done());
const image = sharp(uint8Array);
+6 -8
View File
@@ -1,4 +1,3 @@
import path from "path";
import {
unstable_createFileUploadHandler,
unstable_parseMultipartFormData,
@@ -6,10 +5,12 @@ import {
import { s3UploadHandler } from "./targets/s3/handler";
import { uploadToS3 } from "./targets/s3/uploader";
import { uploadToDisk } from "./targets/disk/upload";
import { assetEnvVariables, fsEnvVariables, s3EnvVariables } from "./schema";
import { assetEnvVariables, s3EnvVariables } from "./schema";
import { Asset } from "@webstudio-is/prisma-client";
import { imageFsDirectory } from "./helpers/image-fs-path";
const isS3Upload = s3EnvVariables.safeParse(process.env).success;
const fsUploadVars = fsEnvVariables.parse(process.env);
const commonUploadVars = assetEnvVariables.parse(process.env);
// user inputs the max value in mb and we transform it to bytes
@@ -18,14 +19,11 @@ export const MAX_UPLOAD_SIZE = parseInt(commonUploadVars.MAX_UPLOAD_SIZE) * 1e6;
export const uploadAssets = async ({
request,
projectId,
dirname,
}: {
request: Request;
projectId: string;
dirname: string;
}) => {
const uploads = path.join(dirname, "../public");
const directory = path.join(uploads, fsUploadVars.FILE_UPLOAD_PATH);
}): Promise<Asset[]> => {
const directory = await imageFsDirectory();
const formData = await unstable_parseMultipartFormData(
request,
isS3Upload
+1 -1
View File
@@ -1,6 +1,6 @@
// Extends can't use node's resolution rn https://github.com/dividab/tsconfig-paths/issues/209#issuecomment-1177261091
{
"extends": "../../node_modules/tsconfig/base.json",
"extends": "../../node_modules/@webstudio-is/tsconfig/base.json",
"include": ["./src/**/*.ts"],
"compilerOptions": {
"module": "commonjs",
+3 -2
View File
@@ -55,9 +55,10 @@
"@radix-ui/react-tabs": "^0.1.5",
"@radix-ui/react-toggle": "^0.1.4",
"@radix-ui/react-toggle-group": "^0.1.5",
"@radix-ui/react-tooltip": "^0.1.7",
"@radix-ui/react-tooltip": "^1.0.0",
"@radix-ui/react-use-layout-effect": "^0.1.0",
"@webstudio-is/icons": "*"
"@webstudio-is/icons": "*",
"@webstudio-is/react-sdk": "*"
},
"main": "lib/index.js",
"types": "lib/index.d.ts",
@@ -1,8 +1,9 @@
import React from "react";
import React, { Ref } from "react";
import { styled } from "../stitches.config";
import * as TooltipPrimitive from "@radix-ui/react-tooltip";
import { Box } from "./box";
import { Text } from "./text";
import type { CSS } from "@webstudio-is/react-sdk";
type TooltipProps = React.ComponentProps<typeof TooltipPrimitive.Root> &
React.ComponentProps<typeof TooltipPrimitive.Content> & {
@@ -10,6 +11,7 @@ type TooltipProps = React.ComponentProps<typeof TooltipPrimitive.Root> &
content: React.ReactNode;
multiline?: boolean;
delayDuration?: number;
css?: CSS;
};
const Content = styled(TooltipPrimitive.Content, {
@@ -18,27 +20,40 @@ const Content = styled(TooltipPrimitive.Content, {
color: "$hiContrast",
borderRadius: "$1",
padding: "$1 $2",
zIndex: "$max",
position: "relative",
variants: {
multiline: {
true: {
maxWidth: 250,
// @todo makew this part of the design system
maxWidth: 110,
pb: 7,
},
},
},
});
export const Tooltip = ({
children,
content,
open,
defaultOpen,
onOpenChange,
multiline,
delayDuration,
...props
}: TooltipProps) => {
const Arrow = styled(TooltipPrimitive.Arrow, {
fill: "$loContrast",
stroke: "$slate7",
strokeWidth: "$1",
marginTop: -0.5,
});
export const Tooltip = React.forwardRef(function TooltipWrapper(
{
children,
content,
open,
defaultOpen,
onOpenChange,
multiline,
delayDuration,
...props
}: TooltipProps,
ref: Ref<HTMLDivElement>
) {
return (
<TooltipPrimitive.Root
open={open}
@@ -49,6 +64,7 @@ export const Tooltip = ({
<TooltipPrimitive.Trigger asChild>{children}</TooltipPrimitive.Trigger>
<Content
ref={ref}
side="top"
align="center"
sideOffset={5}
@@ -66,16 +82,9 @@ export const Tooltip = ({
{content}
</Text>
<Box css={{ color: "$transparentExtreme" }}>
<TooltipPrimitive.Arrow
offset={5}
width={11}
height={5}
style={{
fill: "$loContrast",
}}
/>
<Arrow offset={5} width={11} height={5} />
</Box>
</Content>
</TooltipPrimitive.Root>
);
};
});
+1 -1
View File
@@ -1,6 +1,6 @@
// Extends can't use node's resolution rn https://github.com/dividab/tsconfig-paths/issues/209#issuecomment-1177261091
{
"extends": "../../node_modules/tsconfig/base.json",
"extends": "../../node_modules/@webstudio-is/tsconfig/base.json",
"include": ["./src/**/*.ts", "./src/**/*.tsx"],
"compilerOptions": {
"lib": ["DOM", "DOM.Iterable", "ES2019"],
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "eslint-config-custom",
"version": "0.0.0",
"name": "@webstudio-is/eslint-config-custom",
"version": "1.0.0",
"main": "index.js",
"license": "MIT",
"dependencies": {
+2 -2
View File
@@ -1,7 +1,7 @@
{
"name": "@webstudio-is/icons",
"version": "0.5.0",
"description": "Webstudio JavaScript / TypeScript API",
"version": "0.1.0",
"description": "Webstudio Icons",
"author": "Webstudio <github@webstudio.is>",
"homepage": "https://webstudio.is",
"scripts": {
+1 -1
View File
@@ -1,6 +1,6 @@
// Extends can't use node's resolution rn https://github.com/dividab/tsconfig-paths/issues/209#issuecomment-1177261091
{
"extends": "../../node_modules/tsconfig/base.json",
"extends": "../../node_modules/@webstudio-is/tsconfig/base.json",
"include": ["./src/**/*.ts", "./src/**/*.tsx"],
"compilerOptions": {
"lib": ["DOM", "DOM.Iterable", "ES2019"],
File renamed without changes.
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "jest-config",
"version": "0.0.0",
"name": "@webstudio-is/jest-config",
"version": "1.0.1",
"license": "MIT",
"dependencies": {
"@types/jest": "^27.4.1",
+1 -1
View File
@@ -1,6 +1,6 @@
// Extends can't use node's resolution rn https://github.com/dividab/tsconfig-paths/issues/209#issuecomment-1177261091
{
"extends": "../../node_modules/tsconfig/base.json",
"extends": "../../node_modules/@webstudio-is/tsconfig/base.json",
"include": ["./src/**/*.ts"],
"compilerOptions": {
"module": "commonjs",
+1 -1
View File
@@ -1,5 +1,5 @@
// eslint-disable-next-line
const base = require("jest-config/jest.config.js");
const base = require("@webstudio-is/jest-config");
module.exports = {
...base,
+3 -3
View File
@@ -33,18 +33,18 @@
"@types/css-tree": "^1.0.7",
"@types/fs-extra": "^9.0.13",
"@types/node": "^17.0.21",
"@webstudio-is/prisma-client": "*",
"babel-loader": "^8.2.5",
"camelcase": "^6.3.0",
"css-tree": "^2.1.0",
"esbuild": "^0.14.25",
"esbuild-node-externals": "^1.4.1",
"esbuild-node-loader": "^0.8.0",
"jest-config": "*",
"@webstudio-is/jest-config": "*",
"mdn-data": "2.0.23",
"react": "^17.0.2",
"react-docgen-typescript": "^2.2.2",
"react-dom": "^17.0.2",
"@webstudio-is/prisma-client": "*",
"tsup": "^6.1.3",
"typescript": "^4.6.2"
},
@@ -59,9 +59,9 @@
"@stitches/core": "^1.2.7",
"@webstudio-is/icons": "*",
"immer": "^9.0.12",
"jest-config": "*",
"mitt": "^3.0.0",
"react-nano-state": "^0.4.0",
"@webstudio-is/jest-config": "*",
"zod": "^3.17.3"
},
"main": "lib/index.js",
+1 -1
View File
@@ -1,6 +1,6 @@
// Extends can't use node's resolution rn https://github.com/dividab/tsconfig-paths/issues/209#issuecomment-1177261091
{
"extends": "../../node_modules/tsconfig/base.json",
"extends": "../../node_modules/@webstudio-is/tsconfig/base.json",
"include": ["./src/**/*.ts", "./src/**/*.tsx"],
"compilerOptions": {
"lib": ["DOM", "DOM.Iterable", "ES2019"],
+3 -3
View File
@@ -1,7 +1,7 @@
{
"name": "tsconfig",
"version": "0.0.0",
"private": true,
"name": "@webstudio-is/tsconfig",
"version": "1.0.0",
"private": false,
"main": "index.js",
"files": [
"base.json",
+107 -19
View File
@@ -2752,6 +2752,13 @@
dependencies:
"@babel/runtime" "^7.13.10"
"@radix-ui/primitive@1.0.0":
version "1.0.0"
resolved "https://registry.yarnpkg.com/@radix-ui/primitive/-/primitive-1.0.0.tgz#e1d8ef30b10ea10e69c76e896f608d9276352253"
integrity sha512-3e7rn8FDMin4CgeL7Z/49smCA3rFYY3Ha2rUQ7HRWFadS5iCRw08ZgVT1LaNTCNqgvrUiyczLflrVrF0SRQtNA==
dependencies:
"@babel/runtime" "^7.13.10"
"@radix-ui/react-accessible-icon@^0.1.4":
version "0.1.4"
resolved "https://registry.yarnpkg.com/@radix-ui/react-accessible-icon/-/react-accessible-icon-0.1.4.tgz#bdbf1e3226a0e9e7778b68728b175bdc532b720c"
@@ -2931,6 +2938,18 @@
"@radix-ui/react-use-callback-ref" "0.1.0"
"@radix-ui/react-use-escape-keydown" "0.1.0"
"@radix-ui/react-dismissable-layer@1.0.0":
version "1.0.0"
resolved "https://registry.yarnpkg.com/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.0.0.tgz#35b7826fa262fd84370faef310e627161dffa76b"
integrity sha512-n7kDRfx+LB1zLueRDvZ1Pd0bxdJWDUZNQ/GWoxDn2prnuJKRdxsjulejX/ePkOsLi2tTm6P24mDqlMSgQpsT6g==
dependencies:
"@babel/runtime" "^7.13.10"
"@radix-ui/primitive" "1.0.0"
"@radix-ui/react-compose-refs" "1.0.0"
"@radix-ui/react-primitive" "1.0.0"
"@radix-ui/react-use-callback-ref" "1.0.0"
"@radix-ui/react-use-escape-keydown" "1.0.0"
"@radix-ui/react-dropdown-menu@^0.1.6":
version "0.1.6"
resolved "https://registry.yarnpkg.com/@radix-ui/react-dropdown-menu/-/react-dropdown-menu-0.1.6.tgz#3203229788cd57e552c9f19dcc7008e2b545919c"
@@ -2975,6 +2994,14 @@
"@babel/runtime" "^7.13.10"
"@radix-ui/react-use-layout-effect" "0.1.0"
"@radix-ui/react-id@1.0.0":
version "1.0.0"
resolved "https://registry.yarnpkg.com/@radix-ui/react-id/-/react-id-1.0.0.tgz#8d43224910741870a45a8c9d092f25887bb6d11e"
integrity sha512-Q6iAB/U7Tq3NTolBBQbHTgclPmGWE3OlktGGqrClPozSw4vkQ1DfQAOtzgRPecKsMdJINE05iaoDUG8tRzCBjw==
dependencies:
"@babel/runtime" "^7.13.10"
"@radix-ui/react-use-layout-effect" "1.0.0"
"@radix-ui/react-label@0.1.5", "@radix-ui/react-label@^0.1.5":
version "0.1.5"
resolved "https://registry.yarnpkg.com/@radix-ui/react-label/-/react-label-0.1.5.tgz#12cd965bfc983e0148121d4c99fb8e27a917c45c"
@@ -3046,7 +3073,7 @@
"@radix-ui/react-use-size" "0.1.1"
"@radix-ui/rect" "0.1.1"
"@radix-ui/react-popper@^1.0.0":
"@radix-ui/react-popper@1.0.0", "@radix-ui/react-popper@^1.0.0":
version "1.0.0"
resolved "https://registry.yarnpkg.com/@radix-ui/react-popper/-/react-popper-1.0.0.tgz#fb4f937864bf39c48f27f55beee61fa9f2bef93c"
integrity sha512-k2dDd+1Wl0XWAMs9ZvAxxYsB9sOsEhrFQV4CINd7IUZf0wfdye4OHen9siwxvZImbzhgVeKTJi68OQmPRvVdMg==
@@ -3071,6 +3098,14 @@
"@radix-ui/react-primitive" "0.1.4"
"@radix-ui/react-use-layout-effect" "0.1.0"
"@radix-ui/react-portal@1.0.0":
version "1.0.0"
resolved "https://registry.yarnpkg.com/@radix-ui/react-portal/-/react-portal-1.0.0.tgz#7220b66743394fabb50c55cb32381395cc4a276b"
integrity sha512-a8qyFO/Xb99d8wQdu4o7qnigNjTPG123uADNecz0eX4usnQEj7o+cG4ZX4zkqq98NYekT7UoEQIjxBNWIFuqTA==
dependencies:
"@babel/runtime" "^7.13.10"
"@radix-ui/react-primitive" "1.0.0"
"@radix-ui/react-presence@0.1.2":
version "0.1.2"
resolved "https://registry.yarnpkg.com/@radix-ui/react-presence/-/react-presence-0.1.2.tgz#9f11cce3df73cf65bc348e8b76d891f0d54c1fe3"
@@ -3080,6 +3115,15 @@
"@radix-ui/react-compose-refs" "0.1.0"
"@radix-ui/react-use-layout-effect" "0.1.0"
"@radix-ui/react-presence@1.0.0":
version "1.0.0"
resolved "https://registry.yarnpkg.com/@radix-ui/react-presence/-/react-presence-1.0.0.tgz#814fe46df11f9a468808a6010e3f3ca7e0b2e84a"
integrity sha512-A+6XEvN01NfVWiKu38ybawfHsBjWum42MRPnEuqPsBZ4eV7e/7K321B5VgYMPv3Xx5An6o1/l9ZuDBgmcmWK3w==
dependencies:
"@babel/runtime" "^7.13.10"
"@radix-ui/react-compose-refs" "1.0.0"
"@radix-ui/react-use-layout-effect" "1.0.0"
"@radix-ui/react-primitive@0.1.4":
version "0.1.4"
resolved "https://registry.yarnpkg.com/@radix-ui/react-primitive/-/react-primitive-0.1.4.tgz#6c233cf08b0cb87fecd107e9efecb3f21861edc1"
@@ -3255,26 +3299,24 @@
"@radix-ui/react-primitive" "0.1.4"
"@radix-ui/react-use-controllable-state" "0.1.0"
"@radix-ui/react-tooltip@^0.1.7":
version "0.1.7"
resolved "https://registry.yarnpkg.com/@radix-ui/react-tooltip/-/react-tooltip-0.1.7.tgz#6f8c00d6e489565d14abf209ce0fb8853c8c8ee3"
integrity sha512-eiBUsVOHenZ0JR16tl970bB0DafJBz6mFgSGfIGIVpflFj0LIsIDiLMsYyvYdx1KwwsIUDTEZtxcPm/sWjPzqA==
"@radix-ui/react-tooltip@^1.0.0":
version "1.0.0"
resolved "https://registry.yarnpkg.com/@radix-ui/react-tooltip/-/react-tooltip-1.0.0.tgz#f7fcecf2bac5c31cd14666b5acd056015fc21646"
integrity sha512-RB06pov+O4Npy10ei1C6fsyB9QoOjz7Ubo8Sl3qdKtLgkL9iI96925DYtH0bxx6MH6YB2FuzLU6B75qn3AQQQw==
dependencies:
"@babel/runtime" "^7.13.10"
"@radix-ui/primitive" "0.1.0"
"@radix-ui/react-compose-refs" "0.1.0"
"@radix-ui/react-context" "0.1.1"
"@radix-ui/react-id" "0.1.5"
"@radix-ui/react-popper" "0.1.4"
"@radix-ui/react-portal" "0.1.4"
"@radix-ui/react-presence" "0.1.2"
"@radix-ui/react-primitive" "0.1.4"
"@radix-ui/react-slot" "0.1.2"
"@radix-ui/react-use-controllable-state" "0.1.0"
"@radix-ui/react-use-escape-keydown" "0.1.0"
"@radix-ui/react-use-previous" "0.1.1"
"@radix-ui/react-use-rect" "0.1.1"
"@radix-ui/react-visually-hidden" "0.1.4"
"@radix-ui/primitive" "1.0.0"
"@radix-ui/react-compose-refs" "1.0.0"
"@radix-ui/react-context" "1.0.0"
"@radix-ui/react-dismissable-layer" "1.0.0"
"@radix-ui/react-id" "1.0.0"
"@radix-ui/react-popper" "1.0.0"
"@radix-ui/react-portal" "1.0.0"
"@radix-ui/react-presence" "1.0.0"
"@radix-ui/react-primitive" "1.0.0"
"@radix-ui/react-slot" "1.0.0"
"@radix-ui/react-use-controllable-state" "1.0.0"
"@radix-ui/react-visually-hidden" "1.0.0"
"@radix-ui/react-use-body-pointer-events@0.1.1":
version "0.1.1"
@@ -3291,6 +3333,13 @@
dependencies:
"@babel/runtime" "^7.13.10"
"@radix-ui/react-use-callback-ref@1.0.0":
version "1.0.0"
resolved "https://registry.yarnpkg.com/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.0.0.tgz#9e7b8b6b4946fe3cbe8f748c82a2cce54e7b6a90"
integrity sha512-GZtyzoHz95Rhs6S63D2t/eqvdFCm7I+yHMLVQheKM7nBD8mbZIt+ct1jz4536MDnaOGKIxynJ8eHTkVGVVkoTg==
dependencies:
"@babel/runtime" "^7.13.10"
"@radix-ui/react-use-controllable-state@0.1.0":
version "0.1.0"
resolved "https://registry.yarnpkg.com/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-0.1.0.tgz#4fced164acfc69a4e34fb9d193afdab973a55de1"
@@ -3299,6 +3348,14 @@
"@babel/runtime" "^7.13.10"
"@radix-ui/react-use-callback-ref" "0.1.0"
"@radix-ui/react-use-controllable-state@1.0.0":
version "1.0.0"
resolved "https://registry.yarnpkg.com/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.0.0.tgz#a64deaafbbc52d5d407afaa22d493d687c538b7f"
integrity sha512-FohDoZvk3mEXh9AWAVyRTYR4Sq7/gavuofglmiXB2g1aKyboUD4YtgWxKj8O5n+Uak52gXQ4wKz5IFST4vtJHg==
dependencies:
"@babel/runtime" "^7.13.10"
"@radix-ui/react-use-callback-ref" "1.0.0"
"@radix-ui/react-use-direction@0.1.0":
version "0.1.0"
resolved "https://registry.yarnpkg.com/@radix-ui/react-use-direction/-/react-use-direction-0.1.0.tgz#97ac1d52e497c974389e7988f809238ed72e7df7"
@@ -3314,6 +3371,14 @@
"@babel/runtime" "^7.13.10"
"@radix-ui/react-use-callback-ref" "0.1.0"
"@radix-ui/react-use-escape-keydown@1.0.0":
version "1.0.0"
resolved "https://registry.yarnpkg.com/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.0.0.tgz#aef375db4736b9de38a5a679f6f49b45a060e5d1"
integrity sha512-JwfBCUIfhXRxKExgIqGa4CQsiMemo1Xt0W/B4ei3fpzpvPENKpMKQ8mZSB6Acj3ebrAEgi2xiQvcI1PAAodvyg==
dependencies:
"@babel/runtime" "^7.13.10"
"@radix-ui/react-use-callback-ref" "1.0.0"
"@radix-ui/react-use-layout-effect@0.1.0", "@radix-ui/react-use-layout-effect@^0.1.0":
version "0.1.0"
resolved "https://registry.yarnpkg.com/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-0.1.0.tgz#ebf71bd6d2825de8f1fbb984abf2293823f0f223"
@@ -3374,6 +3439,14 @@
"@babel/runtime" "^7.13.10"
"@radix-ui/react-primitive" "0.1.4"
"@radix-ui/react-visually-hidden@1.0.0":
version "1.0.0"
resolved "https://registry.yarnpkg.com/@radix-ui/react-visually-hidden/-/react-visually-hidden-1.0.0.tgz#4d69d7e3b6d21ee4678ed6de5215dcd068394401"
integrity sha512-MwAhMdX+n6S4InwRKSnpUsp+lLkYG6izQF56ul6guSX2mBBLOMV9Frx7xJlkEe2GjKLzbNuHhaCS6e5gopmZNA==
dependencies:
"@babel/runtime" "^7.13.10"
"@radix-ui/react-primitive" "1.0.0"
"@radix-ui/rect@0.1.1":
version "0.1.1"
resolved "https://registry.yarnpkg.com/@radix-ui/rect/-/rect-0.1.1.tgz#95b5ba51f469bea6b1b841e2d427e17e37d38419"
@@ -4762,6 +4835,11 @@
dependencies:
"@types/estree" "*"
"@types/app-root-path@^1.2.4":
version "1.2.4"
resolved "https://registry.yarnpkg.com/@types/app-root-path/-/app-root-path-1.2.4.tgz#a78b703282b32ac54de768f5512ecc3569919dc7"
integrity sha512-yhURoXmWN/zfw2MXXcOdUTwe5CWhzRWtb4Rs2+JwGpuhm2hxLCzjASi/aIuyVeJB3Iyks1teaHyOEfNNmltdvQ==
"@types/aria-query@^4.2.0":
version "4.2.2"
resolved "https://registry.yarnpkg.com/@types/aria-query/-/aria-query-4.2.2.tgz#ed4e0ad92306a704f9fb132a0cfcf77486dbe2bc"
@@ -5893,6 +5971,11 @@ app-root-dir@^1.0.2:
resolved "https://registry.yarnpkg.com/app-root-dir/-/app-root-dir-1.0.2.tgz#38187ec2dea7577fff033ffcb12172692ff6e118"
integrity sha512-jlpIfsOoNoafl92Sz//64uQHGSyMrD2vYG5d8o2a4qGvyNCvXur7bzIsWtAC/6flI2RYAp3kv8rsfBtaLm7w0g==
app-root-path@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/app-root-path/-/app-root-path-3.0.0.tgz#210b6f43873227e18a4b810a032283311555d5ad"
integrity sha512-qMcx+Gy2UZynHjOHOIXPNvpf+9cjvk3cWrBBK7zg4gH9+clobJRb9NGzcT7mQTcV/6Gm/1WelUtqxVXnNlrwcw==
aproba@^1.0.3, aproba@^1.1.1:
version "1.2.0"
resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a"
@@ -17918,6 +18001,11 @@ yocto-queue@^0.1.0:
resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b"
integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==
zod-form-data@^1.2.1:
version "1.2.1"
resolved "https://registry.yarnpkg.com/zod-form-data/-/zod-form-data-1.2.1.tgz#6da07d7964765b464e959a75f0d797690ee477f7"
integrity sha512-L4zocmlzikYB9A2uVVj3OUKX/t3spOZTzQLOhoCXECn9w+eN3bw0D/KYGX4J72iLdCIwDP6YJyYwVXkx3WTu7A==
zod@^3.17.3:
version "3.17.3"
resolved "https://registry.yarnpkg.com/zod/-/zod-3.17.3.tgz#86abbc670ff0063a4588d85a4dcc917d6e4af2ba"