create-ej-app 16.0.0 → 17.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # create-ej-app
2
2
 
3
- A no-bullshit CLI to bootstrap a production-ready **fucking beast** of a app with Next.js, Better Auth, Drizzle ORM, Elysia, Workflow, shadcn/ui, React Email, and deployment wired up so you don’t screw your shit up in production.
3
+ A no-bullshit CLI to bootstrap a production-ready **fucking beast** of a app with Next.js, Better Auth, Drizzle ORM, Elysia, Workflow, shadcn/ui, React Email, OTEL, and deployment wired up so you don’t screw your shit up in production.
4
4
 
5
5
  ```bash
6
6
  bunx create-ej-app@latest
@@ -11,6 +11,7 @@ bunx create-ej-app@latest
11
11
  ## ✨ Features (aka the good shit)
12
12
 
13
13
  - **Next.js 16** – Powered by React 19, Server Components, App Router. Fast as hell.
14
+
14
15
  - **Better Auth** – Auth that doesn’t suck:
15
16
  - Email/Password
16
17
  - Magic link (because passwords are annoying as fuck)
@@ -19,15 +20,31 @@ bunx create-ej-app@latest
19
20
  - Admin powers
20
21
 
21
22
  - **Drizzle ORM** – Type-safe SQL over PostgreSQL without the usual ORM garbage.
23
+
22
24
  - **Elysia** – Built for Bun. Minimal, fast, no nonsense.
25
+
23
26
  - **Workflow** – Durable jobs so your background tasks don’t randomly die like weak shit.
27
+
24
28
  - **React Email** – Build transactional emails using React components instead of ugly HTML nightmares.
29
+
30
+ - **OTEL (OpenTelemetry)** – Production-grade observability:
31
+ - Distributed tracing
32
+ - Metrics
33
+ - Structured logs
34
+ - Works with GCP, Datadog, Honeycomb, Grafana, etc.
35
+
25
36
  - **shadcn/ui** – Clean, accessible components built on Radix UI.
37
+
26
38
  - **TailwindCSS 4** – Utility-first styling with zero CSS drama.
39
+
27
40
  - **TypeScript** – Strict types so you don’t ship dumb bugs.
41
+
28
42
  - **TanStack Query** – Server state that doesn’t implode.
43
+
29
44
  - **Lucide React** – Icons that don’t look like 2009 garbage.
45
+
30
46
  - **Docker + Terraform** – Ship this shit properly.
47
+
31
48
  - Terraform setup for Google Cloud Platform so you don’t YOLO infra.
32
49
 
33
50
  ---
@@ -68,6 +85,7 @@ Boom. Done.
68
85
  - **Database**: PostgreSQL + Drizzle
69
86
  - **Auth**: Better Auth
70
87
  - **Emails**: React Email (transactional + auth templates ready)
88
+ - **Observability**: OpenTelemetry
71
89
  - **Background Jobs**: Workflow
72
90
  - **UI**: shadcn/ui
73
91
  - **Styling**: TailwindCSS
@@ -232,6 +250,7 @@ MIT. Do whatever the hell you want, just don’t blame us if you screw it up.
232
250
  - [Workflow](https://useworkflow.dev)
233
251
  - [shadcn/ui](https://ui.shadcn.com)
234
252
  - [React Email](https://react.email)
253
+ - [Logging](https://loggingsucks.com)
235
254
 
236
255
  ---
237
256
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-ej-app",
3
- "version": "16.0.0",
3
+ "version": "17.0.0",
4
4
  "description": "Bootstrap a next.js app with my preferred setup",
5
5
  "main": "cli.js",
6
6
  "scripts": {
@@ -24,6 +24,8 @@
24
24
  "@elysiajs/eden": "1.4.8",
25
25
  "@hookform/resolvers": "5.2.2",
26
26
  "@next/third-parties": "16.1.6",
27
+ "@opentelemetry/api": "1.9.0",
28
+ "@opentelemetry/sdk-trace-base": "2.5.1",
27
29
  "@paralleldrive/cuid2": "3.3.0",
28
30
  "@radix-ui/react-label": "2.1.8",
29
31
  "@radix-ui/react-separator": "1.1.8",
@@ -32,12 +34,13 @@
32
34
  "@react-email/render": "2.0.4",
33
35
  "@t3-oss/env-nextjs": "0.13.10",
34
36
  "@tanstack/react-query": "5.90.21",
37
+ "@vercel/otel": "2.1.1",
35
38
  "better-auth": "1.4.18",
36
39
  "class-variance-authority": "0.7.1",
37
40
  "clsx": "2.1.1",
38
41
  "drizzle-orm": "0.45.1",
39
42
  "elysia": "1.4.25",
40
- "lucide-react": "0.563.0",
43
+ "lucide-react": "0.564.0",
41
44
  "next": "16.1.6",
42
45
  "next-safe-action": "8.0.11",
43
46
  "next-themes": "0.4.6",
@@ -48,9 +51,9 @@
48
51
  "react-dom": "19.2.4",
49
52
  "react-hook-form": "7.71.1",
50
53
  "sonner": "2.0.7",
51
- "tailwind-merge": "3.4.0",
54
+ "tailwind-merge": "3.4.1",
52
55
  "tw-animate-css": "1.4.0",
53
- "workflow": "4.1.0-beta.56",
56
+ "workflow": "4.1.0-beta.57",
54
57
  "zod": "4.3.6"
55
58
  },
56
59
  "devDependencies": {
@@ -58,7 +61,7 @@
58
61
  "@react-email/preview-server": "5.2.8",
59
62
  "@tailwindcss/postcss": "4.1.18",
60
63
  "@types/bun": "1.3.9",
61
- "@types/nodemailer": "7.0.9",
64
+ "@types/nodemailer": "7.0.10",
62
65
  "@types/react": "19.2.14",
63
66
  "@types/react-dom": "19.2.3",
64
67
  "babel-plugin-react-compiler": "1.0.0",
@@ -1,7 +1,13 @@
1
+ 'use client'
2
+
3
+ import { Button } from '@/components/ui/button'
1
4
  import Link from 'next/link'
2
- import { Button } from '~/components/ui/button'
5
+ import { usePathname } from 'next/navigation'
3
6
 
4
7
  export default function NotFound() {
8
+ const pathname = usePathname()
9
+ const homeRoute = pathname?.startsWith('/admin') ? '/admin' : '/'
10
+
5
11
  return (
6
12
  <main className="bg-background text-foreground flex min-h-screen flex-col">
7
13
  <section className="flex min-h-[60vh] flex-col items-center justify-center px-4">
@@ -12,7 +18,7 @@ export default function NotFound() {
12
18
  The page you're looking for doesn't exist.
13
19
  </p>
14
20
  <Button asChild>
15
- <Link href="/">Return to Home</Link>
21
+ <Link href={homeRoute}>Return to Home</Link>
16
22
  </Button>
17
23
  </div>
18
24
  </section>
@@ -0,0 +1,26 @@
1
+ import { SpanStatusCode } from '@opentelemetry/api'
2
+ import {
3
+ ConsoleSpanExporter,
4
+ ReadableSpan,
5
+ SimpleSpanProcessor
6
+ } from '@opentelemetry/sdk-trace-base'
7
+ import { registerOTel } from '@vercel/otel'
8
+
9
+ class SmartSamplingSpanProcessor extends SimpleSpanProcessor {
10
+ onEnd(span: ReadableSpan): void {
11
+ const isError = span.status.code === SpanStatusCode.ERROR
12
+ const isSlow = span.duration[0] * 1000 + span.duration[1] / 1e6 > 5000
13
+ const isLucky = Math.random() < 0 // Adjust this value to adjust the sampling rate for non-error and non-slow spans
14
+
15
+ if (isError || isSlow || isLucky) {
16
+ super.onEnd(span)
17
+ }
18
+ }
19
+ }
20
+
21
+ export function register() {
22
+ registerOTel({
23
+ serviceName: 'tdc-ai',
24
+ spanProcessors: [new SmartSamplingSpanProcessor(new ConsoleSpanExporter())]
25
+ })
26
+ }
@@ -0,0 +1,27 @@
1
+ import { SpanStatusCode, trace } from '@opentelemetry/api'
2
+
3
+ function getSpan() {
4
+ const span = trace.getActiveSpan()
5
+ return span
6
+ }
7
+
8
+ export function recordWideEvent(attributes: Record<string, any>) {
9
+ const span = getSpan()
10
+ if (!span) return
11
+
12
+ span.setAttributes(attributes)
13
+ }
14
+
15
+ export function recordError(err: unknown) {
16
+ const span = getSpan()
17
+ if (!span) return
18
+
19
+ span.setStatus({
20
+ code: SpanStatusCode.ERROR,
21
+ message: err instanceof Error ? err.message : String(err)
22
+ })
23
+
24
+ span.recordException(err instanceof Error ? err : String(err))
25
+
26
+ return span.spanContext().traceId
27
+ }
@@ -1,10 +1,18 @@
1
1
  import { createSafeActionClient } from 'next-safe-action'
2
2
  import { getAuthSession } from './auth'
3
+ import { recordWideEvent } from './otel'
3
4
 
4
5
  export const actionClient = createSafeActionClient().use(
5
6
  async ({ next, ctx }) => {
6
7
  const authSession = await getAuthSession()
7
8
 
9
+ if (authSession)
10
+ recordWideEvent({
11
+ 'user.id': authSession.user.id,
12
+ 'user.admin': authSession.isAdmin,
13
+ 'session.id': authSession.session.id
14
+ })
15
+
8
16
  return next({
9
17
  ctx: {
10
18
  ...ctx,