@map-our-system/scanner-server 1.0.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 ADDED
@@ -0,0 +1,88 @@
1
+ # @map-our-system/scanner
2
+
3
+ Pacote npm que expoe um servidor HTTP no projeto externo. Quando o botao "Scan" e clicado no frontend do Map Our System, o backend envia um webhook para este servidor, que le os arquivos por pasta e envia o snapshot JSONB de volta a API.
4
+
5
+ ## Instalacao
6
+
7
+ ```bash
8
+ npm install @map-our-system/scanner
9
+ ```
10
+
11
+ ## Configuracao
12
+
13
+ Crie um arquivo `.map-our-system.json` na raiz do seu projeto:
14
+
15
+ ```json
16
+ {
17
+ "apiKey": "sua-api-key-aqui",
18
+ "projectId": "uuid-do-projeto",
19
+ "apiUrl": "http://localhost:3001",
20
+ "port": 4000,
21
+ "scanPaths": ["."],
22
+ "ignorePaths": ["node_modules", ".git", "dist", ".next", "build"]
23
+ }
24
+ ```
25
+
26
+ | Campo | Obrigatorio | Padrao | Descricao |
27
+ |---------------|-------------|---------------------------------------------------|------------------------------------------------|
28
+ | `apiKey` | sim | - | Gerado automaticamente ao criar o projeto |
29
+ | `projectId` | sim | - | UUID do projeto no Map Our System |
30
+ | `apiUrl` | sim | - | URL base da API NestJS |
31
+ | `port` | nao | `4000` | Porta do servidor scanner |
32
+ | `scanPaths` | nao | `["."]` | Pastas a escanear (relativas a raiz) |
33
+ | `ignorePaths` | nao | `["node_modules", ".git", "dist", ".next", ...]` | Pastas/arquivos a ignorar |
34
+
35
+ ## Uso
36
+
37
+ ```bash
38
+ # Iniciar o servidor scanner (porta do config ou padrao 4000)
39
+ npx map-our-system start
40
+
41
+ # Iniciar em porta especifica
42
+ npx map-our-system start --port 5000
43
+ ```
44
+
45
+ ## Fluxo
46
+
47
+ ```
48
+ [Frontend: botao Scan]
49
+ |
50
+ v
51
+ [NestJS API: POST /user-projects/:id/scan]
52
+ |
53
+ v
54
+ [Webhook: POST http://seu-servidor:4000/scan]
55
+ |
56
+ v
57
+ [Scanner: le arquivos por pasta]
58
+ |
59
+ v
60
+ [POST /user-projects/:id/code-snapshot com X-API-Key]
61
+ |
62
+ v
63
+ [NestJS API: salva JSONB no PostgreSQL]
64
+ |
65
+ v
66
+ [Frontend: exibe arvore de arquivos]
67
+ ```
68
+
69
+ ## Estrutura do JSONB salvo
70
+
71
+ ```json
72
+ {
73
+ "src/components": {
74
+ "Button.tsx": "export function Button...",
75
+ "Header.tsx": "export function Header..."
76
+ },
77
+ "src/lib": {
78
+ "api.ts": "const API_BASE_URL..."
79
+ }
80
+ }
81
+ ```
82
+
83
+ ## Endpoints do servidor scanner
84
+
85
+ | Metodo | Path | Descricao |
86
+ |--------|-----------|--------------------------------------|
87
+ | POST | `/scan` | Dispara leitura e envio do snapshot |
88
+ | GET | `/health` | Verifica se o servidor esta rodando |
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.sendSnapshot = sendSnapshot;
4
+ async function sendSnapshot(config, filesData) {
5
+ const url = `${config.apiUrl}/user-projects/${config.projectId}/code-snapshot`;
6
+ const response = await fetch(url, {
7
+ method: 'POST',
8
+ headers: {
9
+ 'Content-Type': 'application/json',
10
+ 'x-api-key': config.apiKey,
11
+ },
12
+ body: JSON.stringify({ filesData }),
13
+ });
14
+ if (!response.ok) {
15
+ const text = await response.text().catch(() => '');
16
+ throw new Error(`API responded with ${response.status}: ${text}`);
17
+ }
18
+ }
package/dist/cli.js ADDED
@@ -0,0 +1,21 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ const config_1 = require("./config");
5
+ const server_1 = require("./server");
6
+ const args = process.argv.slice(2);
7
+ const portArgIndex = args.indexOf('--port');
8
+ const portOverride = portArgIndex !== -1 ? parseInt(args[portArgIndex + 1], 10) : undefined;
9
+ let port;
10
+ try {
11
+ const config = (0, config_1.loadConfig)();
12
+ port = portOverride ?? config.port;
13
+ }
14
+ catch {
15
+ port = portOverride ?? 4000;
16
+ }
17
+ const app = (0, server_1.createServer)();
18
+ app.listen(port, () => {
19
+ console.log(`[map-our-system/scanner] Server listening on http://localhost:${port}`);
20
+ console.log(`[map-our-system/scanner] POST http://localhost:${port}/scan to trigger a scan`);
21
+ });
package/dist/config.js ADDED
@@ -0,0 +1,65 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.loadConfig = loadConfig;
37
+ const fs = __importStar(require("fs"));
38
+ const path = __importStar(require("path"));
39
+ const CONFIG_FILE = '.map-our-system.json';
40
+ const DEFAULTS = {
41
+ port: 4000,
42
+ scanPaths: ['.'],
43
+ ignorePaths: ['node_modules', '.git', 'dist', '.next', 'build', 'coverage'],
44
+ };
45
+ function loadConfig() {
46
+ const configPath = path.resolve(process.cwd(), CONFIG_FILE);
47
+ if (!fs.existsSync(configPath)) {
48
+ throw new Error(`Config file not found: ${configPath}\nCreate a ${CONFIG_FILE} file at the root of your project.`);
49
+ }
50
+ const raw = fs.readFileSync(configPath, 'utf-8');
51
+ const parsed = JSON.parse(raw);
52
+ if (!parsed.apiKey)
53
+ throw new Error('Missing required config field: apiKey');
54
+ if (!parsed.projectId)
55
+ throw new Error('Missing required config field: projectId');
56
+ if (!parsed.apiUrl)
57
+ throw new Error('Missing required config field: apiUrl');
58
+ return {
59
+ ...DEFAULTS,
60
+ ...parsed,
61
+ apiKey: parsed.apiKey,
62
+ projectId: parsed.projectId,
63
+ apiUrl: parsed.apiUrl.replace(/\/$/, ''),
64
+ };
65
+ }
@@ -0,0 +1,89 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.scanFiles = scanFiles;
37
+ const fs = __importStar(require("fs"));
38
+ const path = __importStar(require("path"));
39
+ function shouldIgnore(name, ignorePaths) {
40
+ return ignorePaths.some((pattern) => name === pattern || name.startsWith(pattern));
41
+ }
42
+ function readDirectory(dirPath, baseDir, ignorePaths, result) {
43
+ let entries;
44
+ try {
45
+ entries = fs.readdirSync(dirPath, { withFileTypes: true });
46
+ }
47
+ catch {
48
+ return;
49
+ }
50
+ for (const entry of entries) {
51
+ if (shouldIgnore(entry.name, ignorePaths))
52
+ continue;
53
+ const fullPath = path.join(dirPath, entry.name);
54
+ if (entry.isDirectory()) {
55
+ readDirectory(fullPath, baseDir, ignorePaths, result);
56
+ }
57
+ else if (entry.isFile()) {
58
+ // Skip binary files and very large files (>512 KB)
59
+ const stats = fs.statSync(fullPath);
60
+ if (stats.size > 512 * 1024)
61
+ continue;
62
+ let content;
63
+ try {
64
+ content = fs.readFileSync(fullPath, 'utf-8');
65
+ }
66
+ catch {
67
+ continue; // Skip binary or unreadable files
68
+ }
69
+ const relativePath = path.relative(baseDir, fullPath);
70
+ const folder = path.dirname(relativePath).replace(/\\/g, '/');
71
+ const fileName = path.basename(relativePath);
72
+ if (!result[folder]) {
73
+ result[folder] = {};
74
+ }
75
+ result[folder][fileName] = content;
76
+ }
77
+ }
78
+ }
79
+ function scanFiles(scanPaths, ignorePaths) {
80
+ const result = {};
81
+ const baseDir = process.cwd();
82
+ for (const scanPath of scanPaths) {
83
+ const fullScanPath = path.resolve(baseDir, scanPath);
84
+ if (!fs.existsSync(fullScanPath))
85
+ continue;
86
+ readDirectory(fullScanPath, baseDir, ignorePaths, result);
87
+ }
88
+ return result;
89
+ }
package/dist/server.js ADDED
@@ -0,0 +1,44 @@
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.createServer = createServer;
7
+ const express_1 = __importDefault(require("express"));
8
+ const api_client_1 = require("./api-client");
9
+ const config_1 = require("./config");
10
+ const file_scanner_1 = require("./file-scanner");
11
+ function createServer() {
12
+ const app = (0, express_1.default)();
13
+ app.use(express_1.default.json());
14
+ app.post('/scan', async (_req, res) => {
15
+ let config;
16
+ try {
17
+ config = (0, config_1.loadConfig)();
18
+ }
19
+ catch (err) {
20
+ const message = err instanceof Error ? err.message : 'Config error';
21
+ res.status(500).json({ error: message });
22
+ return;
23
+ }
24
+ try {
25
+ console.log('[scanner] Starting file scan...');
26
+ const filesData = (0, file_scanner_1.scanFiles)(config.scanPaths, config.ignorePaths);
27
+ const folderCount = Object.keys(filesData).length;
28
+ const fileCount = Object.values(filesData).reduce((acc, files) => acc + Object.keys(files).length, 0);
29
+ console.log(`[scanner] Found ${fileCount} files in ${folderCount} folders. Sending to API...`);
30
+ await (0, api_client_1.sendSnapshot)(config, filesData);
31
+ console.log('[scanner] Snapshot sent successfully.');
32
+ res.status(200).json({ message: 'Scan completed', files: fileCount, folders: folderCount });
33
+ }
34
+ catch (err) {
35
+ const message = err instanceof Error ? err.message : 'Scan failed';
36
+ console.error(`[scanner] Error: ${message}`);
37
+ res.status(500).json({ error: message });
38
+ }
39
+ });
40
+ app.get('/health', (_req, res) => {
41
+ res.json({ status: 'ok' });
42
+ });
43
+ return app;
44
+ }
package/package.json ADDED
@@ -0,0 +1,35 @@
1
+ {
2
+ "name": "@map-our-system/scanner-server",
3
+ "version": "1.0.0",
4
+ "description": "Scanner HTTP server — reads project files by folder and sends JSONB snapshots to Map Our System API",
5
+ "main": "dist/cli.js",
6
+ "bin": {
7
+ "map-our-system": "dist/cli.js"
8
+ },
9
+ "scripts": {
10
+ "build": "tsc",
11
+ "prepublishOnly": "npm run build",
12
+ "start": "node dist/cli.js",
13
+ "dev": "ts-node src/cli.ts"
14
+ },
15
+ "files": [
16
+ "dist/",
17
+ "README.md"
18
+ ],
19
+ "keywords": [
20
+ "scanner",
21
+ "http",
22
+ "server",
23
+ "map-our-system"
24
+ ],
25
+ "license": "UNLICENSED",
26
+ "dependencies": {
27
+ "express": "^4.21.2"
28
+ },
29
+ "devDependencies": {
30
+ "@types/express": "^5.0.0",
31
+ "@types/node": "^22.0.0",
32
+ "typescript": "^5.7.0",
33
+ "ts-node": "^10.9.2"
34
+ }
35
+ }