feat: turbostarter boilerplate

Production-ready Next.js boilerplate with:
- Runtime env validation (fail-fast on missing vars)
- Feature-gated config (S3, Stripe, email, OAuth)
- Docker + Coolify deployment pipeline
- PostgreSQL + pgvector, MinIO S3, Better Auth
- TypeScript strict mode (no ignoreBuildErrors)
- i18n (en/es), AI modules, billing, monitoring

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Alejandro Gutiérrez
2026-02-02 17:29:12 +00:00
commit 3527e732d4
1618 changed files with 338230 additions and 0 deletions

View File

@@ -0,0 +1,3 @@
import baseConfig from "@turbostarter/eslint-config/base";
export default baseConfig;

View File

@@ -0,0 +1,34 @@
{
"name": "@turbostarter/monitoring-extension",
"version": "0.1.0",
"private": true,
"type": "module",
"exports": {
".": "./src/index.ts",
"./env": "./src/env.ts"
},
"scripts": {
"clean": "git clean -xdf .cache .turbo dist node_modules",
"format": "prettier --check . --ignore-path ../../.gitignore",
"lint": "eslint",
"typecheck": "tsc --noEmit"
},
"prettier": "@turbostarter/prettier-config",
"devDependencies": {
"@turbostarter/eslint-config": "workspace:*",
"@turbostarter/prettier-config": "workspace:*",
"@turbostarter/tsconfig": "workspace:*",
"@types/chrome": "^0.0.304",
"eslint": "catalog:",
"prettier": "catalog:",
"typescript": "catalog:"
},
"dependencies": {
"@sentry/browser": "10.30.0",
"@turbostarter/monitoring": "workspace:*",
"@turbostarter/shared": "workspace:*",
"envin": "catalog:",
"posthog-js": "1.283.0",
"uuid": "13.0.0"
}
}

View File

@@ -0,0 +1 @@
export { env, preset } from "./providers";

View File

@@ -0,0 +1 @@
export { captureException, identify, initialize } from "./providers";

View File

@@ -0,0 +1,2 @@
export * from "./posthog";
export * from "./posthog/env";

View File

@@ -0,0 +1,34 @@
/* eslint-disable turbo/no-undeclared-env-vars */
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
/* eslint-disable @typescript-eslint/no-unsafe-argument */
import { defineEnv } from "envin";
import * as z from "zod";
import { envConfig } from "@turbostarter/shared/constants";
import type { Preset } from "envin/types";
export const preset = {
id: "posthog",
clientPrefix: "VITE_",
client: {
VITE_POSTHOG_KEY: z.string(),
VITE_POSTHOG_HOST: z
.string()
.optional()
.default("https://us.i.posthog.com"),
},
} as const satisfies Preset;
export const env = defineEnv({
...envConfig,
...preset,
env: {
VITE_POSTHOG_KEY: import.meta.env.VITE_POSTHOG_KEY,
VITE_POSTHOG_HOST: import.meta.env.VITE_POSTHOG_HOST,
},
skip:
(!!import.meta.env.SKIP_ENV_VALIDATION &&
["1", "true"].includes(import.meta.env.SKIP_ENV_VALIDATION)) ||
["lint", "postinstall"].includes(import.meta.env.npm_lifecycle_event),
});

View File

@@ -0,0 +1,62 @@
import { PostHog } from "posthog-js/dist/module.no-external";
import { v7 as uuidv7 } from "uuid";
import { env } from "./env";
import type { MonitoringProviderStrategy } from "@turbostarter/monitoring";
const posthog = new PostHog();
export async function getSharedDistinctId() {
const stored = await chrome.storage.local.get(["posthog_distinct_id"]);
if (stored.posthog_distinct_id) {
return stored.posthog_distinct_id as string;
}
const distinctId = uuidv7();
await chrome.storage.local.set({ posthog_distinct_id: distinctId });
return distinctId;
}
const init = async () => {
if (posthog.__loaded) {
return;
}
const distinctID = await getSharedDistinctId();
posthog.init(env.VITE_POSTHOG_KEY, {
bootstrap: {
distinctID,
},
api_host: env.VITE_POSTHOG_HOST,
disable_external_dependency_loading: true,
error_tracking: {
captureExtensionExceptions: true,
},
persistence: "localStorage",
});
};
export const { captureException, identify, initialize } = {
captureException: (exception) => {
void (async () => {
await init();
posthog.captureException(exception);
})();
},
identify: <T extends { id: string }>(user: T | null) => {
void (async () => {
await init();
if (user) {
posthog.identify(user.id);
} else {
posthog.reset();
}
})();
},
initialize: () => {
void (async () => {
await init();
})();
},
} satisfies MonitoringProviderStrategy;

View File

@@ -0,0 +1,33 @@
/* eslint-disable @typescript-eslint/no-unsafe-argument */
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
/* eslint-disable turbo/no-undeclared-env-vars */
import { defineEnv } from "envin";
import * as z from "zod";
import { envConfig, NodeEnv } from "@turbostarter/shared/constants";
import type { Preset } from "envin/types";
export const preset = {
id: "sentry",
clientPrefix: "VITE_",
client: {
VITE_SENTRY_DSN: z.string(),
VITE_SENTRY_ENVIRONMENT: z
.string()
.default(process.env.NODE_ENV ?? NodeEnv.DEVELOPMENT),
},
} as const satisfies Preset;
export const env = defineEnv({
...envConfig,
...preset,
env: {
VITE_SENTRY_DSN: import.meta.env.VITE_SENTRY_DSN,
VITE_SENTRY_ENVIRONMENT: import.meta.env.VITE_SENTRY_ENVIRONMENT,
},
skip:
(!!import.meta.env.SKIP_ENV_VALIDATION &&
["1", "true"].includes(import.meta.env.SKIP_ENV_VALIDATION)) ||
["lint", "postinstall"].includes(import.meta.env.npm_lifecycle_event),
});

View File

@@ -0,0 +1,45 @@
import * as Sentry from "@sentry/browser";
import { env } from "./env";
import type { MonitoringProviderStrategy } from "@turbostarter/monitoring";
export const { captureException, identify, initialize } = {
captureException: (exception) => {
Sentry.captureException(exception);
},
identify: (user: Sentry.User | null) => {
Sentry.setUser(user);
},
initialize: () => {
const environment = env.VITE_SENTRY_ENVIRONMENT;
Sentry.init({
dsn: env.VITE_SENTRY_DSN,
environment,
// Replay may only be enabled for the client-side
integrations: [
// add your desired integrations here
// https://docs.sentry.io/platforms/javascript/configuration/integrations/
],
// Set tracesSampleRate to 1.0 to capture 100%
// of transactions for performance monitoring.
// We recommend adjusting this value in production
tracesSampleRate: 1.0,
// Capture Replay for 10% of all sessions,
// plus for 100% of sessions with an error
replaysSessionSampleRate: 0.1,
replaysOnErrorSampleRate: 1.0,
// Adds more context data to events (IP address, cookies, user, etc.)
// For more information, visit: https://docs.sentry.io/platforms/react-native/data-management/data-collected/
sendDefaultPii: true,
// Note: if you want to override the automatic release value, do not set a
// `release` value here - use the environment variable `SENTRY_RELEASE`, so
// that it will also get attached to your source maps,
});
},
} satisfies MonitoringProviderStrategy;

View File

@@ -0,0 +1 @@
/// <reference types="vite/client" />

View File

@@ -0,0 +1,6 @@
{
"extends": "@turbostarter/tsconfig/internal.json",
"compilerOptions": {},
"include": ["*.ts", "src/**/*"],
"exclude": ["node_modules"]
}