create-veap 0.1.2

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.
Files changed (44) hide show
  1. package/README.md +3 -0
  2. package/bin/veap.js +7 -0
  3. package/dist/commands/docker.d.ts +1 -0
  4. package/dist/commands/docker.js +55 -0
  5. package/dist/commands/init.d.ts +5 -0
  6. package/dist/commands/init.js +111 -0
  7. package/dist/index.d.ts +3 -0
  8. package/dist/index.js +16 -0
  9. package/dist/main.d.ts +2 -0
  10. package/dist/main.js +1 -0
  11. package/dist/manifests.d.ts +22 -0
  12. package/dist/manifests.js +32 -0
  13. package/dist/mutate.d.ts +46 -0
  14. package/dist/mutate.js +127 -0
  15. package/dist/package-manager.d.ts +56 -0
  16. package/dist/package-manager.js +222 -0
  17. package/dist/project-name.d.ts +22 -0
  18. package/dist/project-name.js +107 -0
  19. package/dist/prompts.d.ts +20 -0
  20. package/dist/prompts.js +93 -0
  21. package/dist/scaffold.d.ts +26 -0
  22. package/dist/scaffold.js +62 -0
  23. package/dist/utils.d.ts +1 -0
  24. package/dist/utils.js +29 -0
  25. package/package.json +45 -0
  26. package/stubs/docker/.dockerignore.stub +39 -0
  27. package/stubs/docker/Dockerfile.bun.stub +42 -0
  28. package/stubs/docker/Dockerfile.npm.stub +42 -0
  29. package/stubs/docker/Dockerfile.stub +49 -0
  30. package/stubs/docker/Dockerfile.yarn.stub +44 -0
  31. package/stubs/docker/compose.yml.stub +58 -0
  32. package/stubs/overlay-full/AGENTS.md.stub +63 -0
  33. package/stubs/overlay-full/app/[[...catchAll]]/page.tsx.stub +47 -0
  34. package/stubs/overlay-full/app/api/[...catchAll]/route.ts.stub +111 -0
  35. package/stubs/overlay-full/app/error.tsx.stub +34 -0
  36. package/stubs/overlay-full/app/globals.css.stub +124 -0
  37. package/stubs/overlay-full/app/layout.tsx.stub +71 -0
  38. package/stubs/overlay-full/app/not-found.tsx.stub +29 -0
  39. package/stubs/overlay-full/app/storage/[...path]/route.ts.stub +61 -0
  40. package/stubs/overlay-full/lib/plugins.gen.ts.stub +6 -0
  41. package/stubs/overlay-full/lib/veap.ts.stub +26 -0
  42. package/stubs/overlay-full/migrations/index.ts.stub +2 -0
  43. package/stubs/overlay-full/next.config.ts.stub +34 -0
  44. package/stubs/overlay-full/proxy.ts.stub +27 -0
@@ -0,0 +1,42 @@
1
+ # Stage 1: Base image (bun)
2
+ FROM node:22-alpine AS base
3
+ RUN apk add --no-cache libc6-compat
4
+
5
+ # Install bun (Node stays the runtime for the standalone server)
6
+ RUN npm install -g bun
7
+
8
+ # Stage 2: Install dependencies
9
+ FROM base AS deps
10
+ WORKDIR /app
11
+ COPY . .
12
+ RUN bun install --frozen-lockfile
13
+
14
+ # Stage 3: Build the project
15
+ FROM base AS builder
16
+ WORKDIR /app
17
+ COPY --from=deps /app ./
18
+ RUN bun run build
19
+
20
+ # Stage 4: Production runner
21
+ FROM base AS runner
22
+ WORKDIR /app
23
+
24
+ ENV NODE_ENV=production
25
+ ENV NEXT_TELEMETRY_DISABLED=1
26
+
27
+ RUN addgroup --system --gid 1001 nodejs
28
+ RUN adduser --system --uid 1001 nextjs
29
+
30
+ # Automatically leverage output traces to reduce image size
31
+ COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
32
+ COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
33
+ COPY --from=builder --chown=nextjs:nodejs /app/public ./public
34
+
35
+ USER nextjs
36
+
37
+ EXPOSE 3000
38
+
39
+ ENV PORT=3000
40
+ ENV HOSTNAME="0.0.0.0"
41
+
42
+ CMD ["node", "./server.js"]
@@ -0,0 +1,42 @@
1
+ # Stage 1: Base image (npm)
2
+ FROM node:22-alpine AS base
3
+ RUN apk add --no-cache libc6-compat
4
+
5
+ # Stage 2: Install dependencies
6
+ FROM base AS deps
7
+ WORKDIR /app
8
+ COPY . .
9
+ RUN npm config set fetch-retries 10 && \
10
+ npm config set fetch-retry-maxtimeout 120000 && \
11
+ npm config set fetch-timeout 600000 && \
12
+ npm ci
13
+
14
+ # Stage 3: Build the project
15
+ FROM base AS builder
16
+ WORKDIR /app
17
+ COPY --from=deps /app ./
18
+ RUN npm run build
19
+
20
+ # Stage 4: Production runner
21
+ FROM base AS runner
22
+ WORKDIR /app
23
+
24
+ ENV NODE_ENV=production
25
+ ENV NEXT_TELEMETRY_DISABLED=1
26
+
27
+ RUN addgroup --system --gid 1001 nodejs
28
+ RUN adduser --system --uid 1001 nextjs
29
+
30
+ # Automatically leverage output traces to reduce image size
31
+ COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
32
+ COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
33
+ COPY --from=builder --chown=nextjs:nodejs /app/public ./public
34
+
35
+ USER nextjs
36
+
37
+ EXPOSE 3000
38
+
39
+ ENV PORT=3000
40
+ ENV HOSTNAME="0.0.0.0"
41
+
42
+ CMD ["node", "./server.js"]
@@ -0,0 +1,49 @@
1
+ # Stage 1: Base image with pnpm
2
+ FROM node:22-alpine AS base
3
+ RUN apk add --no-cache libc6-compat
4
+ ENV PNPM_HOME="/pnpm"
5
+ ENV PATH="$PNPM_HOME:$PATH"
6
+
7
+ RUN corepack enable
8
+ RUN corepack prepare pnpm@10.29.3 --activate
9
+
10
+ # Stage 2: Install dependencies
11
+ FROM base AS deps
12
+ WORKDIR /app
13
+ COPY . .
14
+ RUN --mount=type=cache,id=pnpm,target=/pnpm/store \
15
+ pnpm config set store-dir /pnpm/store && \
16
+ pnpm config set fetch-retries 10 && \
17
+ pnpm config set fetch-retry-maxtimeout 120000 && \
18
+ pnpm config set fetch-timeout 600000 && \
19
+ pnpm install --frozen-lockfile
20
+
21
+ # Stage 3: Build the project
22
+ FROM base AS builder
23
+ WORKDIR /app
24
+ COPY --from=deps /app ./
25
+ RUN pnpm build
26
+
27
+ # Stage 4: Production runner
28
+ FROM base AS runner
29
+ WORKDIR /app
30
+
31
+ ENV NODE_ENV=production
32
+ ENV NEXT_TELEMETRY_DISABLED=1
33
+
34
+ RUN addgroup --system --gid 1001 nodejs
35
+ RUN adduser --system --uid 1001 nextjs
36
+
37
+ # Automatically leverage output traces to reduce image size
38
+ COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
39
+ COPY --from=builder --chown=nextjs:nodejs /app/.next/static .next/static
40
+ COPY --from=builder --chown=nextjs:nodejs /app/public public
41
+
42
+ USER nextjs
43
+
44
+ EXPOSE 3000
45
+
46
+ ENV PORT=3000
47
+ ENV HOSTNAME="0.0.0.0"
48
+
49
+ CMD ["node", "./server.js"]
@@ -0,0 +1,44 @@
1
+ # Stage 1: Base image (yarn berry)
2
+ FROM node:22-alpine AS base
3
+ RUN apk add --no-cache libc6-compat
4
+
5
+ RUN corepack enable
6
+ RUN corepack prepare yarn@4.9.2 --activate
7
+
8
+ # Stage 2: Install dependencies
9
+ FROM base AS deps
10
+ WORKDIR /app
11
+ COPY . .
12
+ RUN --mount=type=cache,id=yarn,target=/root/.yarn/berry/cache \
13
+ yarn config set enableNetworkConcurrency 8 && \
14
+ yarn install --immutable
15
+
16
+ # Stage 3: Build the project
17
+ FROM base AS builder
18
+ WORKDIR /app
19
+ COPY --from=deps /app ./
20
+ RUN yarn build
21
+
22
+ # Stage 4: Production runner
23
+ FROM base AS runner
24
+ WORKDIR /app
25
+
26
+ ENV NODE_ENV=production
27
+ ENV NEXT_TELEMETRY_DISABLED=1
28
+
29
+ RUN addgroup --system --gid 1001 nodejs
30
+ RUN adduser --system --uid 1001 nextjs
31
+
32
+ # Automatically leverage output traces to reduce image size
33
+ COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
34
+ COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
35
+ COPY --from=builder --chown=nextjs:nodejs /app/public ./public
36
+
37
+ USER nextjs
38
+
39
+ EXPOSE 3000
40
+
41
+ ENV PORT=3000
42
+ ENV HOSTNAME="0.0.0.0"
43
+
44
+ CMD ["node", "./server.js"]
@@ -0,0 +1,58 @@
1
+ services:
2
+ # Node.js service (use with: docker compose up {{name}} --build)
3
+ {{name}}:
4
+ build:
5
+ context: .
6
+ dockerfile: Dockerfile
7
+ image: {{name}}-image
8
+ container_name: {{name}}
9
+ env_file:
10
+ - .env
11
+ environment:
12
+ NODE_ENV: production
13
+ PORT: "3000"
14
+ NEXT_TELEMETRY_DISABLED: "1"
15
+ DATABASE_URL: "postgresql://postgres:postgres@database:5432/postgres?sslmode=disable"
16
+ depends_on:
17
+ database:
18
+ condition: service_healthy
19
+ redis:
20
+ condition: service_started
21
+ ports:
22
+ - "80:3000"
23
+ restart: unless-stopped
24
+ volumes:
25
+ - ./storage:/app/public/storage
26
+
27
+ # PostgreSQL service
28
+ database:
29
+ image: postgres:17-alpine
30
+ container_name: {{name}}-db
31
+ restart: always
32
+ environment:
33
+ POSTGRES_USER: postgres
34
+ POSTGRES_PASSWORD: postgres
35
+ POSTGRES_DB: postgres
36
+ ports:
37
+ - "5432:5432"
38
+ volumes:
39
+ - postgres_data:/var/lib/postgresql/data
40
+ healthcheck:
41
+ test: ["CMD-SHELL", "pg_isready -U postgres -d postgres"]
42
+ interval: 5s
43
+ timeout: 5s
44
+ retries: 5
45
+
46
+ # Redis service
47
+ redis:
48
+ image: redis:7-alpine
49
+ container_name: {{name}}-redis
50
+ restart: always
51
+ ports:
52
+ - "6379:6379"
53
+ volumes:
54
+ - redis_data:/data
55
+
56
+ volumes:
57
+ postgres_data:
58
+ redis_data:
@@ -0,0 +1,63 @@
1
+ <!-- BEGIN:veap-agent-rules -->
2
+
3
+ # Veap Application - Agent Instructions
4
+
5
+ This project is a **Veap** application: the official `create-next-app` base plus the Veap overlay (root layout, router, API pipeline, bootstrap). Next.js owns the shell; **Veap owns routing, auth, database, events, plugins and storage**. When the two disagree, prefer the Veap mechanisms below.
6
+
7
+ ## Project Structure (Veap-owned files)
8
+
9
+ - `lib/veap.ts` - the composition root: `Application.configure()...create()` + `initializeSystem()` (cached bootstrap). Providers and plugins are wired here.
10
+ - `lib/plugins.gen.ts` - **generated by `veap register`. Never edit by hand.** It statically imports installed plugins so the bundler can trace them.
11
+ - `app/[[...catchAll]]/page.tsx` - every page render goes through `VeapRouter` (route tree built from plugins + app routes). Do not add competing page routes without understanding the router.
12
+ - `app/api/[...catchAll]/route.ts` - API pipeline: middlewares, auth, roles/permissions resolved from the route tree.
13
+ - `app/layout.tsx`, `app/not-found.tsx` - root shell with the bootstrap and the **force-dynamic contract (below)**.
14
+ - `plugins/`, `templates/` - workspace packages (pnpm workspace / npm workspaces).
15
+ - `migrations/` - app migrations (`veap make:migration`).
16
+
17
+ ## Critical Contracts (do not break)
18
+
19
+ ### 1. `force-dynamic` on the root layout and not-found
20
+
21
+ `app/layout.tsx` and `app/not-found.tsx` **must** keep:
22
+
23
+ ```ts
24
+ export const dynamic = "force-dynamic";
25
+ ```
26
+
27
+ Why: `Application.bootstrap()` intentionally skips provider boot during `next build` (`NEXT_PHASE=phase-production-build`) - the database and env are not guaranteed at build time. But Next still prerenders `/_not-found` with the full root layout, whose helpers (`getPathPrefix()`, extension points) require booted contexts. Without `force-dynamic` the build fails with `AppError: [Plugins] Context is not bound`. This is a deliberate architectural trade-off (see ADR-006 of the framework): the whole app is dynamic; only `manifest.webmanifest` is static.
28
+
29
+ ### 2. `ENCRYPTION_KEY` is required - no fallback
30
+
31
+ `.env` (generated) contains a random 16-byte base64 key used for AES encryption at rest (recovery codes, TOTP). The application **fails fast at boot** if the key is missing or does not decode to exactly 16/24/32 bytes. Regenerate with `openssl rand -base64 16`. Never commit `.env`.
32
+
33
+ ### 3. Database safety
34
+
35
+ All mutations go through `transaction()` from `@veap/framework/database` (AsyncLocalStorage-based). Raw knex handles are available for reads, but transactions are the only supported write path.
36
+
37
+ ### 4. Plugins and events
38
+
39
+ - Plugins are isolated packages: import only their public exports (`package.json` → `exports`).
40
+ - Use the typed event bus (`system:auth:*` etc.); publish plugin events with your own `plugin:*` prefix.
41
+ - Application services receive dependencies through ports (DI container); never `container.resolve()` inside request code - use the bound contexts/facades (`getCurrentSession`, `sendMail`, ...).
42
+
43
+ ### 5. Mail
44
+
45
+ `MAIL_TRANSPORT` selects the delivery: `smtp` (default), `console` (dev - codes are logged), or `custom` (register your own `IMailer` under `CUSTOM_MAILER`). Transports only deliver `MailMessage` DTOs; semantic emails (verification codes, resets) are built as mailables in application code.
46
+
47
+ ## Commands
48
+
49
+ ```bash
50
+ bun dev # development server (or pnpm dev / npm run dev)
51
+ bun run build # production build
52
+ bun run lint # eslint
53
+ veap add <plugin> # install + register a plugin
54
+ veap register # regenerate lib/plugins.gen.ts
55
+ veap make:plugin <name>
56
+ veap make:migration <name>
57
+ ```
58
+
59
+ ## Source of Truth
60
+
61
+ The installed framework code (`node_modules/@veap/framework`) overrides any documentation, including this file. If this file disagrees with the code, follow the code.
62
+
63
+ <!-- END:veap-agent-rules -->
@@ -0,0 +1,47 @@
1
+ import { VeapRouter } from "@veap/framework/router";
2
+ import { buildRouteTree } from "@veap/framework/router/server";
3
+ import type { Metadata } from "next";
4
+ import { initializeSystem } from "@/lib/veap";
5
+
6
+ export const dynamic = "force-dynamic";
7
+
8
+ export async function generateMetadata({
9
+ params,
10
+ searchParams,
11
+ }: {
12
+ params: Promise<{ catchAll?: string[] }>;
13
+ searchParams: Promise<Record<string, string>>;
14
+ }): Promise<Metadata> {
15
+ const { catchAll } = await params;
16
+ const sParams = await searchParams;
17
+ const path = catchAll && catchAll.length > 0 ? `/${catchAll.join("/")}` : "/";
18
+
19
+ await initializeSystem();
20
+ const tree = await buildRouteTree(true);
21
+ const result = await tree.generateMetadata(path, sParams);
22
+
23
+ return result;
24
+ }
25
+
26
+ export default async function CatchAllPage({
27
+ params,
28
+ searchParams,
29
+ }: {
30
+ params: Promise<{ catchAll?: string[] }>;
31
+ searchParams: Promise<any>;
32
+ }) {
33
+ const { catchAll } = await params;
34
+ const sParams = await searchParams;
35
+ const path = catchAll && catchAll.length > 0 ? `/${catchAll.join("/")}` : "/";
36
+
37
+ await initializeSystem();
38
+ const tree = await buildRouteTree(true);
39
+
40
+ const result = await VeapRouter({
41
+ tree,
42
+ path,
43
+ searchParams: sParams,
44
+ });
45
+
46
+ return result;
47
+ }
@@ -0,0 +1,111 @@
1
+ import { ApiEnsuredAuth, runApiPipeline } from "@veap/framework/router/server";
2
+ import { initializeSystem } from "@/lib/veap";
3
+
4
+ export const dynamic = "force-dynamic";
5
+
6
+ async function handleRequest(
7
+ request: Request,
8
+ context: { params: Promise<{ catchAll: string[] }> },
9
+ ) {
10
+ // Ensure system is initialized (DB injected, plugins registered)
11
+ await initializeSystem();
12
+
13
+ const { catchAll } = await context.params;
14
+ const path = `/api/${catchAll.join("/")}`;
15
+
16
+ const { buildRouteTree } = await import("@veap/framework/router/server");
17
+ const tree = await buildRouteTree(true);
18
+
19
+ const match = tree.match(path);
20
+
21
+ const node = match?.node as any;
22
+
23
+ if (match?.isExact && node?.route) {
24
+ const method = request.method.toUpperCase();
25
+ const handler = node.route[method] || node.route.handler;
26
+
27
+ if (handler) {
28
+ const middlewares = [];
29
+
30
+ // Collect middlewares from the layout chain
31
+ for (const entry of match.layoutChain) {
32
+ if (entry.middlewares) {
33
+ middlewares.push(...entry.middlewares);
34
+ }
35
+ }
36
+
37
+ // Node middlewares
38
+ if (node.middlewares) {
39
+ middlewares.push(...node.middlewares);
40
+ }
41
+
42
+ // Resolve auth from chain (closest to leaf wins)
43
+ let authRequired = node.auth;
44
+ if (authRequired === undefined) {
45
+ for (let i = match.layoutChain.length - 1; i >= 0; i--) {
46
+ if (match.layoutChain[i].auth !== undefined) {
47
+ authRequired = match.layoutChain[i].auth;
48
+ break;
49
+ }
50
+ }
51
+ }
52
+
53
+ // Roles and permissions
54
+ let roles = node.roles;
55
+ if (!roles) {
56
+ for (let i = match.layoutChain.length - 1; i >= 0; i--) {
57
+ if (match.layoutChain[i].roles) {
58
+ roles = match.layoutChain[i].roles;
59
+ break;
60
+ }
61
+ }
62
+ }
63
+
64
+ let permissions = node.permissions;
65
+ if (!permissions) {
66
+ for (let i = match.layoutChain.length - 1; i >= 0; i--) {
67
+ if (match.layoutChain[i].permissions) {
68
+ permissions = match.layoutChain[i].permissions;
69
+ break;
70
+ }
71
+ }
72
+ }
73
+
74
+ if (authRequired) {
75
+ middlewares.unshift(ApiEnsuredAuth);
76
+ }
77
+
78
+ return await runApiPipeline(
79
+ middlewares as any,
80
+ request,
81
+ {
82
+ params: match.params,
83
+ roles,
84
+ permissions,
85
+ },
86
+ async (req: Request, ctx: any) => {
87
+ return handler(req, ctx);
88
+ },
89
+ );
90
+ } else {
91
+ return new Response(
92
+ JSON.stringify({ error: `Method ${method} Not Allowed` }),
93
+ {
94
+ status: 405,
95
+ headers: { "Content-Type": "application/json" },
96
+ },
97
+ );
98
+ }
99
+ }
100
+
101
+ return new Response(JSON.stringify({ error: "API Route not found" }), {
102
+ status: 404,
103
+ headers: { "Content-Type": "application/json" },
104
+ });
105
+ }
106
+
107
+ export const GET = handleRequest;
108
+ export const POST = handleRequest;
109
+ export const PUT = handleRequest;
110
+ export const PATCH = handleRequest;
111
+ export const DELETE = handleRequest;
@@ -0,0 +1,34 @@
1
+ "use client";
2
+
3
+ import { useTranslation } from "@veap/framework/intl";
4
+ import { useEffect } from "react";
5
+
6
+ export default function ErrorPage({
7
+ error,
8
+ reset,
9
+ }: {
10
+ error: Error & { digest?: string };
11
+ reset: () => void;
12
+ }) {
13
+ const { t } = useTranslation();
14
+ useEffect(() => {
15
+ console.error(error);
16
+ }, [error]);
17
+
18
+ return (
19
+ <div className="flex min-h-screen flex-col items-center justify-center p-4">
20
+ <div className="max-w-md text-center">
21
+ <h2 className="text-2xl font-bold mb-4">{t("Something went wrong!")}</h2>
22
+ <p className="text-muted-foreground mb-6">
23
+ {error.message || t("An unexpected error occurred.")}
24
+ </p>
25
+ <button
26
+ onClick={() => reset()}
27
+ className="px-4 py-2 bg-primary text-primary-foreground rounded-md hover:opacity-90"
28
+ >
29
+ {t("Try Again")}
30
+ </button>
31
+ </div>
32
+ </div>
33
+ );
34
+ }
@@ -0,0 +1,124 @@
1
+ @import "tailwindcss";
2
+ @import "tw-animate-css";
3
+
4
+ @custom-variant dark (&:is(.dark *));
5
+
6
+ @theme inline {
7
+ --color-background: var(--background);
8
+ --color-foreground: var(--foreground);
9
+ --color-card: var(--card);
10
+ --color-card-foreground: var(--card-foreground);
11
+ --color-popover: var(--popover);
12
+ --color-popover-foreground: var(--popover-foreground);
13
+ --color-primary: var(--primary);
14
+ --color-primary-foreground: var(--primary-foreground);
15
+ --color-secondary: var(--secondary);
16
+ --color-secondary-foreground: var(--secondary-foreground);
17
+ --color-muted: var(--muted);
18
+ --color-muted-foreground: var(--muted-foreground);
19
+ --color-accent: var(--accent);
20
+ --color-accent-foreground: var(--accent-foreground);
21
+ --color-destructive: var(--destructive);
22
+ --color-destructive-foreground: var(--destructive-foreground);
23
+ --color-border: var(--border);
24
+ --color-input: var(--input);
25
+ --color-ring: var(--ring);
26
+ --color-chart-1: var(--chart-1);
27
+ --color-chart-2: var(--chart-2);
28
+ --color-chart-3: var(--chart-3);
29
+ --color-chart-4: var(--chart-4);
30
+ --color-chart-5: var(--chart-5);
31
+ --radius-sm: calc(var(--radius) * 0.6);
32
+ --radius-md: calc(var(--radius) * 0.8);
33
+ --radius-lg: var(--radius);
34
+ --radius-xl: calc(var(--radius) * 1.4);
35
+ --radius-2xl: calc(var(--radius) * 1.8);
36
+ --radius-3xl: calc(var(--radius) * 2.2);
37
+ --radius-4xl: calc(var(--radius) * 2.6);
38
+ --color-sidebar: var(--sidebar);
39
+ --color-sidebar-foreground: var(--sidebar-foreground);
40
+ --color-sidebar-primary: var(--sidebar-primary);
41
+ --color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
42
+ --color-sidebar-accent: var(--sidebar-accent);
43
+ --color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
44
+ --color-sidebar-border: var(--sidebar-border);
45
+ --color-sidebar-ring: var(--sidebar-ring);
46
+ }
47
+
48
+ :root {
49
+ --radius: 0.625rem;
50
+ --background: oklch(1 0 0);
51
+ --foreground: oklch(0.145 0 0);
52
+ --card: oklch(1 0 0);
53
+ --card-foreground: oklch(0.145 0 0);
54
+ --popover: oklch(1 0 0);
55
+ --popover-foreground: oklch(0.145 0 0);
56
+ --primary: oklch(0.205 0 0);
57
+ --primary-foreground: oklch(0.985 0 0);
58
+ --secondary: oklch(0.97 0 0);
59
+ --secondary-foreground: oklch(0.205 0 0);
60
+ --muted: oklch(0.97 0 0);
61
+ --muted-foreground: oklch(0.556 0 0);
62
+ --accent: oklch(0.97 0 0);
63
+ --accent-foreground: oklch(0.205 0 0);
64
+ --destructive: oklch(0.577 0.245 27.325);
65
+ --border: oklch(0.922 0 0);
66
+ --input: oklch(0.922 0 0);
67
+ --ring: oklch(0.708 0 0);
68
+ --chart-1: oklch(0.646 0.222 41.116);
69
+ --chart-2: oklch(0.6 0.118 184.704);
70
+ --chart-3: oklch(0.398 0.07 227.392);
71
+ --chart-4: oklch(0.828 0.189 84.429);
72
+ --chart-5: oklch(0.769 0.188 70.08);
73
+ --sidebar: oklch(0.985 0 0);
74
+ --sidebar-foreground: oklch(0.145 0 0);
75
+ --sidebar-primary: oklch(0.205 0 0);
76
+ --sidebar-primary-foreground: oklch(0.985 0 0);
77
+ --sidebar-accent: oklch(0.97 0 0);
78
+ --sidebar-accent-foreground: oklch(0.205 0 0);
79
+ --sidebar-border: oklch(0.922 0 0);
80
+ --sidebar-ring: oklch(0.708 0 0);
81
+ }
82
+
83
+ .dark {
84
+ --background: oklch(0.145 0 0);
85
+ --foreground: oklch(0.985 0 0);
86
+ --card: oklch(0.205 0 0);
87
+ --card-foreground: oklch(0.985 0 0);
88
+ --popover: oklch(0.205 0 0);
89
+ --popover-foreground: oklch(0.985 0 0);
90
+ --primary: oklch(0.922 0 0);
91
+ --primary-foreground: oklch(0.205 0 0);
92
+ --secondary: oklch(0.269 0 0);
93
+ --secondary-foreground: oklch(0.985 0 0);
94
+ --muted: oklch(0.269 0 0);
95
+ --muted-foreground: oklch(0.708 0 0);
96
+ --accent: oklch(0.269 0 0);
97
+ --accent-foreground: oklch(0.985 0 0);
98
+ --destructive: oklch(0.704 0.191 22.216);
99
+ --border: oklch(1 0 0 / 10%);
100
+ --input: oklch(1 0 0 / 15%);
101
+ --ring: oklch(0.556 0 0);
102
+ --chart-1: oklch(0.488 0.243 264.376);
103
+ --chart-2: oklch(0.696 0.17 162.48);
104
+ --chart-3: oklch(0.769 0.188 70.08);
105
+ --chart-4: oklch(0.627 0.265 303.9);
106
+ --chart-5: oklch(0.645 0.246 16.439);
107
+ --sidebar: oklch(0.205 0 0);
108
+ --sidebar-foreground: oklch(0.985 0 0);
109
+ --sidebar-primary: oklch(0.488 0.243 264.376);
110
+ --sidebar-primary-foreground: oklch(0.985 0 0);
111
+ --sidebar-accent: oklch(0.269 0 0);
112
+ --sidebar-accent-foreground: oklch(0.985 0 0);
113
+ --sidebar-border: oklch(1 0 0 / 10%);
114
+ --sidebar-ring: oklch(0.556 0 0);
115
+ }
116
+
117
+ @layer base {
118
+ * {
119
+ @apply border-border outline-ring/50;
120
+ }
121
+ body {
122
+ @apply bg-background text-foreground;
123
+ }
124
+ }
@@ -0,0 +1,71 @@
1
+ import type { Metadata } from "next";
2
+ import { Geist, Geist_Mono } from "next/font/google";
3
+ import "./globals.css";
4
+ import { I18nProvider } from "@veap/framework/intl/server";
5
+ import { ExtensionPoint, getPathPrefix } from "@veap/framework/plugins/server";
6
+ import { AppProvider } from "@veap/framework/react";
7
+ import { cookies } from "next/headers";
8
+ import { initializeSystem } from "@/lib/veap";
9
+ import {
10
+ getCurrentSession,
11
+ isSystemInstalled,
12
+ } from "@veap/framework/auth/server";
13
+
14
+ const geistSans = Geist({
15
+ variable: "--font-geist-sans",
16
+ subsets: ["latin"],
17
+ });
18
+
19
+ const geistMono = Geist_Mono({
20
+ variable: "--font-geist-mono",
21
+ subsets: ["latin"],
22
+ });
23
+
24
+ export const metadata: Metadata = {
25
+ title: "Veap App",
26
+ description: "Built with Veap framework",
27
+ };
28
+
29
+ // The whole layout is request-time work: it boots Veap (providers + plugin
30
+ // context), reads cookies and renders plugin extension points. Without this,
31
+ // Next.js prerenders /_not-found during `next build` while
32
+ // NEXT_PHASE=phase-production-build - in which `Application.bootstrap()`
33
+ // intentionally skips provider boot - and context-bound helpers (e.g.
34
+ // getPathPrefix()) throw "Context is not bound".
35
+ export const dynamic = "force-dynamic";
36
+
37
+ export default async function RootLayout({
38
+ children,
39
+ }: Readonly<{
40
+ children: React.ReactNode;
41
+ }>) {
42
+ await initializeSystem();
43
+ const installed = await isSystemInstalled();
44
+
45
+ const session = installed
46
+ ? await getCurrentSession()
47
+ : { session: null, user: null };
48
+
49
+ const pathPrefix = await getPathPrefix();
50
+
51
+ const cookieStore = await cookies();
52
+ const locale = cookieStore.get("VEAP_LOCALE")?.value || "en";
53
+
54
+ return (
55
+ <html lang={locale} suppressHydrationWarning>
56
+ <body
57
+ className={`${geistSans.variable} ${geistMono.variable} antialiased`}
58
+ >
59
+ <I18nProvider>
60
+ <AppProvider initialSession={session} prefix={pathPrefix}>
61
+ <div className="min-h-screen relative bg-background">
62
+ <ExtensionPoint target="app" point="before-content" />
63
+ {children}
64
+ <ExtensionPoint target="app" point="after-content" />
65
+ </div>
66
+ </AppProvider>
67
+ </I18nProvider>
68
+ </body>
69
+ </html>
70
+ );
71
+ }