From 1629600eeb140f7e7c37b42858dc2bdc1642d11b Mon Sep 17 00:00:00 2001 From: Pat Needham Date: Mon, 17 Jun 2024 09:26:02 -0400 Subject: [PATCH] added stroke design; updated package to run script outside of dev --- app/models/design/design.server.ts | 7 +- .../design/stroke/stroke.delete.server.ts | 13 +++ app/models/design/stroke/stroke.get.server.ts | 52 +++++++++ app/models/design/stroke/stroke.server.ts | 31 ++++++ .../stroke/stroke.update.basis.server.ts | 53 +++++++++ .../design/stroke/stroke.update.server.ts | 20 ++++ app/models/design/stroke/utils.ts | 39 +++++++ app/models/design/utils.ts | 3 + app/schema/design/stroke.ts | 60 ++++++++++ package-lock.json | 104 +----------------- package.json | 2 +- .../populate-design-attributes-by-type.ts | 33 ++++++ .../fly.io/app-console-prisma-studio-proxy.sh | 4 +- scripts/fly.io/app-console-prisma-studio.sh | 4 +- 14 files changed, 321 insertions(+), 104 deletions(-) create mode 100644 app/models/design/stroke/stroke.delete.server.ts create mode 100644 app/models/design/stroke/stroke.get.server.ts create mode 100644 app/models/design/stroke/stroke.server.ts create mode 100644 app/models/design/stroke/stroke.update.basis.server.ts create mode 100644 app/models/design/stroke/stroke.update.server.ts create mode 100644 app/models/design/stroke/utils.ts create mode 100644 app/schema/design/stroke.ts diff --git a/app/models/design/design.server.ts b/app/models/design/design.server.ts index efa3c63f..58f8de65 100644 --- a/app/models/design/design.server.ts +++ b/app/models/design/design.server.ts @@ -27,6 +27,10 @@ import { type IDesignAttributesFill, type IDesignFill, } from './fill/fill.server' +import { + type IDesignStroke, + type IDesignAttributesStroke, +} from './stroke/stroke.server' // Omitting 'createdAt' and 'updatedAt' from the Design interface // prisma query returns a string for these fields @@ -47,7 +51,7 @@ export interface IDesign extends BaseDesign { // when adding attributes to a design type, // make sure it starts as optional or is set to a default value // for when parsing the design from the deserializer -export type IDesignAttributes = IDesignAttributesFill +export type IDesignAttributes = IDesignAttributesFill | IDesignAttributesStroke export interface IDesignParsed extends BaseDesign { type: designTypeEnum @@ -60,6 +64,7 @@ export interface IDesignParsed extends BaseDesign { // TODO: replace with this ^^ export type IDesignByType = { designFills: IDesignFill[] + designStroke: IDesignStroke[] } // export interface IDesignsByTypeWithType { diff --git a/app/models/design/stroke/stroke.delete.server.ts b/app/models/design/stroke/stroke.delete.server.ts new file mode 100644 index 00000000..dcb8ea4b --- /dev/null +++ b/app/models/design/stroke/stroke.delete.server.ts @@ -0,0 +1,13 @@ +import { prisma } from '#app/utils/db.server' +import { type IDesignStroke } from './stroke.server' + +export interface IDesignStrokeDeletedResponse { + success: boolean + message?: string +} + +export const deleteDesignStroke = ({ id }: { id: IDesignStroke['id'] }) => { + return prisma.design.delete({ + where: { id }, + }) +} diff --git a/app/models/design/stroke/stroke.get.server.ts b/app/models/design/stroke/stroke.get.server.ts new file mode 100644 index 00000000..74d1ebcb --- /dev/null +++ b/app/models/design/stroke/stroke.get.server.ts @@ -0,0 +1,52 @@ +import { invariant } from '@epic-web/invariant' +import { z } from 'zod' +import { DesignTypeEnum } from '#app/schema/design' +import { prisma } from '#app/utils/db.server' +import { deserializeDesign } from '../utils' +import { type IDesignStroke } from './stroke.server' + +export type queryWhereArgsType = z.infer +const whereArgs = z.object({ + id: z.string().optional(), + ownerId: z.string().optional(), + artworkVersionId: z.string().optional(), + layerId: z.string().optional(), +}) + +// TODO: Add schemas for each type of query and parse with zod +// aka if by id that should be present, if by slug that should be present +// owner id should be present unless admin (not set up yet) +const validateQueryWhereArgsPresent = (where: queryWhereArgsType) => { + const nullValuesAllowed: string[] = [] + const missingValues: Record = {} + for (const [key, value] of Object.entries(where)) { + const valueIsNull = value === null || value === undefined + const nullValueAllowed = nullValuesAllowed.includes(key) + if (valueIsNull && !nullValueAllowed) { + missingValues[key] = value + } + } + + if (Object.keys(missingValues).length > 0) { + console.log('Missing values:', missingValues) + throw new Error( + 'Null or undefined values are not allowed in query parameters for design stroke.', + ) + } +} + +export const getDesignStroke = async ({ + where, +}: { + where: queryWhereArgsType +}): Promise => { + validateQueryWhereArgsPresent(where) + const design = await prisma.design.findFirst({ + where: { + ...where, + type: DesignTypeEnum.STROKE, + }, + }) + invariant(design, 'Design Stroke Not found') + return deserializeDesign({ design }) as IDesignStroke +} diff --git a/app/models/design/stroke/stroke.server.ts b/app/models/design/stroke/stroke.server.ts new file mode 100644 index 00000000..dddb91bd --- /dev/null +++ b/app/models/design/stroke/stroke.server.ts @@ -0,0 +1,31 @@ +import { type DesignTypeEnum } from '#app/schema/design' +import { type IDesignSubmission, type IDesignParsed } from '../design.server' + +export interface IDesignStroke extends IDesignParsed { + type: typeof DesignTypeEnum.STROKE + attributes: IDesignAttributesStroke +} + +export type IDesignStrokeBasis = + | 'defined' + | 'random' + | 'palette-selected' + | 'palette-random' + | 'palette-loop' + | 'palette-loop-reverse' + | 'pixel' + +export type IDesignStrokeStyle = 'solid' + +// when adding attributes to an design type, +// make sure it starts as optional or is set to a default value +// for when parsing the design from the deserializer +export interface IDesignAttributesStroke { + basis?: IDesignStrokeBasis + style?: IDesignStrokeStyle + value?: string +} + +export interface IDesignStrokeSubmission + extends IDesignSubmission, + IDesignAttributesStroke {} diff --git a/app/models/design/stroke/stroke.update.basis.server.ts b/app/models/design/stroke/stroke.update.basis.server.ts new file mode 100644 index 00000000..33da571a --- /dev/null +++ b/app/models/design/stroke/stroke.update.basis.server.ts @@ -0,0 +1,53 @@ +import { type IntentActionArgs } from '#app/definitions/intent-action-args' +import { type IUser } from '#app/models/user/user.server' +import { EditDesignStrokeBasisSchema } from '#app/schema/stroke' +import { ValidateDesignSubmissionStrategy } from '#app/strategies/validate-submission.strategy' +import { validateEntitySubmission } from '#app/utils/conform-utils' +import { prisma } from '#app/utils/db.server' +import { + type IDesignAttributesStroke, + type IDesignStroke, + type IDesignStrokeBasis, +} from './stroke.server' +import { stringifyDesignStrokeAttributes } from './utils' + +export const validateEditBasisDesignStrokeSubmission = async ({ + userId, + formData, +}: IntentActionArgs) => { + const strategy = new ValidateDesignSubmissionStrategy() + + return await validateEntitySubmission({ + userId, + formData, + schema: EditDesignStrokeBasisSchema, + strategy, + }) +} + +export interface IDesignStrokeUpdateBasisSubmission { + userId: IUser['id'] + id: IDesignStroke['id'] + basis: IDesignStrokeBasis +} + +interface IDesignStrokeUpdateBasisData { + attributes: IDesignAttributesStroke +} + +export const updateDesignStrokeBasis = ({ + id, + data, +}: { + id: IDesignStroke['id'] + data: IDesignStrokeUpdateBasisData +}) => { + const { attributes } = data + const jsonAttributes = stringifyDesignStrokeAttributes(attributes) + return prisma.design.update({ + where: { id }, + data: { + attributes: jsonAttributes, + }, + }) +} diff --git a/app/models/design/stroke/stroke.update.server.ts b/app/models/design/stroke/stroke.update.server.ts new file mode 100644 index 00000000..8f486003 --- /dev/null +++ b/app/models/design/stroke/stroke.update.server.ts @@ -0,0 +1,20 @@ +import { type IDesign, type IDesignUpdateData } from '../design.server' +import { + type IDesignStrokeSubmission, + type IDesignAttributesStroke, + type IDesignStroke, +} from './stroke.server' + +export interface IDesignStrokeUpdatedResponse { + success: boolean + message?: string + updatedDesignStroke?: IDesign +} + +export interface IDesignStrokeUpdateSubmission extends IDesignStrokeSubmission { + id: IDesignStroke['id'] +} + +export interface IDesignStrokeUpdateData extends IDesignUpdateData { + attributes: IDesignAttributesStroke +} diff --git a/app/models/design/stroke/utils.ts b/app/models/design/stroke/utils.ts new file mode 100644 index 00000000..c2596cb5 --- /dev/null +++ b/app/models/design/stroke/utils.ts @@ -0,0 +1,39 @@ +import { ZodError } from 'zod' +import { DesignAttributesStrokeSchema } from '#app/schema/design/stroke' +import { type IDesignAttributesStroke } from './stroke.server' + +export const parseDesignStrokeAttributes = ( + attributes: string, +): IDesignAttributesStroke => { + try { + return DesignAttributesStrokeSchema.parse(JSON.parse(attributes)) + } catch (error: any) { + if (error instanceof ZodError) { + throw new Error( + `Validation failed for asset image: ${error.errors.map(e => e.message).join(', ')}`, + ) + } else { + throw new Error( + `Unexpected error during validation for asset image: ${error.message}`, + ) + } + } +} + +export const stringifyDesignStrokeAttributes = ( + attributes: IDesignAttributesStroke, +): string => { + try { + return JSON.stringify(DesignAttributesStrokeSchema.parse(attributes)) + } catch (error: any) { + if (error instanceof ZodError) { + throw new Error( + `Validation failed for asset image: ${error.errors.map(e => e.message).join(', ')}`, + ) + } else { + throw new Error( + `Unexpected error during validation for asset image: ${error.message}`, + ) + } + } +} diff --git a/app/models/design/utils.ts b/app/models/design/utils.ts index af2f1721..9f064440 100644 --- a/app/models/design/utils.ts +++ b/app/models/design/utils.ts @@ -2,6 +2,7 @@ import { ZodError } from 'zod' import { DesignTypeEnum, type designTypeEnum } from '#app/schema/design' import { type IDesign, type IDesignParsed } from './design.server' import { parseDesignFillAttributes } from './fill/utils' +import { parseDesignStrokeAttributes } from './stroke/utils' export const deserializeDesigns = ({ designs, @@ -42,6 +43,8 @@ export const validateDesignAttributes = ({ switch (type) { case DesignTypeEnum.FILL: return parseDesignFillAttributes(attributes) + case DesignTypeEnum.STROKE: + return parseDesignStrokeAttributes(attributes) default: throw new Error(`Unsupported design type: ${type}`) } diff --git a/app/schema/design/stroke.ts b/app/schema/design/stroke.ts new file mode 100644 index 00000000..17fa5412 --- /dev/null +++ b/app/schema/design/stroke.ts @@ -0,0 +1,60 @@ +import { z } from 'zod' +import { type ObjectValues } from '#app/utils/typescript-helpers' +import { HexcodeSchema } from '../colors' + +export const StrokeBasisTypeEnum = { + DEFINED: 'defined', // exact hex value + RANDOM: 'random', // random hex value + PALETTE_SELECTED: 'palette-selected', // first palette in array + PALETTE_RANDOM: 'palette-random', // random palette in array + PALETTE_LOOP: 'palette-loop', // loop palette array by index + PALETTE_LOOP_REVERSE: 'palette-loop-reverse', // loop reversed palette array by index + PIXEL: 'pixel', // pixel color + // add more basis types here +} as const +export const StrokeStyleTypeEnum = { + SOLID: 'solid', // flat color + // add more styles here, like gradient, pattern, etc. +} as const +export type strokeBasisTypeEnum = ObjectValues +export type strokeStyleTypeEnum = ObjectValues + +const StrokeBasisSchema = z.nativeEnum(StrokeBasisTypeEnum) +const StrokeStyleSchema = z.nativeEnum(StrokeStyleTypeEnum) + +// use this to (de)serealize data to/from the db +// when adding attributes to an design type, +// make sure it starts as optional or is set to a default value +// for when parsing the design from the deserializer +export const DesignAttributesStrokeSchema = z.object({ + basis: StrokeBasisSchema.optional(), + style: StrokeStyleSchema.optional(), + value: HexcodeSchema.optional(), +}) + +export const NewDesignStrokeSchema = z.object({ + visible: z.boolean(), + selected: z.boolean(), + basis: StrokeBasisSchema.default(StrokeBasisTypeEnum.DEFINED), + style: StrokeStyleSchema.default(StrokeStyleTypeEnum.SOLID), + value: HexcodeSchema.default('000000'), +}) + +export const EditDesignStrokeBasisSchema = z.object({ + id: z.string(), + basis: StrokeBasisSchema, +}) + +export const EditDesignStrokeStyleSchema = z.object({ + id: z.string(), + style: StrokeStyleSchema, +}) + +export const EditDesignStrokeValueSchema = z.object({ + id: z.string(), + value: HexcodeSchema, +}) + +export const DeleteDesignStrokeSchema = z.object({ + id: z.string(), +}) diff --git a/package-lock.json b/package-lock.json index 668e59f5..a9d8caed 100644 --- a/package-lock.json +++ b/package-lock.json @@ -86,6 +86,7 @@ "tailwindcss": "^3.4.0", "tailwindcss-animate": "^1.0.7", "tailwindcss-radix": "^2.8.0", + "vite-node": "^1.6.0", "zod": "^3.22.4" }, "devDependencies": { @@ -137,7 +138,6 @@ "tsx": "^4.6.0", "typescript": "^5.3.2", "vite": "^5.2.11", - "vite-node": "^1.4.0", "vite-tsconfig-paths": "^4.3.2", "vitest": "^0.34.6" }, @@ -4501,7 +4501,6 @@ "cpu": [ "arm" ], - "dev": true, "optional": true, "os": [ "android" @@ -4514,7 +4513,6 @@ "cpu": [ "arm64" ], - "dev": true, "optional": true, "os": [ "android" @@ -4527,7 +4525,6 @@ "cpu": [ "arm64" ], - "dev": true, "optional": true, "os": [ "darwin" @@ -4540,7 +4537,6 @@ "cpu": [ "x64" ], - "dev": true, "optional": true, "os": [ "darwin" @@ -4553,7 +4549,6 @@ "cpu": [ "arm" ], - "dev": true, "optional": true, "os": [ "linux" @@ -4566,7 +4561,6 @@ "cpu": [ "arm" ], - "dev": true, "optional": true, "os": [ "linux" @@ -4579,7 +4573,6 @@ "cpu": [ "arm64" ], - "dev": true, "optional": true, "os": [ "linux" @@ -4592,7 +4585,6 @@ "cpu": [ "arm64" ], - "dev": true, "optional": true, "os": [ "linux" @@ -4605,7 +4597,6 @@ "cpu": [ "ppc64" ], - "dev": true, "optional": true, "os": [ "linux" @@ -4618,7 +4609,6 @@ "cpu": [ "riscv64" ], - "dev": true, "optional": true, "os": [ "linux" @@ -4631,7 +4621,6 @@ "cpu": [ "s390x" ], - "dev": true, "optional": true, "os": [ "linux" @@ -4644,7 +4633,6 @@ "cpu": [ "x64" ], - "dev": true, "optional": true, "os": [ "linux" @@ -4657,7 +4645,6 @@ "cpu": [ "x64" ], - "dev": true, "optional": true, "os": [ "linux" @@ -4670,7 +4657,6 @@ "cpu": [ "arm64" ], - "dev": true, "optional": true, "os": [ "win32" @@ -4683,7 +4669,6 @@ "cpu": [ "ia32" ], - "dev": true, "optional": true, "os": [ "win32" @@ -4696,7 +4681,6 @@ "cpu": [ "x64" ], - "dev": true, "optional": true, "os": [ "win32" @@ -5961,8 +5945,7 @@ "node_modules/@types/estree": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", - "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", - "dev": true + "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==" }, "node_modules/@types/estree-jsx": { "version": "1.0.5", @@ -7500,7 +7483,6 @@ "version": "6.7.14", "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==", - "dev": true, "engines": { "node": ">=8" } @@ -15401,8 +15383,7 @@ "node_modules/pathe": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.2.tgz", - "integrity": "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==", - "dev": true + "integrity": "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==" }, "node_modules/pathval": { "version": "1.1.1", @@ -17450,7 +17431,6 @@ "version": "4.18.0", "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.18.0.tgz", "integrity": "sha512-QmJz14PX3rzbJCN1SG4Xe/bAAX2a6NpCP8ab2vfu2GiUr8AQcr2nCV/oEO3yneFarB67zk8ShlIyWb2LGTb3Sg==", - "dev": true, "dependencies": { "@types/estree": "1.0.5" }, @@ -20101,7 +20081,6 @@ "version": "5.2.13", "resolved": "https://registry.npmjs.org/vite/-/vite-5.2.13.tgz", "integrity": "sha512-SSq1noJfY9pR3I1TUENL3rQYDQCFqgD+lM6fTRAM8Nv6Lsg5hDLaXkjETVeBt+7vZBCMoibD+6IWnT2mJ+Zb/A==", - "dev": true, "dependencies": { "esbuild": "^0.20.1", "postcss": "^8.4.38", @@ -20156,7 +20135,6 @@ "version": "1.6.0", "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-1.6.0.tgz", "integrity": "sha512-de6HJgzC+TFzOu0NTC4RAIsyf/DY/ibWDYQUcuEA84EMHhcefTUGkjFHKKEJhQN4A+6I0u++kr3l36ZF2d7XRw==", - "dev": true, "dependencies": { "cac": "^6.7.14", "debug": "^4.3.4", @@ -20200,7 +20178,6 @@ "cpu": [ "ppc64" ], - "dev": true, "optional": true, "os": [ "aix" @@ -20216,7 +20193,6 @@ "cpu": [ "arm" ], - "dev": true, "optional": true, "os": [ "android" @@ -20232,7 +20208,6 @@ "cpu": [ "arm64" ], - "dev": true, "optional": true, "os": [ "android" @@ -20248,7 +20223,6 @@ "cpu": [ "x64" ], - "dev": true, "optional": true, "os": [ "android" @@ -20264,7 +20238,6 @@ "cpu": [ "arm64" ], - "dev": true, "optional": true, "os": [ "darwin" @@ -20280,7 +20253,6 @@ "cpu": [ "x64" ], - "dev": true, "optional": true, "os": [ "darwin" @@ -20296,7 +20268,6 @@ "cpu": [ "arm64" ], - "dev": true, "optional": true, "os": [ "freebsd" @@ -20312,7 +20283,6 @@ "cpu": [ "x64" ], - "dev": true, "optional": true, "os": [ "freebsd" @@ -20328,7 +20298,6 @@ "cpu": [ "arm" ], - "dev": true, "optional": true, "os": [ "linux" @@ -20344,7 +20313,6 @@ "cpu": [ "arm64" ], - "dev": true, "optional": true, "os": [ "linux" @@ -20360,7 +20328,6 @@ "cpu": [ "ia32" ], - "dev": true, "optional": true, "os": [ "linux" @@ -20376,7 +20343,6 @@ "cpu": [ "loong64" ], - "dev": true, "optional": true, "os": [ "linux" @@ -20392,7 +20358,6 @@ "cpu": [ "mips64el" ], - "dev": true, "optional": true, "os": [ "linux" @@ -20408,7 +20373,6 @@ "cpu": [ "ppc64" ], - "dev": true, "optional": true, "os": [ "linux" @@ -20424,7 +20388,6 @@ "cpu": [ "riscv64" ], - "dev": true, "optional": true, "os": [ "linux" @@ -20440,7 +20403,6 @@ "cpu": [ "s390x" ], - "dev": true, "optional": true, "os": [ "linux" @@ -20456,7 +20418,6 @@ "cpu": [ "x64" ], - "dev": true, "optional": true, "os": [ "linux" @@ -20472,7 +20433,6 @@ "cpu": [ "x64" ], - "dev": true, "optional": true, "os": [ "netbsd" @@ -20488,7 +20448,6 @@ "cpu": [ "x64" ], - "dev": true, "optional": true, "os": [ "openbsd" @@ -20504,7 +20463,6 @@ "cpu": [ "x64" ], - "dev": true, "optional": true, "os": [ "sunos" @@ -20520,7 +20478,6 @@ "cpu": [ "arm64" ], - "dev": true, "optional": true, "os": [ "win32" @@ -20536,7 +20493,6 @@ "cpu": [ "ia32" ], - "dev": true, "optional": true, "os": [ "win32" @@ -20552,7 +20508,6 @@ "cpu": [ "x64" ], - "dev": true, "optional": true, "os": [ "win32" @@ -20565,7 +20520,6 @@ "version": "0.20.2", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.20.2.tgz", "integrity": "sha512-WdOOppmUNU+IbZ0PaDiTst80zjnrOkyJNHoKupIcVyU8Lvla3Ugx94VzkQ32Ijqd7UhHJy75gNWDMUekcrSJ6g==", - "dev": true, "hasInstallScript": true, "bin": { "esbuild": "bin/esbuild" @@ -23990,112 +23944,96 @@ "version": "4.18.0", "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.18.0.tgz", "integrity": "sha512-Tya6xypR10giZV1XzxmH5wr25VcZSncG0pZIjfePT0OVBvqNEurzValetGNarVrGiq66EBVAFn15iYX4w6FKgQ==", - "dev": true, "optional": true }, "@rollup/rollup-android-arm64": { "version": "4.18.0", "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.18.0.tgz", "integrity": "sha512-avCea0RAP03lTsDhEyfy+hpfr85KfyTctMADqHVhLAF3MlIkq83CP8UfAHUssgXTYd+6er6PaAhx/QGv4L1EiA==", - "dev": true, "optional": true }, "@rollup/rollup-darwin-arm64": { "version": "4.18.0", "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.18.0.tgz", "integrity": "sha512-IWfdwU7KDSm07Ty0PuA/W2JYoZ4iTj3TUQjkVsO/6U+4I1jN5lcR71ZEvRh52sDOERdnNhhHU57UITXz5jC1/w==", - "dev": true, "optional": true }, "@rollup/rollup-darwin-x64": { "version": "4.18.0", "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.18.0.tgz", "integrity": "sha512-n2LMsUz7Ynu7DoQrSQkBf8iNrjOGyPLrdSg802vk6XT3FtsgX6JbE8IHRvposskFm9SNxzkLYGSq9QdpLYpRNA==", - "dev": true, "optional": true }, "@rollup/rollup-linux-arm-gnueabihf": { "version": "4.18.0", "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.18.0.tgz", "integrity": "sha512-C/zbRYRXFjWvz9Z4haRxcTdnkPt1BtCkz+7RtBSuNmKzMzp3ZxdM28Mpccn6pt28/UWUCTXa+b0Mx1k3g6NOMA==", - "dev": true, "optional": true }, "@rollup/rollup-linux-arm-musleabihf": { "version": "4.18.0", "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.18.0.tgz", "integrity": "sha512-l3m9ewPgjQSXrUMHg93vt0hYCGnrMOcUpTz6FLtbwljo2HluS4zTXFy2571YQbisTnfTKPZ01u/ukJdQTLGh9A==", - "dev": true, "optional": true }, "@rollup/rollup-linux-arm64-gnu": { "version": "4.18.0", "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.18.0.tgz", "integrity": "sha512-rJ5D47d8WD7J+7STKdCUAgmQk49xuFrRi9pZkWoRD1UeSMakbcepWXPF8ycChBoAqs1pb2wzvbY6Q33WmN2ftw==", - "dev": true, "optional": true }, "@rollup/rollup-linux-arm64-musl": { "version": "4.18.0", "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.18.0.tgz", "integrity": "sha512-be6Yx37b24ZwxQ+wOQXXLZqpq4jTckJhtGlWGZs68TgdKXJgw54lUUoFYrg6Zs/kjzAQwEwYbp8JxZVzZLRepQ==", - "dev": true, "optional": true }, "@rollup/rollup-linux-powerpc64le-gnu": { "version": "4.18.0", "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.18.0.tgz", "integrity": "sha512-hNVMQK+qrA9Todu9+wqrXOHxFiD5YmdEi3paj6vP02Kx1hjd2LLYR2eaN7DsEshg09+9uzWi2W18MJDlG0cxJA==", - "dev": true, "optional": true }, "@rollup/rollup-linux-riscv64-gnu": { "version": "4.18.0", "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.18.0.tgz", "integrity": "sha512-ROCM7i+m1NfdrsmvwSzoxp9HFtmKGHEqu5NNDiZWQtXLA8S5HBCkVvKAxJ8U+CVctHwV2Gb5VUaK7UAkzhDjlg==", - "dev": true, "optional": true }, "@rollup/rollup-linux-s390x-gnu": { "version": "4.18.0", "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.18.0.tgz", "integrity": "sha512-0UyyRHyDN42QL+NbqevXIIUnKA47A+45WyasO+y2bGJ1mhQrfrtXUpTxCOrfxCR4esV3/RLYyucGVPiUsO8xjg==", - "dev": true, "optional": true }, "@rollup/rollup-linux-x64-gnu": { "version": "4.18.0", "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.18.0.tgz", "integrity": "sha512-xuglR2rBVHA5UsI8h8UbX4VJ470PtGCf5Vpswh7p2ukaqBGFTnsfzxUBetoWBWymHMxbIG0Cmx7Y9qDZzr648w==", - "dev": true, "optional": true }, "@rollup/rollup-linux-x64-musl": { "version": "4.18.0", "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.18.0.tgz", "integrity": "sha512-LKaqQL9osY/ir2geuLVvRRs+utWUNilzdE90TpyoX0eNqPzWjRm14oMEE+YLve4k/NAqCdPkGYDaDF5Sw+xBfg==", - "dev": true, "optional": true }, "@rollup/rollup-win32-arm64-msvc": { "version": "4.18.0", "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.18.0.tgz", "integrity": "sha512-7J6TkZQFGo9qBKH0pk2cEVSRhJbL6MtfWxth7Y5YmZs57Pi+4x6c2dStAUvaQkHQLnEQv1jzBUW43GvZW8OFqA==", - "dev": true, "optional": true }, "@rollup/rollup-win32-ia32-msvc": { "version": "4.18.0", "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.18.0.tgz", "integrity": "sha512-Txjh+IxBPbkUB9+SXZMpv+b/vnTEtFyfWZgJ6iyCmt2tdx0OF5WhFowLmnh8ENGNpfUlUZkdI//4IEmhwPieNg==", - "dev": true, "optional": true }, "@rollup/rollup-win32-x64-msvc": { "version": "4.18.0", "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.18.0.tgz", "integrity": "sha512-UOo5FdvOL0+eIVTgS4tIdbW+TtnBLWg1YBCcU2KWM7nuNwRz9bksDX1bekJJCpu25N1DVWaCwnT39dVQxzqS8g==", - "dev": true, "optional": true }, "@rushstack/eslint-patch": { @@ -25053,8 +24991,7 @@ "@types/estree": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", - "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", - "dev": true + "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==" }, "@types/estree-jsx": { "version": "1.0.5", @@ -26213,8 +26150,7 @@ "cac": { "version": "6.7.14", "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", - "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==", - "dev": true + "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==" }, "cacache": { "version": "17.1.4", @@ -31887,8 +31823,7 @@ "pathe": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.2.tgz", - "integrity": "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==", - "dev": true + "integrity": "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==" }, "pathval": { "version": "1.1.1", @@ -33215,7 +33150,6 @@ "version": "4.18.0", "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.18.0.tgz", "integrity": "sha512-QmJz14PX3rzbJCN1SG4Xe/bAAX2a6NpCP8ab2vfu2GiUr8AQcr2nCV/oEO3yneFarB67zk8ShlIyWb2LGTb3Sg==", - "dev": true, "requires": { "@rollup/rollup-android-arm-eabi": "4.18.0", "@rollup/rollup-android-arm64": "4.18.0", @@ -35030,7 +34964,6 @@ "version": "5.2.13", "resolved": "https://registry.npmjs.org/vite/-/vite-5.2.13.tgz", "integrity": "sha512-SSq1noJfY9pR3I1TUENL3rQYDQCFqgD+lM6fTRAM8Nv6Lsg5hDLaXkjETVeBt+7vZBCMoibD+6IWnT2mJ+Zb/A==", - "dev": true, "requires": { "esbuild": "^0.20.1", "fsevents": "~2.3.3", @@ -35042,168 +34975,144 @@ "version": "0.20.2", "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.20.2.tgz", "integrity": "sha512-D+EBOJHXdNZcLJRBkhENNG8Wji2kgc9AZ9KiPr1JuZjsNtyHzrsfLRrY0tk2H2aoFu6RANO1y1iPPUCDYWkb5g==", - "dev": true, "optional": true }, "@esbuild/android-arm": { "version": "0.20.2", "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.20.2.tgz", "integrity": "sha512-t98Ra6pw2VaDhqNWO2Oph2LXbz/EJcnLmKLGBJwEwXX/JAN83Fym1rU8l0JUWK6HkIbWONCSSatf4sf2NBRx/w==", - "dev": true, "optional": true }, "@esbuild/android-arm64": { "version": "0.20.2", "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.20.2.tgz", "integrity": "sha512-mRzjLacRtl/tWU0SvD8lUEwb61yP9cqQo6noDZP/O8VkwafSYwZ4yWy24kan8jE/IMERpYncRt2dw438LP3Xmg==", - "dev": true, "optional": true }, "@esbuild/android-x64": { "version": "0.20.2", "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.20.2.tgz", "integrity": "sha512-btzExgV+/lMGDDa194CcUQm53ncxzeBrWJcncOBxuC6ndBkKxnHdFJn86mCIgTELsooUmwUm9FkhSp5HYu00Rg==", - "dev": true, "optional": true }, "@esbuild/darwin-arm64": { "version": "0.20.2", "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.20.2.tgz", "integrity": "sha512-4J6IRT+10J3aJH3l1yzEg9y3wkTDgDk7TSDFX+wKFiWjqWp/iCfLIYzGyasx9l0SAFPT1HwSCR+0w/h1ES/MjA==", - "dev": true, "optional": true }, "@esbuild/darwin-x64": { "version": "0.20.2", "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.20.2.tgz", "integrity": "sha512-tBcXp9KNphnNH0dfhv8KYkZhjc+H3XBkF5DKtswJblV7KlT9EI2+jeA8DgBjp908WEuYll6pF+UStUCfEpdysA==", - "dev": true, "optional": true }, "@esbuild/freebsd-arm64": { "version": "0.20.2", "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.20.2.tgz", "integrity": "sha512-d3qI41G4SuLiCGCFGUrKsSeTXyWG6yem1KcGZVS+3FYlYhtNoNgYrWcvkOoaqMhwXSMrZRl69ArHsGJ9mYdbbw==", - "dev": true, "optional": true }, "@esbuild/freebsd-x64": { "version": "0.20.2", "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.20.2.tgz", "integrity": "sha512-d+DipyvHRuqEeM5zDivKV1KuXn9WeRX6vqSqIDgwIfPQtwMP4jaDsQsDncjTDDsExT4lR/91OLjRo8bmC1e+Cw==", - "dev": true, "optional": true }, "@esbuild/linux-arm": { "version": "0.20.2", "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.20.2.tgz", "integrity": "sha512-VhLPeR8HTMPccbuWWcEUD1Az68TqaTYyj6nfE4QByZIQEQVWBB8vup8PpR7y1QHL3CpcF6xd5WVBU/+SBEvGTg==", - "dev": true, "optional": true }, "@esbuild/linux-arm64": { "version": "0.20.2", "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.20.2.tgz", "integrity": "sha512-9pb6rBjGvTFNira2FLIWqDk/uaf42sSyLE8j1rnUpuzsODBq7FvpwHYZxQ/It/8b+QOS1RYfqgGFNLRI+qlq2A==", - "dev": true, "optional": true }, "@esbuild/linux-ia32": { "version": "0.20.2", "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.20.2.tgz", "integrity": "sha512-o10utieEkNPFDZFQm9CoP7Tvb33UutoJqg3qKf1PWVeeJhJw0Q347PxMvBgVVFgouYLGIhFYG0UGdBumROyiig==", - "dev": true, "optional": true }, "@esbuild/linux-loong64": { "version": "0.20.2", "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.20.2.tgz", "integrity": "sha512-PR7sp6R/UC4CFVomVINKJ80pMFlfDfMQMYynX7t1tNTeivQ6XdX5r2XovMmha/VjR1YN/HgHWsVcTRIMkymrgQ==", - "dev": true, "optional": true }, "@esbuild/linux-mips64el": { "version": "0.20.2", "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.20.2.tgz", "integrity": "sha512-4BlTqeutE/KnOiTG5Y6Sb/Hw6hsBOZapOVF6njAESHInhlQAghVVZL1ZpIctBOoTFbQyGW+LsVYZ8lSSB3wkjA==", - "dev": true, "optional": true }, "@esbuild/linux-ppc64": { "version": "0.20.2", "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.20.2.tgz", "integrity": "sha512-rD3KsaDprDcfajSKdn25ooz5J5/fWBylaaXkuotBDGnMnDP1Uv5DLAN/45qfnf3JDYyJv/ytGHQaziHUdyzaAg==", - "dev": true, "optional": true }, "@esbuild/linux-riscv64": { "version": "0.20.2", "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.20.2.tgz", "integrity": "sha512-snwmBKacKmwTMmhLlz/3aH1Q9T8v45bKYGE3j26TsaOVtjIag4wLfWSiZykXzXuE1kbCE+zJRmwp+ZbIHinnVg==", - "dev": true, "optional": true }, "@esbuild/linux-s390x": { "version": "0.20.2", "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.20.2.tgz", "integrity": "sha512-wcWISOobRWNm3cezm5HOZcYz1sKoHLd8VL1dl309DiixxVFoFe/o8HnwuIwn6sXre88Nwj+VwZUvJf4AFxkyrQ==", - "dev": true, "optional": true }, "@esbuild/linux-x64": { "version": "0.20.2", "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.20.2.tgz", "integrity": "sha512-1MdwI6OOTsfQfek8sLwgyjOXAu+wKhLEoaOLTjbijk6E2WONYpH9ZU2mNtR+lZ2B4uwr+usqGuVfFT9tMtGvGw==", - "dev": true, "optional": true }, "@esbuild/netbsd-x64": { "version": "0.20.2", "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.20.2.tgz", "integrity": "sha512-K8/DhBxcVQkzYc43yJXDSyjlFeHQJBiowJ0uVL6Tor3jGQfSGHNNJcWxNbOI8v5k82prYqzPuwkzHt3J1T1iZQ==", - "dev": true, "optional": true }, "@esbuild/openbsd-x64": { "version": "0.20.2", "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.20.2.tgz", "integrity": "sha512-eMpKlV0SThJmmJgiVyN9jTPJ2VBPquf6Kt/nAoo6DgHAoN57K15ZghiHaMvqjCye/uU4X5u3YSMgVBI1h3vKrQ==", - "dev": true, "optional": true }, "@esbuild/sunos-x64": { "version": "0.20.2", "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.20.2.tgz", "integrity": "sha512-2UyFtRC6cXLyejf/YEld4Hajo7UHILetzE1vsRcGL3earZEW77JxrFjH4Ez2qaTiEfMgAXxfAZCm1fvM/G/o8w==", - "dev": true, "optional": true }, "@esbuild/win32-arm64": { "version": "0.20.2", "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.20.2.tgz", "integrity": "sha512-GRibxoawM9ZCnDxnP3usoUDO9vUkpAxIIZ6GQI+IlVmr5kP3zUq+l17xELTHMWTWzjxa2guPNyrpq1GWmPvcGQ==", - "dev": true, "optional": true }, "@esbuild/win32-ia32": { "version": "0.20.2", "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.20.2.tgz", "integrity": "sha512-HfLOfn9YWmkSKRQqovpnITazdtquEW8/SoHW7pWpuEeguaZI4QnCRW6b+oZTztdBnZOS2hqJ6im/D5cPzBTTlQ==", - "dev": true, "optional": true }, "@esbuild/win32-x64": { "version": "0.20.2", "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.20.2.tgz", "integrity": "sha512-N49X4lJX27+l9jbLKSqZ6bKNjzQvHaT8IIFUy+YIqmXQdjYCToGWwOItDrfby14c78aDd5NHQl29xingXfCdLQ==", - "dev": true, "optional": true }, "esbuild": { "version": "0.20.2", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.20.2.tgz", "integrity": "sha512-WdOOppmUNU+IbZ0PaDiTst80zjnrOkyJNHoKupIcVyU8Lvla3Ugx94VzkQ32Ijqd7UhHJy75gNWDMUekcrSJ6g==", - "dev": true, "requires": { "@esbuild/aix-ppc64": "0.20.2", "@esbuild/android-arm": "0.20.2", @@ -35236,7 +35145,6 @@ "version": "1.6.0", "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-1.6.0.tgz", "integrity": "sha512-de6HJgzC+TFzOu0NTC4RAIsyf/DY/ibWDYQUcuEA84EMHhcefTUGkjFHKKEJhQN4A+6I0u++kr3l36ZF2d7XRw==", - "dev": true, "requires": { "cac": "^6.7.14", "debug": "^4.3.4", diff --git a/package.json b/package.json index deb5b4f6..44d43e09 100644 --- a/package.json +++ b/package.json @@ -130,6 +130,7 @@ "tailwindcss": "^3.4.0", "tailwindcss-animate": "^1.0.7", "tailwindcss-radix": "^2.8.0", + "vite-node": "^1.6.0", "zod": "^3.22.4" }, "devDependencies": { @@ -181,7 +182,6 @@ "tsx": "^4.6.0", "typescript": "^5.3.2", "vite": "^5.2.11", - "vite-node": "^1.4.0", "vite-tsconfig-paths": "^4.3.2", "vitest": "^0.34.6" }, diff --git a/prisma/data-migrations/populate-design-attributes-by-type.ts b/prisma/data-migrations/populate-design-attributes-by-type.ts index b5c9f483..0d7e7806 100644 --- a/prisma/data-migrations/populate-design-attributes-by-type.ts +++ b/prisma/data-migrations/populate-design-attributes-by-type.ts @@ -1,10 +1,13 @@ import { getDesignsWithType } from '#app/models/design/design.get.server' import { + type IDesignWithStroke, type IDesignWithFill, type IDesignWithType, } from '#app/models/design/design.server' import { type IDesignAttributesFill } from '#app/models/design/fill/fill.server' import { stringifyDesignFillAttributes } from '#app/models/design/fill/utils' +import { type IDesignAttributesStroke } from '#app/models/design/stroke/stroke.server' +import { stringifyDesignStrokeAttributes } from '#app/models/design/stroke/utils' import { DesignTypeEnum } from '#app/schema/design' import { prisma } from '#app/utils/db.server' @@ -66,6 +69,8 @@ const updateDesignAttributesPromise = (design: IDesignWithType) => { switch (design.type) { case DesignTypeEnum.FILL: return updateDesignFillAttributes(design as IDesignWithFill) + case DesignTypeEnum.STROKE: + return updateDesignStrokeAttributes(design as IDesignWithStroke) default: return Promise.resolve() // throw new Error(`Unsupported design type: ${design.type}`) @@ -100,4 +105,32 @@ const updateDesignFillAttributes = (design: IDesignWithFill) => { }) } +const updateDesignStrokeAttributes = (design: IDesignWithStroke) => { + const { stroke } = design + const { basis, style, value } = stroke + const attributes = { + basis, + style, + value, + } as IDesignAttributesStroke + const jsonAttributes = stringifyDesignStrokeAttributes(attributes) + + return prisma.design + .update({ + where: { id: design.id }, + data: { + attributes: jsonAttributes, + }, + }) + .then(() => { + console.log(`Updated design stroke attributes for design ${design.id}`) + }) + .catch(error => { + console.error( + `Failed to update design stroke attributes for design ${design.id}`, + error, + ) + }) +} + await populateDesignAttributesByType() diff --git a/scripts/fly.io/app-console-prisma-studio-proxy.sh b/scripts/fly.io/app-console-prisma-studio-proxy.sh index 07507eb9..b381927f 100755 --- a/scripts/fly.io/app-console-prisma-studio-proxy.sh +++ b/scripts/fly.io/app-console-prisma-studio-proxy.sh @@ -5,9 +5,9 @@ # chmod +x scripts/fly.io/app-console-prisma-studio-proxy.sh # run prisma studio on fly app -# npm run fly:app:console:prisma:studio +# npm run fly:console:prisma:studio # run proxy to prisma studio on fly app to local port (separate terminal) -# npm run fly:app:console:prisma:studio:proxy +# npm run fly:console:prisma:studio:proxy # Check if the script is running in production environment if [ "$NODE_ENV" = "production" ]; then diff --git a/scripts/fly.io/app-console-prisma-studio.sh b/scripts/fly.io/app-console-prisma-studio.sh index 776da176..efb19888 100755 --- a/scripts/fly.io/app-console-prisma-studio.sh +++ b/scripts/fly.io/app-console-prisma-studio.sh @@ -5,9 +5,9 @@ # chmod +x scripts/fly.io/app-console-prisma-studio.sh # run prisma studio on fly app -# npm run fly:app:console:prisma:studio +# npm run fly:console:prisma:studio # run proxy to prisma studio on fly app to local port (separate terminal) -# npm run fly:app:console:prisma:studio:proxy +# npm run fly:console:prisma:studio:proxy # Check if the script is running in production environment if [ "$NODE_ENV" = "production" ]; then