create-veap 0.1.4 → 0.3.1

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.
@@ -11,7 +11,7 @@ This project is a **Veap** application: the official `create-next-app` base plus
11
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
12
  - `app/api/[...catchAll]/route.ts` - API pipeline: middlewares, auth, roles/permissions resolved from the route tree.
13
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).
14
+ - `plugins/` - workspace packages (pnpm workspace / npm workspaces).
15
15
  - `migrations/` - app migrations (`veap make:migration`).
16
16
 
17
17
  ## Critical Contracts (do not break)
@@ -0,0 +1,132 @@
1
+ # {{name}}
2
+
3
+ A modern full-stack web application built with [Veap](https://veap.pl) and Next.js 16 (App Router & React Server Components).
4
+
5
+ ## What is Veap?
6
+
7
+ **Veap** is a modular application framework built on top of the Next.js App Router. It combines Next.js frontend flexibility with robust, enterprise-grade backend architectural primitives:
8
+
9
+ - **Clean Architecture & Service Providers**: Inversion of control container and modular service providers configured in `lib/veap.ts`.
10
+ - **Virtual Routing**: Dynamic route tree serving installed plugin routes and application routes through `app/[[...catchAll]]/page.tsx` and `app/api/[...catchAll]/route.ts`.
11
+ - **Plugin System**: Isolated, composable packages located in `plugins/` with declarative extensions and widgets.
12
+ - **ActiveRecord ORM**: Eloquent-style models and query builder backed by Knex with transactions and migrations.
13
+ - **Authentication & RBAC**: Built-in session management, user models, role-based access control, and password hashing.
14
+ - **Storage & Communication**: Unified file storage drivers and mail transports.
15
+
16
+ ---
17
+
18
+ ## Getting Started
19
+
20
+ ### 1. Requirements
21
+
22
+ - **Node.js** `>= 22` or **Bun** `>= 1.2`
23
+ - Supported package managers: `bun`, `pnpm`, `npm`, or `yarn`
24
+
25
+ ### 2. Environment Configuration
26
+
27
+ During project initialization, a default `.env` file is generated with a secure random `ENCRYPTION_KEY` and a local SQLite database configuration:
28
+
29
+ ```bash
30
+ DATABASE_CLIENT=sqlite3
31
+ DATABASE_URL=./storage/veap.sqlite
32
+ ENCRYPTION_KEY=<generated-key>
33
+ ```
34
+
35
+ > **Important**: The application enforces fail-fast verification on `ENCRYPTION_KEY`. Never commit `.env` to version control.
36
+
37
+ ### 3. Run the Development Server
38
+
39
+ Start the local development server:
40
+
41
+ ```bash
42
+ # Using bun
43
+ bun dev
44
+
45
+ # Or using pnpm
46
+ pnpm dev
47
+
48
+ # Or using npm
49
+ npm run dev
50
+ ```
51
+
52
+ Open [http://localhost:3000](http://localhost:3000) with your browser to view the application.
53
+
54
+ ---
55
+
56
+ ## Available Scripts
57
+
58
+ | Command | Description |
59
+ | --- | --- |
60
+ | `bun dev` | Starts the Next.js development server with Turbopack |
61
+ | `bun run build` | Builds the application for production |
62
+ | `bun run start` | Runs the production build |
63
+ | `bun run lint` | Runs ESLint checks |
64
+
65
+ *(Replace `bun` with `pnpm` or `npm run` depending on your package manager)*
66
+
67
+ ---
68
+
69
+ ## Veap CLI
70
+
71
+ The `veap` CLI manages plugins, database migrations, and framework components:
72
+
73
+ ```bash
74
+ # Add and register a plugin package
75
+ veap add <plugin-name>
76
+
77
+ # Scaffold a new workspace plugin in plugins/
78
+ veap make:plugin <plugin-name>
79
+
80
+ # Create a new database migration
81
+ veap make:migration <migration-name>
82
+
83
+ # Scaffold a new site layout template
84
+ veap make:template <template-name>
85
+
86
+ # Re-synchronize and generate lib/plugins.gen.ts
87
+ veap register
88
+
89
+ # Generate Docker deployment configuration
90
+ veap docker
91
+ ```
92
+
93
+ ---
94
+
95
+ ## Project Structure
96
+
97
+ ```
98
+ ├── app/
99
+ │ ├── (site)/ # Declarative site layout route group
100
+ │ ├── [[...catchAll]]/ # Veap virtual page router
101
+ │ ├── api/[...catchAll]/ # Veap API pipeline (middlewares, RBAC, auth)
102
+ │ ├── storage/ # Local storage route handler
103
+ │ ├── layout.tsx # Root layout shell with force-dynamic bootstrap
104
+ │ └── globals.css # Tailwind CSS styling
105
+ ├── components/ # Reusable UI components (SiteLayout, Navbar, Footer)
106
+ ├── lib/
107
+ │ ├── veap.ts # Composition root: Application configuration & bootstrap
108
+ │ └── plugins.gen.ts # Generated plugin registry (managed by veap register)
109
+ ├── migrations/ # Database migrations
110
+ ├── plugins/ # Local workspace plugins
111
+ ├── storage/ # Local SQLite database & uploaded files
112
+ ├── AGENTS.md # AI coding agent instructions and architectural contracts
113
+ └── next.config.ts # Next.js configuration with Veap & Turbopack support
114
+ ```
115
+
116
+ ---
117
+
118
+ ## Documentation & Resources
119
+
120
+ Explore the official Veap documentation to learn more about architecture, plugins, and guides:
121
+
122
+ - **Official Website**: [https://veap.pl](https://veap.pl)
123
+ - **Documentation**: [https://veap.pl/docs](https://veap.pl/docs)
124
+ - [Getting Started](https://veap.pl/docs/getting-started/introduction)
125
+ - [Architecture & Fundamentals](https://veap.pl/docs/fundamentals/architecture)
126
+ - [Virtual Routing & Middleware](https://veap.pl/docs/routing/routing)
127
+ - [Database & ORM](https://veap.pl/docs/data/database)
128
+ - [Authentication & RBAC](https://veap.pl/docs/auth)
129
+ - [Authoring Plugins](https://veap.pl/docs/plugins/creating-plugins)
130
+ - [CLI Reference](https://veap.pl/docs/reference/cli)
131
+ - **GitHub Repository**: [https://github.com/veapjs/veap](https://github.com/veapjs/veap)
132
+ - **Community Discussions**: [https://github.com/veapjs/veap/discussions](https://github.com/veapjs/veap/discussions)
@@ -0,0 +1,10 @@
1
+ import { SiteLayout } from "@/components/site-layout";
2
+ import type { ReactNode } from "react";
3
+
4
+ export default function NativeSiteLayout({
5
+ children,
6
+ }: {
7
+ children: ReactNode;
8
+ }) {
9
+ return <SiteLayout>{children}</SiteLayout>;
10
+ }
@@ -1,47 +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
- }
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
+ }
@@ -1,111 +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;
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;
@@ -1,34 +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
- }
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
+ }