From 13c9765a0f90c2b67b6e2091e3b4cd0fc6feb4ac Mon Sep 17 00:00:00 2001 From: Mischa Spiegelmock Date: Mon, 1 Jul 2024 20:06:41 -0700 Subject: [PATCH] base lint passing --- .prettierrc.yaml | 2 +- backend/package.json | 2 +- backend/src/api/resolver/greeting.test.ts | 24 +- backend/src/db/seed/fakeSampleData.ts | 16 +- backend/src/db/testUtil.ts | 32 +- backend/src/middleware/lambda.ts | 51 ++-- backend/src/util/testResolver.ts | 45 ++- eslint.config.mjs | 112 ++----- package.json | 5 +- pnpm-lock.yaml | 349 +++++++--------------- sst.config.ts | 27 +- stacks/database.ts | 167 ++++++----- stacks/resources/prismaLayer.ts | 82 +++-- tsconfig.json | 8 +- web/eslint.config.mjs | 65 ++-- web/package.json | 3 +- web/tsconfig.json | 3 +- 17 files changed, 386 insertions(+), 607 deletions(-) diff --git a/.prettierrc.yaml b/.prettierrc.yaml index 5c2a25d..c7bd74d 100644 --- a/.prettierrc.yaml +++ b/.prettierrc.yaml @@ -1,5 +1,5 @@ tabWidth: 2 semi: false printWidth: 120 -trailingComma: es5 +trailingComma: all singleQuote: true diff --git a/backend/package.json b/backend/package.json index adbb6d1..ea647e0 100644 --- a/backend/package.json +++ b/backend/package.json @@ -26,7 +26,7 @@ "@aws-sdk/client-kms": "~3.145.0", "@aws-sdk/client-secrets-manager": "~3.145.0", "@aws-sdk/signature-v4-crt": "^3.130.0", - "@middy/core": "^2.5.7", + "@middy/core": "^5.4.2", "@prisma/client": "5.16.1", "@prisma/internals": "5.16.1", "@prisma/migrate": "5.16.1", diff --git a/backend/src/api/resolver/greeting.test.ts b/backend/src/api/resolver/greeting.test.ts index 9e7e444..09a5704 100644 --- a/backend/src/api/resolver/greeting.test.ts +++ b/backend/src/api/resolver/greeting.test.ts @@ -1,22 +1,18 @@ -import { testCallResolver } from '../../util/testResolver'; -import { GREETING } from './greeting'; - -const { getGreeting, greetInner } = await import('./greeting'); +import { testCallResolver } from '../../util/testResolver' +import { getGreeting, GREETING, greetInner } from './greeting' describe('Greeting resolvers', () => { it('gets current greeting', async () => { - const greeting = await testCallResolver({ - args: {}, - resolverFunc: getGreeting, - }); - expect(greeting.currentGreeting).toBe(GREETING); - }); + const greeting = getGreeting() + expect(greeting.currentGreeting).toBe(GREETING) + }) it('greets user by name', async () => { const greeting = await testCallResolver({ + userName: 'cognitoUsername', args: { name: 'Lebowski' }, resolverFunc: greetInner, - }); - expect(greeting.greeting).toBe(`${GREETING}, Lebowski!`); - }); -}); + }) + expect(greeting.greeting).toBe(`${GREETING}, Lebowski!`) + }) +}) diff --git a/backend/src/db/seed/fakeSampleData.ts b/backend/src/db/seed/fakeSampleData.ts index 352ac96..a527a70 100644 --- a/backend/src/db/seed/fakeSampleData.ts +++ b/backend/src/db/seed/fakeSampleData.ts @@ -1,26 +1,26 @@ -import { truncateAllTables } from '@backend/db/seed/truncate'; -import { userFactory } from '@backend/db/factory/user'; -import { Prisma } from '@prisma/client'; +import { truncateAllTables } from '@backend/db/seed/truncate' +import { userFactory } from '@backend/db/factory/user' +import { Prisma } from '@prisma/client' -const { prisma } = await import('@backend/db/clientSync'); +const { prisma } = await import('@backend/db/clientSync') export async function seedFakerSampleData() { - prisma.$transaction(startSeeding, { maxWait: 30_000, timeout: 60_000 }); + await prisma.$transaction(startSeeding, { maxWait: 30_000, timeout: 60_000 }) } export async function startSeeding(prisma: Prisma.TransactionClient) { if (process.env.CLEAN_SEED) { // Remove old data - truncateAllTables(prisma); + await truncateAllTables(prisma) } // Create 5 fake users with generated data - await prisma.user.createMany({ data: userFactory.buildList(5) }); + await prisma.user.createMany({ data: userFactory.buildList(5) }) // Create 1 user with some custom data await prisma.user.create({ data: userFactory.build({ name: 'Robert Lewandowski', email: 'lewa@fcbarcelona.com', }), - }); + }) } diff --git a/backend/src/db/testUtil.ts b/backend/src/db/testUtil.ts index 0ff6344..15c0d79 100644 --- a/backend/src/db/testUtil.ts +++ b/backend/src/db/testUtil.ts @@ -1,22 +1,22 @@ -import { PrismaTestingHelper } from '@chax-at/transactional-prisma-testing'; -import { truncateAllTables } from '@backend/db/seed/truncate'; -import { PrismaClient } from '@prisma/client'; +import { PrismaTestingHelper } from '@chax-at/transactional-prisma-testing' +import { truncateAllTables } from '@backend/db/seed/truncate' +import { PrismaClient } from '@prisma/client' -const prismaClient = new PrismaClient(); -await truncateAllTables(prismaClient); // can be skipped for speed maybe +const prismaClient = new PrismaClient() +await truncateAllTables(prismaClient) // can be skipped for speed maybe // create prisma proxy to wrap all tests in a transaction -const prismaTestingHelper = new PrismaTestingHelper(prismaClient); +const prismaTestingHelper = new PrismaTestingHelper(prismaClient) vi.mock('@backend/db/client', async () => { - const actual = await vi.importActual('@backend/db/client'); + const actual = await vi.importActual('@backend/db/client') return { ...actual, getPrisma: async () => { - return prismaTestingHelper.getProxyClient(); + return prismaTestingHelper.getProxyClient() }, - }; -}); + } +}) /** * Define a database integration test suite. @@ -26,12 +26,12 @@ vi.mock('@backend/db/client', async () => { */ export const describeIntegrationTest = (title: string, inner: () => void) => { beforeEach(async () => { - await prismaTestingHelper.startNewTransaction(); - }); + await prismaTestingHelper.startNewTransaction() + }) afterEach(async () => { - await prismaTestingHelper?.rollbackCurrentTransaction(); - }); + prismaTestingHelper?.rollbackCurrentTransaction() + }) - describe(title, inner); -}; + describe(title, inner) +} diff --git a/backend/src/middleware/lambda.ts b/backend/src/middleware/lambda.ts index f31f118..9e943eb 100644 --- a/backend/src/middleware/lambda.ts +++ b/backend/src/middleware/lambda.ts @@ -1,36 +1,35 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ -import { injectLambdaContext } from '@aws-lambda-powertools/logger'; -import { logMetrics } from '@aws-lambda-powertools/metrics'; -import { captureLambdaHandler } from '@aws-lambda-powertools/tracer'; -import { logger } from '@backend/util/logger'; -import { metrics } from '@backend/util/metrics'; -import { tracer } from '@backend/util/tracer'; -import middy from '@middy/core'; -import { Callback, Context } from 'aws-lambda'; -import { wrapLambdaHandlerWithSentry } from './sentryLambdaWrapper'; -import { appSyncXrayMiddleware } from './xray'; +import { injectLambdaContext } from '@aws-lambda-powertools/logger' +import { logMetrics } from '@aws-lambda-powertools/metrics' +import { captureLambdaHandler } from '@aws-lambda-powertools/tracer' +import { logger } from '@backend/util/logger' +import { metrics } from '@backend/util/metrics' +import { tracer } from '@backend/util/tracer' +import middy from '@middy/core' +import { Callback, Context } from 'aws-lambda' +import { wrapLambdaHandlerWithSentry } from './sentryLambdaWrapper' +import { appSyncXrayMiddleware } from './xray' export type Handler = ( event: TEvent, context: Context, - callback: Callback -) => void | Promise; + callback: Callback, +) => void | Promise const DEFAULT_MIDDLEWARE = [ injectLambdaContext(logger), captureLambdaHandler(tracer), logMetrics(metrics, { captureColdStartMetric: true }), -]; +] const DEFAULT_APPSYNC_MIDDLEWARE = [ ...DEFAULT_MIDDLEWARE, // add xray annotations appSyncXrayMiddleware(), -]; +] // if you want it -const isSentryEnabled = false; +const isSentryEnabled = false /** * Default middleware to apply to all resolver functions. @@ -39,13 +38,13 @@ const isSentryEnabled = false; export const defaultAppSyncMiddleware = any>() => { return (resolverFunc: T) => { // apply middleware - const handlerWithMiddlewares = middy(resolverFunc).use(DEFAULT_APPSYNC_MIDDLEWARE); + const handlerWithMiddlewares = middy(resolverFunc).use(DEFAULT_APPSYNC_MIDDLEWARE) // sentry wrapper - if (isSentryEnabled) return wrapLambdaHandlerWithSentry(handlerWithMiddlewares); - else return handlerWithMiddlewares; - }; -}; + if (isSentryEnabled) return wrapLambdaHandlerWithSentry(handlerWithMiddlewares) + else return handlerWithMiddlewares + } +} /** * Middleware for any lambda function. @@ -53,10 +52,10 @@ export const defaultAppSyncMiddleware = any>() => export const defaultLambdaMiddleware = any>() => { return (resolverFunc: T) => { // apply middleware - const handlerWithMiddlewares = middy(resolverFunc).use(DEFAULT_MIDDLEWARE); + const handlerWithMiddlewares = middy(resolverFunc).use(DEFAULT_MIDDLEWARE) // sentry wrapper - if (isSentryEnabled) return wrapLambdaHandlerWithSentry(handlerWithMiddlewares); - else return handlerWithMiddlewares; - }; -}; + if (isSentryEnabled) return wrapLambdaHandlerWithSentry(handlerWithMiddlewares) + else return handlerWithMiddlewares + } +} diff --git a/backend/src/util/testResolver.ts b/backend/src/util/testResolver.ts index 07719f4..2247a3b 100644 --- a/backend/src/util/testResolver.ts +++ b/backend/src/util/testResolver.ts @@ -1,11 +1,12 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ -import { AppSyncIdentity, AppSyncResolverEvent, Callback, Context } from 'aws-lambda'; +import { AppSyncResolverHandler, Context } from 'aws-lambda' -export interface TestCallResolverArgs { - userName?: string; - resolverFunc: (event: AppSyncResolverEvent, context?: Context, callback?: Callback) => R; - args: A; - source?: S; +type InferFunc = T extends (...p: infer P) => infer R | void ? (...p: P) => R : never + +export interface CallAuthenticatedResolver { + userName: string + resolverFunc: InferFunc> + args: A + source?: S } /** @@ -18,29 +19,27 @@ export const testCallResolver = ({ args, source = null as unknown as S, resolverFunc, -}: TestCallResolverArgs) => +}: CallAuthenticatedResolver) => resolverFunc( { arguments: args, - identity: userName - ? { - sub: userName, - issuer: 'King Mischa', - sourceIp: ['1.2.3.4'], - defaultAuthStrategy: 'whatever', - groups: [], - claims: { 'cognito:username': userName }, - username: userName, - } - : ({} as AppSyncIdentity), + identity: { + sub: userName, + issuer: 'King Mischa', + sourceIp: ['1.2.3.4'], + defaultAuthStrategy: 'whatever', + groups: [], + claims: { 'cognito:username': userName }, + username: userName, + }, source, - info: {} as any, + info: { selectionSetList: [] } as any, prev: {} as any, request: {} as any, stash: {} as any, }, {} as Context, () => { - throw new Error("don't call lambda callback"); - } - ); + throw new Error("don't call lambda callback") + }, + ) diff --git a/eslint.config.mjs b/eslint.config.mjs index a8e342a..a069081 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -1,87 +1,35 @@ -import { fixupConfigRules, fixupPluginRules } from "@eslint/compat"; -import typescriptEslint from "@typescript-eslint/eslint-plugin"; -import globals from "globals"; -import tsParser from "@typescript-eslint/parser"; -import path from "node:path"; -import { fileURLToPath } from "node:url"; -import js from "@eslint/js"; -import { FlatCompat } from "@eslint/eslintrc"; - -const __filename = fileURLToPath(import.meta.url); -const __dirname = path.dirname(__filename); -const compat = new FlatCompat({ - baseDirectory: __dirname, - recommendedConfig: js.configs.recommended, - allConfig: js.configs.all -}); - -export default [{ - ignores: [ - "**/node_modules", - "**/build", - "**/prisma-generated-client", - "**/generated/", - "**/cdk.out", - "**/__mocks__", - ], -}, ...fixupConfigRules(compat.extends( - "eslint:recommended", - "plugin:@typescript-eslint/eslint-recommended", - "plugin:@typescript-eslint/recommended", - "plugin:import/recommended", - "plugin:import/typescript", -)).map(config => ({ - ...config, - files: ["**/*.ts", "**/*.tsx"], -})), { - files: ["**/*.ts", "**/*.tsx"], - - plugins: { - "@typescript-eslint": fixupPluginRules(typescriptEslint), - }, +import eslint from '@eslint/js' +import tseslint from 'typescript-eslint' +export default tseslint.config( + eslint.configs.recommended, + ...tseslint.configs.recommendedTypeChecked, + { languageOptions: { - globals: { - ...Object.fromEntries(Object.entries(globals.browser).map(([key]) => [key, "off"])), - ...globals.node, - Atomics: "readonly", - SharedArrayBuffer: "readonly", - }, - - parser: tsParser, - ecmaVersion: 2019, - sourceType: "module", + parserOptions: { + project: true, + tsconfigRootDir: import.meta.dirname, + }, }, - - settings: { - "import/parsers": { - "@typescript-eslint/parser": [".ts", ".tsx"], - }, - - "import/ignore": [ - "\\.test\\.ts$", - "\\.(scss|less|css)$", - "node_modules", - "build/", - "\\.next", - ], - - "import/resolver": { - typescript: { - alwaysTryTypes: true, - project: ["tsconfig.json", "*/tsconfig.json"], - }, - }, - }, - + }, + { + ignores: ['.sst', 'web', 'common/generated/graphql'], + }, + { rules: { - "@typescript-eslint/explicit-module-boundary-types": "off", - "@typescript-eslint/no-unused-vars": "off", - "@typescript-eslint/no-non-null-assertion": "off", - "@typescript-eslint/no-empty-interface": "off", - "@typescript-eslint/no-explicit-any": "off", - "@typescript-eslint/triple-slash-reference": "off", - "@typescript-eslint/ban-types": "off", - "no-case-declarations": "off", + '@typescript-eslint/explicit-module-boundary-types': 0, + '@typescript-eslint/no-unused-vars': 0, + '@typescript-eslint/no-non-null-assertion': 0, + '@typescript-eslint/no-empty-interface': 0, + '@typescript-eslint/no-explicit-any': 0, + '@typescript-eslint/no-unsafe-assignment': 0, + '@typescript-eslint/no-unsafe-argument': 0, + '@typescript-eslint/no-unsafe-member-access': 0, + '@typescript-eslint/no-unsafe-call': 0, + '@typescript-eslint/triple-slash-reference': 0, + '@typescript-eslint/ban-types': 0, + 'no-case-declarations': 0, + '@typescript-eslint/require-await': 1, }, -}]; \ No newline at end of file + }, +) diff --git a/package.json b/package.json index 1970b1b..c88caa9 100644 --- a/package.json +++ b/package.json @@ -58,11 +58,10 @@ "@graphql-codegen/typescript-operations": "^4.2.2", "@graphql-codegen/typescript-react-apollo": "^4.3.0", "@graphql-typed-document-node/core": "^3.2.0", + "@total-typescript/ts-reset": "^0.5.1", "@tsconfig/node20": "^1.0.0", "@types/aws-lambda": "^8.10.140", "@types/node": "^20.0.0", - "@typescript-eslint/eslint-plugin": "^7.15.0", - "@typescript-eslint/parser": "^7.15.0", "aws-cdk": "2.142.1", "aws-cdk-lib": "2.142.1", "concurrently": "^8.2.2", @@ -73,7 +72,6 @@ "eslint": "^9.6.0", "eslint-config-airbnb": "^19.0.4", "eslint-config-prettier": "^9.1.0", - "eslint-import-resolver-typescript": "^3.5.3", "eslint-plugin-import": "^2.29.1", "eslint-plugin-jsx-a11y": "^6.5.1", "eslint-plugin-react": "^7.30.0", @@ -88,6 +86,7 @@ "rimraf": "^5.0.7", "tsx": "^4.15.8", "typescript": "^5.5.2", + "typescript-eslint": "^7.15.0", "vite": "^5.3.2", "vitest": "^1.6.0" }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d36274d..5f37b53 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -51,6 +51,9 @@ importers: '@graphql-typed-document-node/core': specifier: ^3.2.0 version: 3.2.0(graphql@16.9.0) + '@total-typescript/ts-reset': + specifier: ^0.5.1 + version: 0.5.1 '@tsconfig/node20': specifier: ^1.0.0 version: 1.0.2 @@ -60,12 +63,6 @@ importers: '@types/node': specifier: ^20.0.0 version: 20.14.9 - '@typescript-eslint/eslint-plugin': - specifier: ^7.15.0 - version: 7.15.0(@typescript-eslint/parser@7.15.0(eslint@9.6.0)(typescript@5.5.2))(eslint@9.6.0)(typescript@5.5.2) - '@typescript-eslint/parser': - specifier: ^7.15.0 - version: 7.15.0(eslint@9.6.0)(typescript@5.5.2) aws-cdk: specifier: 2.142.1 version: 2.142.1 @@ -92,16 +89,13 @@ importers: version: 9.6.0 eslint-config-airbnb: specifier: ^19.0.4 - version: 19.0.4(eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.15.0(eslint@9.6.0)(typescript@5.5.2))(eslint-import-resolver-typescript@3.6.1)(eslint@9.6.0))(eslint-plugin-jsx-a11y@6.9.0(eslint@9.6.0))(eslint-plugin-react-hooks@4.6.2(eslint@9.6.0))(eslint-plugin-react@7.34.3(eslint@9.6.0))(eslint@9.6.0) + version: 19.0.4(eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.15.0(eslint@9.6.0)(typescript@5.5.2))(eslint@9.6.0))(eslint-plugin-jsx-a11y@6.9.0(eslint@9.6.0))(eslint-plugin-react-hooks@4.6.2(eslint@9.6.0))(eslint-plugin-react@7.34.3(eslint@9.6.0))(eslint@9.6.0) eslint-config-prettier: specifier: ^9.1.0 version: 9.1.0(eslint@9.6.0) - eslint-import-resolver-typescript: - specifier: ^3.5.3 - version: 3.6.1(@typescript-eslint/parser@7.15.0(eslint@9.6.0)(typescript@5.5.2))(eslint-plugin-import@2.29.1)(eslint@9.6.0) eslint-plugin-import: specifier: ^2.29.1 - version: 2.29.1(@typescript-eslint/parser@7.15.0(eslint@9.6.0)(typescript@5.5.2))(eslint-import-resolver-typescript@3.6.1)(eslint@9.6.0) + version: 2.29.1(@typescript-eslint/parser@7.15.0(eslint@9.6.0)(typescript@5.5.2))(eslint@9.6.0) eslint-plugin-jsx-a11y: specifier: ^6.5.1 version: 6.9.0(eslint@9.6.0) @@ -141,6 +135,9 @@ importers: typescript: specifier: ^5.5.2 version: 5.5.2 + typescript-eslint: + specifier: ^7.15.0 + version: 7.15.0(eslint@9.6.0)(typescript@5.5.2) vite: specifier: ^5.3.2 version: 5.3.2(@types/node@20.14.9)(sass@1.77.6)(terser@5.31.1) @@ -172,8 +169,8 @@ importers: specifier: ^3.130.0 version: 3.598.0 '@middy/core': - specifier: ^2.5.7 - version: 2.5.7 + specifier: ^5.4.2 + version: 5.4.2 '@prisma/client': specifier: 5.16.1 version: 5.16.1(prisma@5.16.1) @@ -283,6 +280,9 @@ importers: specifier: ^4.3.2 version: 4.3.2(typescript@5.5.2)(vite@5.3.2(@types/node@20.14.9)(sass@1.77.6)(terser@5.31.1)) devDependencies: + '@next/eslint-plugin-next': + specifier: ^14.2.4 + version: 14.2.4 '@testing-library/react': specifier: ^16.0.0 version: 16.0.0(@testing-library/dom@10.2.0)(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -295,9 +295,9 @@ importers: '@types/react-dom': specifier: ^18 version: 18.3.0 - eslint-config-next: - specifier: 14.2.4 - version: 14.2.4(eslint@9.6.0)(typescript@5.5.2) + eslint-plugin-react: + specifier: ^7.30.0 + version: 7.34.3(eslint@9.6.0) postcss: specifier: ^8 version: 8.4.38 @@ -1254,6 +1254,10 @@ packages: peerDependencies: '@prisma/client': ^4.7.0 || 5 + '@datastream/core@0.0.35': + resolution: {integrity: sha512-jmKFcDTYqtDy8DHPahaheg3MlLBiQboYX4jYX8oxE1tO5x7cfLl5M6bqR/o46RCEFZ3M9yMVfEEh0hy5raErEw==} + engines: {node: '>=18'} + '@envelop/core@3.0.6': resolution: {integrity: sha512-06t1xCPXq6QFN7W1JUEf68aCwYN0OUDNAIoJe7bAqhaoa2vn7NCcuX1VHkJ/OWpmElUgCsRO6RiBbIru1in0Ig==} @@ -1896,9 +1900,9 @@ packages: resolution: {integrity: sha512-9I2Zn6+NJLfaGoz9jN3lpwDgAYvfGeNYdbAIjJOqzs4Tpc+VU3Jqq4IofSUBKajiDS8k9fZIg18/z13mpk1bsA==} engines: {node: '>=8'} - '@middy/core@2.5.7': - resolution: {integrity: sha512-KX5Ud0SP+pol6PGkYtMCH4goHobs1XJo3OvEUwdiZUIjZgo56Q08nLu5N7Bs6P+FwGTQHA+hlQ3I5SZbfpO/jg==} - engines: {node: '>=12'} + '@middy/core@5.4.2': + resolution: {integrity: sha512-58xZaDDuJ/oaxdFp73R46GOZMR3NfWMzcbKYO6KmPgIwJLMTyHzQGJUbDicpKHkE4Hy2J2zxYioQrO62KPRiLg==} + engines: {node: '>=18'} '@next/env@14.2.4': resolution: {integrity: sha512-3EtkY5VDkuV2+lNmKlbkibIJxcO4oIHEhBWne6PaAp+76J9KoSsGvNikp6ivzAT8dhhBMYrm6op2pS1ApG0Hzg==} @@ -2122,9 +2126,6 @@ packages: cpu: [x64] os: [win32] - '@rushstack/eslint-patch@1.10.3': - resolution: {integrity: sha512-qC/xYId4NMebE6w/V33Fh9gWxLgURiNYgVNObbJl2LZv0GUUItCcCqC5axQSwRaAgaxl2mELq1rMzlswaQ0Zxg==} - '@sentry-internal/browser-utils@8.13.0': resolution: {integrity: sha512-lqq8BYbbs9KTlDuyB5NjdZB6P/llqQs32KUgaCQ/k5DFB4Zf56+BFHXObnMHxwx375X1uixtnEphagWZa+nsLQ==} engines: {node: '>=14.18'} @@ -2553,6 +2554,9 @@ packages: resolution: {integrity: sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==} engines: {node: '>= 10'} + '@total-typescript/ts-reset@0.5.1': + resolution: {integrity: sha512-AqlrT8YA1o7Ff5wPfMOL0pvL+1X+sw60NN6CcOCqs658emD6RfiXhF7Gu9QcfKBH7ELY2nInLhKSCWVoNL70MQ==} + '@trpc/server@9.16.0': resolution: {integrity: sha512-IENsJs41ZR4oeFUJhsNNTSgEOtuRN0m9u7ec4u3eG/qOc7bIoo1nDoYtx4bl6OJJSQYEytG9tlcVz9G8OAaHbg==} @@ -2676,24 +2680,10 @@ packages: typescript: optional: true - '@typescript-eslint/parser@7.2.0': - resolution: {integrity: sha512-5FKsVcHTk6TafQKQbuIVkXq58Fnbkd2wDL4LB7AURN7RUOu1utVP+G8+6u3ZhEroW3DF6hyo3ZEXxgKgp4KeCg==} - engines: {node: ^16.0.0 || >=18.0.0} - peerDependencies: - eslint: ^8.56.0 - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true - '@typescript-eslint/scope-manager@7.15.0': resolution: {integrity: sha512-Q/1yrF/XbxOTvttNVPihxh1b9fxamjEoz2Os/Pe38OHwxC24CyCqXxGTOdpb4lt6HYtqw9HetA/Rf6gDGaMPlw==} engines: {node: ^18.18.0 || >=20.0.0} - '@typescript-eslint/scope-manager@7.2.0': - resolution: {integrity: sha512-Qh976RbQM/fYtjx9hs4XkayYujB/aPwglw2choHmf3zBjB4qOywWSdt9+KLRdHubGcoSwBnXUH2sR3hkyaERRg==} - engines: {node: ^16.0.0 || >=18.0.0} - '@typescript-eslint/type-utils@7.15.0': resolution: {integrity: sha512-SkgriaeV6PDvpA6253PDVep0qCqgbO1IOBiycjnXsszNTVQe5flN5wR5jiczoEoDEnAqYFSFFc9al9BSGVltkg==} engines: {node: ^18.18.0 || >=20.0.0} @@ -2708,10 +2698,6 @@ packages: resolution: {integrity: sha512-aV1+B1+ySXbQH0pLK0rx66I3IkiZNidYobyfn0WFsdGhSXw+P3YOqeTq5GED458SfB24tg+ux3S+9g118hjlTw==} engines: {node: ^18.18.0 || >=20.0.0} - '@typescript-eslint/types@7.2.0': - resolution: {integrity: sha512-XFtUHPI/abFhm4cbCDc5Ykc8npOKBSJePY3a3s+lwumt7XWJuzP5cZcfZ610MIPHjQjNsOLlYK8ASPaNG8UiyA==} - engines: {node: ^16.0.0 || >=18.0.0} - '@typescript-eslint/typescript-estree@7.15.0': resolution: {integrity: sha512-gjyB/rHAopL/XxfmYThQbXbzRMGhZzGw6KpcMbfe8Q3nNQKStpxnUKeXb0KiN/fFDR42Z43szs6rY7eHk0zdGQ==} engines: {node: ^18.18.0 || >=20.0.0} @@ -2721,15 +2707,6 @@ packages: typescript: optional: true - '@typescript-eslint/typescript-estree@7.2.0': - resolution: {integrity: sha512-cyxS5WQQCoBwSakpMrvMXuMDEbhOo9bNHHrNcEWis6XHx6KF518tkF1wBvKIn/tpq5ZpUYK7Bdklu8qY0MsFIA==} - engines: {node: ^16.0.0 || >=18.0.0} - peerDependencies: - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true - '@typescript-eslint/utils@7.15.0': resolution: {integrity: sha512-hfDMDqaqOqsUVGiEPSMLR/AjTSCsmJwjpKkYQRo1FNbmW4tBwBspYDwO9eh7sKSTwMQgBw9/T4DHudPaqshRWA==} engines: {node: ^18.18.0 || >=20.0.0} @@ -2740,10 +2717,6 @@ packages: resolution: {integrity: sha512-Hqgy/ETgpt2L5xueA/zHHIl4fJI2O4XUE9l4+OIfbJIRSnTJb/QscncdqqZzofQegIJugRIF57OJea1khw2SDw==} engines: {node: ^18.18.0 || >=20.0.0} - '@typescript-eslint/visitor-keys@7.2.0': - resolution: {integrity: sha512-c6EIQRHhcpl6+tO8EMR+kjkkV+ugUNXOmeASA1rlzkd8EPIriavpWoiEz1HR/VLhbVIdhqnV6E7JZm00cBDx2A==} - engines: {node: ^16.0.0 || >=18.0.0} - '@vitejs/plugin-react@4.3.1': resolution: {integrity: sha512-m/V2syj5CuVnaxcUJOQRel/Wr31FFXRFlnOoq1TVtkCxsY5veGMTEmpWHndrhB2U8ScHtCQB1e+4hWYExQc6Lg==} engines: {node: ^14.18.0 || >=16.0.0} @@ -2816,6 +2789,10 @@ packages: resolution: {integrity: sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==} deprecated: Use your platform's native atob() and btoa() methods instead + abort-controller@3.0.0: + resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==} + engines: {node: '>=6.5'} + accepts@1.3.8: resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==} engines: {node: '>= 0.6'} @@ -3320,6 +3297,9 @@ packages: resolution: {integrity: sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==} engines: {node: '>=0.8'} + cloneable-readable@3.0.0: + resolution: {integrity: sha512-Lkfd9IRx1nfiBr7UHNxJSl/x7DOeUfYmxzCkxYJC2tyc/9vKgV75msgLGurGQsak/NvJDHMWcshzEXRlxfvhqg==} + cls-hooked@4.2.2: resolution: {integrity: sha512-J4Xj5f5wq/4jAvcdgoGsL3G103BtWpZrMo8NEinRltN+xpTZdI+M38pyQqhuFU/P792xkMFvnKSf+Lm81U1bxw==} engines: {node: ^4.7 || >=6.9 || >=7.3 || >=8.2.1} @@ -3709,10 +3689,6 @@ packages: end-of-stream@1.4.4: resolution: {integrity: sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==} - enhanced-resolve@5.17.0: - resolution: {integrity: sha512-dwDPwZL0dmye8Txp2gzFmA6sxALaSvdRDjPH0viLcKrtlOL3tw62nWWweVD1SdILDTJrbrL6tdWVN58Wo6U3eA==} - engines: {node: '>=10.13.0'} - entities@2.2.0: resolution: {integrity: sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==} @@ -3826,15 +3802,6 @@ packages: eslint-plugin-react: ^7.28.0 eslint-plugin-react-hooks: ^4.3.0 - eslint-config-next@14.2.4: - resolution: {integrity: sha512-Qr0wMgG9m6m4uYy2jrYJmyuNlYZzPRQq5Kvb9IDlYwn+7yq6W6sfMNFgb+9guM1KYwuIo6TIaiFhZJ6SnQ/Efw==} - peerDependencies: - eslint: ^7.23.0 || ^8.0.0 - typescript: '>=3.3.1' - peerDependenciesMeta: - typescript: - optional: true - eslint-config-prettier@9.1.0: resolution: {integrity: sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw==} hasBin: true @@ -3844,13 +3811,6 @@ packages: eslint-import-resolver-node@0.3.9: resolution: {integrity: sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==} - eslint-import-resolver-typescript@3.6.1: - resolution: {integrity: sha512-xgdptdoi5W3niYeuQxKmzVDTATvLYqhpwmykwsh7f6HIOStGWEIL9iqZgQDF9u9OEzrRwR8no5q2VT+bjAujTg==} - engines: {node: ^14.18.0 || >=16.0.0} - peerDependencies: - eslint: '*' - eslint-plugin-import: '*' - eslint-module-utils@2.8.1: resolution: {integrity: sha512-rXDXR3h7cs7dy9RNpUlQf80nX31XWJEyGq1tRMo+6GsO5VmTe4UTwtmonAD4ZkAsrfMVDA2wlGJ3790Ys+D49Q==} engines: {node: '>=4'} @@ -3956,6 +3916,10 @@ packages: event-emitter@0.3.5: resolution: {integrity: sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA==} + event-target-shim@5.0.1: + resolution: {integrity: sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==} + engines: {node: '>=6'} + eventemitter3@5.0.1: resolution: {integrity: sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==} @@ -3963,6 +3927,10 @@ packages: resolution: {integrity: sha512-kEcvvCBByWXGnZy6JUlgAp2gBIUjfCAV6P6TgT1/aaQKcmuAEC4OZTV1I4EWQLz2gxZw76atuVyvHhTxvi0Flw==} engines: {node: '>=0.4.x'} + events@3.3.0: + resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} + engines: {node: '>=0.8.x'} + execa@8.0.1: resolution: {integrity: sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==} engines: {node: '>=16.17'} @@ -5030,10 +4998,6 @@ packages: resolution: {integrity: sha512-sauLxniAmvnhhRjFwPNnJKaPFYyddAgbYdeUpHULtCT/GhzdCx/MDNy+Y40lBxTQUrMzDE8e0S43Z5uqfO0REg==} engines: {node: '>=10'} - minimatch@9.0.3: - resolution: {integrity: sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==} - engines: {node: '>=16 || 14 >=14.17'} - minimatch@9.0.5: resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} engines: {node: '>=16 || 14 >=14.17'} @@ -5676,6 +5640,10 @@ packages: resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} engines: {node: '>= 6'} + readable-stream@4.5.2: + resolution: {integrity: sha512-yjavECdqeZ3GLXNgRXgeQEdz9fvDDkNKyHnbHRFtOr7/LcfgBcmct7t/ET+HaCTqfh06OzoAxrkN/IfjJBVe+g==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + readdir-glob@1.1.3: resolution: {integrity: sha512-v05I2k7xN8zXvPD9N+z/uhXPaj0sUFCe2rcWZIpBsqxfP7xXFQ0tipAd/wjj1YxWyWtUS5IDJpOG82JKt2EAVA==} @@ -6135,10 +6103,6 @@ packages: engines: {node: '>=14.0.0'} hasBin: true - tapable@2.2.1: - resolution: {integrity: sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==} - engines: {node: '>=6'} - tar-stream@2.2.0: resolution: {integrity: sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==} engines: {node: '>=6'} @@ -6295,6 +6259,16 @@ packages: typedarray@0.0.6: resolution: {integrity: sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==} + typescript-eslint@7.15.0: + resolution: {integrity: sha512-Ta40FhMXBCwHura4X4fncaCVkVcnJ9jnOq5+Lp4lN8F4DzHZtOwZdRvVBiNUGznUDHPwdGnrnwxmUOU2fFQqFA==} + engines: {node: ^18.18.0 || >=20.0.0} + peerDependencies: + eslint: ^8.56.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + typescript@5.5.2: resolution: {integrity: sha512-NcRtPEOsPFFWjobJEtfihkLCZCXZt/os3zf8nTxjVH3RvTSxjrCamJpbExGvYOF+tFHc3pA65qpdwPbzjohhew==} engines: {node: '>=14.17'} @@ -8852,6 +8826,10 @@ snapshots: dependencies: '@prisma/client': 5.16.1(prisma@5.16.1) + '@datastream/core@0.0.35': + dependencies: + cloneable-readable: 3.0.0 + '@envelop/core@3.0.6': dependencies: '@envelop/types': 3.0.2 @@ -9642,7 +9620,9 @@ snapshots: '@lukeed/ms@2.0.2': {} - '@middy/core@2.5.7': {} + '@middy/core@5.4.2': + dependencies: + '@datastream/core': 0.0.35 '@next/env@14.2.4': {} @@ -9824,8 +9804,6 @@ snapshots: '@rollup/rollup-win32-x64-msvc@4.18.0': optional: true - '@rushstack/eslint-patch@1.10.3': {} - '@sentry-internal/browser-utils@8.13.0': dependencies: '@sentry/core': 8.13.0 @@ -10453,6 +10431,8 @@ snapshots: '@tootallnate/once@2.0.0': optional: true + '@total-typescript/ts-reset@0.5.1': {} + '@trpc/server@9.16.0': dependencies: tslib: 2.6.3 @@ -10604,29 +10584,11 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@7.2.0(eslint@9.6.0)(typescript@5.5.2)': - dependencies: - '@typescript-eslint/scope-manager': 7.2.0 - '@typescript-eslint/types': 7.2.0 - '@typescript-eslint/typescript-estree': 7.2.0(typescript@5.5.2) - '@typescript-eslint/visitor-keys': 7.2.0 - debug: 4.3.5 - eslint: 9.6.0 - optionalDependencies: - typescript: 5.5.2 - transitivePeerDependencies: - - supports-color - '@typescript-eslint/scope-manager@7.15.0': dependencies: '@typescript-eslint/types': 7.15.0 '@typescript-eslint/visitor-keys': 7.15.0 - '@typescript-eslint/scope-manager@7.2.0': - dependencies: - '@typescript-eslint/types': 7.2.0 - '@typescript-eslint/visitor-keys': 7.2.0 - '@typescript-eslint/type-utils@7.15.0(eslint@9.6.0)(typescript@5.5.2)': dependencies: '@typescript-eslint/typescript-estree': 7.15.0(typescript@5.5.2) @@ -10641,8 +10603,6 @@ snapshots: '@typescript-eslint/types@7.15.0': {} - '@typescript-eslint/types@7.2.0': {} - '@typescript-eslint/typescript-estree@7.15.0(typescript@5.5.2)': dependencies: '@typescript-eslint/types': 7.15.0 @@ -10658,21 +10618,6 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/typescript-estree@7.2.0(typescript@5.5.2)': - dependencies: - '@typescript-eslint/types': 7.2.0 - '@typescript-eslint/visitor-keys': 7.2.0 - debug: 4.3.5 - globby: 11.1.0 - is-glob: 4.0.3 - minimatch: 9.0.3 - semver: 7.6.2 - ts-api-utils: 1.3.0(typescript@5.5.2) - optionalDependencies: - typescript: 5.5.2 - transitivePeerDependencies: - - supports-color - '@typescript-eslint/utils@7.15.0(eslint@9.6.0)(typescript@5.5.2)': dependencies: '@eslint-community/eslint-utils': 4.4.0(eslint@9.6.0) @@ -10689,11 +10634,6 @@ snapshots: '@typescript-eslint/types': 7.15.0 eslint-visitor-keys: 3.4.3 - '@typescript-eslint/visitor-keys@7.2.0': - dependencies: - '@typescript-eslint/types': 7.2.0 - eslint-visitor-keys: 3.4.3 - '@vitejs/plugin-react@4.3.1(vite@5.3.2(@types/node@20.14.9)(sass@1.77.6)(terser@5.31.1))': dependencies: '@babel/core': 7.24.7 @@ -10797,6 +10737,10 @@ snapshots: abab@2.0.6: optional: true + abort-controller@3.0.0: + dependencies: + event-target-shim: 5.0.1 + accepts@1.3.8: dependencies: mime-types: 2.1.35 @@ -11275,7 +11219,7 @@ snapshots: buffer@4.9.2: dependencies: base64-js: 1.5.1 - ieee754: 1.1.13 + ieee754: 1.2.1 isarray: 1.0.0 buffer@5.7.1: @@ -11484,6 +11428,10 @@ snapshots: clone@1.0.4: {} + cloneable-readable@3.0.0: + dependencies: + readable-stream: 4.5.2 + cls-hooked@4.2.2: dependencies: async-hook-jl: 1.7.6 @@ -11865,11 +11813,6 @@ snapshots: dependencies: once: 1.4.0 - enhanced-resolve@5.17.0: - dependencies: - graceful-fs: 4.2.11 - tapable: 2.2.1 - entities@2.2.0: {} entities@4.5.0: @@ -12080,44 +12023,26 @@ snapshots: source-map: 0.6.1 optional: true - eslint-config-airbnb-base@15.0.0(eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.15.0(eslint@9.6.0)(typescript@5.5.2))(eslint-import-resolver-typescript@3.6.1)(eslint@9.6.0))(eslint@9.6.0): + eslint-config-airbnb-base@15.0.0(eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.15.0(eslint@9.6.0)(typescript@5.5.2))(eslint@9.6.0))(eslint@9.6.0): dependencies: confusing-browser-globals: 1.0.11 eslint: 9.6.0 - eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.15.0(eslint@9.6.0)(typescript@5.5.2))(eslint-import-resolver-typescript@3.6.1)(eslint@9.6.0) + eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.15.0(eslint@9.6.0)(typescript@5.5.2))(eslint@9.6.0) object.assign: 4.1.5 object.entries: 1.1.8 semver: 6.3.1 - eslint-config-airbnb@19.0.4(eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.15.0(eslint@9.6.0)(typescript@5.5.2))(eslint-import-resolver-typescript@3.6.1)(eslint@9.6.0))(eslint-plugin-jsx-a11y@6.9.0(eslint@9.6.0))(eslint-plugin-react-hooks@4.6.2(eslint@9.6.0))(eslint-plugin-react@7.34.3(eslint@9.6.0))(eslint@9.6.0): + eslint-config-airbnb@19.0.4(eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.15.0(eslint@9.6.0)(typescript@5.5.2))(eslint@9.6.0))(eslint-plugin-jsx-a11y@6.9.0(eslint@9.6.0))(eslint-plugin-react-hooks@4.6.2(eslint@9.6.0))(eslint-plugin-react@7.34.3(eslint@9.6.0))(eslint@9.6.0): dependencies: eslint: 9.6.0 - eslint-config-airbnb-base: 15.0.0(eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.15.0(eslint@9.6.0)(typescript@5.5.2))(eslint-import-resolver-typescript@3.6.1)(eslint@9.6.0))(eslint@9.6.0) - eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.15.0(eslint@9.6.0)(typescript@5.5.2))(eslint-import-resolver-typescript@3.6.1)(eslint@9.6.0) + eslint-config-airbnb-base: 15.0.0(eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.15.0(eslint@9.6.0)(typescript@5.5.2))(eslint@9.6.0))(eslint@9.6.0) + eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.15.0(eslint@9.6.0)(typescript@5.5.2))(eslint@9.6.0) eslint-plugin-jsx-a11y: 6.9.0(eslint@9.6.0) eslint-plugin-react: 7.34.3(eslint@9.6.0) eslint-plugin-react-hooks: 4.6.2(eslint@9.6.0) object.assign: 4.1.5 object.entries: 1.1.8 - eslint-config-next@14.2.4(eslint@9.6.0)(typescript@5.5.2): - dependencies: - '@next/eslint-plugin-next': 14.2.4 - '@rushstack/eslint-patch': 1.10.3 - '@typescript-eslint/parser': 7.2.0(eslint@9.6.0)(typescript@5.5.2) - eslint: 9.6.0 - eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@7.2.0(eslint@9.6.0)(typescript@5.5.2))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@9.6.0) - eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.15.0(eslint@9.6.0)(typescript@5.5.2))(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.15.0(eslint@9.6.0)(typescript@5.5.2))(eslint-plugin-import@2.29.1)(eslint@9.6.0))(eslint@9.6.0) - eslint-plugin-jsx-a11y: 6.9.0(eslint@9.6.0) - eslint-plugin-react: 7.34.3(eslint@9.6.0) - eslint-plugin-react-hooks: 4.6.2(eslint@9.6.0) - optionalDependencies: - typescript: 5.5.2 - transitivePeerDependencies: - - eslint-import-resolver-webpack - - supports-color - eslint-config-prettier@9.1.0(eslint@9.6.0): dependencies: eslint: 9.6.0 @@ -12130,73 +12055,17 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.15.0(eslint@9.6.0)(typescript@5.5.2))(eslint-plugin-import@2.29.1)(eslint@9.6.0): - dependencies: - debug: 4.3.5 - enhanced-resolve: 5.17.0 - eslint: 9.6.0 - eslint-module-utils: 2.8.1(@typescript-eslint/parser@7.15.0(eslint@9.6.0)(typescript@5.5.2))(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.15.0(eslint@9.6.0)(typescript@5.5.2))(eslint-plugin-import@2.29.1)(eslint@9.6.0))(eslint@9.6.0) - eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.15.0(eslint@9.6.0)(typescript@5.5.2))(eslint-import-resolver-typescript@3.6.1)(eslint@9.6.0) - fast-glob: 3.3.2 - get-tsconfig: 4.7.5 - is-core-module: 2.14.0 - is-glob: 4.0.3 - transitivePeerDependencies: - - '@typescript-eslint/parser' - - eslint-import-resolver-node - - eslint-import-resolver-webpack - - supports-color - - eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.2.0(eslint@9.6.0)(typescript@5.5.2))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@9.6.0): - dependencies: - debug: 4.3.5 - enhanced-resolve: 5.17.0 - eslint: 9.6.0 - eslint-module-utils: 2.8.1(@typescript-eslint/parser@7.2.0(eslint@9.6.0)(typescript@5.5.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.2.0(eslint@9.6.0)(typescript@5.5.2))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@9.6.0))(eslint@9.6.0) - eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.15.0(eslint@9.6.0)(typescript@5.5.2))(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.15.0(eslint@9.6.0)(typescript@5.5.2))(eslint-plugin-import@2.29.1)(eslint@9.6.0))(eslint@9.6.0) - fast-glob: 3.3.2 - get-tsconfig: 4.7.5 - is-core-module: 2.14.0 - is-glob: 4.0.3 - transitivePeerDependencies: - - '@typescript-eslint/parser' - - eslint-import-resolver-node - - eslint-import-resolver-webpack - - supports-color - - eslint-module-utils@2.8.1(@typescript-eslint/parser@7.15.0(eslint@9.6.0)(typescript@5.5.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.15.0(eslint@9.6.0)(typescript@5.5.2))(eslint-plugin-import@2.29.1)(eslint@9.6.0))(eslint@9.6.0): + eslint-module-utils@2.8.1(@typescript-eslint/parser@7.15.0(eslint@9.6.0)(typescript@5.5.2))(eslint-import-resolver-node@0.3.9)(eslint@9.6.0): dependencies: debug: 3.2.7 optionalDependencies: '@typescript-eslint/parser': 7.15.0(eslint@9.6.0)(typescript@5.5.2) eslint: 9.6.0 eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@7.15.0(eslint@9.6.0)(typescript@5.5.2))(eslint-plugin-import@2.29.1)(eslint@9.6.0) - transitivePeerDependencies: - - supports-color - - eslint-module-utils@2.8.1(@typescript-eslint/parser@7.15.0(eslint@9.6.0)(typescript@5.5.2))(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.15.0(eslint@9.6.0)(typescript@5.5.2))(eslint-plugin-import@2.29.1)(eslint@9.6.0))(eslint@9.6.0): - dependencies: - debug: 3.2.7 - optionalDependencies: - '@typescript-eslint/parser': 7.15.0(eslint@9.6.0)(typescript@5.5.2) - eslint: 9.6.0 - eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@7.15.0(eslint@9.6.0)(typescript@5.5.2))(eslint-plugin-import@2.29.1)(eslint@9.6.0) - transitivePeerDependencies: - - supports-color - - eslint-module-utils@2.8.1(@typescript-eslint/parser@7.2.0(eslint@9.6.0)(typescript@5.5.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.2.0(eslint@9.6.0)(typescript@5.5.2))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@9.6.0))(eslint@9.6.0): - dependencies: - debug: 3.2.7 - optionalDependencies: - '@typescript-eslint/parser': 7.2.0(eslint@9.6.0)(typescript@5.5.2) - eslint: 9.6.0 - eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@7.2.0(eslint@9.6.0)(typescript@5.5.2))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@9.6.0) transitivePeerDependencies: - supports-color - eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.15.0(eslint@9.6.0)(typescript@5.5.2))(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.15.0(eslint@9.6.0)(typescript@5.5.2))(eslint-plugin-import@2.29.1)(eslint@9.6.0))(eslint@9.6.0): + eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.15.0(eslint@9.6.0)(typescript@5.5.2))(eslint@9.6.0): dependencies: array-includes: 3.1.8 array.prototype.findlastindex: 1.2.5 @@ -12206,34 +12075,7 @@ snapshots: doctrine: 2.1.0 eslint: 9.6.0 eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.8.1(@typescript-eslint/parser@7.2.0(eslint@9.6.0)(typescript@5.5.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.2.0(eslint@9.6.0)(typescript@5.5.2))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@9.6.0))(eslint@9.6.0) - hasown: 2.0.2 - is-core-module: 2.14.0 - is-glob: 4.0.3 - minimatch: 3.1.2 - object.fromentries: 2.0.8 - object.groupby: 1.0.3 - object.values: 1.2.0 - semver: 6.3.1 - tsconfig-paths: 3.15.0 - optionalDependencies: - '@typescript-eslint/parser': 7.15.0(eslint@9.6.0)(typescript@5.5.2) - transitivePeerDependencies: - - eslint-import-resolver-typescript - - eslint-import-resolver-webpack - - supports-color - - eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.15.0(eslint@9.6.0)(typescript@5.5.2))(eslint-import-resolver-typescript@3.6.1)(eslint@9.6.0): - dependencies: - array-includes: 3.1.8 - array.prototype.findlastindex: 1.2.5 - array.prototype.flat: 1.3.2 - array.prototype.flatmap: 1.3.2 - debug: 3.2.7 - doctrine: 2.1.0 - eslint: 9.6.0 - eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.8.1(@typescript-eslint/parser@7.15.0(eslint@9.6.0)(typescript@5.5.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.15.0(eslint@9.6.0)(typescript@5.5.2))(eslint-plugin-import@2.29.1)(eslint@9.6.0))(eslint@9.6.0) + eslint-module-utils: 2.8.1(@typescript-eslint/parser@7.15.0(eslint@9.6.0)(typescript@5.5.2))(eslint-import-resolver-node@0.3.9)(eslint@9.6.0) hasown: 2.0.2 is-core-module: 2.14.0 is-glob: 4.0.3 @@ -12383,10 +12225,14 @@ snapshots: d: 1.0.2 es5-ext: 0.10.64 + event-target-shim@5.0.1: {} + eventemitter3@5.0.1: {} events@1.1.1: {} + events@3.3.0: {} + execa@8.0.1: dependencies: cross-spawn: 7.0.3 @@ -13540,10 +13386,6 @@ snapshots: dependencies: brace-expansion: 2.0.1 - minimatch@9.0.3: - dependencies: - brace-expansion: 2.0.1 - minimatch@9.0.5: dependencies: brace-expansion: 2.0.1 @@ -14224,6 +14066,14 @@ snapshots: string_decoder: 1.3.0 util-deprecate: 1.0.2 + readable-stream@4.5.2: + dependencies: + abort-controller: 3.0.0 + buffer: 6.0.3 + events: 3.3.0 + process: 0.11.10 + string_decoder: 1.3.0 + readdir-glob@1.1.3: dependencies: minimatch: 5.1.6 @@ -14833,8 +14683,6 @@ snapshots: transitivePeerDependencies: - ts-node - tapable@2.2.1: {} - tar-stream@2.2.0: dependencies: bl: 4.1.0 @@ -14994,6 +14842,17 @@ snapshots: typedarray@0.0.6: {} + typescript-eslint@7.15.0(eslint@9.6.0)(typescript@5.5.2): + dependencies: + '@typescript-eslint/eslint-plugin': 7.15.0(@typescript-eslint/parser@7.15.0(eslint@9.6.0)(typescript@5.5.2))(eslint@9.6.0)(typescript@5.5.2) + '@typescript-eslint/parser': 7.15.0(eslint@9.6.0)(typescript@5.5.2) + '@typescript-eslint/utils': 7.15.0(eslint@9.6.0)(typescript@5.5.2) + eslint: 9.6.0 + optionalDependencies: + typescript: 5.5.2 + transitivePeerDependencies: + - supports-color + typescript@5.5.2: {} ua-parser-js@1.0.38: {} diff --git a/sst.config.ts b/sst.config.ts index dd62488..d8a0eeb 100644 --- a/sst.config.ts +++ b/sst.config.ts @@ -1,34 +1,35 @@ -import type { SSTConfig } from 'sst'; +import type { SSTConfig } from 'sst' +import { ConfigOptions } from 'sst/project.js' -process.env.SST_BUILD_CONCURRENCY = '8'; +process.env.SST_BUILD_CONCURRENCY = '8' // you can configure your default profiles and regions to use here // map of stage to profile name -const PROFILE = { +const PROFILE: { [key: string]: string | undefined } = { default: undefined, -}; +} // map of stage to region -const REGION = { +const REGION: { [key: string]: string | undefined } = { default: undefined, -}; +} export default { config(input) { - const stage = input.stage; + const stage = input.stage - const region = (stage && REGION[stage]) || REGION.default || process.env.AWS_DEFAULT_REGION; - const profile = (stage && PROFILE[stage]) || PROFILE.default || process.env.AWS_PROFILE; + const region = (stage && REGION[stage]) || REGION.default || process.env.AWS_DEFAULT_REGION + const profile = (stage && PROFILE[stage]) || PROFILE.default || process.env.AWS_PROFILE return { name: 'myapp', // replace me ...(region && { region }), ...(profile && !process.env.CI && { profile }), stage, - }; + } as ConfigOptions }, async stacks(app) { - const appStacks = await import('./stacks'); - appStacks.default(app); + const appStacks = await import('./stacks') + appStacks.default(app) }, -} satisfies SSTConfig; +} satisfies SSTConfig diff --git a/stacks/database.ts b/stacks/database.ts index 32220bb..0afc52f 100644 --- a/stacks/database.ts +++ b/stacks/database.ts @@ -1,6 +1,5 @@ -import { IAspect } from 'aws-cdk-lib'; -import { ISecurityGroup, IVpc, Port, SecurityGroup } from 'aws-cdk-lib/aws-ec2'; -import { CfnFunction } from 'aws-cdk-lib/aws-lambda'; +import { ISecurityGroup, IVpc, Port, SecurityGroup } from 'aws-cdk-lib/aws-ec2' +import { CfnFunction } from 'aws-cdk-lib/aws-lambda' import { AuroraCapacityUnit, AuroraPostgresEngineVersion, @@ -14,35 +13,35 @@ import { ServerlessClusterFromSnapshot, ServerlessClusterProps, SnapshotCredentials, -} from 'aws-cdk-lib/aws-rds'; -import { ISecret, Secret } from 'aws-cdk-lib/aws-secretsmanager'; -import { Construct, IConstruct } from 'constructs'; -import { App, Script, Function, Config, Stack, StackContext, use, RDS } from 'sst/constructs'; -import { config } from 'dotenv'; -import { APP_NAME } from '@common/index'; -import { Duration, RemovalPolicy } from 'aws-cdk-lib'; -import { Network } from 'stacks/network'; -import { IS_PRODUCTION } from './config'; +} from 'aws-cdk-lib/aws-rds' +import { ISecret, Secret } from 'aws-cdk-lib/aws-secretsmanager' +import { Construct, IConstruct } from 'constructs' +import { App, Script, Function, Config, Stack, StackContext, use, RDS } from 'sst/constructs' +import { config } from 'dotenv' +import { APP_NAME } from '@common/index' +import { Duration, IAspect, RemovalPolicy } from 'aws-cdk-lib' +import { Network } from 'stacks/network' +import { IS_PRODUCTION } from './config' // if no parameter group specified, log queries that take at least this long -export const logMinDurationStatementDefault = 90; // ms +export const logMinDurationStatementDefault = 90 // ms export function Database({ stack, app }: StackContext) { - const net = use(Network); - const { vpc } = net; + const net = use(Network) + const { vpc } = net - const defaultDatabaseName = APP_NAME; + const defaultDatabaseName = APP_NAME - const dbSecurityGroupId = process.env.DB_SECURITY_GROUP_ID; + const dbSecurityGroupId = process.env.DB_SECURITY_GROUP_ID const dbAccessSecurityGroup = dbSecurityGroupId ? SecurityGroup.fromSecurityGroupId(stack, 'DbAccessSecurityGroup', dbSecurityGroupId) : new SecurityGroup(stack, 'DatabaseAccessSecurityGroup', { vpc, description: 'Allow access to the database', - }); + }) - let db: DatabaseWithSecret | undefined = undefined; - if (!process.env.CREATE_AURORA_DATABASE) return {}; + let db: DatabaseWithSecret | undefined = undefined + if (!process.env.CREATE_AURORA_DATABASE) return {} // database settings const dbProps: DatabaseProps & Partial = { @@ -66,11 +65,11 @@ export function Database({ stack, app }: StackContext) { autoPause: IS_PRODUCTION ? Duration.hours(0) : Duration.hours(8), }, defaultDatabaseName: getDefaultDatabaseName(), - } as const; + } as const // DB config - const dbSnapshotName = process.env.DB_SNAPSHOT_NAME; - const dbSecretName = process.env.DB_SECRET_NAME; + const dbSnapshotName = process.env.DB_SNAPSHOT_NAME + const dbSecretName = process.env.DB_SECRET_NAME // DB credentials - import or generate const dbSecret = dbSecretName @@ -82,20 +81,20 @@ export function Database({ stack, app }: StackContext) { generateStringKey: 'password', excludePunctuation: true, }, - }); + }) // create DB or use snapshot or import existing for dev environments - const existingDbId = process.env.DB_CLUSTER_IDENTIFIER; + const existingDbId = process.env.DB_CLUSTER_IDENTIFIER if (existingDbId) { // import existing DB - const clusterEndpointAddress = process.env.DB_CLUSTER_ENDPOINT; + const clusterEndpointAddress = process.env.DB_CLUSTER_ENDPOINT db = ServerlessDatabaseCluster.fromDatabaseClusterAttributes(stack, 'DatabaseImported', { clusterIdentifier: existingDbId, port: 5432, clusterEndpointAddress, securityGroups: [dbAccessSecurityGroup], - }); - db.secret = dbSecret; + }) + db.secret = dbSecret } else if (dbSnapshotName) { // from snapshot db = new DatabaseFromSnapshot(stack, 'DB', { @@ -104,7 +103,7 @@ export function Database({ stack, app }: StackContext) { snapshotIdentifier: dbSnapshotName, credentials: SnapshotCredentials.fromSecret(dbSecret), securityGroups: [dbAccessSecurityGroup], - }); + }) } else { db = new ServerlessDatabaseCluster(stack, 'DB', { ...dbProps, @@ -112,23 +111,23 @@ export function Database({ stack, app }: StackContext) { credentials: Credentials.fromSecret(dbSecret), writer: ClusterInstance.serverlessV2('writer'), securityGroups: [dbAccessSecurityGroup], - }); + }) } // allow stack security group to access the database if (db.connections) - db.connections.allowFrom(dbAccessSecurityGroup, Port.tcp(5432), 'Allow access from DB access security group'); + db.connections.allowFrom(dbAccessSecurityGroup, Port.tcp(5432), 'Allow access from DB access security group') - db.connections.allowDefaultPortFrom(net.defaultLambdaSecurityGroup, 'Allow access from lambda functions'); + db.connections.allowDefaultPortFrom(net.defaultLambdaSecurityGroup, 'Allow access from lambda functions') - const prismaConnectionLimit = process.env.PRISMA_CONNECTION_LIMIT || 5; + const prismaConnectionLimit = process.env.PRISMA_CONNECTION_LIMIT || 5 const config = [ new Config.Parameter(stack, 'DATABASE_NAME', { value: defaultDatabaseName }), new Config.Parameter(stack, 'CLUSTER_ARN', { value: db.clusterArn }), new Config.Parameter(stack, 'DB_SECRET_ARN', { value: db.secret?.secretArn ?? '' }), new Config.Parameter(stack, 'PRISMA_CONNECTION_LIMIT', { value: prismaConnectionLimit.toString() ?? '' }), - ]; + ] stack.addOutputs({ DBName: { value: defaultDatabaseName, description: 'Name of the default database' }, @@ -136,21 +135,21 @@ export function Database({ stack, app }: StackContext) { value: `aws secretsmanager get-secret-value --region ${stack.region} --secret-id ${db.secret?.secretArn ?? 'unknown'} --query SecretString --output text`, description: 'Command to get DB connection info and credentials', }, - }); - app.addDefaultFunctionBinding(config); + }) + app.addDefaultFunctionBinding(config) // DB connection for local dev can be overridden // https://docs.sst.dev/environment-variables#is_local - const localDatabaseUrl = process.env['DATABASE_URL']; + const localDatabaseUrl = process.env['DATABASE_URL'] if (process.env.IS_LOCAL && localDatabaseUrl) { app.addDefaultFunctionEnv({ ['DATABASE_URL']: localDatabaseUrl, - }); + }) } // app.addDefaultFunctionPermissions([dbSecret, 'grantRead']); - return { db, defaultDatabaseName, dbAccessSecurityGroup }; + return { db, defaultDatabaseName, dbAccessSecurityGroup } } /////// @@ -162,65 +161,67 @@ export const prismaCommandHooks = { // need to copy over prisma dir with migrations `cp -r "${inputDir}/backend/prisma" "${outputDir}"`, ], -}; +} -export type DatabaseType = ServerlessDatabaseCluster | DatabaseFromSnapshot; +export type DatabaseType = ServerlessDatabaseCluster | DatabaseFromSnapshot /** * Generate a database connection string (DSN). */ function makeDatabaseUrl(db: DatabaseWithSecret): string { - const _secret = db.secret; - const dbUsername = _secret?.secretValueFromJson('username'); - const dbPassword = _secret?.secretValueFromJson('password'); + const _secret = db.secret + const dbUsername = _secret?.secretValueFromJson('username') + const dbPassword = _secret?.secretValueFromJson('password') + if (!dbUsername || !dbPassword) throw new Error('missing db credentials') - const defaultDatabaseName = getDefaultDatabaseName(); - let url = `postgresql://${dbUsername}:${dbPassword}@${db.clusterEndpoint.hostname}/${defaultDatabaseName}`; + const defaultDatabaseName = getDefaultDatabaseName() + // eslint-disable-next-line @typescript-eslint/restrict-template-expressions + let url = `postgresql://${dbUsername}:${dbPassword}@${db.clusterEndpoint.hostname}/${defaultDatabaseName}` - const prismaConnectionLimitEnv = process.env.PRISMA_CONNECTION_LIMIT; - const prismaConnectionLimit = prismaConnectionLimitEnv ? parseInt(prismaConnectionLimitEnv) : 5; - if (prismaConnectionLimit) url += `?connection_limit=${prismaConnectionLimit}`; + const prismaConnectionLimitEnv = process.env.PRISMA_CONNECTION_LIMIT + const prismaConnectionLimit = prismaConnectionLimitEnv ? parseInt(prismaConnectionLimitEnv) : 5 + if (prismaConnectionLimit) url += `?connection_limit=${prismaConnectionLimit}` - return url; + return url } export function makeDatabaseConfigs( stack: Stack, - db: DatabaseWithSecret + db: DatabaseWithSecret, ): Record { - const _secret = db.secret; - const app = App.of(stack) as App; + const _secret = db.secret + const app = App.of(stack) as App - config({ path: 'backend/.env' }); - const localDatabaseUrl = process.env['DATABASE_URL']; + config({ path: 'backend/.env' }) + const localDatabaseUrl = process.env['DATABASE_URL'] if (app.local || localDatabaseUrl) { - if (!localDatabaseUrl) throw new Error('localDatabaseUrl not set'); - const dbName = localDatabaseUrl.split('/').pop(); + if (!localDatabaseUrl) throw new Error('localDatabaseUrl not set') + const dbName = localDatabaseUrl.split('/').pop() return { databaseName: new Config.Parameter(stack, 'databaseName', { value: dbName || getDefaultDatabaseName() }), useLocalDb: new Config.Parameter(stack, 'useLocalDb', { value: 'true' }), - }; + } } return { databaseName: new Config.Parameter(stack, 'databaseName', { value: getDefaultDatabaseName() }), databaseClusterArn: new Config.Parameter(stack, 'databaseArn', { value: db.clusterArn }), databaseSecretArn: new Config.Parameter(stack, 'databaseSecretArn', { value: _secret?.secretArn ?? 'UNKNOWN' }), - }; + } } export interface DatabaseProps { - vpc: IVpc; - prismaConnectionLimit?: number; + vpc: IVpc + prismaConnectionLimit?: number } export interface DatabaseWithSecret extends IServerlessCluster { - secret?: ISecret; + secret?: ISecret } export function getDefaultDatabaseName(): string { - return process.env.DB_NAME || APP_NAME; + return process.env.DB_NAME || APP_NAME } export class ServerlessDatabaseCluster extends DatabaseCluster { @@ -230,37 +231,37 @@ export class ServerlessDatabaseCluster extends DatabaseCluster { * or call grantDataApiAccess() */ getDataApiParams() { - if (!this.secret) throw new Error('cluster missing secret'); + if (!this.secret) throw new Error('cluster missing secret') return { clusterArn: this.clusterArn, secretArn: this.secret.secretArn, - }; + } } } export class DatabaseFromSnapshot extends ServerlessClusterFromSnapshot { getDataApiParams() { - if (!this.secret) throw new Error('cluster missing secret'); + if (!this.secret) throw new Error('cluster missing secret') return { clusterArn: this.clusterArn, secretArn: this.secret.secretArn, - }; + } } } export class DatabaseSeedScript extends Construct { constructor(scope: Construct, id: string, { vpc }: Pick) { - super(scope, id); + super(scope, id) const seedFunction = new Function(this, 'SeedScriptLambda', { vpc, handler: 'lib/lambdas/database/seedDev.handler', enableLiveDev: false, timeout: '10 minutes', - }); + }) new Script(this, 'SeedScript', { onCreate: seedFunction, - }); + }) } } @@ -270,31 +271,31 @@ export class DatabaseSeedScript extends Construct { export class GrantDBAccess implements IAspect { constructor( protected database: IServerlessCluster, - protected dbAccessSecurityGroup: ISecurityGroup + protected dbAccessSecurityGroup: ISecurityGroup, ) {} public visit(node: IConstruct): void { - if (!(node instanceof Function)) return; + if (!(node instanceof Function)) return - const app = App.of(node) as App; - if (!app) return; + const app = App.of(node) as App + if (!app) return // override database for local dev if (app.local) { // load backend/prisma/.env.test - config({ path: 'backend/.env' }); - const localDatabaseUrl = process.env['DATABASE_URL']; - if (!localDatabaseUrl) throw new Error('localDatabaseUrl not set'); - app.addDefaultFunctionEnv({ ['DATABASE_URL']: localDatabaseUrl }); + config({ path: 'backend/.env' }) + const localDatabaseUrl = process.env['DATABASE_URL'] + if (!localDatabaseUrl) throw new Error('localDatabaseUrl not set') + app.addDefaultFunctionEnv({ ['DATABASE_URL']: localDatabaseUrl }) } // allow to connect to postgres // (this is a hack to add a security group to the function) - const funcCfn = node.node.defaultChild as CfnFunction; - const vpcConfig = funcCfn.vpcConfig; - if (!vpcConfig) throw new Error('Missing VPC Config on lambda function: ' + node); - (vpcConfig as any).securityGroupIds ||= []; - (vpcConfig as any).securityGroupIds.push(this.dbAccessSecurityGroup.securityGroupId); + const funcCfn = node.node.defaultChild as CfnFunction + const vpcConfig = funcCfn.vpcConfig + if (!vpcConfig) throw new Error('Missing VPC Config on lambda function: ' + node.toString()) + ;(vpcConfig as any).securityGroupIds ||= [] + ;(vpcConfig as any).securityGroupIds.push(this.dbAccessSecurityGroup.securityGroupId) // to enable one day: // db.grantDataApiAccess(func) diff --git a/stacks/resources/prismaLayer.ts b/stacks/resources/prismaLayer.ts index e947328..f0030e7 100644 --- a/stacks/resources/prismaLayer.ts +++ b/stacks/resources/prismaLayer.ts @@ -1,30 +1,30 @@ -import { AssetHashType, IgnoreMode } from 'aws-cdk-lib'; -import { Code, LayerVersion, LayerVersionProps, Runtime } from 'aws-cdk-lib/aws-lambda'; -import { Construct } from 'constructs'; -import crypto from 'crypto'; -import { App } from 'sst/constructs'; -import { RUNTIME } from 'stacks'; +import { AssetHashType, IgnoreMode } from 'aws-cdk-lib' +import { Code, LayerVersion, LayerVersionProps, Runtime } from 'aws-cdk-lib/aws-lambda' +import { Construct } from 'constructs' +import crypto from 'crypto' +import { App } from 'sst/constructs' +import { RUNTIME } from 'stacks' // modules to mark as "external" when bundling // added to prismaModules -const PRISMA_LAYER_EXTERNAL = ['@prisma/engines', '@prisma/engines-version', '@prisma/internals']; +const PRISMA_LAYER_EXTERNAL = ['@prisma/engines', '@prisma/engines-version', '@prisma/internals'] -type PrismaEngine = 'introspection-engine' | 'schema-engine' | 'prisma-fmt' | 'libquery_engine'; +type PrismaEngine = 'introspection-engine' | 'schema-engine' | 'prisma-fmt' | 'libquery_engine' export interface PrismaLayerProps extends Omit { // e.g. 5.0.0 - prismaVersion?: string; + prismaVersion?: string // some more modules to add to the layer - nodeModules?: string[]; + nodeModules?: string[] // binary targets, e.g. "linux-arm64-openssl-3.0.x" - binaryTargets?: string[]; + binaryTargets?: string[] // prisma libs - prismaModules?: string[]; + prismaModules?: string[] // engines to keep - prismaEngines?: PrismaEngine[]; + prismaEngines?: PrismaEngine[] } /** @@ -49,39 +49,37 @@ export interface PrismaLayerProps extends Omit { * } */ export class PrismaLayer extends LayerVersion { - externalModules: string[]; + externalModules: string[] - environment: Record; + environment: Record constructor(scope: Construct, id: string, props: PrismaLayerProps = {}) { - const { prismaVersion, prismaModules, ...rest } = props; - const nodeModules = props.nodeModules || []; + const { prismaVersion, prismaModules, ...rest } = props + const nodeModules = props.nodeModules || [] - const app = App.of(scope) as App; + const app = App.of(scope) as App - const layerDir = '/asset-output/nodejs'; - const nm = `${layerDir}/node_modules`; - const engineDir = `${nm}/@prisma/engines`; - const internalsDir = `${nm}/@prisma/internals`; - const clientDir = `${nm}/@prisma/client`; + const layerDir = '/asset-output/nodejs' + const nm = `${layerDir}/node_modules` + const engineDir = `${nm}/@prisma/engines` + const internalsDir = `${nm}/@prisma/internals` + const clientDir = `${nm}/@prisma/client` // what are we asking npm to install? // deps to npm install to the layer - const modulesToInstall = prismaModules || ['@prisma/client', '@prisma/engines']; + const modulesToInstall = prismaModules || ['@prisma/client', '@prisma/engines'] const modulesToInstallWithVersion = prismaVersion ? modulesToInstall.map((dep) => `${dep}@${prismaVersion}`) - : modulesToInstall; - const modulesToInstallArgs = modulesToInstallWithVersion.concat(nodeModules).join(' '); + : modulesToInstall + const modulesToInstallArgs = modulesToInstallWithVersion.concat(nodeModules).join(' ') // target architectures - const binaryTargets = props.binaryTargets || ['linux-arm64-openssl-3.0.x']; + const binaryTargets = props.binaryTargets || ['linux-arm64-openssl-3.0.x'] // delete engines not requested - const allEngines: PrismaEngine[] = ['introspection-engine', 'schema-engine', 'libquery_engine', 'prisma-fmt']; - const prismaEngines = props.prismaEngines || ['libquery_engine']; - const deleteEngineCmds = allEngines - .filter((e) => !prismaEngines.includes(e)) - .map((e) => `rm -f ${engineDir}/${e}*`); + const allEngines: PrismaEngine[] = ['introspection-engine', 'schema-engine', 'libquery_engine', 'prisma-fmt'] + const prismaEngines = props.prismaEngines || ['libquery_engine'] + const deleteEngineCmds = allEngines.filter((e) => !prismaEngines.includes(e)).map((e) => `rm -f ${engineDir}/${e}*`) const createBundleCommand = [ // create asset bundle in docker @@ -111,14 +109,14 @@ export class PrismaLayer extends LayerVersion { `rm -rf ${nm}/@types`, `rm -rf ${nm}/.prisma`, ].join(' && '), - ]; + ] // hash our parameters so we know when we need to rebuild - const bundleCommandHash = crypto.createHash('sha256'); - bundleCommandHash.update(JSON.stringify(createBundleCommand)); - const bundleCommandDigest = bundleCommandHash.digest('hex'); + const bundleCommandHash = crypto.createHash('sha256') + bundleCommandHash.update(JSON.stringify(createBundleCommand)) + const bundleCommandDigest = bundleCommandHash.digest('hex') - const binaryTarget = binaryTargets[0]; + const binaryTarget = binaryTargets[0] // bundle const code = Code.fromAsset('.', { @@ -137,17 +135,17 @@ export class PrismaLayer extends LayerVersion { image: RUNTIME.bundlingImage, command: createBundleCommand, }, - }); + }) - super(scope, id, { ...rest, code }); + super(scope, id, { ...rest, code }) // hint for prisma to find the engine this.environment = app.local ? {} : { - PRISMA_QUERY_ENGINE_LIBRARY: `/opt/nodejs/node_modules/@prisma/engines/libquery_engine-${binaryTargets}.so.node`, - }; + PRISMA_QUERY_ENGINE_LIBRARY: `/opt/nodejs/node_modules/@prisma/engines/libquery_engine-${binaryTarget}.so.node`, + } // modules provided by layer - this.externalModules = [...new Set([...PRISMA_LAYER_EXTERNAL, ...nodeModules])]; + this.externalModules = [...new Set([...PRISMA_LAYER_EXTERNAL, ...nodeModules])] } } diff --git a/tsconfig.json b/tsconfig.json index 35d2f98..8fb78f5 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -10,13 +10,17 @@ }, "extends": "./tsconfig.base.json", "include": [ + "eslint.config.mjs", "stacks/**/*.ts", "backend/src/**/*.ts", "common/src/**/*.ts", "index.d.ts", "vite.config.ts", - "backend/src/index.d.ts" + "backend/src/index.d.ts", + "graphql/codegen.ts", + "sst.config.ts", + "testSetup.ts" ], "files": ["sst-env.d.ts", "reset.d.ts"], - "exclude": ["sst.config.ts", "node_modules", "web/public"] + "exclude": ["node_modules", "web/public"] } diff --git a/web/eslint.config.mjs b/web/eslint.config.mjs index 063e654..f6f50ee 100644 --- a/web/eslint.config.mjs +++ b/web/eslint.config.mjs @@ -1,52 +1,25 @@ -import { fixupConfigRules } from "@eslint/compat"; -import globals from "globals"; -import path from "node:path"; -import { fileURLToPath } from "node:url"; -import js from "@eslint/js"; -import { FlatCompat } from "@eslint/eslintrc"; +const next = require('@next/eslint-plugin-next') +const globals = require('globals') -const __filename = fileURLToPath(import.meta.url); -const __dirname = path.dirname(__filename); -const compat = new FlatCompat({ - baseDirectory: __dirname, - recommendedConfig: js.configs.recommended, - allConfig: js.configs.all -}); - -export default [...fixupConfigRules(compat.extends( - "../.eslintrc.yml", - "plugin:react/recommended", - "plugin:react-hooks/recommended", - "plugin:react/jsx-runtime", -)), { - languageOptions: { - globals: { - ...globals.browser, - ...Object.fromEntries(Object.entries(globals.node).map(([key]) => [key, "off"])), - }, +module.exports = [ + { + files: ['**/*.{js,jsx,mjs,cjs,ts,tsx}'], + ignore: ['node_modules', '.next', '.vercel'], + plugins: { + next, }, - - settings: { - react: { - version: "detect", + languageOptions: { + parserOptions: { + ecmaFeatures: { + jsx: true, }, + }, + globals: { + ...globals.browser, + }, }, - rules: { - "@typescript-eslint/no-empty-interface": "off", - "react/prop-types": 0, - "react/destructuring-assignment": 0, - "react/no-unescaped-entities": 1, - "react/require-default-props": 0, - "react/jsx-props-no-spreading": 0, - "react/jsx-no-useless-fragment": 1, - "react/jsx-curly-brace-presence": 1, - "react/no-unused-prop-types": 1, - "react/button-has-type": 0, - "react/jsx-no-duplicate-props": 0, - - "react/jsx-filename-extension": [1, { - extensions: [".tsx", ".jsx"], - }], + 'react/jsx-uses-vars': 'error', }, -}]; \ No newline at end of file + }, +] diff --git a/web/package.json b/web/package.json index ceb1cb9..cebb4c2 100644 --- a/web/package.json +++ b/web/package.json @@ -25,11 +25,12 @@ "vite-tsconfig-paths": "^4.3.2" }, "devDependencies": { + "@next/eslint-plugin-next": "^14.2.4", "@testing-library/react": "^16.0.0", "@types/node": "^20", "@types/react": "^18", "@types/react-dom": "^18", - "eslint-config-next": "14.2.4", + "eslint-plugin-react": "^7.30.0", "postcss": "^8", "tailwindcss": "^3.4.1", "typescript": "^5" diff --git a/web/tsconfig.json b/web/tsconfig.json index 148e8ed..85ddad1 100644 --- a/web/tsconfig.json +++ b/web/tsconfig.json @@ -30,7 +30,8 @@ ".next/types/**/*.ts", "../common/src/**/*.ts", "../reset.d.ts", - "../sst-env.d.ts" + "../sst-env.d.ts", + "eslint.config.mjs" ], "exclude": ["node_modules"] }