@arikajs/docs 0.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 ArikaJs
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,105 @@
1
+ ## Arika Docs
2
+
3
+ `@arikajs/docs` is the **documentation engine** of the ArikaJS ecosystem.
4
+
5
+ It allows you to automatically generate high-quality API documentation, Postman collections, and OpenAPI specifications by analyzing your application's routes.
6
+
7
+ The goal of this package is to eliminate the manual work of keeping documentation in sync with your codebase.
8
+
9
+ ---
10
+
11
+ ### Status
12
+
13
+ - **Stage**: Experimental / v0.x
14
+ - **Scope (v0.x)**:
15
+ - Route metadata extraction from `@arikajs/router`
16
+ - Postman Collection (v2.1.0) generation
17
+ - Arika-themed interactive HTML documentation
18
+ - Markdown (DOCS.md) generation
19
+ - OpenAPI 3.0 specification generation
20
+
21
+ ---
22
+
23
+ ## Features
24
+
25
+ - **Multi-Format Generation**
26
+ - **HTML**: A premium, interactive web page for your API.
27
+ - **Postman**: Ready-to-import JSON collection with pre-configured headers.
28
+ - **OpenAPI**: Industry-standard Swagger/OpenAPI 3.0 specification.
29
+ - **Markdown**: Clean, readable `DOCS.md` for GitHub or local documentation.
30
+
31
+ - **Route Analysis**
32
+ - Automatically groups endpoints by prefix (e.g., `api`, `admin`).
33
+ - Captures route names, methods, and full path hierarchies.
34
+ - Displays middleware information for each endpoint.
35
+
36
+ - **Environment Support**
37
+ - Automatically generates Postman environment JSON with your `base_url`.
38
+
39
+ ---
40
+
41
+ ## Installation
42
+
43
+ ```bash
44
+ npm install @arikajs/docs
45
+ ```
46
+
47
+ This package is designed to be used with the ArikaJS CLI but can also be used as a standalone library.
48
+
49
+ ---
50
+
51
+ ## Usage (via CLI)
52
+
53
+ The easiest way to generate documentation is using the ArikaJS CLI:
54
+
55
+ ```bash
56
+ arika docs:generate
57
+ ```
58
+
59
+ This will create a `docs/` directory in your project root containing all generation artifacts.
60
+
61
+ ---
62
+
63
+ ## Standalone Usage
64
+
65
+ ```ts
66
+ import { DocumentationGenerator } from '@arikajs/docs';
67
+
68
+ const generator = new DocumentationGenerator();
69
+ generator.generateAll('My App Name', './docs-output');
70
+ ```
71
+
72
+ ---
73
+
74
+ ## Project Structure
75
+
76
+ Inside the `docs` package:
77
+
78
+ - `src/`
79
+ - `PostmanGenerator.ts` – Handles Postman JSON generation
80
+ - `HtmlGenerator.ts` – Handles premium HTML documentation
81
+ - `MarkdownGenerator.ts` – Handles Markdown generation
82
+ - `OpenApiGenerator.ts` – Handles OpenAPI 3.0 generation
83
+ - `Generator.ts` – The main orchestrator
84
+ - `index.ts` – Public exports
85
+
86
+ ---
87
+
88
+ ## Philosophy
89
+
90
+ > “Your code is the source of truth; your documentation should reflect it instantly.”
91
+
92
+ ---
93
+
94
+ ## Contributing
95
+
96
+ Contributions are welcome, especially around:
97
+ - Adding support for JSDoc-based parameter descriptions.
98
+ - Enhancing the HTML documentation search and "Try it out" features.
99
+ - Adding support for more documentation formats.
100
+
101
+ ---
102
+
103
+ ## License
104
+
105
+ `@arikajs/docs` is open-sourced software licensed under the **MIT license**.
@@ -0,0 +1,12 @@
1
+ import { PostmanGenerator } from './PostmanGenerator';
2
+ import { MarkdownGenerator } from './MarkdownGenerator';
3
+ import { HtmlGenerator } from './HtmlGenerator';
4
+ import { OpenApiGenerator } from './OpenApiGenerator';
5
+ export declare class DocumentationGenerator {
6
+ protected postman: PostmanGenerator;
7
+ protected markdown: MarkdownGenerator;
8
+ protected html: HtmlGenerator;
9
+ protected openApi: OpenApiGenerator;
10
+ constructor();
11
+ generateAll(appName: string, outputDir: string): void;
12
+ }
@@ -0,0 +1,52 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.DocumentationGenerator = void 0;
7
+ const router_1 = require("@arikajs/router");
8
+ const PostmanGenerator_1 = require("./PostmanGenerator");
9
+ const MarkdownGenerator_1 = require("./MarkdownGenerator");
10
+ const HtmlGenerator_1 = require("./HtmlGenerator");
11
+ const OpenApiGenerator_1 = require("./OpenApiGenerator");
12
+ const fs_1 = __importDefault(require("fs"));
13
+ const path_1 = __importDefault(require("path"));
14
+ class DocumentationGenerator {
15
+ postman;
16
+ markdown;
17
+ html;
18
+ openApi;
19
+ constructor() {
20
+ this.postman = new PostmanGenerator_1.PostmanGenerator();
21
+ this.markdown = new MarkdownGenerator_1.MarkdownGenerator();
22
+ this.html = new HtmlGenerator_1.HtmlGenerator();
23
+ this.openApi = new OpenApiGenerator_1.OpenApiGenerator();
24
+ }
25
+ generateAll(appName, outputDir) {
26
+ const routes = router_1.RouteRegistry.getInstance().getRoutes();
27
+ if (!fs_1.default.existsSync(outputDir)) {
28
+ fs_1.default.mkdirSync(outputDir, { recursive: true });
29
+ }
30
+ // Postman
31
+ const postmanJson = this.postman.generate(routes, appName);
32
+ fs_1.default.writeFileSync(path_1.default.join(outputDir, 'postman_collection.json'), JSON.stringify(postmanJson, null, 2));
33
+ // Environment
34
+ const envJson = {
35
+ name: `${appName} Env`,
36
+ values: [
37
+ { key: "base_url", value: "http://localhost:3000", enabled: true }
38
+ ]
39
+ };
40
+ fs_1.default.writeFileSync(path_1.default.join(outputDir, 'postman_environment.json'), JSON.stringify(envJson, null, 2));
41
+ // Markdown
42
+ const md = this.markdown.generate(routes, appName);
43
+ fs_1.default.writeFileSync(path_1.default.join(outputDir, 'DOCS.md'), md);
44
+ // HTML
45
+ const html = this.html.generate(routes, appName);
46
+ fs_1.default.writeFileSync(path_1.default.join(outputDir, 'api_docs.html'), html);
47
+ // OpenAPI
48
+ const openApiJson = this.openApi.generate(routes, appName);
49
+ fs_1.default.writeFileSync(path_1.default.join(outputDir, 'openapi.json'), JSON.stringify(openApiJson, null, 2));
50
+ }
51
+ }
52
+ exports.DocumentationGenerator = DocumentationGenerator;
@@ -0,0 +1,6 @@
1
+ import { RouteEntry } from '@arikajs/router';
2
+ export declare class HtmlGenerator {
3
+ generate(routes: RouteEntry[], appName: string): string;
4
+ private renderGroup;
5
+ private groupByPrefix;
6
+ }
@@ -0,0 +1,115 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.HtmlGenerator = void 0;
4
+ class HtmlGenerator {
5
+ generate(routes, appName) {
6
+ const groups = this.groupByPrefix(routes);
7
+ const groupHtml = Object.entries(groups).map(([name, groupRoutes]) => this.renderGroup(name, groupRoutes)).join('');
8
+ return `<!DOCTYPE html>
9
+ <html lang="en">
10
+ <head>
11
+ <meta charset="UTF-8">
12
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
13
+ <title>${appName} API Documentation</title>
14
+ <link href="https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@300;400;600;700&display=swap" rel="stylesheet">
15
+ <style>
16
+ :root {
17
+ --primary: #8b5cf6;
18
+ --bg: #f8fafc;
19
+ --card: #ffffff;
20
+ --text-main: #0f172a;
21
+ --text-muted: #64748b;
22
+ --border: #e2e8f0;
23
+ --get: #10b981;
24
+ --post: #3b82f6;
25
+ --put: #f59e0b;
26
+ --delete: #ef4444;
27
+ }
28
+
29
+ * { margin:0; padding:0; box-sizing:border-box; }
30
+ body { font-family: 'Plus Jakarta Sans', sans-serif; background: var(--bg); color: var(--text-main); line-height: 1.6; }
31
+ .container { max-width: 1000px; margin: 0 auto; padding: 4rem 2rem; }
32
+ header { margin-bottom: 4rem; }
33
+ h1 { font-size: 2.5rem; margin-bottom: 0.5rem; }
34
+ .badge { display: inline-block; padding: 0.25rem 0.75rem; background: var(--primary); color: white; border-radius: 99px; font-size: 0.8rem; font-weight: 600; }
35
+
36
+ .group { margin-bottom: 3rem; }
37
+ .group-title { font-size: 1.5rem; margin-bottom: 1.5rem; border-bottom: 2px solid var(--border); padding-bottom: 0.5rem; text-transform: capitalize; }
38
+
39
+ .route-card { background: var(--card); border: 1px solid var(--border); border-radius: 12px; padding: 1.5rem; margin-bottom: 1rem; transition: transform 0.2s; }
40
+ .route-card:hover { transform: translateY(-2px); box-shadow: 0 10px 20px rgba(0,0,0,0.05); }
41
+
42
+ .route-header { display: flex; align-items: center; gap: 1rem; margin-bottom: 1rem; }
43
+ .method { padding: 0.25rem 0.75rem; border-radius: 6px; font-weight: 700; font-size: 0.8rem; min-width: 70px; text-align: center; color: white; }
44
+ .method.GET { background: var(--get); }
45
+ .method.POST { background: var(--post); }
46
+ .method.PUT { background: var(--put); }
47
+ .method.DELETE { background: var(--delete); }
48
+ .path { font-family: monospace; font-size: 1.1rem; font-weight: 600; color: var(--text-main); }
49
+
50
+ .route-info { display: grid; grid-template-columns: 1fr 1fr; gap: 1rem; font-size: 0.9rem; }
51
+ .label { color: var(--text-muted); font-weight: 600; margin-bottom: 0.25rem; }
52
+ .value { color: var(--text-main); }
53
+
54
+ .middleware-tag { display: inline-block; padding: 0.1rem 0.5rem; background: #f1f5f9; border: 1px solid var(--border); border-radius: 4px; font-size: 0.75rem; margin-right: 0.25rem; }
55
+
56
+ footer { text-align: center; margin-top: 4rem; color: var(--text-muted); font-size: 0.8rem; }
57
+ </style>
58
+ </head>
59
+ <body>
60
+ <div class="container">
61
+ <header>
62
+ <div class="badge">ArikaJS Docs</div>
63
+ <h1>${appName}</h1>
64
+ <p>API documentation generated on ${new Date().toLocaleDateString()}</p>
65
+ </header>
66
+
67
+ ${groupHtml}
68
+
69
+ <footer>
70
+ Generated by @arikajs/docs &copy; 2026
71
+ </footer>
72
+ </div>
73
+ </body>
74
+ </html>`;
75
+ }
76
+ renderGroup(name, routes) {
77
+ const routesHtml = routes.map(route => `
78
+ <div class="route-card">
79
+ <div class="route-header">
80
+ <span class="method ${route.method}">${route.method}</span>
81
+ <span class="path">${route.path}</span>
82
+ </div>
83
+ <div class="route-info">
84
+ <div>
85
+ <div class="label">Route Name</div>
86
+ <div class="value">${route.name || '-'}</div>
87
+ </div>
88
+ <div>
89
+ <div class="label">Middleware</div>
90
+ <div class="value">
91
+ ${route.middleware.map(m => `<span class="middleware-tag">${typeof m === 'string' ? m : m.name || 'Closure'}</span>`).join('') || '-'}
92
+ </div>
93
+ </div>
94
+ </div>
95
+ </div>
96
+ `).join('');
97
+ return `
98
+ <div class="group">
99
+ <h2 class="group-title">${name.replace('/', '')}</h2>
100
+ ${routesHtml}
101
+ </div>
102
+ `;
103
+ }
104
+ groupByPrefix(routes) {
105
+ const groups = {};
106
+ routes.forEach(route => {
107
+ const group = route.prefix || 'General';
108
+ if (!groups[group])
109
+ groups[group] = [];
110
+ groups[group].push(route);
111
+ });
112
+ return groups;
113
+ }
114
+ }
115
+ exports.HtmlGenerator = HtmlGenerator;
@@ -0,0 +1,5 @@
1
+ import { RouteEntry } from '@arikajs/router';
2
+ export declare class MarkdownGenerator {
3
+ generate(routes: RouteEntry[], appName: string): string;
4
+ private groupByPrefix;
5
+ }
@@ -0,0 +1,32 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.MarkdownGenerator = void 0;
4
+ class MarkdownGenerator {
5
+ generate(routes, appName) {
6
+ let markdown = `# API Documentation: ${appName}\n\n`;
7
+ markdown += `Generated on ${new Date().toLocaleDateString()}\n\n`;
8
+ const groups = this.groupByPrefix(routes);
9
+ for (const [group, groupRoutes] of Object.entries(groups)) {
10
+ markdown += `## ${group}\n\n`;
11
+ markdown += `| Method | Path | Name | Middleware |\n`;
12
+ markdown += `| :--- | :--- | :--- | :--- |\n`;
13
+ groupRoutes.forEach(route => {
14
+ const middleware = route.middleware.map(m => typeof m === 'string' ? m : m.name || 'Closure').join(', ') || '-';
15
+ markdown += `| **${route.method}** | \`${route.path}\` | ${route.name || '-'} | ${middleware} |\n`;
16
+ });
17
+ markdown += `\n`;
18
+ }
19
+ return markdown;
20
+ }
21
+ groupByPrefix(routes) {
22
+ const groups = {};
23
+ routes.forEach(route => {
24
+ const group = route.prefix || 'General';
25
+ if (!groups[group])
26
+ groups[group] = [];
27
+ groups[group].push(route);
28
+ });
29
+ return groups;
30
+ }
31
+ }
32
+ exports.MarkdownGenerator = MarkdownGenerator;
@@ -0,0 +1,6 @@
1
+ import { RouteEntry } from '@arikajs/router';
2
+ export declare class OpenApiGenerator {
3
+ generate(routes: RouteEntry[], appName: string): any;
4
+ private formatPaths;
5
+ private extractParameters;
6
+ }
@@ -0,0 +1,64 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.OpenApiGenerator = void 0;
4
+ class OpenApiGenerator {
5
+ generate(routes, appName) {
6
+ return {
7
+ openapi: "3.0.0",
8
+ info: {
9
+ title: appName,
10
+ description: `API Specification for ${appName}`,
11
+ version: "1.0.0"
12
+ },
13
+ paths: this.formatPaths(routes),
14
+ components: {
15
+ schemas: {},
16
+ securitySchemes: {
17
+ bearerAuth: {
18
+ type: "http",
19
+ scheme: "bearer",
20
+ bearerFormat: "JWT"
21
+ }
22
+ }
23
+ }
24
+ };
25
+ }
26
+ formatPaths(routes) {
27
+ const paths = {};
28
+ routes.forEach(route => {
29
+ const path = route.path.replace(/:([a-zA-Z0-9_]+)|\{([a-zA-Z0-9_]+)\}/g, '{$1$2}');
30
+ if (!paths[path]) {
31
+ paths[path] = {};
32
+ }
33
+ paths[path][route.method.toLowerCase()] = {
34
+ summary: route.name || `Endpoint for ${path}`,
35
+ tags: [route.prefix || 'General'],
36
+ parameters: this.extractParameters(route),
37
+ responses: {
38
+ "200": {
39
+ description: "Successful response",
40
+ content: {
41
+ "application/json": {
42
+ schema: {
43
+ type: "object"
44
+ }
45
+ }
46
+ }
47
+ }
48
+ }
49
+ };
50
+ });
51
+ return paths;
52
+ }
53
+ extractParameters(route) {
54
+ return route.paramKeys.map(key => ({
55
+ name: key,
56
+ in: "path",
57
+ required: true,
58
+ schema: {
59
+ type: "string"
60
+ }
61
+ }));
62
+ }
63
+ }
64
+ exports.OpenApiGenerator = OpenApiGenerator;
@@ -0,0 +1,5 @@
1
+ import { RouteEntry } from '@arikajs/router';
2
+ export declare class PostmanGenerator {
3
+ generate(routes: RouteEntry[], appName: string): any;
4
+ private formatRoutes;
5
+ }
@@ -0,0 +1,60 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PostmanGenerator = void 0;
4
+ class PostmanGenerator {
5
+ generate(routes, appName) {
6
+ return {
7
+ info: {
8
+ name: appName,
9
+ _postman_id: Math.random().toString(36).substring(7),
10
+ description: `API Collection for ${appName}`,
11
+ schema: "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
12
+ },
13
+ item: this.formatRoutes(routes),
14
+ variable: [
15
+ {
16
+ key: "base_url",
17
+ value: "http://localhost:3000",
18
+ type: "string"
19
+ }
20
+ ]
21
+ };
22
+ }
23
+ formatRoutes(routes) {
24
+ // Group routes by prefix/module
25
+ const groups = {};
26
+ routes.forEach(route => {
27
+ const groupName = route.prefix || 'General';
28
+ if (!groups[groupName]) {
29
+ groups[groupName] = [];
30
+ }
31
+ groups[groupName].push({
32
+ name: route.name || route.path,
33
+ request: {
34
+ method: route.method,
35
+ header: [
36
+ {
37
+ key: "Content-Type",
38
+ value: "application/json"
39
+ },
40
+ {
41
+ key: "Accept",
42
+ value: "application/json"
43
+ }
44
+ ],
45
+ url: {
46
+ raw: "{{base_url}}" + route.path,
47
+ host: ["{{base_url}}"],
48
+ path: route.path.split('/').filter(p => p !== '')
49
+ }
50
+ },
51
+ response: []
52
+ });
53
+ });
54
+ return Object.keys(groups).map(name => ({
55
+ name: name.charAt(0).toUpperCase() + name.slice(1).replace('/', ''),
56
+ item: groups[name]
57
+ }));
58
+ }
59
+ }
60
+ exports.PostmanGenerator = PostmanGenerator;
@@ -0,0 +1,5 @@
1
+ export { DocumentationGenerator } from './Generator';
2
+ export { PostmanGenerator } from './PostmanGenerator';
3
+ export { MarkdownGenerator } from './MarkdownGenerator';
4
+ export { HtmlGenerator } from './HtmlGenerator';
5
+ export { OpenApiGenerator } from './OpenApiGenerator';
package/dist/index.js ADDED
@@ -0,0 +1,13 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.OpenApiGenerator = exports.HtmlGenerator = exports.MarkdownGenerator = exports.PostmanGenerator = exports.DocumentationGenerator = void 0;
4
+ var Generator_1 = require("./Generator");
5
+ Object.defineProperty(exports, "DocumentationGenerator", { enumerable: true, get: function () { return Generator_1.DocumentationGenerator; } });
6
+ var PostmanGenerator_1 = require("./PostmanGenerator");
7
+ Object.defineProperty(exports, "PostmanGenerator", { enumerable: true, get: function () { return PostmanGenerator_1.PostmanGenerator; } });
8
+ var MarkdownGenerator_1 = require("./MarkdownGenerator");
9
+ Object.defineProperty(exports, "MarkdownGenerator", { enumerable: true, get: function () { return MarkdownGenerator_1.MarkdownGenerator; } });
10
+ var HtmlGenerator_1 = require("./HtmlGenerator");
11
+ Object.defineProperty(exports, "HtmlGenerator", { enumerable: true, get: function () { return HtmlGenerator_1.HtmlGenerator; } });
12
+ var OpenApiGenerator_1 = require("./OpenApiGenerator");
13
+ Object.defineProperty(exports, "OpenApiGenerator", { enumerable: true, get: function () { return OpenApiGenerator_1.OpenApiGenerator; } });
package/package.json ADDED
@@ -0,0 +1,32 @@
1
+ {
2
+ "name": "@arikajs/docs",
3
+ "version": "0.2.0",
4
+ "description": "API documentation and Postman collection generator for ArikaJS.",
5
+ "license": "MIT",
6
+ "main": "dist/index.js",
7
+ "types": "dist/index.d.ts",
8
+ "scripts": {
9
+ "build": "tsc -p tsconfig.json",
10
+ "clean": "rm -rf dist",
11
+ "test": "npm run build && node --test 'dist/tests/**/*.test.js'"
12
+ },
13
+ "files": [
14
+ "dist"
15
+ ],
16
+ "keywords": [
17
+ "arika",
18
+ "arika-js",
19
+ "docs",
20
+ "postman",
21
+ "openapi",
22
+ "documentation"
23
+ ],
24
+ "dependencies": {
25
+ "@arikajs/router": "^0.1.0"
26
+ },
27
+ "devDependencies": {
28
+ "@types/node": "^20.11.24",
29
+ "typescript": "^5.3.3"
30
+ },
31
+ "author": "Prakash Tank"
32
+ }