sparkzen 1.0.0 → 1.0.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.
package/README.md ADDED
@@ -0,0 +1,294 @@
1
+ # SparkZen — Official Documentation
2
+
3
+ **SparkZen** is a high‑performance TypeScript framework focused on extreme simplicity, aggressively smooth DX, and stylish logs that make any developer smile. Designed for builders who want fast, scalable APIs with an organized architecture — *without corporate bureaucracy*.
4
+
5
+ Minimal on the outside, powerful on the inside.
6
+
7
+ ---
8
+
9
+ # 🚀 1. Overview
10
+
11
+ SparkZen follows three core principles:
12
+
13
+ * **Zero initial configuration** – import → use → run.
14
+ * **Folder‑driven architecture** – routes are auto‑registered.
15
+ * **DX first** – schemas, middlewares, and handlers fully typed.
16
+
17
+ Perfect for modern REST APIs, microservices, and backends that demand clarity.
18
+
19
+ ---
20
+
21
+ # 📦 2. Creating a New SparkZen Project
22
+
23
+ Kickstart a new SparkZen application using the official CLI:
24
+
25
+ ```bash
26
+ npx sparkzen init
27
+ ```
28
+
29
+ The CLI will walk you through scaffolding, project structure, and essentials.
30
+
31
+ ---
32
+
33
+ Installation
34
+
35
+ ```bash
36
+ npm install sparkzen
37
+ ```
38
+
39
+ or:
40
+
41
+ ```bash
42
+ yarn add sparkzen
43
+ ```
44
+
45
+ ---
46
+
47
+ # 🏗️ 3. API Initialization
48
+
49
+ ```ts
50
+ import sparkzen from "sparkzen";
51
+
52
+ async function spark() {
53
+ try {
54
+ const app = await sparkzen();
55
+ await app.listen({ port: 3000 });
56
+ } catch (error) {
57
+ console.error(error);
58
+ process.exit(1);
59
+ }
60
+ }
61
+
62
+ spark();
63
+ ```
64
+
65
+ When you run this, SparkZen automatically:
66
+
67
+ * initializes the server
68
+ * loads all routes inside `src/routes`
69
+ * applies default middlewares
70
+ * prints clean, structured logs
71
+
72
+ ---
73
+
74
+ # 📂 4. Folder Structure
75
+
76
+ SparkZen uses a modern, intuitive pattern:
77
+
78
+ ```
79
+ src/
80
+ └── routes/
81
+ ├── users/
82
+ │ ├── get.ts → GET /users
83
+ │ └── [id]/
84
+ │ └── post.ts → POST /users/:id
85
+ └── auth/
86
+ └── login/post.ts → POST /auth/login
87
+ ```
88
+
89
+ 📌 **Golden Rule:**
90
+
91
+ * File name → HTTP method
92
+ * Folder name → endpoint path
93
+ * `[id]` → dynamic param
94
+
95
+ Simple. Beautiful. Powerful.
96
+
97
+ ---
98
+
99
+ # 🧩 5. Handlers
100
+
101
+ Handlers are the core of each route.
102
+
103
+ ### Basic Example
104
+
105
+ ```ts
106
+ import type { Handler } from "sparkzen";
107
+
108
+ const handler: Handler = async (request, reply) => {
109
+ return reply.status(200).send({ message: "Hello, SparkZen!" });
110
+ };
111
+
112
+ export default handler;
113
+ ```
114
+
115
+ ### What you get inside a handler?
116
+
117
+ * `request.params`
118
+ * `request.body`
119
+ * `request.query`
120
+ * `request.headers`
121
+
122
+ All fully typed.
123
+
124
+ ---
125
+
126
+ # 🛡️ 6. Schemas (TypeBox Powered)
127
+
128
+ SparkZen uses **TypeBox** for strong validation + auto‑generated TypeScript types.
129
+
130
+ ### Example Schema
131
+
132
+ ```ts
133
+ export const schema = {
134
+ params: T.Object({
135
+ id: T.Number({ description: "User ID", examples: [1] }),
136
+ }),
137
+
138
+ body: T.Object({
139
+ name: T.String({ minLength: 2 }),
140
+ nick: T.Optional(T.String()),
141
+ }),
142
+
143
+ response: {
144
+ 200: T.Object({
145
+ ok: T.Boolean(),
146
+ message: T.String(),
147
+ received: T.Object({
148
+ id: T.Number(),
149
+ name: T.String(),
150
+ nick: T.Optional(T.String()),
151
+ }),
152
+ }),
153
+ },
154
+ };
155
+ ```
156
+
157
+ Define once → SparkZen types everything.
158
+
159
+ ---
160
+
161
+ # ⚙️ 7. Middlewares
162
+
163
+ Clean, lightweight, powerful.
164
+
165
+ ### Example
166
+
167
+ ```ts
168
+ export const middlewares: Middleware<typeof schema> = [
169
+ (request, _reply, done) => {
170
+ console.log("[SparkZen] Incoming request:", {
171
+ params: request.params,
172
+ body: request.body,
173
+ });
174
+ done();
175
+ },
176
+
177
+ (_request, _reply, done) => {
178
+ console.log("[SparkZen] Second middleware executed");
179
+ done();
180
+ },
181
+ ];
182
+ ```
183
+
184
+ Each middleware receives:
185
+
186
+ * `request`
187
+ * `reply`
188
+ * `done()`
189
+
190
+ Similar to Fastify — but tighter and more TS‑integrated.
191
+
192
+ ---
193
+
194
+ # 🔥 8. Full Route (Schema + Middleware + Handler)
195
+
196
+ ```ts
197
+ import { Middleware, T, type Handler } from "sparkzen";
198
+
199
+ export const schema = {
200
+ params: T.Object({
201
+ id: T.Number({ description: "User ID", examples: [1] }),
202
+ }),
203
+ body: T.Object({
204
+ name: T.String({ minLength: 2 }),
205
+ nick: T.Optional(T.String()),
206
+ }),
207
+ response: {
208
+ 200: T.Object({
209
+ ok: T.Boolean(),
210
+ message: T.String(),
211
+ received: T.Object({
212
+ id: T.Number(),
213
+ name: T.String(),
214
+ nick: T.Optional(T.String()),
215
+ }),
216
+ }),
217
+ },
218
+ };
219
+
220
+ export const middlewares: Middleware<typeof schema> = [
221
+ (request, _reply, done) => {
222
+ console.log("[SparkZen] Incoming request:", {
223
+ params: request.params,
224
+ body: request.body,
225
+ });
226
+ done();
227
+ },
228
+ () => {
229
+ console.log("[SparkZen] Second middleware executed");
230
+ },
231
+ ];
232
+
233
+ const handler: Handler<typeof schema> = async (request, reply) => {
234
+ const { id } = request.params;
235
+ const { name, nick } = request.body;
236
+
237
+ return reply.status(200).send({
238
+ ok: true,
239
+ message: "User processed successfully",
240
+ received: { id, name, nick },
241
+ });
242
+ };
243
+
244
+ export default handler;
245
+ ```
246
+
247
+ ---
248
+
249
+ # 📡 9. Boot Logs
250
+
251
+ On startup, SparkZen prints something like:
252
+
253
+ ```
254
+ SPARKZEN INITIALIZING API SPARKZEN
255
+
256
+ SPARKZEN LOADING ROUTES SPARKZEN
257
+
258
+ ROUTE GET /api/users .../src/routes/users/get.ts
259
+ ROUTE POST /api/users/:id .../src/routes/users/[id]/post.ts
260
+
261
+ SPARKZEN STARTING SERVER SPARKZEN
262
+
263
+ SERVER Running at: http://localhost:3000
264
+ ```
265
+
266
+ Logs are designed for:
267
+
268
+ * maximum clarity
269
+ * fast debugging
270
+ * clean aesthetics
271
+
272
+ ---
273
+
274
+ # 🧠 10. Best Practices
275
+
276
+ * Always use **schemas** for strong typing
277
+ * Keep **handlers minimal**
278
+ * Group middlewares by responsibility
279
+ * Keep route folders small and focused
280
+ * Name files after HTTP methods (SparkZen standard ❤️)
281
+
282
+ ---
283
+
284
+ # 🔮 11. SparkZen Roadmap
285
+
286
+ Planned features:
287
+
288
+ * Advanced CLI
289
+ * Official plugins
290
+ * Simplified authentication utilities
291
+ * Auto‑generated documentation
292
+ * Edge‑runtime adapters
293
+
294
+ SparkZen was built to scale — and you scale with it.
package/dist/cli/index.js CHANGED
@@ -9,21 +9,38 @@ import { Command } from "commander";
9
9
  import degit from "degit";
10
10
  import enquirer from "enquirer";
11
11
  import fs from "fs-extra";
12
+ import { bgBlue, blue, bold, error, logfy, success } from "logfy-x";
12
13
  import path from "path";
13
14
  var { prompt } = enquirer;
14
- var init = new Command().name("init").description("Initialize a new SparkZen project").action(async () => {
15
- const { projectName, packageManager, installNow } = await prompt([
15
+ var promptOptions = async () => {
16
+ const terminalWidth = process.stdout.columns || 80;
17
+ const line1 = " \u{1F680} Welcome to SparkZen! \u{1F680} ";
18
+ const line2 = " Let's set up your new project. ";
19
+ const padFor = (text) => {
20
+ const available = Math.max(0, terminalWidth - text.length);
21
+ const half = Math.floor(available / 2);
22
+ const left = " ".repeat(half);
23
+ const right = " ".repeat(available - half);
24
+ return bgBlue(left) + text + bgBlue(right);
25
+ };
26
+ logfy(`
27
+ ${padFor(line1)}
28
+ ${padFor(line2)}
29
+ `, { style: "bold blue -underline" });
30
+ const answers = await prompt([
16
31
  {
17
32
  type: "input",
18
33
  name: "projectName",
19
34
  message: "Project name:",
20
- required: true
35
+ required: true,
36
+ validate: (v) => v && v.trim() ? true : "Please enter a project name"
21
37
  },
22
38
  {
23
39
  type: "select",
24
40
  name: "packageManager",
25
41
  message: "Package manager:",
26
- choices: ["npm", "pnpm", "yarn", "bun"]
42
+ choices: ["npm", "pnpm", "yarn", "bun"],
43
+ initial: 0
27
44
  },
28
45
  {
29
46
  type: "confirm",
@@ -32,45 +49,84 @@ var init = new Command().name("init").description("Initialize a new SparkZen pro
32
49
  initial: true
33
50
  }
34
51
  ]);
35
- const targetPath = path.resolve(process.cwd(), projectName);
36
- console.log("\u{1F4C1} Creating project folder...");
37
- await fs.mkdir(targetPath, { recursive: true });
38
- console.log("\u{1F4E6} Copying template...");
52
+ return answers;
53
+ };
54
+ var cloneProjectTemplate = async (projectName, targetPath) => {
55
+ console.log(`
56
+ ${bgBlue(bold(" \u{1F4C1} CREATING "))} ${projectName}`);
57
+ await fs.mkdirp(path.dirname(targetPath));
58
+ console.log(`
59
+ ${bgBlue(bold(" \u{1F501} COPYING "))} Default template`);
39
60
  const template = degit("MatheusSantos360/sparkzen-templates/default", {
40
61
  cache: false,
41
62
  force: true
42
63
  });
43
- await template.clone(projectName);
64
+ try {
65
+ await template.clone(targetPath);
66
+ } catch (err) {
67
+ throw new Error(`Failed to clone template: ${err.message}`);
68
+ }
44
69
  const pkgJsonPath = path.join(targetPath, "package.json");
45
- const pkgJson = JSON.parse(await fs.readFile(pkgJsonPath, "utf-8"));
46
- pkgJson.name = projectName;
47
- await fs.writeFile(pkgJsonPath, JSON.stringify(pkgJson, null, 2));
70
+ if (!await fs.pathExists(pkgJsonPath)) {
71
+ error("NOT FOUND", "package.json not found in the template.");
72
+ return;
73
+ }
74
+ try {
75
+ const pkgJson = JSON.parse(await fs.readFile(pkgJsonPath, "utf-8"));
76
+ pkgJson.name = projectName;
77
+ await fs.writeFile(pkgJsonPath, JSON.stringify(pkgJson, null, 2));
78
+ } catch (err) {
79
+ error("ERROR", `Failed to update package.json: ${err.message}`);
80
+ }
81
+ };
82
+ var installDependencies = (packageManager, targetPath) => {
48
83
  console.log(`
49
- \u{1F680} Project "${projectName}" created successfully!`);
50
- if (installNow) {
51
- console.log(`\u26A1 Installing dependencies with ${packageManager}...`);
84
+ ${bgBlue(bold(" \u26A1 DEPENDENCIES "))} Installing with ${blue(packageManager)}:
85
+ `);
86
+ try {
52
87
  execSync(`${packageManager} install`, {
53
88
  cwd: targetPath,
54
89
  stdio: "inherit",
55
90
  shell: process.platform === "win32" ? "cmd.exe" : "/bin/sh"
56
91
  });
92
+ } catch (err) {
93
+ throw new Error(`Dependency install failed: ${err.message}`);
94
+ }
95
+ };
96
+ var init = new Command().name("init").description("Initialize a new SparkZen project").action(async () => {
97
+ try {
98
+ const { projectName, packageManager, installNow } = await promptOptions();
99
+ const targetPath = path.resolve(process.cwd(), projectName);
100
+ await cloneProjectTemplate(projectName, targetPath);
101
+ if (installNow) {
102
+ installDependencies(packageManager, targetPath);
103
+ }
104
+ console.log();
105
+ success("CREATED!", `Project "${blue(projectName)}" has been created successfully.`);
106
+ console.log(`
107
+ ${bgBlue(bold(" \u{1F525} NEXT STEPS "))} To get started:
108
+ `);
109
+ console.log(` cd ${blue(projectName)}`);
110
+ console.log(` ${blue(packageManager)} run ${blue("dev")}
111
+ `);
112
+ } catch (err) {
113
+ error("ERROR", err.message);
114
+ process.exit(1);
57
115
  }
58
- console.log(`\u2705 Project ${projectName} created successfully!`);
59
- console.log(`
60
- Next steps:
61
- cd ${projectName}
62
- ${packageManager} run dev`);
63
116
  });
64
117
  var init_default = init;
65
118
 
66
119
  // src/cli/commands/dev.ts
67
120
  import { execSync as execSync2 } from "child_process";
68
121
  import { Command as Command2 } from "commander";
122
+ import { bgBlue as bgBlue2, bold as bold2 } from "logfy-x";
69
123
  import path2 from "path";
70
124
  var dev = new Command2().name("dev").description("Run the project in development mode").action(() => {
71
125
  const projectPath = process.cwd();
72
- const tsxPath = path2.join(projectPath, "node_modules", ".bin", "tsx");
73
- console.log("\u{1F680} Starting dev server...");
126
+ const tsxPath = path2.join(projectPath, "node_modules", ".bin", process.platform === "win32" ? "tsx.cmd" : "tsx");
127
+ console.log(`
128
+ ${bgBlue2(bold2(" \u{1F680} DEV "))} Starting development server...
129
+ `);
74
130
  execSync2(`${tsxPath} watch src/index.ts`, {
75
131
  cwd: projectPath,
76
132
  stdio: "inherit",
@@ -82,16 +138,21 @@ var dev_default = dev;
82
138
  // src/cli/commands/build.ts
83
139
  import { execSync as execSync3 } from "child_process";
84
140
  import { Command as Command3 } from "commander";
141
+ import { bgGreenBright, bold as bold3 } from "logfy-x";
85
142
  import path3 from "path";
86
143
  var build = new Command3().name("build").description("Build the project").action(() => {
87
144
  const projectPath = process.cwd();
88
- console.log("\u{1F680} Building the project...");
145
+ console.log(`${bgGreenBright(bold3(" \u{1F4E6} BUILD "))} Building the project...
146
+ `);
89
147
  const tsupPath = path3.join(process.cwd(), "node_modules", ".bin", "tsup");
90
- execSync3(`${tsupPath} src/index.ts --format esm --dts --out-dir dist`, {
148
+ execSync3(`${tsupPath} src/index.ts --format esm --out-dir dist`, {
91
149
  cwd: projectPath,
92
150
  stdio: "inherit",
93
151
  shell: process.platform === "win32" ? "cmd.exe" : "/bin/sh"
94
152
  });
153
+ console.log(`
154
+ ${bgGreenBright(bold3(" \u2705 BUILD "))} Build completed successfully!
155
+ `);
95
156
  });
96
157
  var build_default = build;
97
158
 
@@ -102,8 +163,12 @@ function registerCommands(program2) {
102
163
  program2.addCommand(build_default);
103
164
  }
104
165
 
166
+ // package.json
167
+ var name = "sparkzen";
168
+ var version = "1.0.2";
169
+
105
170
  // src/cli/index.ts
106
171
  var program = new Command4();
107
- program.name("sparkzen").description("SparkZen CLI").version("1.0.0");
172
+ program.name(name).description("SparkZen CLI").version(version);
108
173
  registerCommands(program);
109
174
  program.parse();
package/dist/index.js CHANGED
@@ -1,5 +1,6 @@
1
1
  // src/core/sparkzen.ts
2
2
  import fastify from "fastify";
3
+ import { bgBlueBright as bgBlueBright2, bgGreenBright, blue, bold as bold3, dim as dim3, error } from "logfy-x";
3
4
 
4
5
  // src/core/routing/isValidRoute.ts
5
6
  var isValidRoute = (routeModule) => {
@@ -20,9 +21,17 @@ var getRouteUrl = (filePath) => {
20
21
  };
21
22
 
22
23
  // src/core/routing/registerRoute.ts
24
+ import { bgBlueBright } from "logfy-x";
25
+ import * as colors from "logfy-x";
23
26
  var registerRoute = (app, routeModule, method, finalPath) => {
24
27
  const routeURL = getRouteUrl(finalPath);
25
- console.log(`Registering route: [${method.toUpperCase()}] ${routeURL}`);
28
+ const methodColors = {
29
+ get: "green",
30
+ post: "yellow",
31
+ put: "blue",
32
+ delete: "red",
33
+ patch: "magenta"
34
+ };
26
35
  const routeOptions = {
27
36
  ...routeModule,
28
37
  method,
@@ -31,6 +40,7 @@ var registerRoute = (app, routeModule, method, finalPath) => {
31
40
  preHandler: routeModule.middlewares
32
41
  };
33
42
  app.route(routeOptions);
43
+ console.log(bgBlueBright(colors.bold(` ROUTE `)) + ` ${colors[methodColors[method]](method.toUpperCase())} ${routeURL} ${colors.dim(finalPath)}`);
34
44
  };
35
45
 
36
46
  // src/core/loadRoutes.ts
@@ -44,6 +54,7 @@ var dynamicImport = async (modulePath) => {
44
54
  };
45
55
 
46
56
  // src/core/loadRoutes.ts
57
+ import { bgRedBright, bold as bold2, dim as dim2, red } from "logfy-x";
47
58
  var loadRoutes = async (app, directory) => {
48
59
  const production = process.env.NODE_ENV === "production";
49
60
  const ext = production ? ".js" : ".ts";
@@ -63,16 +74,38 @@ var loadRoutes = async (app, directory) => {
63
74
  const method = item.name.replace(new RegExp(`${ext}$`), "").toLowerCase();
64
75
  registerRoute(app, routeModule, method, finalPath);
65
76
  } else {
66
- console.warn(`Invalid route module: ${modulePath}`);
77
+ console.log(bgRedBright(bold2(` ROUTE `)) + ` ${red("Invalid Route (No handler exported)")} ${dim2(finalPath)}`);
67
78
  }
68
79
  }
69
80
  }
70
81
  };
71
82
 
72
83
  // src/core/sparkzen.ts
84
+ function sparkzenBanner(message, color) {
85
+ const tag = color(bold3(" SPARKZEN "));
86
+ const line = dim3("\u2500".repeat(10 * 2 + message.length + 2));
87
+ console.log(`
88
+ ${tag} ${bold3(message)} ${tag}
89
+ ${line}`);
90
+ }
73
91
  async function sparkzen() {
92
+ sparkzenBanner("INITIALIZING API", bgBlueBright2);
74
93
  const app = fastify().withTypeProvider();
94
+ sparkzenBanner("LOADING ROUTES", bgGreenBright);
75
95
  await loadRoutes(app);
96
+ console.log();
97
+ const originalListen = app.listen.bind(app);
98
+ app.listen = async function(opts) {
99
+ try {
100
+ sparkzenBanner("STARTING SERVER", bgGreenBright);
101
+ const result = await originalListen(opts);
102
+ console.log(`${bgGreenBright(bold3(" SERVER "))} Running at: ${blue(`http://localhost:${opts.port}`)}
103
+ `);
104
+ return result;
105
+ } catch (err) {
106
+ error("ERROR", err.message);
107
+ }
108
+ };
76
109
  return app;
77
110
  }
78
111
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sparkzen",
3
- "version": "1.0.0",
3
+ "version": "1.0.2",
4
4
  "license": "MIT",
5
5
  "author": "Matheus dos Santos Paixão",
6
6
  "type": "module",
@@ -21,7 +21,8 @@
21
21
  "degit": "^2.8.4",
22
22
  "enquirer": "^2.4.1",
23
23
  "fastify": "^5.6.1",
24
- "fs-extra": "^11.3.2"
24
+ "fs-extra": "^11.3.2",
25
+ "logfy-x": "^1.0.3"
25
26
  },
26
27
  "devDependencies": {
27
28
  "@changesets/cli": "^2.29.7",