easy-starter 1.1.0 → 1.2.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
@@ -33,6 +33,7 @@ The generated boilerplate comes pre-configured with a stack designed for develop
33
33
  * **Database:** [`easy-db-node`](https://www.npmjs.com/package/easy-db-node) - A simple, local JSON-based database perfect for small to medium projects where setting up Postgres/MongoDB is overkill.
34
34
  * **Routing:** [`easy-page-router`](https://www.npmjs.com/package/easy-page-router) - A lightweight, programmatic routing solution.
35
35
  * **Analytics:** [`easy-analytics`](https://www.npmjs.com/package/easy-analytics) - Privacy-friendly, local analytics tracking.
36
+ * **AI-Friendly (.cursorrules):** Pre-configured, LLM-optimized guidelines in the project root so AI coding assistants (Cursor, Windsurf, Copilot, etc.) immediately understand the codebase conventions, API restrictions, routing patterns, and database operations.
36
37
  * **Deployment:** Integrated Github Actions workflow and PM2 deployment script for seamless, automated deployment straight to your own VPS.
37
38
 
38
39
  ## Core Concepts & Examples
@@ -96,6 +97,10 @@ export default function Index() {
96
97
  }
97
98
  ```
98
99
 
100
+ ### 3. AI Integration (`.cursorrules`)
101
+
102
+ This template includes a pre-configured `.cursorrules` file in the root. If you develop using AI assistants such as Cursor, Windsurf, or GitHub Copilot, they will automatically read this file to understand the specific conventions of `easy-starter`.
103
+
99
104
  ## Getting Started
100
105
 
101
106
  Bootstrapping a new project is as simple as running one command:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "easy-starter",
3
- "version": "1.1.0",
3
+ "version": "1.2.0",
4
4
  "description": "A lightweight CLI tool to bootstrap modern fullstack React web projects.",
5
5
  "bin": {
6
6
  "easy-starter": "./bin/index.js"
@@ -15,7 +15,10 @@
15
15
  "typescript",
16
16
  "fullstack",
17
17
  "easy-db",
18
- "parcel"
18
+ "parcel",
19
+ "ai",
20
+ "cursorrules",
21
+ "ai-friendly"
19
22
  ],
20
23
  "author": "Filip Paulů <ing.fenix@seznam.cz>",
21
24
  "license": "MIT",
@@ -0,0 +1,92 @@
1
+ # AI Coding Assistant Guidelines & Project Rules
2
+
3
+ You are an expert AI software engineer pair-programming in a fullstack React application bootstrapped with `easy-starter`.
4
+ Follow these strict rules, project structures, and conventions at all times.
5
+
6
+ ---
7
+
8
+ ## 1. API Architecture & Type Safety (`typed-client-server-api`)
9
+
10
+ All React-to-Express communication must use the RPC contract defined in `types.ts` via the `API` type (`APIDefinition`).
11
+
12
+ ### Naming Conventions for Endpoints
13
+ EVERY endpoint name in `API` **MUST** start with exactly one of the following prefixes:
14
+ * `add...` (Create/Insert operations. Example: `addItem`, `addRecord`)
15
+ * `get...` (Read/Query operations. Example: `getItems`, `getItem`, `getAnalytics`)
16
+ * `update...` (Update/Edit operations. Example: `updateItem`, `updateUser`)
17
+ * `remove...` (Delete/Remove operations. Example: `removeItem`, `removePost`)
18
+
19
+ **CRITICAL**: Do NOT use other prefixes (e.g., `create`, `delete`, `post`, `fetch`, `save`).
20
+
21
+ ### Frontend API Invocation Methods
22
+ You have exactly three (3) methods to trigger API endpoints on the frontend. Use them strictly according to these boundaries:
23
+
24
+ | Method | Import | Use Case | Pattern / Example |
25
+ | :--- | :--- | :--- | :--- |
26
+ | **`useSSRApi`** | `import { useSSRApi } from "../services/api";` | **ONLY** inside React components during initial SSR render. Enables server-side rendering and hydration without flash-of-loading or spinners. | `const [items, error, isLoading, reload] = useSSRApi.getItems({});` |
27
+ | **`useApi`** | `import { useApi } from "../services/api";` | **ONLY** inside React components for client-side-only reactive data fetching (e.g., late loaded components). | `const [items, error, isLoading, reload] = useApi.getItems({});` |
28
+ | **`api`** | `import { api } from "../services/api";` | **ONLY** inside event handlers (e.g., `onClick`, `onSubmit`), utility/non-React functions, or async triggers. Returns a Promise resolving to `[data, error]`. | `const [newId, err] = await api.addItem({ name: "Foo" });` |
29
+
30
+ **NEVER** use standard `fetch`, `axios`, or any other fetcher libraries for internal API calls.
31
+
32
+ ---
33
+
34
+ ## 2. Routing & Navigation (`easy-page-router`)
35
+
36
+ This project uses `easy-page-router`, a lightweight, universal routing library.
37
+
38
+ ### Route Configurations
39
+ * Router paths are defined in `src/Router.tsx` within the `renderPage({ path })` callback of the `Router` component.
40
+ * The `path` parameter is a string array (`string[]`) of URL segments:
41
+ * `/` -> `path.length === 0`
42
+ * `/admin` -> `path[0] === "admin"`
43
+ * `/users/123` -> `path[0] === "users" && path[1]` (where `path[1]` is the user ID segment).
44
+
45
+ ### Navigation
46
+ 1. **Declarative Navigation**: Use `<Link href="/path">Text</Link>` imported from `"easy-page-router/react"`.
47
+ 2. **Programmatic Navigation**: Destructure `push` from the `useRouter()` hook imported from `"easy-page-router/react"`.
48
+
49
+ ```tsx
50
+ import { Link, useRouter } from "easy-page-router/react";
51
+
52
+ // Declarative:
53
+ <Link href="/admin">Go to Admin</Link>
54
+
55
+ // Programmatic:
56
+ const { push } = useRouter();
57
+ const onSubmit = async () => {
58
+ await saveSomething();
59
+ push("/dashboard");
60
+ };
61
+ ```
62
+
63
+ ---
64
+
65
+ ## 3. Database Operations (`easy-db-node`)
66
+
67
+ The local database stores records as JSON files.
68
+
69
+ ### Schema Definition
70
+ * The database collections/tables must be structured and typed in `types.ts` inside the exported `Database` type.
71
+ ```typescript
72
+ export type Database = {
73
+ item: Item;
74
+ user: User;
75
+ };
76
+ ```
77
+
78
+ ### Writing to Database
79
+ * **Auto-generated ID (Standard Insertion)**:
80
+ * Use `insert(table, data)`.
81
+ * **Rule**: Do **NOT** pass `_id` in the input data. `easy-db-node` ignores any `_id` property passed to `insert()` and automatically generates and returns the unique string ID.
82
+ ```typescript
83
+ // CORRECT:
84
+ const newId = await insert("item", { name: "Hello", value: 10 });
85
+ ```
86
+
87
+ * **Custom / Specific ID (Explicit Key)**:
88
+ * To insert or overwrite a record with a **specific/predefined ID**, you **MUST** use `update(table, id, data)` instead of `insert`.
89
+ ```typescript
90
+ // CORRECT (custom ID):
91
+ await update("item", "explicit-custom-id", { name: "Custom ID Item", value: 99 });
92
+ ```
@@ -83,6 +83,10 @@ export default function Index() {
83
83
  }
84
84
  ```
85
85
 
86
+ ### 3. AI Integration (`.cursorrules`)
87
+
88
+ This template includes a pre-configured `.cursorrules` file in the root. If you develop using AI assistants such as Cursor, Windsurf, or GitHub Copilot, they will automatically read this file to understand the specific conventions of `easy-starter`:
89
+
86
90
  ## Scripts
87
91
 
88
92
  * `npm run dev` - Starts the development server.
@@ -94,7 +98,8 @@ export default function Index() {
94
98
 
95
99
  * `/src` - Your React frontend code.
96
100
  * `/server` - Your Express backend code.
97
- * `/types.d.ts` - Shared TypeScript definitions for your API and Database.
101
+ * `/types.ts` - Shared TypeScript definitions for your API and Database.
102
+ * `/.cursorrules` - Rules instructing AI coding assistants how to interact with the project stack.
98
103
  * `/public` - Static assets (images, fonts, etc.).
99
104
  * `/easy-db` - Where your local database JSON files are stored.
100
105
 
package/template/env CHANGED
@@ -1,8 +1,10 @@
1
- NODE_ENV=development
1
+ NODE_ENV=development # development/production
2
2
  SERVER_PORT=1111
3
3
  ADMIN_PASSWORD=admin
4
4
 
5
5
  # deploy
6
- SSH_HOST=***
7
- SSH_USERNAME=***
8
- SSH_USERNAME=***
6
+ SSH_HOST=[IP_ADDRESS]
7
+ SSH_USERNAME=root
8
+ SSH_PASSWORD=[PASSWORD]
9
+ SERVER_URL=https://your-production-url.com/
10
+ PM2_APP_NAME=easy-starter-app
@@ -9,16 +9,21 @@ const username = process.env.SSH_USERNAME;
9
9
  const password = process.env.SSH_PASSWORD;
10
10
 
11
11
  // Configuration for your target server
12
- const server = "https://your-production-url.com/";
13
- const pm2AppName = "easy-starter-app"; // The name of the process in PM2
14
- const sshPath = resolve("/", "root", "your-production-url.com"); // Path on the remote VPS
12
+ const server = process.env.SERVER_URL;
13
+ const pm2AppName = process.env.PM2_APP_NAME;
14
+ const sshPath = resolve("/", "root", pm2AppName || ""); // Path on the remote VPS
15
15
 
16
16
  const path = resolve(process.cwd()); // Local project path
17
17
  const ignoreFiles = [
18
18
  ".git",
19
+ ".github",
20
+ ".parcel-cache",
19
21
  "node_modules",
20
22
  "scripts",
21
- ".github"
23
+ "easy-db",
24
+ "easy-db-files",
25
+ "easy-db-backup",
26
+ ".DS_Store",
22
27
  ];
23
28
 
24
29
  // Ensure credentials are provided
@@ -73,20 +78,28 @@ async function deploy() {
73
78
  console.log(`✔ Build project.\n`);
74
79
 
75
80
  // PM2
76
- try {
77
- await sshExec(ssh, `pm2 reload ${pm2AppName}`);
78
- } catch (e) {
79
- console.log(`✘ ${pm2AppName} is not running in PM2!`);
80
- await sshExec(ssh, `pm2 start npm --name "${pm2AppName}" -- start`, `${pm2AppName} could not be registered in PM2!`);
81
+ if (pm2AppName) {
82
+ try {
83
+ await sshExec(ssh, `pm2 reload ${pm2AppName}`);
84
+ } catch (e) {
85
+ console.log(`✘ ${pm2AppName} is not running in PM2!`);
86
+ await sshExec(ssh, `pm2 start npm --name "${pm2AppName}" -- start`, `${pm2AppName} could not be registered in PM2!`);
87
+ }
88
+ await sshExec(ssh, `pm2 list`);
89
+ } else {
90
+ console.log(`i PM2 step is skipped because PM2_APP_NAME is not defined.\n`);
81
91
  }
82
- await sshExec(ssh, `pm2 list`);
83
92
 
84
93
  ssh.dispose();
85
94
  console.log(`✔ SSH connection is closed.\n`);
86
95
 
87
- if (!await serverCheck(server))
88
- throw new Error(`Server ${server} is not available!`);
89
- console.log(`✔ Server ${server} is running.\n`);
96
+ if (server) {
97
+ if (!await serverCheck(server))
98
+ throw new Error(`Server ${server} is not available!`);
99
+ console.log(`✔ Server ${server} is running.\n`);
100
+ } else {
101
+ console.log(`i Server check is skipped because SERVER_URL is not defined.\n`);
102
+ }
90
103
 
91
104
  console.log(`i Server was ${(new Date().getTime() - timeServerOff) / 1000}s off!\n`);
92
105
 
@@ -1,6 +1,7 @@
1
1
  import React, { useEffect, useState } from "react";
2
2
  import { useGroup, GroupTable, getGroupedCount } from "easy-analytics/react";
3
3
  import { Link } from "easy-page-router/react";
4
+
4
5
  import { useHead } from "../../services/common";
5
6
  import { api, handleError } from "../../services/api";
6
7
 
@@ -18,6 +18,6 @@
18
18
  "include": [
19
19
  "src/**/*",
20
20
  "server/**/*",
21
- "types.d.ts"
21
+ "types.ts"
22
22
  ]
23
23
  }
@@ -22,5 +22,5 @@ export type Item = {
22
22
  // The Database type defines the structure of your JSON files managed by easy-db-node.
23
23
  // Each property represents a 'table' (or a separate JSON file).
24
24
  export type Database = {
25
- item: Omit<Item, "_id">;
25
+ item: Item;
26
26
  };