From c451be0ab708bfe1230405026f0af630ca03a5e9 Mon Sep 17 00:00:00 2001 From: Bentroen <29354120+Bentroen@users.noreply.github.com> Date: Sat, 3 Jan 2026 23:10:57 -0300 Subject: [PATCH 01/37] chore: add setting to use project's TypeScript version instead of bundled VS Code's https://stackoverflow.com/a/75470675/9045426 https://stackoverflow.com/a/75357623/9045426 --- .vscode/settings.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index ad6a31bc..76a9c3a4 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -16,5 +16,7 @@ "files.associations": { ".css": "tailwindcss", "*.scss": "tailwindcss" - } + }, + "typescript.tsdk": "./node_modules/typescript/lib", + "typescript.enablePromptUseWorkspaceTsdk": true } From 757b729585e8372f71e2f36edae10874779b442f Mon Sep 17 00:00:00 2001 From: Bentroen <29354120+Bentroen@users.noreply.github.com> Date: Sat, 3 Jan 2026 23:13:35 -0300 Subject: [PATCH 02/37] chore: replace root `tsconfig.json` with `references` field --- tsconfig.build.json | 18 ++++++++++++++++++ tsconfig.json | 18 ------------------ 2 files changed, 18 insertions(+), 18 deletions(-) create mode 100644 tsconfig.build.json delete mode 100644 tsconfig.json diff --git a/tsconfig.build.json b/tsconfig.build.json new file mode 100644 index 00000000..49978a90 --- /dev/null +++ b/tsconfig.build.json @@ -0,0 +1,18 @@ +{ + // Root build orchestrator. + // This file does NOT compile code - only defines project references for `tsc -b`. + // Real projects live in /apps and /packages and extend tsconfig.base.json. + + // This config does not type-check files directly. + // Use `tsc -b` or `tsc -b --noEmit`. + "files": [], + "references": [ + { "path": "apps/frontend" }, + { "path": "apps/backend" }, + { "path": "packages/configs" }, + { "path": "packages/database" }, + { "path": "packages/song" }, + { "path": "packages/sounds" }, + { "path": "packages/thumbnail" } + ] +} diff --git a/tsconfig.json b/tsconfig.json deleted file mode 100644 index ef156702..00000000 --- a/tsconfig.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "extends": "./tsconfig.base.json", - "compilerOptions": { - "module": "nodenext", - // Root project specific overrides - "strictNullChecks": true, - "noImplicitAny": true, - "strictBindCallApply": true, - "strictPropertyInitialization": false - }, - "exclude": [ - "node_modules", - "**/dist/**/*", - "**/dist/**/*", - "**/*.js", - "**/*.mjs" - ] -} From 2720e2555550c7e471ac2a3a82244629a6a3a668 Mon Sep 17 00:00:00 2001 From: Bentroen <29354120+Bentroen@users.noreply.github.com> Date: Sat, 3 Jan 2026 23:23:38 -0300 Subject: [PATCH 03/37] chore: enable `composite` in package-inherited root TS config All packages should be `composite` so they're aware of each other within the monorepo's context. We also extract common settings `emitDeclarationOnly`, `declaration` and `outDir` so this standard config is extended by all packages, avoiding the need to redefine them individually per package. `"noEmit": false` is required for `"composite": true`, so this change is also included here. --- tsconfig.package.json | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/tsconfig.package.json b/tsconfig.package.json index 7027807e..6ff029e3 100644 --- a/tsconfig.package.json +++ b/tsconfig.package.json @@ -1,12 +1,17 @@ { "extends": "./tsconfig.base.json", "compilerOptions": { + // Package-specific settings + "composite": true, + "noEmit": false, + "emitDeclarationOnly": true, + "declaration": true, + "outDir": "dist", // Modern bundler mode "module": "ESNext", "moduleResolution": "bundler", "allowImportingTsExtensions": true, "verbatimModuleSyntax": true, - "noEmit": true, "jsx": "react-jsx", // Additional strict checks for packages "noUncheckedIndexedAccess": true, From 16d85c8db2395065784bc9ed80f441f67cc61fcf Mon Sep 17 00:00:00 2001 From: Bentroen <29354120+Bentroen@users.noreply.github.com> Date: Sun, 4 Jan 2026 00:21:56 -0300 Subject: [PATCH 04/37] chore: rename `tsconfig.build.json` to `tsconfig.json` --- packages/song/tsconfig.build.json | 11 ----------- packages/song/tsconfig.json | 3 ++- tsconfig.build.json => tsconfig.json | 0 3 files changed, 2 insertions(+), 12 deletions(-) delete mode 100644 packages/song/tsconfig.build.json rename tsconfig.build.json => tsconfig.json (100%) diff --git a/packages/song/tsconfig.build.json b/packages/song/tsconfig.build.json deleted file mode 100644 index 0fc16b66..00000000 --- a/packages/song/tsconfig.build.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "extends": "./tsconfig.json", - "compilerOptions": { - "noEmit": false, - "emitDeclarationOnly": true, - "declaration": true, - "outDir": "dist" - }, - "include": ["src/**/*"], - "exclude": ["node_modules", "dist", "**/*.spec.ts", "**/*.test.ts"] -} diff --git a/packages/song/tsconfig.json b/packages/song/tsconfig.json index 8ac70592..7c179325 100644 --- a/packages/song/tsconfig.json +++ b/packages/song/tsconfig.json @@ -1,3 +1,4 @@ { - "extends": "../../tsconfig.package.json" + "extends": "../../tsconfig.package.json", + "include": ["src"] } diff --git a/tsconfig.build.json b/tsconfig.json similarity index 100% rename from tsconfig.build.json rename to tsconfig.json From b5216a08ebabc9058509e75cf569f7d14f7ee640 Mon Sep 17 00:00:00 2001 From: Bentroen <29354120+Bentroen@users.noreply.github.com> Date: Sun, 4 Jan 2026 00:43:47 -0300 Subject: [PATCH 05/37] chore: review base TS settings in global `tsconfig.base.json` strictPropertyInitialization: - Remained after removing: "strictNullChecks": true, "noImplicitAny": true, "strictBindCallApply": true, which are already implied by "strict": true - Overrides default value for "strict": true to allow for decorators (Nest.js, class-validator etc.) - May be moved to specific packages instead, but doesn't cause harm if kept allowJs: - Likely added due to the adasync.js experiment - We don't have any other .js files on the repo incremental: - Removed because `composite: true` already implies `incremental` - incremental without composite produces .tsbuildinfo in weird places exclude: - if not explicitly included, none of these paths will be part of the TS project - **/*.spec.ts excludes test files, which should be type-checked as well --- tsconfig.base.json | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/tsconfig.base.json b/tsconfig.base.json index 4201cfdd..66165032 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -6,16 +6,13 @@ "skipLibCheck": true, "noFallthroughCasesInSwitch": true, "forceConsistentCasingInFileNames": true, + "strictPropertyInitialization": false, // Modern JavaScript features "target": "ESNext", "lib": ["ESNext"], - "allowJs": true, "moduleDetection": "force", - // Build performance - "incremental": true, // Decorator support for backend compatibility "experimentalDecorators": true, "emitDecoratorMetadata": true - }, - "exclude": ["node_modules", "dist", "**/*.spec.ts", "**/*.test.ts"] + } } From 3c8beda0a1219b0df31e15e0804d16999de3a75f Mon Sep 17 00:00:00 2001 From: Bentroen <29354120+Bentroen@users.noreply.github.com> Date: Sun, 4 Jan 2026 00:51:54 -0300 Subject: [PATCH 06/37] chore: review base TS settings for packages in `tsconfig.package.json` allowImportingTsExtensions: - Unused (unnecessary) jsx: - Belongs in the frontend config, not globally --- tsconfig.package.json | 2 -- 1 file changed, 2 deletions(-) diff --git a/tsconfig.package.json b/tsconfig.package.json index 6ff029e3..fd9a1265 100644 --- a/tsconfig.package.json +++ b/tsconfig.package.json @@ -10,9 +10,7 @@ // Modern bundler mode "module": "ESNext", "moduleResolution": "bundler", - "allowImportingTsExtensions": true, "verbatimModuleSyntax": true, - "jsx": "react-jsx", // Additional strict checks for packages "noUncheckedIndexedAccess": true, "noImplicitOverride": true, From 01e098407b44098f47492f5a42b386b93fc14b18 Mon Sep 17 00:00:00 2001 From: Bentroen <29354120+Bentroen@users.noreply.github.com> Date: Sun, 4 Jan 2026 01:00:03 -0300 Subject: [PATCH 07/37] chore: merge `tsconfig.build.json` into `tsconfig.json` for packages While it's fine (and recommended) to have a separate TS config for building in apps, for packages, it adds unnecessary complexity and possible IDE/build mismatches in error reporting. So we're resorting to one simple rule: unless we can justify why a separate build config is needed in one sentence, it should be out - it's easy to reintroduce something later if things break. The necessary rules have been merged into the base tsconfig.json and other unnecessary rules have been removed from either/both of them. --- packages/configs/tsconfig.build.json | 14 -------------- packages/configs/tsconfig.json | 6 +----- packages/database/tsconfig.build.json | 16 ---------------- packages/database/tsconfig.json | 17 +---------------- packages/sounds/tsconfig.build.json | 11 ----------- packages/sounds/tsconfig.json | 4 +--- packages/thumbnail/tsconfig.build.json | 11 ----------- 7 files changed, 3 insertions(+), 76 deletions(-) delete mode 100644 packages/configs/tsconfig.build.json delete mode 100644 packages/database/tsconfig.build.json delete mode 100644 packages/sounds/tsconfig.build.json delete mode 100644 packages/thumbnail/tsconfig.build.json diff --git a/packages/configs/tsconfig.build.json b/packages/configs/tsconfig.build.json deleted file mode 100644 index 22eb2159..00000000 --- a/packages/configs/tsconfig.build.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "extends": "./tsconfig.json", - "compilerOptions": { - "noEmit": false, - "declaration": true, - "outDir": "dist", - "allowImportingTsExtensions": false, - "verbatimModuleSyntax": false, - "module": "esnext", - "moduleResolution": "bundler" - }, - "include": ["src/**/*"], - "exclude": ["node_modules", "dist", "**/*.spec.ts", "**/*.test.ts"] -} diff --git a/packages/configs/tsconfig.json b/packages/configs/tsconfig.json index 069a1dc8..7c179325 100644 --- a/packages/configs/tsconfig.json +++ b/packages/configs/tsconfig.json @@ -1,8 +1,4 @@ { "extends": "../../tsconfig.package.json", - "compilerOptions": { - // Package-specific overrides (if any) - "module": "esnext", - "moduleResolution": "bundler" - } + "include": ["src"] } diff --git a/packages/database/tsconfig.build.json b/packages/database/tsconfig.build.json deleted file mode 100644 index 15e1f5c2..00000000 --- a/packages/database/tsconfig.build.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "extends": "./tsconfig.json", - "compilerOptions": { - "emitDecoratorMetadata": true, - "noEmit": false, - "declaration": true, - "outDir": "dist", - "allowImportingTsExtensions": false, - "verbatimModuleSyntax": false, - "moduleResolution": "node", - "module": "ESNext", - "typeRoots": ["./node_modules/@types"] - }, - "include": ["src/**/*"], - "exclude": ["node_modules", "dist", "**/*.spec.ts", "**/*.test.ts"] -} diff --git a/packages/database/tsconfig.json b/packages/database/tsconfig.json index 1a7a926f..42d932fb 100644 --- a/packages/database/tsconfig.json +++ b/packages/database/tsconfig.json @@ -2,21 +2,6 @@ "extends": "../../tsconfig.package.json", "compilerOptions": { // Database-specific settings - "declaration": true, - "removeComments": true, - "allowSyntheticDefaultImports": true, - "sourceMap": true, - "outDir": "./dist", - "baseUrl": "./", - "emitDecoratorMetadata": true, - // Relaxed strict settings for database entities - "strictNullChecks": false, - "noImplicitAny": false, - "strictBindCallApply": false, - "forceConsistentCasingInFileNames": false, - // Path mapping - "paths": { - "@database/*": ["src/*"] - } + "emitDecoratorMetadata": true // Necessary for class-validator } } diff --git a/packages/sounds/tsconfig.build.json b/packages/sounds/tsconfig.build.json deleted file mode 100644 index 0fc16b66..00000000 --- a/packages/sounds/tsconfig.build.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "extends": "./tsconfig.json", - "compilerOptions": { - "noEmit": false, - "emitDeclarationOnly": true, - "declaration": true, - "outDir": "dist" - }, - "include": ["src/**/*"], - "exclude": ["node_modules", "dist", "**/*.spec.ts", "**/*.test.ts"] -} diff --git a/packages/sounds/tsconfig.json b/packages/sounds/tsconfig.json index 92f024c9..7c179325 100644 --- a/packages/sounds/tsconfig.json +++ b/packages/sounds/tsconfig.json @@ -1,6 +1,4 @@ { "extends": "../../tsconfig.package.json", - "compilerOptions": { - "baseUrl": "./src" - } + "include": ["src"] } diff --git a/packages/thumbnail/tsconfig.build.json b/packages/thumbnail/tsconfig.build.json deleted file mode 100644 index 0fc16b66..00000000 --- a/packages/thumbnail/tsconfig.build.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "extends": "./tsconfig.json", - "compilerOptions": { - "noEmit": false, - "emitDeclarationOnly": true, - "declaration": true, - "outDir": "dist" - }, - "include": ["src/**/*"], - "exclude": ["node_modules", "dist", "**/*.spec.ts", "**/*.test.ts"] -} From d5f531d286edc388aa3f239409cdb2911dd4bce8 Mon Sep 17 00:00:00 2001 From: Bentroen <29354120+Bentroen@users.noreply.github.com> Date: Sun, 4 Jan 2026 01:06:05 -0300 Subject: [PATCH 08/37] fix: remove `@database` path alias in database package This change was prompted by the removal of this line from `database/tsconfig.json` in commit 01e098407b44098f47492f5a42b386b93fc14b18: "paths": { "@database/*": ["src/*"] } This adds an internal alias to the package itself, whereas this would only be needed in cross-package references. This bypassed package boundaries, creating an internal public API that does not exist. For this reason and to be consistent with the rest of the project, internal imports are being changed to use relative path syntax. --- packages/database/src/common/dto/PageQuery.dto.ts | 3 ++- packages/database/src/song/dto/SongPreview.dto.ts | 2 +- packages/database/src/song/dto/SongView.dto.ts | 4 ++-- packages/database/src/song/dto/UploadSongResponseDto.dto.ts | 2 +- 4 files changed, 6 insertions(+), 5 deletions(-) diff --git a/packages/database/src/common/dto/PageQuery.dto.ts b/packages/database/src/common/dto/PageQuery.dto.ts index 6c508b1e..a0f0025c 100644 --- a/packages/database/src/common/dto/PageQuery.dto.ts +++ b/packages/database/src/common/dto/PageQuery.dto.ts @@ -11,9 +11,10 @@ import { Min, } from 'class-validator'; -import type { TimespanType } from '@database/song/dto/types'; import { TIMESPANS } from '@nbw/config'; +import type { TimespanType } from '../../song/dto/types'; + export class PageQueryDTO { @Min(1) @ApiProperty({ diff --git a/packages/database/src/song/dto/SongPreview.dto.ts b/packages/database/src/song/dto/SongPreview.dto.ts index 97acff3c..58bdc344 100644 --- a/packages/database/src/song/dto/SongPreview.dto.ts +++ b/packages/database/src/song/dto/SongPreview.dto.ts @@ -1,6 +1,6 @@ import { IsNotEmpty, IsString, IsUrl, MaxLength } from 'class-validator'; -import type { SongWithUser } from '@database/song/entity/song.entity'; +import type { SongWithUser } from '../../song/entity/song.entity'; type SongPreviewUploader = { username: string; diff --git a/packages/database/src/song/dto/SongView.dto.ts b/packages/database/src/song/dto/SongView.dto.ts index b7b5ed6f..58d07c04 100644 --- a/packages/database/src/song/dto/SongView.dto.ts +++ b/packages/database/src/song/dto/SongView.dto.ts @@ -7,8 +7,8 @@ import { IsUrl, } from 'class-validator'; -import { SongStats } from '@database/song/dto/SongStats'; -import type { SongDocument } from '@database/song/entity/song.entity'; +import { SongStats } from '../../song/dto/SongStats'; +import type { SongDocument } from '../../song/entity/song.entity'; import type { CategoryType, LicenseType, VisibilityType } from './types'; diff --git a/packages/database/src/song/dto/UploadSongResponseDto.dto.ts b/packages/database/src/song/dto/UploadSongResponseDto.dto.ts index 2fedb3e1..b83acb2b 100644 --- a/packages/database/src/song/dto/UploadSongResponseDto.dto.ts +++ b/packages/database/src/song/dto/UploadSongResponseDto.dto.ts @@ -7,7 +7,7 @@ import { ValidateNested, } from 'class-validator'; -import type { SongWithUser } from '@database/song/entity/song.entity'; +import type { SongWithUser } from '../../song/entity/song.entity'; import * as SongViewDto from './SongView.dto'; import { ThumbnailData } from './ThumbnailData.dto'; From 21867170608e528d043ef1dad9d73cc46c7ee2c5 Mon Sep 17 00:00:00 2001 From: Bentroen <29354120+Bentroen@users.noreply.github.com> Date: Sun, 4 Jan 2026 01:22:18 -0300 Subject: [PATCH 09/37] chore: remove `build:types` script from packages and replace with `tsc -b` We no longer have a separate `tsconfig.build.ts` specific for building, so there doesn't make sense to be a separate command for building types - it's not meant to be run standalone anyway, as each package is now composite. --- packages/configs/package.json | 3 +-- packages/song/package.json | 3 +-- packages/sounds/package.json | 3 +-- packages/thumbnail/package.json | 1 - 4 files changed, 3 insertions(+), 7 deletions(-) diff --git a/packages/configs/package.json b/packages/configs/package.json index b641eb87..8d0d3e69 100644 --- a/packages/configs/package.json +++ b/packages/configs/package.json @@ -13,8 +13,7 @@ } }, "scripts": { - "build": "bun run clean && bun build src/index.ts --outdir dist --target node && bun run build:types", - "build:types": "tsc --project tsconfig.build.json", + "build": "bun run clean && bun build src/index.ts --outdir dist --target node && tsc -b", "clean": "rm -rf dist", "dev": "bun build src/index.ts --outdir dist --target node --watch", "lint": "eslint \"src/**/*.ts\" --fix", diff --git a/packages/song/package.json b/packages/song/package.json index 0f0003d3..84a3e03c 100644 --- a/packages/song/package.json +++ b/packages/song/package.json @@ -15,10 +15,9 @@ } }, "scripts": { - "build": "bun run clean && bun run build:node && bun run build:browser && bun run build:types", + "build": "bun run clean && bun run build:node && bun run build:browser && tsc -b", "build:node": "bun build src/index.ts --outdir dist --target node && mv dist/index.js dist/index.node.js", "build:browser": "bun build src/index.ts --outdir dist --target browser && mv dist/index.js dist/index.browser.js", - "build:types": "tsc --project tsconfig.build.json", "clean": "rm -rf dist", "dev": "bun build src/index.ts --outdir dist --target node --watch", "lint": "eslint \"src/**/*.ts\" --fix", diff --git a/packages/sounds/package.json b/packages/sounds/package.json index 88bd226f..70abe826 100644 --- a/packages/sounds/package.json +++ b/packages/sounds/package.json @@ -13,8 +13,7 @@ } }, "scripts": { - "build": "bun run clean && bun build src/index.ts --outdir dist --target node && bun run build:types", - "build:types": "tsc --project tsconfig.build.json", + "build": "bun run clean && bun build src/index.ts --outdir dist --target node && tsc -b", "clean": "rm -rf dist", "dev": "bun build src/index.ts --outdir dist --target node --watch", "lint": "eslint \"src/**/*.ts\" --fix", diff --git a/packages/thumbnail/package.json b/packages/thumbnail/package.json index 4f4b95d9..55f73053 100644 --- a/packages/thumbnail/package.json +++ b/packages/thumbnail/package.json @@ -17,7 +17,6 @@ "build": "bun run clean && bun run build:browser && bun run build:node && bun run build:types", "build:browser": "bun build src/browser/index.ts --outdir dist/browser --target browser --external @napi-rs/canvas", "build:node": "bun build src/node/index.ts --outdir dist/node --target node", - "build:types": "tsc --project tsconfig.build.json", "clean": "rm -rf dist", "dev:browser": "bun build src/browser/index.ts --outdir dist/browser --target node --watch", "dev:node": "bun build src/node/index.ts --outdir dist/node --target node --watch", From bcf7af3a5ef0d04f04698fcf753edcbef1561ac4 Mon Sep 17 00:00:00 2001 From: Bentroen <29354120+Bentroen@users.noreply.github.com> Date: Sun, 4 Jan 2026 01:24:11 -0300 Subject: [PATCH 10/37] chore: add `typecheck` command to workspace root Runs against the root `tsconfig.json`, which defines the build graph for the entire project. The `-b` option actually makes sure the entire chain is built, then reports the errors. --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index ec99f8c3..1338fdd6 100644 --- a/package.json +++ b/package.json @@ -54,6 +54,7 @@ "dev:web": "bun run --filter '@nbw/frontend' dev", "dev:apps": "bun run --filter './apps/*' dev", "dev": "concurrently --names 'backend,frontend' --prefix-colors 'cyan,magenta' 'cd apps/backend && bun run start:dev' 'cd apps/frontend && bun run dev'", + "typecheck": "tsc -b --noEmit", "lint": "eslint \"**/*.{ts,tsx}\" --fix", "lint:check": "eslint \"**/*.{ts,tsx}\"", "format": "prettier --write .", From 2b0eeff7cfb8230236932af535e92435432cf48d Mon Sep 17 00:00:00 2001 From: Bentroen <29354120+Bentroen@users.noreply.github.com> Date: Sun, 4 Jan 2026 01:51:10 -0300 Subject: [PATCH 11/37] chore: make backend and frontend TS projects `composite` (following root `references`) --- apps/backend/tsconfig.json | 1 + apps/frontend/tsconfig.json | 1 + 2 files changed, 2 insertions(+) diff --git a/apps/backend/tsconfig.json b/apps/backend/tsconfig.json index 7dc80e0f..08d461db 100644 --- a/apps/backend/tsconfig.json +++ b/apps/backend/tsconfig.json @@ -1,6 +1,7 @@ { "extends": "../../tsconfig.base.json", "compilerOptions": { + "composite": true, // NestJS specific settings "module": "commonjs", "target": "ES2021", diff --git a/apps/frontend/tsconfig.json b/apps/frontend/tsconfig.json index 515247c9..3a86295a 100644 --- a/apps/frontend/tsconfig.json +++ b/apps/frontend/tsconfig.json @@ -1,6 +1,7 @@ { "extends": "../../tsconfig.base.json", "compilerOptions": { + "composite": true, // Next.js specific settings "target": "es5", "lib": ["dom", "dom.iterable", "esnext"], From 67daa09515d1f682936b58e205dd1ac7ba312ccc Mon Sep 17 00:00:00 2001 From: Bentroen <29354120+Bentroen@users.noreply.github.com> Date: Sun, 4 Jan 2026 01:52:21 -0300 Subject: [PATCH 12/37] chore: clean up frontend's `tsconfig.json` - Removes no-op duplication - Removes redundant configs (e.g. strictNullChecks, noImplicitAny, strictBindCallApply already included in strict: true) --- apps/frontend/tsconfig.json | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/apps/frontend/tsconfig.json b/apps/frontend/tsconfig.json index 3a86295a..f4228eeb 100644 --- a/apps/frontend/tsconfig.json +++ b/apps/frontend/tsconfig.json @@ -16,9 +16,6 @@ "isolatedModules": true, // Override base strict settings for Next.js compatibility "strict": true, - "strictNullChecks": true, - "noImplicitAny": true, - "strictBindCallApply": true, "strictPropertyInitialization": false, // Next.js plugins and types "plugins": [ @@ -38,10 +35,5 @@ "**/*.tsx", "**/*.mdx", ".next/types/**/*.ts" - ], - "types": ["mdx"], - // Path mapping - "paths": { - "@web/*": ["./src/*"] - } + ] } From dc7f599edb553cdd2f33b58e1f35bed91687a243 Mon Sep 17 00:00:00 2001 From: Bentroen <29354120+Bentroen@users.noreply.github.com> Date: Sun, 4 Jan 2026 11:43:37 -0300 Subject: [PATCH 13/37] chore: clean up backend's `tsconfig.json` - Merged `baseUrl` into `paths` - Removed exclusion of tests, node_modules and dist - Included `scripts` folder in the config - Removed relaxed strict settings (pitfall) - Removed redundant `outDir` (already defined in base config) - Add missing comment in start section - Remove tsconfig.build.json (no longer necessary) --- apps/backend/tsconfig.build.json | 4 ---- apps/backend/tsconfig.json | 13 ++----------- 2 files changed, 2 insertions(+), 15 deletions(-) delete mode 100644 apps/backend/tsconfig.build.json diff --git a/apps/backend/tsconfig.build.json b/apps/backend/tsconfig.build.json deleted file mode 100644 index 64f86c6b..00000000 --- a/apps/backend/tsconfig.build.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "extends": "./tsconfig.json", - "exclude": ["node_modules", "test", "dist", "**/*spec.ts"] -} diff --git a/apps/backend/tsconfig.json b/apps/backend/tsconfig.json index 08d461db..64ed9240 100644 --- a/apps/backend/tsconfig.json +++ b/apps/backend/tsconfig.json @@ -9,20 +9,11 @@ "removeComments": true, "allowSyntheticDefaultImports": true, "sourceMap": true, - "outDir": "./dist", - "baseUrl": "./", "emitDecoratorMetadata": true, - // Relaxed strict settings for backend - "strictNullChecks": false, - "noImplicitAny": false, - "strictBindCallApply": false, - "forceConsistentCasingInFileNames": false, - "noFallthroughCasesInSwitch": false, // Path mapping "paths": { - "@server/*": ["src/*"] + "@server/*": ["./src/*"] } }, - "include": ["src/**/*.ts", "src/**/*.d.ts"], - "exclude": ["node_modules", "dist", "e2e/**/*", "test/**/*"] + "include": ["src", "scripts"] } From 710d79e3803a3ef55f0b5791a6a1a4e1eb1935b9 Mon Sep 17 00:00:00 2001 From: Bentroen <29354120+Bentroen@users.noreply.github.com> Date: Sun, 4 Jan 2026 11:51:33 -0300 Subject: [PATCH 14/37] style: add comments and line breaks to frontend/backend tsconfig --- apps/backend/tsconfig.json | 3 +++ apps/frontend/tsconfig.json | 5 +++++ 2 files changed, 8 insertions(+) diff --git a/apps/backend/tsconfig.json b/apps/backend/tsconfig.json index 64ed9240..be7af5a2 100644 --- a/apps/backend/tsconfig.json +++ b/apps/backend/tsconfig.json @@ -1,7 +1,9 @@ { "extends": "../../tsconfig.base.json", "compilerOptions": { + // This is a referenced package "composite": true, + // NestJS specific settings "module": "commonjs", "target": "ES2021", @@ -10,6 +12,7 @@ "allowSyntheticDefaultImports": true, "sourceMap": true, "emitDecoratorMetadata": true, + // Path mapping "paths": { "@server/*": ["./src/*"] diff --git a/apps/frontend/tsconfig.json b/apps/frontend/tsconfig.json index f4228eeb..fe8b9aea 100644 --- a/apps/frontend/tsconfig.json +++ b/apps/frontend/tsconfig.json @@ -1,7 +1,9 @@ { "extends": "../../tsconfig.base.json", "compilerOptions": { + // This is a referenced package "composite": true, + // Next.js specific settings "target": "es5", "lib": ["dom", "dom.iterable", "esnext"], @@ -14,9 +16,11 @@ "esModuleInterop": true, "resolveJsonModule": true, "isolatedModules": true, + // Override base strict settings for Next.js compatibility "strict": true, "strictPropertyInitialization": false, + // Next.js plugins and types "plugins": [ { @@ -24,6 +28,7 @@ } ], "types": ["mdx"], + // Path mapping "paths": { "@web/*": ["./src/*"] From b5e6023ea2f601fa146b6e96313ff38d8cac6a43 Mon Sep 17 00:00:00 2001 From: Bentroen <29354120+Bentroen@users.noreply.github.com> Date: Sun, 4 Jan 2026 12:58:48 -0300 Subject: [PATCH 15/37] style: fully comment settings in base and package `tsconfig.json` --- tsconfig.base.json | 42 +++++++++++++++++++++++++++++++++++++----- tsconfig.package.json | 36 ++++++++++++++++++++++++++++++++---- 2 files changed, 69 insertions(+), 9 deletions(-) diff --git a/tsconfig.base.json b/tsconfig.base.json index 66165032..5f5450ea 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -1,17 +1,49 @@ { "compilerOptions": { + // Default output directory. + // Actual emitting is controlled by extending configs (packages/apps). "outDir": "./dist", - // Base strict settings + + // ============================== + // Global type-safety invariant + // ============================== + + // These rules define the minimum safety bar for the entire repo. + // Disabling any of its sub-rules should be rare and explicitly justified. "strict": true, - "skipLibCheck": true, + + // Disabled to allow compatibility with frameworks and libraries that + // initialize fields outside constructors (e.g. ORMs, DI containers). + "strictPropertyInitialization": false, + + // Prevents subtle runtime bugs caused by switch fallthrough. "noFallthroughCasesInSwitch": true, + + // Enforces consistent file casing across platforms (Windows/macOS/Linux). "forceConsistentCasingInFileNames": true, - "strictPropertyInitialization": false, - // Modern JavaScript features + + // Improves build performance by skipping type-checking of .d.ts files. + // Safe as long as dependencies are trusted. + "skipLibCheck": true, + + // ============================== + // Language & runtime model + // ============================== + + // Target modern runtimes; downleveling is handled by bundlers if needed. "target": "ESNext", "lib": ["ESNext"], + + // Forces explicit module boundaries. + // Prevents accidental global scripts and implicit module inference. "moduleDetection": "force", - // Decorator support for backend compatibility + + // ============================== + // Decorators (backend / ORM support) + // ============================== + + // Required for libraries relying on legacy decorators and runtime metadata + // (e.g. class-validator). "experimentalDecorators": true, "emitDecoratorMetadata": true } diff --git a/tsconfig.package.json b/tsconfig.package.json index fd9a1265..4fdac436 100644 --- a/tsconfig.package.json +++ b/tsconfig.package.json @@ -1,20 +1,48 @@ { "extends": "./tsconfig.base.json", "compilerOptions": { - // Package-specific settings + // ============================== + // Project reference participation + // ============================== + + // Required for tsc --build and incremental compilation. "composite": true, + + // Packages emit types only; JS output is handled by bundlers. "noEmit": false, "emitDeclarationOnly": true, "declaration": true, "outDir": "dist", - // Modern bundler mode + + // ============================== + // Module system (bundler-friendly) + // ============================== + + // Preserve native ESM syntax and let the bundler handle resolution. "module": "ESNext", "moduleResolution": "bundler", + + // Do not rewrite imports/exports. + // Ensures the emitted .d.ts matches source semantics exactly. "verbatimModuleSyntax": true, - // Additional strict checks for packages + + // ============================== + // Extra safety for library code + // ============================== + + // Prevents unsafe indexed access (e.g. obj[key] returning undefined). "noUncheckedIndexedAccess": true, + + // Requires `override` keyword when overriding class methods. + // Helps prevent accidental API mismatches. "noImplicitOverride": true, - // Relaxed flags (can be enabled per package) + + // ============================== + // Linting rules + // ============================== + + // Disabled since they are covered by ESLint rules. + // Can be enabled in individual packages if desired. "noUnusedLocals": false, "noUnusedParameters": false, "noPropertyAccessFromIndexSignature": false From f508b89accefe53e81981f45bb07394db3589806 Mon Sep 17 00:00:00 2001 From: Bentroen <29354120+Bentroen@users.noreply.github.com> Date: Sun, 4 Jan 2026 13:01:35 -0300 Subject: [PATCH 16/37] chore: add type checking workflow on push and pull request --- .github/workflows/typecheck.yml | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 .github/workflows/typecheck.yml diff --git a/.github/workflows/typecheck.yml b/.github/workflows/typecheck.yml new file mode 100644 index 00000000..f1b4d8cc --- /dev/null +++ b/.github/workflows/typecheck.yml @@ -0,0 +1,32 @@ +name: Type Check + +on: + push: + branches: + - develop + - main + pull_request: + branches: + - develop + - main + +jobs: + typecheck: + runs-on: ubuntu-latest + env: + THUMBNAIL_URL: ${{ vars.THUMBNAIL_URL }} + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Install bun + uses: oven-sh/setup-bun@v2 + with: + bun-version: latest + + - name: Install dependencies + run: bun install + + - name: Type check (project references) + run: bun run typecheck From a0115043008d850d743bb5752c70a93f83b0d3b4 Mon Sep 17 00:00:00 2001 From: Bentroen <29354120+Bentroen@users.noreply.github.com> Date: Mon, 5 Jan 2026 19:49:16 -0300 Subject: [PATCH 17/37] chore: add `eslint-import-resolver-typescript` dependency --- bun.lock | 31 +++++++++++++++++++++++-------- package.json | 1 + 2 files changed, 24 insertions(+), 8 deletions(-) diff --git a/bun.lock b/bun.lock index a7ed6734..b2cdbafe 100644 --- a/bun.lock +++ b/bun.lock @@ -17,6 +17,7 @@ "concurrently": "^9.2.1", "eslint": "^9.36.0", "eslint-config-prettier": "^8.10.2", + "eslint-import-resolver-typescript": "^4.4.4", "eslint-plugin-import": "^2.32.0", "eslint-plugin-mdx": "^3.6.2", "eslint-plugin-unused-imports": "^4.2.0", @@ -1668,9 +1669,11 @@ "eslint-config-prettier": ["eslint-config-prettier@8.10.2", "", { "peerDependencies": { "eslint": ">=7.0.0" }, "bin": { "eslint-config-prettier": "bin/cli.js" } }, "sha512-/IGJ6+Dka158JnP5n5YFMOszjDWrXggGz1LaK/guZq9vZTmniaKlHcsscvkAhn9y4U+BU3JuUdYvtAMcv30y4A=="], + "eslint-import-context": ["eslint-import-context@0.1.9", "", { "dependencies": { "get-tsconfig": "^4.10.1", "stable-hash-x": "^0.2.0" }, "peerDependencies": { "unrs-resolver": "^1.0.0" }, "optionalPeers": ["unrs-resolver"] }, "sha512-K9Hb+yRaGAGUbwjhFNHvSmmkZs9+zbuoe3kFQ4V1wYjrepUFYM2dZAfNtjbbj3qsPfUfsA68Bx/ICWQMi+C8Eg=="], + "eslint-import-resolver-node": ["eslint-import-resolver-node@0.3.9", "", { "dependencies": { "debug": "^3.2.7", "is-core-module": "^2.13.0", "resolve": "^1.22.4" } }, "sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g=="], - "eslint-import-resolver-typescript": ["eslint-import-resolver-typescript@3.10.1", "", { "dependencies": { "@nolyfill/is-core-module": "1.0.39", "debug": "^4.4.0", "get-tsconfig": "^4.10.0", "is-bun-module": "^2.0.0", "stable-hash": "^0.0.5", "tinyglobby": "^0.2.13", "unrs-resolver": "^1.6.2" }, "peerDependencies": { "eslint": "*", "eslint-plugin-import": "*", "eslint-plugin-import-x": "*" }, "optionalPeers": ["eslint-plugin-import", "eslint-plugin-import-x"] }, "sha512-A1rHYb06zjMGAxdLSkN2fXPBwuSaQ0iO5M/hdyS0Ajj1VBaRp0sPD3dn1FhME3c/JluGFbwSxyCfqdSbtQLAHQ=="], + "eslint-import-resolver-typescript": ["eslint-import-resolver-typescript@4.4.4", "", { "dependencies": { "debug": "^4.4.1", "eslint-import-context": "^0.1.8", "get-tsconfig": "^4.10.1", "is-bun-module": "^2.0.0", "stable-hash-x": "^0.2.0", "tinyglobby": "^0.2.14", "unrs-resolver": "^1.7.11" }, "peerDependencies": { "eslint": "*", "eslint-plugin-import": "*", "eslint-plugin-import-x": "*" }, "optionalPeers": ["eslint-plugin-import", "eslint-plugin-import-x"] }, "sha512-1iM2zeBvrYmUNTj2vSC/90JTHDth+dfOfiNKkxApWRsTJYNrc8rOdxxIf5vazX+BiAXTeOT0UvWpGI/7qIWQOw=="], "eslint-mdx": ["eslint-mdx@3.6.2", "", { "dependencies": { "acorn": "^8.15.0", "acorn-jsx": "^5.3.2", "espree": "^9.6.1 || ^10.4.0", "estree-util-visit": "^2.0.0", "remark-mdx": "^3.1.0", "remark-parse": "^11.0.0", "remark-stringify": "^11.0.0", "synckit": "^0.11.8", "unified": "^11.0.5", "unified-engine": "^11.2.2", "unist-util-visit": "^5.0.0", "uvu": "^0.5.6", "vfile": "^6.0.3" }, "peerDependencies": { "eslint": ">=8.0.0", "remark-lint-file-extension": "*" }, "optionalPeers": ["remark-lint-file-extension"] }, "sha512-5hczn5iSSEcwtNtVXFwCKIk6iLEDaZpwc3vjYDl/B779OzaAAK/ou16J2xVdO6ecOLEO1WZqp7MRCQ/WsKDUig=="], @@ -2626,7 +2629,7 @@ "picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="], - "picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], + "picomatch": ["picomatch@4.0.3", "", {}, "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q=="], "pidtree": ["pidtree@0.6.0", "", { "bin": { "pidtree": "bin/pidtree.js" } }, "sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g=="], @@ -2898,6 +2901,8 @@ "stable-hash": ["stable-hash@0.0.5", "", {}, "sha512-+L3ccpzibovGXFK+Ap/f8LOS0ahMrHTf3xu7mMLSpEGU0EO9ucaysSylKo9eRDFNhWve/y275iPmIZ4z39a9iA=="], + "stable-hash-x": ["stable-hash-x@0.2.0", "", {}, "sha512-o3yWv49B/o4QZk5ZcsALc6t0+eCelPc44zZsLtCQnZPDwFpDYSWcDnrv2TtMmMbQ7uKo3J0HTURCqckw23czNQ=="], + "stack-utils": ["stack-utils@2.0.6", "", { "dependencies": { "escape-string-regexp": "^2.0.0" } }, "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ=="], "statuses": ["statuses@2.0.2", "", {}, "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw=="], @@ -3424,6 +3429,8 @@ "ansi-escapes/type-fest": ["type-fest@0.21.3", "", {}, "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w=="], + "anymatch/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], + "babel-plugin-istanbul/istanbul-lib-instrument": ["istanbul-lib-instrument@5.2.1", "", { "dependencies": { "@babel/core": "^7.12.3", "@babel/parser": "^7.14.7", "@istanbuljs/schema": "^0.1.2", "istanbul-lib-coverage": "^3.2.0", "semver": "^6.3.0" } }, "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg=="], "bl/readable-stream": ["readable-stream@3.6.2", "", { "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", "util-deprecate": "^1.0.1" } }, "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA=="], @@ -3456,6 +3463,8 @@ "eslint/ignore": ["ignore@5.3.2", "", {}, "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g=="], + "eslint-config-next/eslint-import-resolver-typescript": ["eslint-import-resolver-typescript@3.10.1", "", { "dependencies": { "@nolyfill/is-core-module": "1.0.39", "debug": "^4.4.0", "get-tsconfig": "^4.10.0", "is-bun-module": "^2.0.0", "stable-hash": "^0.0.5", "tinyglobby": "^0.2.13", "unrs-resolver": "^1.6.2" }, "peerDependencies": { "eslint": "*", "eslint-plugin-import": "*", "eslint-plugin-import-x": "*" }, "optionalPeers": ["eslint-plugin-import", "eslint-plugin-import-x"] }, "sha512-A1rHYb06zjMGAxdLSkN2fXPBwuSaQ0iO5M/hdyS0Ajj1VBaRp0sPD3dn1FhME3c/JluGFbwSxyCfqdSbtQLAHQ=="], + "eslint-config-next/globals": ["globals@16.4.0", "", {}, "sha512-ob/2LcVVaVGCYN+r14cnwnoDPUufjiYgSqRhiFD0Q1iI4Odora5RE8Iv1D24hAz5oMophRGkGz+yuvQmmUMnMw=="], "eslint-import-resolver-node/debug": ["debug@3.2.7", "", { "dependencies": { "ms": "^2.1.1" } }, "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ=="], @@ -3474,8 +3483,6 @@ "fast-glob/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="], - "fdir/picomatch": ["picomatch@4.0.3", "", {}, "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q=="], - "filelist/minimatch": ["minimatch@5.1.6", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g=="], "fixpack/chalk": ["chalk@3.0.0", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg=="], @@ -3540,8 +3547,6 @@ "jest-snapshot/semver": ["semver@7.7.3", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q=="], - "jest-util/picomatch": ["picomatch@4.0.3", "", {}, "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q=="], - "jest-watcher/@types/node": ["@types/node@24.10.1", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-GNWcUTRBgIRJD5zj+Tq0fKOJ5XZajIiBroOF0yvj2bSU1WvNdYS/dn9UxwsujGW4JX06dnHyjV2y9rRaybH0iQ=="], "js-beautify/glob": ["glob@10.4.5", "", { "dependencies": { "foreground-child": "^3.1.0", "jackspeak": "^3.1.2", "minimatch": "^9.0.4", "minipass": "^7.1.2", "package-json-from-dist": "^1.0.0", "path-scurry": "^1.11.1" }, "bin": { "glob": "dist/esm/bin.mjs" } }, "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg=="], @@ -3568,6 +3573,8 @@ "make-dir/semver": ["semver@7.7.3", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q=="], + "micromatch/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], + "mjml-cli/chokidar": ["chokidar@3.6.0", "", { "dependencies": { "anymatch": "~3.1.2", "braces": "~3.0.2", "glob-parent": "~5.1.2", "is-binary-path": "~2.1.0", "is-glob": "~4.0.1", "normalize-path": "~3.0.0", "readdirp": "~3.6.0" }, "optionalDependencies": { "fsevents": "~2.3.2" } }, "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw=="], "mjml-cli/glob": ["glob@10.4.5", "", { "dependencies": { "foreground-child": "^3.1.0", "jackspeak": "^3.1.2", "minimatch": "^9.0.4", "minipass": "^7.1.2", "package-json-from-dist": "^1.0.0", "path-scurry": "^1.11.1" }, "bin": { "glob": "dist/esm/bin.mjs" } }, "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg=="], @@ -3968,8 +3975,6 @@ "test-exclude/glob": ["glob@7.1.7", "", { "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", "minimatch": "^3.0.4", "once": "^1.3.0", "path-is-absolute": "^1.0.0" } }, "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ=="], - "tinyglobby/picomatch": ["picomatch@4.0.3", "", {}, "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q=="], - "ts-jest/semver": ["semver@7.7.3", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q=="], "ts-loader/semver": ["semver@7.7.3", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q=="], @@ -4066,6 +4071,8 @@ "@jest/transform/jest-util/ci-info": ["ci-info@3.9.0", "", {}, "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ=="], + "@jest/transform/jest-util/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], + "@nbw/backend/@types/bun/bun-types": ["bun-types@1.3.5", "", { "dependencies": { "@types/node": "*" } }, "sha512-inmAYe2PFLs0SUbFOWSVD24sg1jFlMPxOjOSSCYqUgn4Hsc3rDc7dFvfVYjFPNHtov6kgUeulV4SxbuIV/stPw=="], "@nbw/backend/@types/node/undici-types": ["undici-types@7.16.0", "", {}, "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw=="], @@ -4188,6 +4195,8 @@ "create-jest/jest-util/ci-info": ["ci-info@3.9.0", "", {}, "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ=="], + "create-jest/jest-util/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], + "editorconfig/minimatch/brace-expansion": ["brace-expansion@2.0.2", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ=="], "eslint-plugin-import/tsconfig-paths/json5": ["json5@1.0.2", "", { "dependencies": { "minimist": "^1.2.0" }, "bin": { "json5": "lib/cli.js" } }, "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA=="], @@ -4528,6 +4537,8 @@ "log-update/cli-cursor/restore-cursor/signal-exit": ["signal-exit@4.1.0", "", {}, "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw=="], + "mjml-cli/chokidar/readdirp/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], + "mjml-cli/glob/path-scurry/lru-cache": ["lru-cache@10.4.3", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="], "multer/type-is/mime-types/mime-db": ["mime-db@1.52.0", "", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="], @@ -4642,6 +4653,8 @@ "@nbw/thumbnail/jest/@jest/core/jest-snapshot/semver": ["semver@7.7.3", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q=="], + "@nbw/thumbnail/jest/@jest/core/jest-util/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], + "@nbw/thumbnail/jest/@jest/core/pretty-format/@jest/schemas": ["@jest/schemas@29.6.3", "", { "dependencies": { "@sinclair/typebox": "^0.27.8" } }, "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA=="], "@nbw/thumbnail/jest/@jest/core/pretty-format/ansi-styles": ["ansi-styles@5.2.0", "", {}, "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA=="], @@ -4672,6 +4685,8 @@ "@nbw/thumbnail/jest/jest-cli/jest-util/ci-info": ["ci-info@3.9.0", "", {}, "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ=="], + "@nbw/thumbnail/jest/jest-cli/jest-util/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], + "@nbw/thumbnail/jest/jest-cli/jest-validate/pretty-format": ["pretty-format@29.7.0", "", { "dependencies": { "@jest/schemas": "^29.6.3", "ansi-styles": "^5.0.0", "react-is": "^18.0.0" } }, "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ=="], "@nestjs/mongoose/mongoose/mongodb/mongodb-connection-string-url/@types/whatwg-url": ["@types/whatwg-url@8.2.2", "", { "dependencies": { "@types/node": "*", "@types/webidl-conversions": "*" } }, "sha512-FtQu10RWgn3D9U4aazdwIE2yzphmTJREDqNdODHrbrZmmMqI0vMheC/6NE/J1Yveaj8H+ela+YwWTjq5PGmuhA=="], diff --git a/package.json b/package.json index 1338fdd6..6d8e442f 100644 --- a/package.json +++ b/package.json @@ -82,6 +82,7 @@ "concurrently": "^9.2.1", "eslint": "^9.36.0", "eslint-config-prettier": "^8.10.2", + "eslint-import-resolver-typescript": "^4.4.4", "eslint-plugin-import": "^2.32.0", "eslint-plugin-mdx": "^3.6.2", "eslint-plugin-unused-imports": "^4.2.0", From 06dbb5c5dd56f709bb404aa9cf120d74126847e9 Mon Sep 17 00:00:00 2001 From: Bentroen <29354120+Bentroen@users.noreply.github.com> Date: Mon, 5 Jan 2026 19:52:36 -0300 Subject: [PATCH 18/37] chore: add separate TS config for ESLint - add `tsconfig.eslint.json` - unify projects under ESLint config - make all TS configs in monorepo visible to parser Result: ESLint treats the project as a single, cohesive unit rather than separate projects. --- eslint.config.mjs | 13 ++++++++----- tsconfig.eslint.json | 20 ++++++++++++++++++++ 2 files changed, 28 insertions(+), 5 deletions(-) create mode 100644 tsconfig.eslint.json diff --git a/eslint.config.mjs b/eslint.config.mjs index 688d6684..dedf562d 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -31,6 +31,13 @@ export default defineConfig( { files: ['**/*.{js,jsx,mjs,cjs,ts,tsx}'], languageOptions: { + parserOptions: { + project: [ + './tsconfig.base.json', + './packages/*/tsconfig.json', + './apps/*/tsconfig.json', + ], + }, globals: { ...globals.node, ...globals.es2021, ...globals.bun }, }, plugins: { @@ -40,11 +47,7 @@ export default defineConfig( settings: { 'import/resolver': { typescript: { - project: [ - 'apps/*/tsconfig.json', - 'packages/*/tsconfig.json', - './tsconfig.json', - ], + project: './tsconfig.eslint.json', }, node: true, }, diff --git a/tsconfig.eslint.json b/tsconfig.eslint.json new file mode 100644 index 00000000..a627c79a --- /dev/null +++ b/tsconfig.eslint.json @@ -0,0 +1,20 @@ +{ + "extends": "./tsconfig.base.json", + "compilerOptions": { + "noEmit": true, + "baseUrl": ".", + "paths": { + "@web/*": ["apps/frontend/src/*"], + "@server/*": ["apps/backend/src/*"] + } + }, + "references": [ + { "path": "apps/frontend" }, + { "path": "apps/backend" }, + { "path": "packages/configs" }, + { "path": "packages/database" }, + { "path": "packages/song" }, + { "path": "packages/sounds" }, + { "path": "packages/thumbnail" } + ] +} From 2b3c4a761eea61990603a01349c65ea6d121ef09 Mon Sep 17 00:00:00 2001 From: Bentroen <29354120+Bentroen@users.noreply.github.com> Date: Mon, 5 Jan 2026 19:53:32 -0300 Subject: [PATCH 19/37] chore: add `baseUrl` and `@nbw` path alias to root TS config --- tsconfig.base.json | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tsconfig.base.json b/tsconfig.base.json index 5f5450ea..18ce6011 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -4,6 +4,14 @@ // Actual emitting is controlled by extending configs (packages/apps). "outDir": "./dist", + // Project root for module resolution. + "baseUrl": ".", + + // Path mapping for monorepo packages. + "paths": { + "@nbw/*": ["packages/*"] + }, + // ============================== // Global type-safety invariant // ============================== From 9faf154c9fcbb931481fefe9c0f4151699d194aa Mon Sep 17 00:00:00 2001 From: Bentroen <29354120+Bentroen@users.noreply.github.com> Date: Mon, 5 Jan 2026 19:58:13 -0300 Subject: [PATCH 20/37] chore: ensure that orchestrator TS config doesn't emit types Prevents the file from being used for type generation at all. --- tsconfig.json | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tsconfig.json b/tsconfig.json index 49978a90..48dd729b 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -5,6 +5,9 @@ // This config does not type-check files directly. // Use `tsc -b` or `tsc -b --noEmit`. + "compilerOptions": { + "noEmit": true + }, "files": [], "references": [ { "path": "apps/frontend" }, From 79f2bf94f6289c62eea8a6914357367d53c4a4de Mon Sep 17 00:00:00 2001 From: Bentroen <29354120+Bentroen@users.noreply.github.com> Date: Tue, 6 Jan 2026 13:30:17 -0300 Subject: [PATCH 21/37] chore: remove `outDir` from base TS config It's arguable whether we should govern where each individual package's output directory from the base config. While it's okay to leave here as standardization, we oughtn't enforce this at the root level for every package, as exceptions may exist. We've removed this setting from the root in favor of defining it explicitly in each package, which is something reasonable to keep their own responsibility (even if it requires some duplication). --- tsconfig.base.json | 4 ---- tsconfig.package.json | 1 - 2 files changed, 5 deletions(-) diff --git a/tsconfig.base.json b/tsconfig.base.json index 18ce6011..b52ee70a 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -1,9 +1,5 @@ { "compilerOptions": { - // Default output directory. - // Actual emitting is controlled by extending configs (packages/apps). - "outDir": "./dist", - // Project root for module resolution. "baseUrl": ".", diff --git a/tsconfig.package.json b/tsconfig.package.json index 4fdac436..d4ac7340 100644 --- a/tsconfig.package.json +++ b/tsconfig.package.json @@ -12,7 +12,6 @@ "noEmit": false, "emitDeclarationOnly": true, "declaration": true, - "outDir": "dist", // ============================== // Module system (bundler-friendly) From 8498c2f3ad4e950ee20ddc64f1dd3da7b85a7edb Mon Sep 17 00:00:00 2001 From: Bentroen <29354120+Bentroen@users.noreply.github.com> Date: Mon, 5 Jan 2026 19:59:30 -0300 Subject: [PATCH 22/37] fix: add `baseUrl` to app TS configs --- apps/backend/tsconfig.json | 3 ++- apps/frontend/tsconfig.json | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/apps/backend/tsconfig.json b/apps/backend/tsconfig.json index be7af5a2..0a35d985 100644 --- a/apps/backend/tsconfig.json +++ b/apps/backend/tsconfig.json @@ -14,8 +14,9 @@ "emitDecoratorMetadata": true, // Path mapping + "baseUrl": ".", "paths": { - "@server/*": ["./src/*"] + "@server/*": ["src/*"] } }, "include": ["src", "scripts"] diff --git a/apps/frontend/tsconfig.json b/apps/frontend/tsconfig.json index fe8b9aea..3780e8be 100644 --- a/apps/frontend/tsconfig.json +++ b/apps/frontend/tsconfig.json @@ -30,8 +30,9 @@ "types": ["mdx"], // Path mapping + "baseUrl": ".", "paths": { - "@web/*": ["./src/*"] + "@web/*": ["src/*"] } }, "include": [ From f85b959d560f3afb0fbaecbb6b8b881d2303d003 Mon Sep 17 00:00:00 2001 From: Bentroen <29354120+Bentroen@users.noreply.github.com> Date: Mon, 5 Jan 2026 20:07:34 -0300 Subject: [PATCH 23/37] fix: ensure types are emitted in `configs` package through `rootDir` and `outDir` --- packages/configs/tsconfig.json | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/packages/configs/tsconfig.json b/packages/configs/tsconfig.json index 7c179325..e7c50a5e 100644 --- a/packages/configs/tsconfig.json +++ b/packages/configs/tsconfig.json @@ -1,4 +1,11 @@ { "extends": "../../tsconfig.package.json", - "include": ["src"] + "compilerOptions": { + // Explicit rootDir ensures that TypeScript emits declaration files. + // Required for small or reference-only packages where TS cannot + // infer a stable project root (the case for this 'config' package). + "rootDir": "src", + "outDir": "dist" + }, + "include": ["src/**/*.ts"] } From ab269fc9f2902f44e1a42a58784b199f481549bb Mon Sep 17 00:00:00 2001 From: Bentroen <29354120+Bentroen@users.noreply.github.com> Date: Mon, 5 Jan 2026 20:09:00 -0300 Subject: [PATCH 24/37] fix: enforce `src -> dist` output structure for all packages --- packages/database/tsconfig.json | 6 +++++- packages/song/tsconfig.json | 6 +++++- packages/sounds/tsconfig.json | 6 +++++- packages/thumbnail/tsconfig.json | 6 +++++- 4 files changed, 20 insertions(+), 4 deletions(-) diff --git a/packages/database/tsconfig.json b/packages/database/tsconfig.json index 42d932fb..c2e8e762 100644 --- a/packages/database/tsconfig.json +++ b/packages/database/tsconfig.json @@ -1,7 +1,11 @@ { "extends": "../../tsconfig.package.json", "compilerOptions": { + "rootDir": "src", + "outDir": "dist", + // Database-specific settings "emitDecoratorMetadata": true // Necessary for class-validator - } + }, + "include": ["src/**/*.ts"] } diff --git a/packages/song/tsconfig.json b/packages/song/tsconfig.json index 7c179325..e83ed4d3 100644 --- a/packages/song/tsconfig.json +++ b/packages/song/tsconfig.json @@ -1,4 +1,8 @@ { "extends": "../../tsconfig.package.json", - "include": ["src"] + "compilerOptions": { + "rootDir": "src", + "outDir": "dist" + }, + "include": ["src/**/*.ts"] } diff --git a/packages/sounds/tsconfig.json b/packages/sounds/tsconfig.json index 7c179325..e83ed4d3 100644 --- a/packages/sounds/tsconfig.json +++ b/packages/sounds/tsconfig.json @@ -1,4 +1,8 @@ { "extends": "../../tsconfig.package.json", - "include": ["src"] + "compilerOptions": { + "rootDir": "src", + "outDir": "dist" + }, + "include": ["src/**/*.ts"] } diff --git a/packages/thumbnail/tsconfig.json b/packages/thumbnail/tsconfig.json index 168733fc..0fec8883 100644 --- a/packages/thumbnail/tsconfig.json +++ b/packages/thumbnail/tsconfig.json @@ -1,7 +1,11 @@ { "extends": "../../tsconfig.package.json", "compilerOptions": { + "rootDir": "src", + "outDir": "dist", + // DOM support for canvas operations "lib": ["dom", "dom.iterable", "esnext"] - } + }, + "include": ["src/**/*.ts"] } From 4eab8c956f47a0d81b53ad388379735b5a08a8e5 Mon Sep 17 00:00:00 2001 From: Bentroen <29354120+Bentroen@users.noreply.github.com> Date: Tue, 6 Jan 2026 12:50:44 -0300 Subject: [PATCH 25/37] build: re-introduce separate `js` and `types` steps in database build The `database` package is the one exception where builds aren't handled by our bundler (bun), but by tsc. In every other case, tsc only acts as a type genenerator. Here, we must open an exception to make sure the project builds correctly. But instead of using command-line flags to enable or disable emit, we explicitly configure different TS projects fitting each stage's requirements. --- packages/database/package.json | 4 ++-- packages/database/tsconfig.build.json | 21 +++++++++++++++++++++ packages/database/tsconfig.json | 4 ++-- packages/database/tsconfig.types.json | 8 ++++++++ 4 files changed, 33 insertions(+), 4 deletions(-) create mode 100644 packages/database/tsconfig.build.json create mode 100644 packages/database/tsconfig.types.json diff --git a/packages/database/package.json b/packages/database/package.json index 6b09960d..61e5d06f 100644 --- a/packages/database/package.json +++ b/packages/database/package.json @@ -17,8 +17,8 @@ }, "scripts": { "build": "bun run clean && bun run build:js && bun run build:types", - "build:js": "tsc --project tsconfig.build.json --declaration false --emitDeclarationOnly false", - "build:types": "tsc --project tsconfig.build.json --emitDeclarationOnly", + "build:js": "tsc --project tsconfig.build.json", + "build:types": "tsc --project tsconfig.types.json", "clean": "rm -rf dist", "dev": "tsc --project tsconfig.build.json --watch", "lint": "eslint \"src/**/*.ts\" --fix", diff --git a/packages/database/tsconfig.build.json b/packages/database/tsconfig.build.json new file mode 100644 index 00000000..0fc0958a --- /dev/null +++ b/packages/database/tsconfig.build.json @@ -0,0 +1,21 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + // Unlike other packages where we use a bundler to output JS, + // this package uses tsc for its build step. + // We must disable the default 'composite' config to output JS. + "composite": false, + "declaration": false, + "emitDeclarationOnly": false, + + // Module target for Node runtime + "module": "CommonJS", + "moduleResolution": "node", + "target": "ES2021", + + // Allow ES imports + "verbatimModuleSyntax": false + }, + "include": ["src/**/*"], + "exclude": ["**/*.spec.ts", "**/*.test.ts"] +} diff --git a/packages/database/tsconfig.json b/packages/database/tsconfig.json index c2e8e762..0a4d9be7 100644 --- a/packages/database/tsconfig.json +++ b/packages/database/tsconfig.json @@ -4,8 +4,8 @@ "rootDir": "src", "outDir": "dist", - // Database-specific settings - "emitDecoratorMetadata": true // Necessary for class-validator + // Database runtime requirements + "emitDecoratorMetadata": true }, "include": ["src/**/*.ts"] } diff --git a/packages/database/tsconfig.types.json b/packages/database/tsconfig.types.json new file mode 100644 index 00000000..be175bda --- /dev/null +++ b/packages/database/tsconfig.types.json @@ -0,0 +1,8 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + // Emit-only configuration for building types. + "declaration": true, + "emitDeclarationOnly": true + } +} From 774326d51b80f907029bd0f0794c8e101b97fed9 Mon Sep 17 00:00:00 2001 From: Bentroen <29354120+Bentroen@users.noreply.github.com> Date: Tue, 6 Jan 2026 13:16:03 -0300 Subject: [PATCH 26/37] chore: ensure TS build cache files are output to `dist` `tsconfig.tsbuildinfo` files were being emitted near `tsconfig.json` instead of `dist/`, which creates pollution and doesn't delete the files when the compilation must restart from scratch. See: https://www.typescriptlang.org/tsconfig/#tsBuildInfoFile --- apps/backend/tsconfig.json | 2 ++ apps/frontend/tsconfig.json | 2 ++ packages/configs/tsconfig.json | 3 ++- packages/database/tsconfig.json | 1 + packages/song/tsconfig.json | 3 ++- packages/sounds/tsconfig.json | 3 ++- packages/thumbnail/tsconfig.json | 1 + 7 files changed, 12 insertions(+), 3 deletions(-) diff --git a/apps/backend/tsconfig.json b/apps/backend/tsconfig.json index 0a35d985..d0e12899 100644 --- a/apps/backend/tsconfig.json +++ b/apps/backend/tsconfig.json @@ -3,6 +3,8 @@ "compilerOptions": { // This is a referenced package "composite": true, + // Store build cache files in a non-source location + "tsBuildInfoFile": "dist/.tsbuildinfo", // NestJS specific settings "module": "commonjs", diff --git a/apps/frontend/tsconfig.json b/apps/frontend/tsconfig.json index 3780e8be..c851d984 100644 --- a/apps/frontend/tsconfig.json +++ b/apps/frontend/tsconfig.json @@ -3,6 +3,8 @@ "compilerOptions": { // This is a referenced package "composite": true, + // Store build cache files in a non-source location + "tsBuildInfoFile": "dist/.tsbuildinfo", // Next.js specific settings "target": "es5", diff --git a/packages/configs/tsconfig.json b/packages/configs/tsconfig.json index e7c50a5e..89fd5895 100644 --- a/packages/configs/tsconfig.json +++ b/packages/configs/tsconfig.json @@ -5,7 +5,8 @@ // Required for small or reference-only packages where TS cannot // infer a stable project root (the case for this 'config' package). "rootDir": "src", - "outDir": "dist" + "outDir": "dist", + "tsBuildInfoFile": "dist/.tsbuildinfo" }, "include": ["src/**/*.ts"] } diff --git a/packages/database/tsconfig.json b/packages/database/tsconfig.json index 0a4d9be7..9547fe19 100644 --- a/packages/database/tsconfig.json +++ b/packages/database/tsconfig.json @@ -3,6 +3,7 @@ "compilerOptions": { "rootDir": "src", "outDir": "dist", + "tsBuildInfoFile": "dist/.tsbuildinfo", // Database runtime requirements "emitDecoratorMetadata": true diff --git a/packages/song/tsconfig.json b/packages/song/tsconfig.json index e83ed4d3..5273baea 100644 --- a/packages/song/tsconfig.json +++ b/packages/song/tsconfig.json @@ -2,7 +2,8 @@ "extends": "../../tsconfig.package.json", "compilerOptions": { "rootDir": "src", - "outDir": "dist" + "outDir": "dist", + "tsBuildInfoFile": "dist/.tsbuildinfo" }, "include": ["src/**/*.ts"] } diff --git a/packages/sounds/tsconfig.json b/packages/sounds/tsconfig.json index e83ed4d3..5273baea 100644 --- a/packages/sounds/tsconfig.json +++ b/packages/sounds/tsconfig.json @@ -2,7 +2,8 @@ "extends": "../../tsconfig.package.json", "compilerOptions": { "rootDir": "src", - "outDir": "dist" + "outDir": "dist", + "tsBuildInfoFile": "dist/.tsbuildinfo" }, "include": ["src/**/*.ts"] } diff --git a/packages/thumbnail/tsconfig.json b/packages/thumbnail/tsconfig.json index 0fec8883..be7e6f6d 100644 --- a/packages/thumbnail/tsconfig.json +++ b/packages/thumbnail/tsconfig.json @@ -3,6 +3,7 @@ "compilerOptions": { "rootDir": "src", "outDir": "dist", + "tsBuildInfoFile": "dist/.tsbuildinfo", // DOM support for canvas operations "lib": ["dom", "dom.iterable", "esnext"] From d148680fced082bd9d52a452bc7ddaebead1926a Mon Sep 17 00:00:00 2001 From: Bentroen <29354120+Bentroen@users.noreply.github.com> Date: Tue, 6 Jan 2026 13:26:00 -0300 Subject: [PATCH 27/37] chore: let TS infer build cache output location as `dist/` In [1], we have: 2. If rootDir and outDir are set, [...]. 3. If outDir is set, [...]. 3) is actually the behavior we want, and it comes by default (without needing to specify `tsBuildInfoFile` in the config) if we remove `rootDir` from the configuration. Then, what's going to happen, as per [2], is: "The inferred value for rootDir is the longest common path of all non-declaration input files [...]". So it resolves to `src` in our projects without us needing to be explicit! See: [1] https://www.typescriptlang.org/tsconfig/#tsBuildInfoFile [2] https://www.typescriptlang.org/tsconfig/#rootDir --- apps/backend/tsconfig.json | 3 --- apps/frontend/tsconfig.json | 3 --- packages/configs/tsconfig.json | 7 +------ packages/database/tsconfig.json | 2 -- packages/song/tsconfig.json | 4 +--- packages/sounds/tsconfig.json | 4 +--- packages/thumbnail/tsconfig.json | 2 -- 7 files changed, 3 insertions(+), 22 deletions(-) diff --git a/apps/backend/tsconfig.json b/apps/backend/tsconfig.json index d0e12899..461171ea 100644 --- a/apps/backend/tsconfig.json +++ b/apps/backend/tsconfig.json @@ -3,9 +3,6 @@ "compilerOptions": { // This is a referenced package "composite": true, - // Store build cache files in a non-source location - "tsBuildInfoFile": "dist/.tsbuildinfo", - // NestJS specific settings "module": "commonjs", "target": "ES2021", diff --git a/apps/frontend/tsconfig.json b/apps/frontend/tsconfig.json index c851d984..50bec94e 100644 --- a/apps/frontend/tsconfig.json +++ b/apps/frontend/tsconfig.json @@ -3,9 +3,6 @@ "compilerOptions": { // This is a referenced package "composite": true, - // Store build cache files in a non-source location - "tsBuildInfoFile": "dist/.tsbuildinfo", - // Next.js specific settings "target": "es5", "lib": ["dom", "dom.iterable", "esnext"], diff --git a/packages/configs/tsconfig.json b/packages/configs/tsconfig.json index 89fd5895..50237165 100644 --- a/packages/configs/tsconfig.json +++ b/packages/configs/tsconfig.json @@ -1,12 +1,7 @@ { "extends": "../../tsconfig.package.json", "compilerOptions": { - // Explicit rootDir ensures that TypeScript emits declaration files. - // Required for small or reference-only packages where TS cannot - // infer a stable project root (the case for this 'config' package). - "rootDir": "src", - "outDir": "dist", - "tsBuildInfoFile": "dist/.tsbuildinfo" + "outDir": "dist" }, "include": ["src/**/*.ts"] } diff --git a/packages/database/tsconfig.json b/packages/database/tsconfig.json index 9547fe19..ba7946ba 100644 --- a/packages/database/tsconfig.json +++ b/packages/database/tsconfig.json @@ -1,9 +1,7 @@ { "extends": "../../tsconfig.package.json", "compilerOptions": { - "rootDir": "src", "outDir": "dist", - "tsBuildInfoFile": "dist/.tsbuildinfo", // Database runtime requirements "emitDecoratorMetadata": true diff --git a/packages/song/tsconfig.json b/packages/song/tsconfig.json index 5273baea..50237165 100644 --- a/packages/song/tsconfig.json +++ b/packages/song/tsconfig.json @@ -1,9 +1,7 @@ { "extends": "../../tsconfig.package.json", "compilerOptions": { - "rootDir": "src", - "outDir": "dist", - "tsBuildInfoFile": "dist/.tsbuildinfo" + "outDir": "dist" }, "include": ["src/**/*.ts"] } diff --git a/packages/sounds/tsconfig.json b/packages/sounds/tsconfig.json index 5273baea..50237165 100644 --- a/packages/sounds/tsconfig.json +++ b/packages/sounds/tsconfig.json @@ -1,9 +1,7 @@ { "extends": "../../tsconfig.package.json", "compilerOptions": { - "rootDir": "src", - "outDir": "dist", - "tsBuildInfoFile": "dist/.tsbuildinfo" + "outDir": "dist" }, "include": ["src/**/*.ts"] } diff --git a/packages/thumbnail/tsconfig.json b/packages/thumbnail/tsconfig.json index be7e6f6d..a3b1f8cd 100644 --- a/packages/thumbnail/tsconfig.json +++ b/packages/thumbnail/tsconfig.json @@ -1,9 +1,7 @@ { "extends": "../../tsconfig.package.json", "compilerOptions": { - "rootDir": "src", "outDir": "dist", - "tsBuildInfoFile": "dist/.tsbuildinfo", // DOM support for canvas operations "lib": ["dom", "dom.iterable", "esnext"] From 3dbd11f3fcae5438e268e75f771e1d7f4bb03360 Mon Sep 17 00:00:00 2001 From: Bentroen <29354120+Bentroen@users.noreply.github.com> Date: Tue, 6 Jan 2026 16:19:32 -0300 Subject: [PATCH 28/37] fix: incorrect import alias in `database` package --- packages/database/src/song/dto/UploadSongDto.dto.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/database/src/song/dto/UploadSongDto.dto.ts b/packages/database/src/song/dto/UploadSongDto.dto.ts index 5e9e3e08..8e973050 100644 --- a/packages/database/src/song/dto/UploadSongDto.dto.ts +++ b/packages/database/src/song/dto/UploadSongDto.dto.ts @@ -10,9 +10,10 @@ import { ValidateNested, } from 'class-validator'; -import type { SongDocument } from '@database/song/entity/song.entity'; import { UPLOAD_CONSTANTS } from '@nbw/config'; +import type { SongDocument } from '../../song/entity/song.entity'; + import { ThumbnailData } from './ThumbnailData.dto'; import type { CategoryType, LicenseType, VisibilityType } from './types'; From 0ca8c478f07ec2caf89dd8509e4fcb577f1c90d7 Mon Sep 17 00:00:00 2001 From: Bentroen <29354120+Bentroen@users.noreply.github.com> Date: Tue, 6 Jan 2026 16:20:09 -0300 Subject: [PATCH 29/37] fix: `thumbnail` package using removed `build:types` script --- packages/thumbnail/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/thumbnail/package.json b/packages/thumbnail/package.json index 55f73053..cd4b7ded 100644 --- a/packages/thumbnail/package.json +++ b/packages/thumbnail/package.json @@ -14,7 +14,7 @@ } }, "scripts": { - "build": "bun run clean && bun run build:browser && bun run build:node && bun run build:types", + "build": "bun run clean && bun run build:browser && bun run build:node && tsc -b", "build:browser": "bun build src/browser/index.ts --outdir dist/browser --target browser --external @napi-rs/canvas", "build:node": "bun build src/node/index.ts --outdir dist/node --target node", "clean": "rm -rf dist", From 5d94120af9cce96cdedd565c04b79fa10d476ce1 Mon Sep 17 00:00:00 2001 From: Bentroen <29354120+Bentroen@users.noreply.github.com> Date: Tue, 6 Jan 2026 17:02:54 -0300 Subject: [PATCH 30/37] chore: remove `composite` from frontend and backend packages "You should add `composite: true` to your `tsconfig.json` when creating a TypeScript library or shared component that will be referenced by other TypeScript projects, enabling faster, incremental builds and efficient dependency management in a monorepo setup, ensuring referenced projects are built first. It signals to the TypeScript compiler that this project outputs build information (`.tsbuildinfo`) for quick lookups and requires `declaration: true`." See: https://github.com/microsoft/TypeScript/issues/60465 https://stackoverflow.com/a/79383253/9045426 https://devblogs.microsoft.com/typescript/announcing-typescript-5-7-rc/#faster-project-ownership-checks-in-editors-for-composite-projects:~:text=every%20project%20that%20can%20be%20referenced%20by%20another%20(non%2Dworkspace)%20project%20must%20enable%20a%20flag%20called%20composite%2C --- apps/backend/tsconfig.json | 2 -- apps/frontend/tsconfig.json | 2 -- 2 files changed, 4 deletions(-) diff --git a/apps/backend/tsconfig.json b/apps/backend/tsconfig.json index 461171ea..400170a1 100644 --- a/apps/backend/tsconfig.json +++ b/apps/backend/tsconfig.json @@ -1,8 +1,6 @@ { "extends": "../../tsconfig.base.json", "compilerOptions": { - // This is a referenced package - "composite": true, // NestJS specific settings "module": "commonjs", "target": "ES2021", diff --git a/apps/frontend/tsconfig.json b/apps/frontend/tsconfig.json index 50bec94e..0b662962 100644 --- a/apps/frontend/tsconfig.json +++ b/apps/frontend/tsconfig.json @@ -1,8 +1,6 @@ { "extends": "../../tsconfig.base.json", "compilerOptions": { - // This is a referenced package - "composite": true, // Next.js specific settings "target": "es5", "lib": ["dom", "dom.iterable", "esnext"], From 5e5bf4a6139f4969daf06484ce617f80407d52ba Mon Sep 17 00:00:00 2001 From: Bentroen <29354120+Bentroen@users.noreply.github.com> Date: Sat, 10 Jan 2026 17:16:00 -0300 Subject: [PATCH 31/37] chore: re-add `rootDir` and `tsInfoBuildFile` on package projects --- packages/configs/tsconfig.json | 4 +++- packages/database/tsconfig.json | 2 ++ packages/song/tsconfig.json | 4 +++- packages/sounds/tsconfig.json | 4 +++- packages/thumbnail/tsconfig.json | 2 ++ 5 files changed, 13 insertions(+), 3 deletions(-) diff --git a/packages/configs/tsconfig.json b/packages/configs/tsconfig.json index 50237165..5273baea 100644 --- a/packages/configs/tsconfig.json +++ b/packages/configs/tsconfig.json @@ -1,7 +1,9 @@ { "extends": "../../tsconfig.package.json", "compilerOptions": { - "outDir": "dist" + "rootDir": "src", + "outDir": "dist", + "tsBuildInfoFile": "dist/.tsbuildinfo" }, "include": ["src/**/*.ts"] } diff --git a/packages/database/tsconfig.json b/packages/database/tsconfig.json index ba7946ba..9547fe19 100644 --- a/packages/database/tsconfig.json +++ b/packages/database/tsconfig.json @@ -1,7 +1,9 @@ { "extends": "../../tsconfig.package.json", "compilerOptions": { + "rootDir": "src", "outDir": "dist", + "tsBuildInfoFile": "dist/.tsbuildinfo", // Database runtime requirements "emitDecoratorMetadata": true diff --git a/packages/song/tsconfig.json b/packages/song/tsconfig.json index 50237165..5273baea 100644 --- a/packages/song/tsconfig.json +++ b/packages/song/tsconfig.json @@ -1,7 +1,9 @@ { "extends": "../../tsconfig.package.json", "compilerOptions": { - "outDir": "dist" + "rootDir": "src", + "outDir": "dist", + "tsBuildInfoFile": "dist/.tsbuildinfo" }, "include": ["src/**/*.ts"] } diff --git a/packages/sounds/tsconfig.json b/packages/sounds/tsconfig.json index 50237165..5273baea 100644 --- a/packages/sounds/tsconfig.json +++ b/packages/sounds/tsconfig.json @@ -1,7 +1,9 @@ { "extends": "../../tsconfig.package.json", "compilerOptions": { - "outDir": "dist" + "rootDir": "src", + "outDir": "dist", + "tsBuildInfoFile": "dist/.tsbuildinfo" }, "include": ["src/**/*.ts"] } diff --git a/packages/thumbnail/tsconfig.json b/packages/thumbnail/tsconfig.json index a3b1f8cd..be7e6f6d 100644 --- a/packages/thumbnail/tsconfig.json +++ b/packages/thumbnail/tsconfig.json @@ -1,7 +1,9 @@ { "extends": "../../tsconfig.package.json", "compilerOptions": { + "rootDir": "src", "outDir": "dist", + "tsBuildInfoFile": "dist/.tsbuildinfo", // DOM support for canvas operations "lib": ["dom", "dom.iterable", "esnext"] From 3914f6a29c15a5ab31fe2c92088a8bcaa61008cc Mon Sep 17 00:00:00 2001 From: tomast1337 Date: Sat, 10 Jan 2026 17:37:04 -0300 Subject: [PATCH 32/37] chore: refactor build scripts and improve TypeScript configuration - Updated build scripts in `configs` and `song` packages to streamline the build process using Bun. - Added external packages to the build configuration to prevent bundling of specific dependencies. - Enhanced TypeScript configuration by specifying `rootDir` for better output structure and organization. - Introduced a new build script in the `configs` package to handle cleaning, building, and fixing declaration files. - Updated `SongPreviewDto` to use a specific `VisibilityType` for improved type safety. --- apps/backend/scripts/build.ts | 22 ++-- apps/frontend/next.config.mjs | 5 +- packages/configs/package.json | 2 +- packages/configs/scripts/build.ts | 87 +++++++++++++ packages/configs/tsconfig.json | 3 +- .../database/src/song/dto/SongPreview.dto.ts | 4 +- packages/song/package.json | 3 +- packages/song/scripts/build.ts | 119 ++++++++++++++++++ packages/song/src/types.ts | 27 +++- packages/song/tsconfig.json | 3 +- 10 files changed, 257 insertions(+), 18 deletions(-) create mode 100644 packages/configs/scripts/build.ts create mode 100644 packages/song/scripts/build.ts diff --git a/apps/backend/scripts/build.ts b/apps/backend/scripts/build.ts index 5417739e..2dbb03c2 100644 --- a/apps/backend/scripts/build.ts +++ b/apps/backend/scripts/build.ts @@ -65,14 +65,20 @@ const build = async () => { target: 'bun', minify: false, sourcemap: 'linked', - external: optionalRequirePackages.filter((pkg) => { - try { - require(pkg); - return false; - } catch (_) { - return true; - } - }), + external: [ + ...optionalRequirePackages.filter((pkg) => { + try { + require(pkg); + return false; + } catch (_) { + return true; + } + }), + '@nbw/config', + '@nbw/database', + '@nbw/song', + '@nbw/sounds', + ], splitting: true, }); diff --git a/apps/frontend/next.config.mjs b/apps/frontend/next.config.mjs index cdcf10ed..46a3c3fd 100644 --- a/apps/frontend/next.config.mjs +++ b/apps/frontend/next.config.mjs @@ -4,6 +4,8 @@ import createMDX from '@next/mdx'; const nextConfig = { pageExtensions: ['js', 'jsx', 'md', 'mdx', 'ts', 'tsx'], + // Externalize packages that use Node.js built-in modules for server components + serverExternalPackages: ['@nbw/database', '@nbw/config'], // See: https://github.com/Automattic/node-canvas/issues/867#issuecomment-1925284985 webpack: (config, { isServer }) => { config.externals.push({ @@ -12,8 +14,9 @@ const nextConfig = { // Prevent @nbw/thumbnail from being bundled on the server // It uses HTMLCanvasElement which is not available in Node.js + // Also externalize backend packages that use Node.js modules if (isServer) { - config.externals.push('@nbw/thumbnail'); + config.externals.push('@nbw/thumbnail', '@nbw/database'); } return config; diff --git a/packages/configs/package.json b/packages/configs/package.json index 8d0d3e69..26f7f0b5 100644 --- a/packages/configs/package.json +++ b/packages/configs/package.json @@ -13,7 +13,7 @@ } }, "scripts": { - "build": "bun run clean && bun build src/index.ts --outdir dist --target node && tsc -b", + "build": "bun run scripts/build.ts", "clean": "rm -rf dist", "dev": "bun build src/index.ts --outdir dist --target node --watch", "lint": "eslint \"src/**/*.ts\" --fix", diff --git a/packages/configs/scripts/build.ts b/packages/configs/scripts/build.ts new file mode 100644 index 00000000..b0444cc1 --- /dev/null +++ b/packages/configs/scripts/build.ts @@ -0,0 +1,87 @@ +import { $ } from 'bun'; +import { existsSync, readdirSync, renameSync, rmSync } from 'fs'; +import { join } from 'path'; + +// When running via "bun run scripts/build.ts", process.cwd() is the package root +const packageRoot = process.cwd(); + +async function clean() { + const distPath = join(packageRoot, 'dist'); + if (existsSync(distPath)) { + rmSync(distPath, { recursive: true, force: true }); + } +} + +async function buildWithBun() { + const result = await Bun.build({ + entrypoints: [join(packageRoot, 'src', 'index.ts')], + outdir: join(packageRoot, 'dist'), + target: 'node', + }); + + if (!result.success) { + console.error('Bun build failed:', result.logs); + process.exit(1); + } + + console.log('Bun build completed successfully'); +} + +async function buildTypes() { + // Change to package root directory for tsc command + const result = await $`cd ${packageRoot} && tsc -b`.quiet(); + + if (result.exitCode !== 0) { + console.error('TypeScript build failed'); + process.exit(1); + } + + console.log('TypeScript declaration files generated'); +} + +function fixDeclarationFiles() { + const distSrcPath = join(packageRoot, 'dist', 'src'); + const distPath = join(packageRoot, 'dist'); + + if (existsSync(distSrcPath)) { + const files = readdirSync(distSrcPath); + + // Move all .d.ts files from dist/src to dist + for (const file of files) { + if (file.endsWith('.d.ts')) { + const srcFile = join(distSrcPath, file); + const destFile = join(distPath, file); + renameSync(srcFile, destFile); + console.log(`Moved ${file} to dist/`); + } + } + + // Remove the now-empty dist/src directory + try { + rmSync(distSrcPath, { recursive: true, force: true }); + } catch (error) { + // Ignore errors if directory is not empty or doesn't exist + } + } +} + +async function build() { + console.log('Cleaning dist directory...'); + await clean(); + + console.log('Building with Bun...'); + await buildWithBun(); + + console.log('Building TypeScript declaration files...'); + await buildTypes(); + + console.log('Fixing declaration file locations...'); + fixDeclarationFiles(); + + console.log('Build completed successfully!'); +} + +build().catch((error) => { + console.error('Build failed:', error); + process.exit(1); +}); diff --git a/packages/configs/tsconfig.json b/packages/configs/tsconfig.json index 50237165..a80d76b2 100644 --- a/packages/configs/tsconfig.json +++ b/packages/configs/tsconfig.json @@ -1,7 +1,8 @@ { "extends": "../../tsconfig.package.json", "compilerOptions": { - "outDir": "dist" + "outDir": "dist", + "rootDir": "src" }, "include": ["src/**/*.ts"] } diff --git a/packages/database/src/song/dto/SongPreview.dto.ts b/packages/database/src/song/dto/SongPreview.dto.ts index 58bdc344..38ce760a 100644 --- a/packages/database/src/song/dto/SongPreview.dto.ts +++ b/packages/database/src/song/dto/SongPreview.dto.ts @@ -2,6 +2,8 @@ import { IsNotEmpty, IsString, IsUrl, MaxLength } from 'class-validator'; import type { SongWithUser } from '../../song/entity/song.entity'; +import type { VisibilityType } from './types'; + type SongPreviewUploader = { username: string; profileImage: string; @@ -50,7 +52,7 @@ export class SongPreviewDto { @IsNotEmpty() @IsString() - visibility: string; + visibility: VisibilityType; constructor(partial: Partial) { Object.assign(this, partial); diff --git a/packages/song/package.json b/packages/song/package.json index 84a3e03c..612ae51e 100644 --- a/packages/song/package.json +++ b/packages/song/package.json @@ -15,7 +15,7 @@ } }, "scripts": { - "build": "bun run clean && bun run build:node && bun run build:browser && tsc -b", + "build": "bun run scripts/build.ts", "build:node": "bun build src/index.ts --outdir dist --target node && mv dist/index.js dist/index.node.js", "build:browser": "bun build src/index.ts --outdir dist --target browser && mv dist/index.js dist/index.browser.js", "clean": "rm -rf dist", @@ -30,7 +30,6 @@ }, "dependencies": { "@encode42/nbs.js": "^5.0.2", - "@nbw/database": "workspace:*", "@timohausmann/quadtree-ts": "^2.2.2", "jszip": "^3.10.1", "unidecode": "^1.1.0" diff --git a/packages/song/scripts/build.ts b/packages/song/scripts/build.ts new file mode 100644 index 00000000..c3eb47a9 --- /dev/null +++ b/packages/song/scripts/build.ts @@ -0,0 +1,119 @@ +import { $ } from 'bun'; +import { existsSync, readdirSync, renameSync, rmSync } from 'fs'; +import { join } from 'path'; + +// When running via "bun run scripts/build.ts", process.cwd() is the package root +const packageRoot = process.cwd(); + +async function clean() { + const distPath = join(packageRoot, 'dist'); + if (existsSync(distPath)) { + rmSync(distPath, { recursive: true, force: true }); + } +} + +async function buildNode() { + const result = await Bun.build({ + entrypoints: [join(packageRoot, 'src', 'index.ts')], + outdir: join(packageRoot, 'dist'), + target: 'node', + }); + + if (!result.success) { + console.error('Bun build (node) failed:', result.logs); + process.exit(1); + } + + // Rename index.js to index.node.js + const indexJs = join(packageRoot, 'dist', 'index.js'); + const indexNodeJs = join(packageRoot, 'dist', 'index.node.js'); + if (existsSync(indexJs)) { + renameSync(indexJs, indexNodeJs); + } + + console.log('Bun build (node) completed successfully'); +} + +async function buildBrowser() { + const result = await Bun.build({ + entrypoints: [join(packageRoot, 'src', 'index.ts')], + outdir: join(packageRoot, 'dist'), + target: 'browser', + }); + + if (!result.success) { + console.error('Bun build (browser) failed:', result.logs); + process.exit(1); + } + + // Rename index.js to index.browser.js + const indexJs = join(packageRoot, 'dist', 'index.js'); + const indexBrowserJs = join(packageRoot, 'dist', 'index.browser.js'); + if (existsSync(indexJs)) { + renameSync(indexJs, indexBrowserJs); + } + + console.log('Bun build (browser) completed successfully'); +} + +async function buildTypes() { + // Change to package root directory for tsc command + const result = await $`cd ${packageRoot} && tsc -b`.quiet(); + + if (result.exitCode !== 0) { + console.error('TypeScript build failed'); + process.exit(1); + } + + console.log('TypeScript declaration files generated'); +} + +function fixDeclarationFiles() { + const distSrcPath = join(packageRoot, 'dist', 'src'); + const distPath = join(packageRoot, 'dist'); + + if (existsSync(distSrcPath)) { + const files = readdirSync(distSrcPath); + + // Move all .d.ts files from dist/src to dist + for (const file of files) { + if (file.endsWith('.d.ts')) { + const srcFile = join(distSrcPath, file); + const destFile = join(distPath, file); + renameSync(srcFile, destFile); + console.log(`Moved ${file} to dist/`); + } + } + + // Remove the now-empty dist/src directory + try { + rmSync(distSrcPath, { recursive: true, force: true }); + } catch (error) { + // Ignore errors if directory is not empty or doesn't exist + } + } +} + +async function build() { + console.log('Cleaning dist directory...'); + await clean(); + + console.log('Building with Bun (node target)...'); + await buildNode(); + + console.log('Building with Bun (browser target)...'); + await buildBrowser(); + + console.log('Building TypeScript declaration files...'); + await buildTypes(); + + console.log('Fixing declaration file locations...'); + fixDeclarationFiles(); + + console.log('Build completed successfully!'); +} + +build().catch((error) => { + console.error('Build failed:', error); + process.exit(1); +}); diff --git a/packages/song/src/types.ts b/packages/song/src/types.ts index 1de71613..19dc4bbe 100644 --- a/packages/song/src/types.ts +++ b/packages/song/src/types.ts @@ -1,5 +1,3 @@ -import { SongStats } from '@nbw/database'; - import { NoteQuadTree } from './notes'; export type SongFileType = { @@ -30,4 +28,27 @@ export type Instrument = { count: number; }; -export type SongStatsType = InstanceType; +// Type definition matching SongStats from @nbw/database +// Defined here to avoid pulling in backend dependencies +export type SongStatsType = { + midiFileName: string; + noteCount: number; + tickCount: number; + layerCount: number; + tempo: number; + tempoRange: number[] | null; + timeSignature: number; + duration: number; + loop: boolean; + loopStartTick: number; + minutesSpent: number; + vanillaInstrumentCount: number; + customInstrumentCount: number; + firstCustomInstrumentIndex: number; + outOfRangeNoteCount: number; + detunedNoteCount: number; + customInstrumentNoteCount: number; + incompatibleNoteCount: number; + compatible: boolean; + instrumentNoteCounts: number[]; +}; diff --git a/packages/song/tsconfig.json b/packages/song/tsconfig.json index 50237165..a80d76b2 100644 --- a/packages/song/tsconfig.json +++ b/packages/song/tsconfig.json @@ -1,7 +1,8 @@ { "extends": "../../tsconfig.package.json", "compilerOptions": { - "outDir": "dist" + "outDir": "dist", + "rootDir": "src" }, "include": ["src/**/*.ts"] } From 9c75b72f229660c2af91dcaf05869fc500cdc3ca Mon Sep 17 00:00:00 2001 From: tomast1337 Date: Sat, 10 Jan 2026 17:54:41 -0300 Subject: [PATCH 33/37] refactor: update imports to use 'type' for TypeScript types in various components --- apps/frontend/src/app/(content)/search/page.tsx | 2 +- apps/frontend/src/app/(content)/song/[id]/page.tsx | 2 +- apps/frontend/src/modules/browse/components/SongCard.tsx | 2 +- .../browse/components/client/context/FeaturedSongs.context.tsx | 2 +- .../browse/components/client/context/HomePage.context.tsx | 2 +- .../browse/components/client/context/RecentSongs.context.tsx | 2 +- .../frontend/src/modules/my-songs/components/client/SongRow.tsx | 2 +- .../src/modules/shared/components/layout/RandomSongButton.tsx | 2 +- .../src/modules/song-edit/components/client/EditSongPage.tsx | 2 +- .../src/modules/song/components/client/DownloadSongModal.tsx | 2 +- apps/frontend/src/modules/song/components/client/SongCanvas.tsx | 2 +- 11 files changed, 11 insertions(+), 11 deletions(-) diff --git a/apps/frontend/src/app/(content)/search/page.tsx b/apps/frontend/src/app/(content)/search/page.tsx index 91ed2c49..de4388ee 100644 --- a/apps/frontend/src/app/(content)/search/page.tsx +++ b/apps/frontend/src/app/(content)/search/page.tsx @@ -17,7 +17,7 @@ import Skeleton from 'react-loading-skeleton'; import { create } from 'zustand'; import { UPLOAD_CONSTANTS, SEARCH_FEATURES, INSTRUMENTS } from '@nbw/config'; -import { SongPreviewDtoType } from '@nbw/database'; +import type { SongPreviewDtoType } from '@nbw/database'; import axiosInstance from '@web/lib/axios'; import LoadMoreButton from '@web/modules/browse/components/client/LoadMoreButton'; import SongCard from '@web/modules/browse/components/SongCard'; diff --git a/apps/frontend/src/app/(content)/song/[id]/page.tsx b/apps/frontend/src/app/(content)/song/[id]/page.tsx index 44d852c4..75a489d8 100644 --- a/apps/frontend/src/app/(content)/song/[id]/page.tsx +++ b/apps/frontend/src/app/(content)/song/[id]/page.tsx @@ -1,7 +1,7 @@ import type { Metadata } from 'next'; import { cookies } from 'next/headers'; -import { SongViewDtoType } from '@nbw/database'; +import type { SongViewDtoType } from '@nbw/database'; import axios from '@web/lib/axios'; import { SongPage } from '@web/modules/song/components/SongPage'; diff --git a/apps/frontend/src/modules/browse/components/SongCard.tsx b/apps/frontend/src/modules/browse/components/SongCard.tsx index 33e62bfe..e2e3d4ac 100644 --- a/apps/frontend/src/modules/browse/components/SongCard.tsx +++ b/apps/frontend/src/modules/browse/components/SongCard.tsx @@ -5,7 +5,7 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import Link from 'next/link'; import Skeleton from 'react-loading-skeleton'; -import { SongPreviewDtoType } from '@nbw/database'; +import type { SongPreviewDtoType } from '@nbw/database'; import { formatDuration, formatTimeAgo } from '@web/modules/shared/util/format'; import SongThumbnail from '../../shared/components/layout/SongThumbnail'; diff --git a/apps/frontend/src/modules/browse/components/client/context/FeaturedSongs.context.tsx b/apps/frontend/src/modules/browse/components/client/context/FeaturedSongs.context.tsx index c61d116e..f9dda42b 100644 --- a/apps/frontend/src/modules/browse/components/client/context/FeaturedSongs.context.tsx +++ b/apps/frontend/src/modules/browse/components/client/context/FeaturedSongs.context.tsx @@ -4,7 +4,7 @@ import { useEffect } from 'react'; import { create } from 'zustand'; import { TIMESPANS } from '@nbw/config'; -import { type FeaturedSongsDto, type SongPreviewDto } from '@nbw/database'; +import type { FeaturedSongsDto, SongPreviewDto } from '@nbw/database'; type TimespanType = (typeof TIMESPANS)[number]; diff --git a/apps/frontend/src/modules/browse/components/client/context/HomePage.context.tsx b/apps/frontend/src/modules/browse/components/client/context/HomePage.context.tsx index f16a9346..fa0ec87b 100644 --- a/apps/frontend/src/modules/browse/components/client/context/HomePage.context.tsx +++ b/apps/frontend/src/modules/browse/components/client/context/HomePage.context.tsx @@ -1,6 +1,6 @@ 'use client'; -import { FeaturedSongsDtoType, SongPreviewDtoType } from '@nbw/database'; +import type { FeaturedSongsDtoType, SongPreviewDtoType } from '@nbw/database'; import { FeaturedSongsProvider, diff --git a/apps/frontend/src/modules/browse/components/client/context/RecentSongs.context.tsx b/apps/frontend/src/modules/browse/components/client/context/RecentSongs.context.tsx index dfb02318..8e5efd6d 100644 --- a/apps/frontend/src/modules/browse/components/client/context/RecentSongs.context.tsx +++ b/apps/frontend/src/modules/browse/components/client/context/RecentSongs.context.tsx @@ -3,7 +3,7 @@ import { useEffect } from 'react'; import { create } from 'zustand'; -import { PageDto, SongPreviewDtoType } from '@nbw/database'; +import type { PageDto, SongPreviewDtoType } from '@nbw/database'; import axiosInstance from '@web/lib/axios'; interface RecentSongsState { diff --git a/apps/frontend/src/modules/my-songs/components/client/SongRow.tsx b/apps/frontend/src/modules/my-songs/components/client/SongRow.tsx index a875b484..418e8f38 100644 --- a/apps/frontend/src/modules/my-songs/components/client/SongRow.tsx +++ b/apps/frontend/src/modules/my-songs/components/client/SongRow.tsx @@ -8,7 +8,7 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import Link from 'next/link'; import Skeleton from 'react-loading-skeleton'; -import { SongPreviewDtoType } from '@nbw/database'; +import type { SongPreviewDtoType } from '@nbw/database'; import SongThumbnail from '@web/modules/shared/components/layout/SongThumbnail'; import { formatDuration } from '@web/modules/shared/util/format'; diff --git a/apps/frontend/src/modules/shared/components/layout/RandomSongButton.tsx b/apps/frontend/src/modules/shared/components/layout/RandomSongButton.tsx index 6eeff915..9d82e188 100644 --- a/apps/frontend/src/modules/shared/components/layout/RandomSongButton.tsx +++ b/apps/frontend/src/modules/shared/components/layout/RandomSongButton.tsx @@ -4,7 +4,7 @@ import { faDice } from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { useRouter } from 'next/navigation'; -import { PageDto, SongPreviewDto } from '@nbw/database'; +import type { PageDto, SongPreviewDto } from '@nbw/database'; import axios from '@web/lib/axios'; import { MusicalNote } from './MusicalNote'; diff --git a/apps/frontend/src/modules/song-edit/components/client/EditSongPage.tsx b/apps/frontend/src/modules/song-edit/components/client/EditSongPage.tsx index 36be5f6a..e6da180d 100644 --- a/apps/frontend/src/modules/song-edit/components/client/EditSongPage.tsx +++ b/apps/frontend/src/modules/song-edit/components/client/EditSongPage.tsx @@ -1,4 +1,4 @@ -import { UploadSongDtoType } from '@nbw/database'; +import type { UploadSongDtoType } from '@nbw/database'; import axiosInstance from '@web/lib/axios'; import { getTokenServer, diff --git a/apps/frontend/src/modules/song/components/client/DownloadSongModal.tsx b/apps/frontend/src/modules/song/components/client/DownloadSongModal.tsx index b6095ef4..b1b75efa 100644 --- a/apps/frontend/src/modules/song/components/client/DownloadSongModal.tsx +++ b/apps/frontend/src/modules/song/components/client/DownloadSongModal.tsx @@ -2,7 +2,7 @@ import { useState } from 'react'; -import { SongViewDtoType } from '@nbw/database'; +import type { SongViewDtoType } from '@nbw/database'; import { DownloadPopupAdSlot } from '@web/modules/shared/components/client/ads/AdSlots'; import GenericModal from '@web/modules/shared/components/client/GenericModal'; diff --git a/apps/frontend/src/modules/song/components/client/SongCanvas.tsx b/apps/frontend/src/modules/song/components/client/SongCanvas.tsx index 0deb67a0..07e34e6d 100644 --- a/apps/frontend/src/modules/song/components/client/SongCanvas.tsx +++ b/apps/frontend/src/modules/song/components/client/SongCanvas.tsx @@ -2,7 +2,7 @@ import { useEffect, useRef } from 'react'; -import { SongViewDtoType } from '@nbw/database'; +import type { SongViewDtoType } from '@nbw/database'; import axios from '@web/lib/axios'; export const SongCanvas = ({ song }: { song: SongViewDtoType }) => { From b323da581ecf3eef974a3d21a3438540edb4f14d Mon Sep 17 00:00:00 2001 From: tomast1337 Date: Sat, 10 Jan 2026 18:05:47 -0300 Subject: [PATCH 34/37] fix: BlogPageProps and HelpPageProps to use Promise for params --- apps/frontend/src/app/(content)/(info)/blog/[id]/page.tsx | 2 +- apps/frontend/src/app/(content)/(info)/help/[id]/page.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/frontend/src/app/(content)/(info)/blog/[id]/page.tsx b/apps/frontend/src/app/(content)/(info)/blog/[id]/page.tsx index cbc777ba..5773f799 100644 --- a/apps/frontend/src/app/(content)/(info)/blog/[id]/page.tsx +++ b/apps/frontend/src/app/(content)/(info)/blog/[id]/page.tsx @@ -7,7 +7,7 @@ import { PostType, getPostData } from '@web/lib/posts'; import { CustomMarkdown } from '@web/modules/shared/components/CustomMarkdown'; type BlogPageProps = { - params: { id: string }; + params: Promise<{ id: string }>; }; export async function generateMetadata({ diff --git a/apps/frontend/src/app/(content)/(info)/help/[id]/page.tsx b/apps/frontend/src/app/(content)/(info)/help/[id]/page.tsx index 5ace7da7..4f8ed867 100644 --- a/apps/frontend/src/app/(content)/(info)/help/[id]/page.tsx +++ b/apps/frontend/src/app/(content)/(info)/help/[id]/page.tsx @@ -7,7 +7,7 @@ import { PostType, getPostData } from '@web/lib/posts'; import { CustomMarkdown } from '@web/modules/shared/components/CustomMarkdown'; type HelpPageProps = { - params: { id: string }; + params: Promise<{ id: string }>; }; export async function generateMetadata({ From 7536bd78d7bb70c04b395eb42e48e5fa04da20c9 Mon Sep 17 00:00:00 2001 From: tomast1337 Date: Sat, 10 Jan 2026 18:27:09 -0300 Subject: [PATCH 35/37] Fix: update build script to use webpack --- apps/frontend/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/frontend/package.json b/apps/frontend/package.json index 0785ad84..f6186544 100644 --- a/apps/frontend/package.json +++ b/apps/frontend/package.json @@ -4,7 +4,7 @@ "private": true, "scripts": { "dev": "next dev", - "build": "next build", + "build": "next build --webpack", "start": "next start", "lint": "eslint \"src/**/*.{ts,tsx}\" --fix", "test": "jest" From 8a18083be9836f06c93a8278f0a365a4bbed0b6b Mon Sep 17 00:00:00 2001 From: tomast1337 Date: Sat, 10 Jan 2026 18:33:34 -0300 Subject: [PATCH 36/37] fix: HelpPost component to handle async params --- apps/frontend/src/app/(content)/(info)/help/[id]/page.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/frontend/src/app/(content)/(info)/help/[id]/page.tsx b/apps/frontend/src/app/(content)/(info)/help/[id]/page.tsx index 4f8ed867..d776871e 100644 --- a/apps/frontend/src/app/(content)/(info)/help/[id]/page.tsx +++ b/apps/frontend/src/app/(content)/(info)/help/[id]/page.tsx @@ -34,8 +34,8 @@ export async function generateMetadata({ }; } -const HelpPost = ({ params }: HelpPageProps) => { - const { id } = params; +const HelpPost = async ({ params }: HelpPageProps) => { + const { id } = await params; let post: PostType; try { From 696f3f92828aa37dd8cf2a0ce8a36769f98c8a76 Mon Sep 17 00:00:00 2001 From: tomast1337 Date: Sat, 10 Jan 2026 18:34:08 -0300 Subject: [PATCH 37/37] fix: update form types and default values in Thumbnail components --- .../song/components/client/SongThumbnailInput.tsx | 15 +++++++++------ .../song/components/client/ThumbnailRenderer.tsx | 14 ++++++++------ 2 files changed, 17 insertions(+), 12 deletions(-) diff --git a/apps/frontend/src/modules/song/components/client/SongThumbnailInput.tsx b/apps/frontend/src/modules/song/components/client/SongThumbnailInput.tsx index 27bdab49..40acb09b 100644 --- a/apps/frontend/src/modules/song/components/client/SongThumbnailInput.tsx +++ b/apps/frontend/src/modules/song/components/client/SongThumbnailInput.tsx @@ -11,7 +11,7 @@ import { import { Slider } from '@web/modules/shared/components/ui/slider'; import { useSongProvider } from './context/Song.context'; -import { EditSongForm, UploadSongForm } from './SongForm.zod'; +import { EditSongFormInput, UploadSongFormInput } from './SongForm.zod'; import { ThumbnailRendererCanvas } from './ThumbnailRenderer'; const formatZoomLevel = (zoomLevel: number) => { @@ -20,7 +20,8 @@ const formatZoomLevel = (zoomLevel: number) => { }; type ThumbnailSlidersProps = { - formMethods: UseFormReturn & UseFormReturn; + formMethods: UseFormReturn & + UseFormReturn; isLocked: boolean; maxTick: number; maxLayer: number; @@ -48,7 +49,7 @@ const ThumbnailSliders: React.FC = ({
{ setValue('thumbnailData.zoomLevel', value[0], { shouldValidate: true, @@ -60,14 +61,16 @@ const ThumbnailSliders: React.FC = ({ max={THUMBNAIL_CONSTANTS.zoomLevel.max} />
-
{formatZoomLevel(zoomLevel)}
+
+ {formatZoomLevel(zoomLevel ?? THUMBNAIL_CONSTANTS.zoomLevel.default)} +
{ setValue('thumbnailData.startTick', value[0], { shouldValidate: true, @@ -86,7 +89,7 @@ const ThumbnailSliders: React.FC = ({
{ setValue('thumbnailData.startLayer', value[0], { shouldValidate: true, diff --git a/apps/frontend/src/modules/song/components/client/ThumbnailRenderer.tsx b/apps/frontend/src/modules/song/components/client/ThumbnailRenderer.tsx index 6d11a28b..6cd0da89 100644 --- a/apps/frontend/src/modules/song/components/client/ThumbnailRenderer.tsx +++ b/apps/frontend/src/modules/song/components/client/ThumbnailRenderer.tsx @@ -3,14 +3,15 @@ import { useEffect, useRef, useState } from 'react'; import { UseFormReturn } from 'react-hook-form'; +import { THUMBNAIL_CONSTANTS } from '@nbw/config'; import { NoteQuadTree } from '@nbw/song'; import { drawNotesOffscreen, swap } from '@nbw/thumbnail/browser'; -import { UploadSongForm } from './SongForm.zod'; +import { UploadSongFormInput } from './SongForm.zod'; type ThumbnailRendererCanvasProps = { notes: NoteQuadTree; - formMethods: UseFormReturn; + formMethods: UseFormReturn; }; export const ThumbnailRendererCanvas = ({ @@ -60,10 +61,11 @@ export const ThumbnailRendererCanvas = ({ try { const output = (await drawNotesOffscreen({ notes, - startTick, - startLayer, - zoomLevel, - backgroundColor, + startTick: startTick ?? THUMBNAIL_CONSTANTS.startTick.default, + startLayer: startLayer ?? THUMBNAIL_CONSTANTS.startLayer.default, + zoomLevel: zoomLevel ?? THUMBNAIL_CONSTANTS.zoomLevel.default, + backgroundColor: + backgroundColor ?? THUMBNAIL_CONSTANTS.backgroundColor.default, canvasWidth: canvas.width, imgWidth: 1280, imgHeight: 768,