diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index 6522cba..08abbc9 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -15,4 +15,4 @@ jobs: - run: npm ci - - run: npm test + - run: npm run test:ci diff --git a/README.md b/README.md index df53f77..ded68fb 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,118 @@ -# tabnews-clone +# SaaS-Base -Implementação do tabnews.com.br para o curso.dev. +A **lean, fully wired foundation for SaaS development**. It provides a complete Next.js setup, **Docker-managed PostgreSQL**, structured **migrations**, and automated environment orchestration - so your project boots with a single command and stays consistent across machines. + +The foundation also includes **integration testing**, plus enforced code quality through **ESLint** and **Prettier**. For Git hygiene, it uses **Husky**, **Commitlint**, and **Commitizen** to keep your commit history clean, predictable, and automation-friendly. + +Everything is preconfigured to eliminate setup friction, prevent “works on my machine” headaches, and let you focus directly on building product features - not infrastructure. + +--- + +## 🚀 Features + +### 🌟 Automated Dev Workflow + +The automated development workflow ensures you never waste time manually preparing your environment. + +Instead of juggling multiple terminal commands, connection waits, and database setup steps, a single script handles everything: starting infrastructure, validating service readiness, applying migrations, and booting the application. This removes entire classes of “it works on my machine” problems, keeps onboarding effortless for new developers, and makes day-to-day development dramatically smoother and more reliable. + +The `dev` script performs everything in sequence: + +1. Starts Docker services +2. Waits for PostgreSQL +3. Applies migrations +4. Runs Next.js in development mode + +Run it with: + +```bash +npm run dev +``` + +Under the hood, what this command will do is: + +``` +npm run services:up && npm run services:wait:database && npm run migrations:up && next dev +``` + +### 🌟 Husky + Commitlint + Commitizen + +To ensure your project’s Git history stays clean, consistent, and reliable. + +Husky enforces checks before bad commits sneak in, Commitlint guarantees every commit message follows a predictable and searchable format, and Commitizen gives contributors an easy, structured way to write proper commit messages without memorizing conventions. Together, they prevent chaotic commit logs, improve collaboration, power automated changelogs, and make long-term maintenance far less painful. + +- Husky hooks installed automatically through `prepare` script +- Commitlint enforces conventional commits +- Commitizen interactive prompts via: + +```bash +npm run commit +``` + +### 🌟 Jest Test Suite (Integration-Ready) + +- Jest fully configured for ESM +- Integration tests located in `tests/integration/api/v1/...` +- Tests call real Next.js API routes +- CI test flow automatically spins up services and runs migrations +- Scripts: + - `npm test` + - `npm run test:watch` + - `npm run test:ci` — runs Next.js + Jest concurrently + +### 🏅 GitHub Actions to ensure quality + +Made to ensure the project grows in a clean, secure and professional way, running this GitHub Action on all pull requests: + +- Automated tests +- Commit linting +- Code quality linting +- Code style linting + +If you or someone else ever make a bad PR, GitHub will let you know. + +![alt text](image.png) + +> Tip: You can make the tests Required for merge in GitHub's interface. That way, if some check don't pass, the Merge button won't be available, adding some extra security for the project. + +### 🐳 Dockerized Local Environment + +Docker Compose (`infra/compose.yaml`) that provides a PostgreSQL container. + +Service commands: + +- `npm run services:up` — start infrastructure containers +- `npm run services:stop` — stop containers +- `npm run services:down` — remove containers and volumes + +### 🐘 PostgreSQL + Migrations + +- PostgreSQL managed via Docker Compose +- Automatic database readiness check (`infra/scripts/wait-for-postgres.js`) +- Migration system powered by **node-pg-migrate** +- Migration files stored in `infra/migrations` +- Scripts: + - `npm run migrations:create` — create a new migration file + - `npm run migrations:up` — apply pending migrations + +### 🏘️ Next.js Application Structure + +- API routes organized under `pages/api/v1/...` +- Example endpoints (such as `status`) +- Ready-to-expand modular folder structure +- ESM-first setup (`"type": "module"` in package.json) + +### 🧼 ESLint + Prettier + +- ESLint with Next.js, Jest, and Prettier integrations +- Prettier config prewired +- Linting commands: + - `npm run lint:eslint:check` + - `npm run lint:prettier:check` + - `npm run lint:prettier:fix` + +--- + +## 📜 License + +MIT License. diff --git a/commitlint.config.js b/commitlint.config.js index 69b4242..0374f70 100644 --- a/commitlint.config.js +++ b/commitlint.config.js @@ -1,3 +1,5 @@ -module.exports = { +const commitlintConfig = { extends: ["@commitlint/config-conventional"], }; + +export default commitlintConfig; diff --git a/image.png b/image.png new file mode 100644 index 0000000..69fd274 Binary files /dev/null and b/image.png differ diff --git a/infra/migrations/1764719520015_test-migration.js b/infra/migrations/1764719520015_test-migration.js deleted file mode 100644 index 086f59e..0000000 --- a/infra/migrations/1764719520015_test-migration.js +++ /dev/null @@ -1,7 +0,0 @@ -/* eslint-disable no-unused-vars */ - -exports.shorthands = undefined; - -exports.up = (pgm) => {}; - -exports.down = (pgm) => {}; diff --git a/infra/migrations/1765591892999_test-migration.js b/infra/migrations/1765591892999_test-migration.js new file mode 100644 index 0000000..652497b --- /dev/null +++ b/infra/migrations/1765591892999_test-migration.js @@ -0,0 +1,19 @@ +/* eslint-disable no-unused-vars */ +/** + * @type {import('node-pg-migrate').ColumnDefinitions | undefined} + */ +export const shorthands = undefined; + +/** + * @param pgm {import('node-pg-migrate').MigrationBuilder} + * @param run {() => void | undefined} + * @returns {Promise | void} + */ +export const up = (pgm) => {}; + +/** + * @param pgm {import('node-pg-migrate').MigrationBuilder} + * @param run {() => void | undefined} + * @returns {Promise | void} + */ +export const down = (pgm) => {}; diff --git a/infra/scripts/orchestrator.js b/infra/scripts/orchestrator.js index 5198282..0402da3 100644 --- a/infra/scripts/orchestrator.js +++ b/infra/scripts/orchestrator.js @@ -1,5 +1,7 @@ import retry from "async-retry"; -import database from "infra/database"; +import database from "../database"; + +// Web server must be running in order for the tests to work. Otherwise they will take some time and then timeout. Make sure you ran `npm run dev` on another terminal. async function waitForAllServices() { await waitForWebServer(); diff --git a/infra/scripts/wait-for-postgres.js b/infra/scripts/wait-for-postgres.js index b3b9d06..5327960 100644 --- a/infra/scripts/wait-for-postgres.js +++ b/infra/scripts/wait-for-postgres.js @@ -1,10 +1,12 @@ -const { exec } = require("node:child_process"); +import { exec } from "node:child_process"; function checkPostgres() { exec("docker exec postgres-dev pg_isready --host localhost", handleReturn); function handleReturn(error, stdout) { - if (stdout.search("accepting connections") === -1) { + const postgresIsReady = stdout.search("accepting connections") !== -1; + + if (!postgresIsReady) { process.stdout.write("."); checkPostgres(); return; @@ -15,4 +17,5 @@ function checkPostgres() { } process.stdout.write("\n\n🔴 Waiting for Postgres to accept connections..."); + checkPostgres(); diff --git a/jest.config.js b/jest.config.js index 92818c6..463b5de 100644 --- a/jest.config.js +++ b/jest.config.js @@ -1,7 +1,7 @@ -const dotenv = require("dotenv"); -dotenv.config({ path: ".env.development" }); +import dotenv from "dotenv"; +import nextJest from "next/jest.js"; -const nextJest = require("next/jest"); +dotenv.config({ path: ".env.development" }); const createJestConfig = nextJest({ dir: ".", @@ -9,7 +9,7 @@ const createJestConfig = nextJest({ const jestConfig = createJestConfig({ moduleDirectories: ["node_modules", ""], - testTimeout: 60000, + testTimeout: 15000, }); -module.exports = jestConfig; +export default jestConfig; diff --git a/package-lock.json b/package-lock.json index 3284c4b..8daa40f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,8 +12,8 @@ "async-retry": "1.3.3", "dotenv": "17.2.3", "dotenv-expand": "12.0.3", - "next": "16.0.8", - "node-pg-migrate": "7.6.1", + "next": "16.0.10", + "node-pg-migrate": "^8.0.4", "pg": "8.16.3", "react": "19.2.1", "react-dom": "19.2.1" @@ -1654,11 +1654,31 @@ "url": "https://opencollective.com/libvips" } }, + "node_modules/@isaacs/balanced-match": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@isaacs/balanced-match/-/balanced-match-4.0.1.tgz", + "integrity": "sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ==", + "license": "MIT", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/@isaacs/brace-expansion": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@isaacs/brace-expansion/-/brace-expansion-5.0.0.tgz", + "integrity": "sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA==", + "license": "MIT", + "dependencies": { + "@isaacs/balanced-match": "^4.0.1" + }, + "engines": { + "node": "20 || >=22" + } + }, "node_modules/@isaacs/cliui": { "version": "8.0.2", "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", - "dev": true, "license": "ISC", "dependencies": { "string-width": "^5.1.2", @@ -1676,7 +1696,6 @@ "version": "6.2.2", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", - "dev": true, "license": "MIT", "engines": { "node": ">=12" @@ -1689,7 +1708,6 @@ "version": "6.2.3", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", - "dev": true, "license": "MIT", "engines": { "node": ">=12" @@ -1702,14 +1720,12 @@ "version": "9.2.2", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", - "dev": true, "license": "MIT" }, "node_modules/@isaacs/cliui/node_modules/string-width": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", - "dev": true, "license": "MIT", "dependencies": { "eastasianwidth": "^0.2.0", @@ -1727,7 +1743,6 @@ "version": "7.1.2", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz", "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", - "dev": true, "license": "MIT", "dependencies": { "ansi-regex": "^6.0.1" @@ -1743,7 +1758,6 @@ "version": "8.1.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", - "dev": true, "license": "MIT", "dependencies": { "ansi-styles": "^6.1.0", @@ -2368,9 +2382,9 @@ } }, "node_modules/@next/env": { - "version": "16.0.8", - "resolved": "https://registry.npmjs.org/@next/env/-/env-16.0.8.tgz", - "integrity": "sha512-xP4WrQZuj9MdmLJy3eWFHepo+R3vznsMSS8Dy3wdA7FKpjCiesQ6DxZvdGziQisj0tEtCgBKJzjcAc4yZOgLEQ==", + "version": "16.0.10", + "resolved": "https://registry.npmjs.org/@next/env/-/env-16.0.10.tgz", + "integrity": "sha512-8tuaQkyDVgeONQ1MeT9Mkk8pQmZapMKFh5B+OrFUlG3rVmYTXcXlBetBgTurKXGaIZvkoqRT9JL5K3phXcgang==", "license": "MIT" }, "node_modules/@next/eslint-plugin-next": { @@ -2414,9 +2428,9 @@ } }, "node_modules/@next/swc-darwin-arm64": { - "version": "16.0.8", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-16.0.8.tgz", - "integrity": "sha512-yjVMvTQN21ZHOclQnhSFbjBTEizle+1uo4NV6L4rtS9WO3nfjaeJYw+H91G+nEf3Ef43TaEZvY5mPWfB/De7tA==", + "version": "16.0.10", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-16.0.10.tgz", + "integrity": "sha512-4XgdKtdVsaflErz+B5XeG0T5PeXKDdruDf3CRpnhN+8UebNa5N2H58+3GDgpn/9GBurrQ1uWW768FfscwYkJRg==", "cpu": [ "arm64" ], @@ -2430,9 +2444,9 @@ } }, "node_modules/@next/swc-darwin-x64": { - "version": "16.0.8", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-16.0.8.tgz", - "integrity": "sha512-+zu2N3QQ0ZOb6RyqQKfcu/pn0UPGmg+mUDqpAAEviAcEVEYgDckemOpiMRsBP3IsEKpcoKuNzekDcPczEeEIzA==", + "version": "16.0.10", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-16.0.10.tgz", + "integrity": "sha512-spbEObMvRKkQ3CkYVOME+ocPDFo5UqHb8EMTS78/0mQ+O1nqE8toHJVioZo4TvebATxgA8XMTHHrScPrn68OGw==", "cpu": [ "x64" ], @@ -2446,9 +2460,9 @@ } }, "node_modules/@next/swc-linux-arm64-gnu": { - "version": "16.0.8", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-16.0.8.tgz", - "integrity": "sha512-LConttk+BeD0e6RG0jGEP9GfvdaBVMYsLJ5aDDweKiJVVCu6sGvo+Ohz9nQhvj7EQDVVRJMCGhl19DmJwGr6bQ==", + "version": "16.0.10", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-16.0.10.tgz", + "integrity": "sha512-uQtWE3X0iGB8apTIskOMi2w/MKONrPOUCi5yLO+v3O8Mb5c7K4Q5KD1jvTpTF5gJKa3VH/ijKjKUq9O9UhwOYw==", "cpu": [ "arm64" ], @@ -2462,9 +2476,9 @@ } }, "node_modules/@next/swc-linux-arm64-musl": { - "version": "16.0.8", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-16.0.8.tgz", - "integrity": "sha512-JaXFAlqn8fJV+GhhA9lpg6da/NCN/v9ub98n3HoayoUSPOVdoxEEt86iT58jXqQCs/R3dv5ZnxGkW8aF4obMrQ==", + "version": "16.0.10", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-16.0.10.tgz", + "integrity": "sha512-llA+hiDTrYvyWI21Z0L1GiXwjQaanPVQQwru5peOgtooeJ8qx3tlqRV2P7uH2pKQaUfHxI/WVarvI5oYgGxaTw==", "cpu": [ "arm64" ], @@ -2478,9 +2492,9 @@ } }, "node_modules/@next/swc-linux-x64-gnu": { - "version": "16.0.8", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-16.0.8.tgz", - "integrity": "sha512-O7M9it6HyNhsJp3HNAsJoHk5BUsfj7hRshfptpGcVsPZ1u0KQ/oVy8oxF7tlwxA5tR43VUP0yRmAGm1us514ng==", + "version": "16.0.10", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-16.0.10.tgz", + "integrity": "sha512-AK2q5H0+a9nsXbeZ3FZdMtbtu9jxW4R/NgzZ6+lrTm3d6Zb7jYrWcgjcpM1k8uuqlSy4xIyPR2YiuUr+wXsavA==", "cpu": [ "x64" ], @@ -2494,9 +2508,9 @@ } }, "node_modules/@next/swc-linux-x64-musl": { - "version": "16.0.8", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-16.0.8.tgz", - "integrity": "sha512-8+KClEC/GLI2dLYcrWwHu5JyC5cZYCFnccVIvmxpo6K+XQt4qzqM5L4coofNDZYkct/VCCyJWGbZZDsg6w6LFA==", + "version": "16.0.10", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-16.0.10.tgz", + "integrity": "sha512-1TDG9PDKivNw5550S111gsO4RGennLVl9cipPhtkXIFVwo31YZ73nEbLjNC8qG3SgTz/QZyYyaFYMeY4BKZR/g==", "cpu": [ "x64" ], @@ -2510,9 +2524,9 @@ } }, "node_modules/@next/swc-win32-arm64-msvc": { - "version": "16.0.8", - "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-16.0.8.tgz", - "integrity": "sha512-rpQ/PgTEgH68SiXmhu/cJ2hk9aZ6YgFvspzQWe2I9HufY6g7V02DXRr/xrVqOaKm2lenBFPNQ+KAaeveywqV+A==", + "version": "16.0.10", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-16.0.10.tgz", + "integrity": "sha512-aEZIS4Hh32xdJQbHz121pyuVZniSNoqDVx1yIr2hy+ZwJGipeqnMZBJHyMxv2tiuAXGx6/xpTcQJ6btIiBjgmg==", "cpu": [ "arm64" ], @@ -2526,9 +2540,9 @@ } }, "node_modules/@next/swc-win32-x64-msvc": { - "version": "16.0.8", - "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-16.0.8.tgz", - "integrity": "sha512-jWpWjWcMQu2iZz4pEK2IktcfR+OA9+cCG8zenyLpcW8rN4rzjfOzH4yj/b1FiEAZHKS+5Vq8+bZyHi+2yqHbFA==", + "version": "16.0.10", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-16.0.10.tgz", + "integrity": "sha512-E+njfCoFLb01RAFEnGZn6ERoOqhK1Gl3Lfz1Kjnj0Ulfu7oJbuMyvBKNj/bw8XZnenHDASlygTjZICQW+rYW1Q==", "cpu": [ "x64" ], @@ -4577,7 +4591,6 @@ "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", - "dev": true, "license": "MIT", "dependencies": { "path-key": "^3.1.0", @@ -4984,7 +4997,6 @@ "version": "0.2.0", "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", - "dev": true, "license": "MIT" }, "node_modules/electron-to-chromium": { @@ -6083,7 +6095,6 @@ "version": "3.3.1", "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", - "dev": true, "license": "ISC", "dependencies": { "cross-spawn": "^7.0.6", @@ -6100,7 +6111,6 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", - "dev": true, "license": "ISC", "engines": { "node": ">=14" @@ -7349,7 +7359,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true, "license": "ISC" }, "node_modules/istanbul-lib-coverage": { @@ -7454,6 +7463,21 @@ "node": ">= 0.4" } }, + "node_modules/jackspeak": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.1.1.tgz", + "integrity": "sha512-zptv57P3GpL+O0I7VdMJNBZCu+BPHVQUk55Ft8/QCJjTVxrnJHuVuX/0Bl2A6/+2oyR/ZMEuFKwmzqqZ/U5nPQ==", + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/jest": { "version": "30.2.0", "resolved": "https://registry.npmjs.org/jest/-/jest-30.2.0.tgz", @@ -8665,7 +8689,6 @@ "version": "7.1.2", "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", - "dev": true, "license": "ISC", "engines": { "node": ">=16 || 14 >=14.17" @@ -8727,12 +8750,12 @@ "license": "MIT" }, "node_modules/next": { - "version": "16.0.8", - "resolved": "https://registry.npmjs.org/next/-/next-16.0.8.tgz", - "integrity": "sha512-LmcZzG04JuzNXi48s5P+TnJBsTGPJunViNKV/iE4uM6kstjTQsQhvsAv+xF6MJxU2Pr26tl15eVbp0jQnsv6/g==", + "version": "16.0.10", + "resolved": "https://registry.npmjs.org/next/-/next-16.0.10.tgz", + "integrity": "sha512-RtWh5PUgI+vxlV3HdR+IfWA1UUHu0+Ram/JBO4vWB54cVPentCD0e+lxyAYEsDTqGGMg7qpjhKh6dc6aW7W/sA==", "license": "MIT", "dependencies": { - "@next/env": "16.0.8", + "@next/env": "16.0.10", "@swc/helpers": "0.5.15", "caniuse-lite": "^1.0.30001579", "postcss": "8.4.31", @@ -8745,14 +8768,14 @@ "node": ">=20.9.0" }, "optionalDependencies": { - "@next/swc-darwin-arm64": "16.0.8", - "@next/swc-darwin-x64": "16.0.8", - "@next/swc-linux-arm64-gnu": "16.0.8", - "@next/swc-linux-arm64-musl": "16.0.8", - "@next/swc-linux-x64-gnu": "16.0.8", - "@next/swc-linux-x64-musl": "16.0.8", - "@next/swc-win32-arm64-msvc": "16.0.8", - "@next/swc-win32-x64-msvc": "16.0.8", + "@next/swc-darwin-arm64": "16.0.10", + "@next/swc-darwin-x64": "16.0.10", + "@next/swc-linux-arm64-gnu": "16.0.10", + "@next/swc-linux-arm64-musl": "16.0.10", + "@next/swc-linux-x64-gnu": "16.0.10", + "@next/swc-linux-x64-musl": "16.0.10", + "@next/swc-win32-arm64-msvc": "16.0.10", + "@next/swc-win32-x64-msvc": "16.0.10", "sharp": "^0.34.4" }, "peerDependencies": { @@ -8786,20 +8809,19 @@ "license": "MIT" }, "node_modules/node-pg-migrate": { - "version": "7.6.1", - "resolved": "https://registry.npmjs.org/node-pg-migrate/-/node-pg-migrate-7.6.1.tgz", - "integrity": "sha512-CUocdo8kh25QZU2MeCjss2/OQ/Jq8ejCeilpja1MVOgk2c03r4U7vux9fAZCfrVg0YASwnhnpjz+rSK4tAVB2w==", + "version": "8.0.4", + "resolved": "https://registry.npmjs.org/node-pg-migrate/-/node-pg-migrate-8.0.4.tgz", + "integrity": "sha512-HTlJ6fOT/2xHhAUtsqSN85PGMAqSbfGJNRwQF8+ZwQ1+sVGNUTl/ZGEshPsOI3yV22tPIyHXrKXr3S0JxeYLrg==", "license": "MIT", "dependencies": { + "glob": "~11.1.0", "yargs": "~17.7.0" }, "bin": { - "node-pg-migrate": "bin/node-pg-migrate.js", - "node-pg-migrate-cjs": "bin/node-pg-migrate.js", - "node-pg-migrate-esm": "bin/node-pg-migrate.mjs" + "node-pg-migrate": "bin/node-pg-migrate.js" }, "engines": { - "node": ">=18.19.0" + "node": ">=20.11.0" }, "peerDependencies": { "@types/pg": ">=6.0.0 <9.0.0", @@ -8811,6 +8833,69 @@ } } }, + "node_modules/node-pg-migrate/node_modules/glob": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-11.1.0.tgz", + "integrity": "sha512-vuNwKSaKiqm7g0THUBu2x7ckSs3XJLXE+2ssL7/MfTGPLLcrJQ/4Uq1CjPTtO5cCIiRxqvN6Twy1qOwhL0Xjcw==", + "license": "BlueOak-1.0.0", + "dependencies": { + "foreground-child": "^3.3.1", + "jackspeak": "^4.1.1", + "minimatch": "^10.1.1", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^2.0.0" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/node-pg-migrate/node_modules/lru-cache": { + "version": "11.2.4", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.4.tgz", + "integrity": "sha512-B5Y16Jr9LB9dHVkh6ZevG+vAbOsNOYCX+sXvFWFu7B3Iz5mijW3zdbMyhsh8ANd2mSWBYdJgnqi+mL7/LrOPYg==", + "license": "BlueOak-1.0.0", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/node-pg-migrate/node_modules/minimatch": { + "version": "10.1.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.1.1.tgz", + "integrity": "sha512-enIvLvRAFZYXJzkCYG5RKmPfrFArdLv+R+lbQ53BmIMLIry74bjKzX6iHAm8WYamJkhSSEabrWN5D97XnKObjQ==", + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/brace-expansion": "^5.0.0" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/node-pg-migrate/node_modules/path-scurry": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.1.tgz", + "integrity": "sha512-oWyT4gICAu+kaA7QWk/jvCHWarMKNs6pXOGWKDTr7cw4IGcUbW+PeTfbaQiLGheFRpjo6O9J0PmyMfQPjH71oA==", + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^11.0.0", + "minipass": "^7.1.2" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/node-releases": { "version": "2.0.27", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz", @@ -9119,7 +9204,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", - "dev": true, "license": "BlueOak-1.0.0" }, "node_modules/parent-module": { @@ -9188,7 +9272,6 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -10027,7 +10110,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, "license": "MIT", "dependencies": { "shebang-regex": "^3.0.0" @@ -10040,7 +10122,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -10275,7 +10356,6 @@ "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, "license": "MIT", "dependencies": { "emoji-regex": "^8.0.0", @@ -10416,7 +10496,6 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, "license": "MIT", "dependencies": { "ansi-regex": "^5.0.1" @@ -11038,7 +11117,6 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, "license": "ISC", "dependencies": { "isexe": "^2.0.0" @@ -11171,7 +11249,6 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, "license": "MIT", "dependencies": { "ansi-styles": "^4.0.0", diff --git a/package.json b/package.json index 52d756c..d554d89 100644 --- a/package.json +++ b/package.json @@ -13,10 +13,11 @@ "license": "MIT", "author": "Isaac Muniz", "main": "index.js", + "type": "module", "scripts": { "dev": "npm run services:up && npm run services:wait:database && npm run migrations:up && next dev", - "test": "npm run services:up && npm run services:wait:database && concurrently --names next,jest --hide next --kill-others --success command-jest 'next dev' 'jest --runInBand --verbose'", - "posttest": "npm run services:stop", + "test": "jest --runInBand --verbose", + "test:ci": "npm run services:up && npm run services:wait:database && concurrently --names next,jest --kill-others --success command-jest 'next dev' 'jest --runInBand --verbose'", "test:watch": "jest --watchAll --runInBand --verbose", "services:up": "docker compose -f infra/compose.yaml up -d", "services:stop": "docker compose -f infra/compose.yaml stop", @@ -34,8 +35,8 @@ "async-retry": "1.3.3", "dotenv": "17.2.3", "dotenv-expand": "12.0.3", - "next": "16.0.8", - "node-pg-migrate": "7.6.1", + "next": "16.0.10", + "node-pg-migrate": "8.0.4", "pg": "8.16.3", "react": "19.2.1", "react-dom": "19.2.1" diff --git a/pages/index.js b/pages/index.js index 922847f..61248bb 100644 --- a/pages/index.js +++ b/pages/index.js @@ -1,5 +1,29 @@ function Home() { - return

Em construção. 🏗️

; + return ( +
+

SaaS Base

+ +

+ It is much easier to build your SaaS from here, as this is made to cover + the foundational stuff well, and to be extensible 😉 +

+ +

+ Please, help your fellow developer and{" "} + + give the project a star on GitHub + {" "} + 🍻 +

+
+ ); } export default Home;