legacyver 3.2.0 → 3.4.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.
@@ -0,0 +1,80 @@
1
+ ## Overview
2
+ This is the `OrderController` class, responsible for handling CRUD (Create, Read, Update, Delete) operations on orders. It provides a RESTful API interface to interact with the order data.
3
+
4
+ ## Functions
5
+
6
+ ### index
7
+ #### Description
8
+ Returns a paginated list of orders.
9
+ #### Parameters
10
+ | Parameter | Type | Required |
11
+ | --- | --- | --- |
12
+ | `paginate` | int | - |
13
+ | `response` | object | - |
14
+ | `json` | function | - |
15
+
16
+ #### Return Value
17
+ A `JsonResponse` containing the paginated list of orders.
18
+
19
+ ### store
20
+ #### Description
21
+ Creates a new order and returns its details.
22
+ #### Parameters
23
+ | Parameter | Type | Required |
24
+ | --- | --- | --- |
25
+ | `$request:StoreOrderRequest` | object | Yes |
26
+
27
+ #### Return Value
28
+ A `JsonResponse` with the created order data and HTTP status code 201.
29
+
30
+ ### show
31
+ #### Description
32
+ Returns a single order by its ID.
33
+ #### Parameters
34
+ | Parameter | Type | Required |
35
+ | --- | --- | --- |
36
+ | `$id:int` | int | Yes |
37
+
38
+ #### Return Value
39
+ A `JsonResponse` containing the order details.
40
+
41
+ ### update
42
+ #### Description
43
+ Updates an existing order.
44
+ #### Parameters
45
+ | Parameter | Type | Required |
46
+ | --- | --- | --- |
47
+ | `$request:UpdateOrderRequest` | object | Yes |
48
+ | `$id:int` | int | Yes |
49
+
50
+ #### Return Value
51
+ A `JsonResponse` with the updated order data.
52
+
53
+ ### destroy
54
+ #### Description
55
+ Deletes an existing order.
56
+ #### Parameters
57
+ | Parameter | Type | Required |
58
+ | --- | --- | --- |
59
+ | `$id:int` | int | Yes |
60
+
61
+ #### Return Value
62
+ A `JsonResponse` with HTTP status code 204.
63
+
64
+ ## Dependencies
65
+
66
+ * `App\Models\Order`
67
+ * `App\Models\User`
68
+ * `App\Http\Requests\StoreOrderRequest`
69
+ * `App\Http\Requests\UpdateOrderRequest`
70
+ * `App\Services\OrderService`
71
+ * `Illuminate\Http\JsonResponse`
72
+
73
+ ## Usage Example
74
+ ```php
75
+ $orderController = new OrderController();
76
+ $orders = $orderController->index(); // returns paginated list of orders
77
+
78
+ $order = $orderController->store(new StoreOrderRequest(['name' => 'New Order'])); // creates and returns order details
79
+ ```
80
+ Note: The usage example is just a demonstration and should be replaced with actual usage scenarios.
@@ -1,3 +1,3 @@
1
- # Summary
2
-
3
- * [legacyver.js](legacyver.md)
1
+ # Summary
2
+
3
+ * [OrderController.php](OrderController.md)
@@ -0,0 +1,71 @@
1
+ ## Overview
2
+ `auth.js` provides two asynchronous helpers for handling CLI session tokens stored in the `user_sessions` table: validating a token and revoking (logging out) a token.
3
+
4
+ ## Functions
5
+
6
+ ### `validateToken(token)`
7
+ Validates a raw CLI session token and returns basic user information if the token is active.
8
+
9
+ | Parameter | Type | Description |
10
+ |-----------|------|-------------|
11
+ | `token` | `string` | Raw token read from `~/.legacyver/session.json`. |
12
+
13
+ **Returns**
14
+ `Promise<{ userId: string, username: string, email: string } | null>` – Resolves to an object containing the user’s ID, username, and email when the token is found, not expired, and not revoked; otherwise resolves to `null`.
15
+
16
+ **Logic summary**
17
+ 1. If `token` is falsy, the function returns `null` immediately.
18
+ 2. The token is hashed with SHA‑256 (`crypto.createHash('sha256')`).
19
+ 3. A database client is created via `createDbClient(token)` – the token is passed so the client can bypass row‑level security.
20
+ 4. The client queries the `public.user_sessions` table, selecting `user_id` and the related `users` fields `username` and `email` (inner join).
21
+ 5. The query filters:
22
+ * `token_hash` equals the computed hash,
23
+ * `expires_at` is greater than the current ISO timestamp,
24
+ * `revoked_at` is `null`.
25
+ 6. `maybeSingle()` ensures at most one row is returned.
26
+ 7. If the query yields an error or no data, the function returns `null`.
27
+ 8. Otherwise it builds the result object, converting `user_id` to a string and falling back to default values (`'unknown'` for username, empty string for email) if the joined user record is missing.
28
+
29
+ ---
30
+
31
+ ### `revokeToken(token)`
32
+ Revokes a CLI session token, effectively logging the user out.
33
+
34
+ | Parameter | Type | Description |
35
+ |-----------|------|-------------|
36
+ | `token` | `string` | Raw token to be revoked. |
37
+
38
+ **Returns**
39
+ `Promise<void>` – Resolves when the revocation update completes; throws an error if the database operation fails.
40
+
41
+ **Logic summary**
42
+ 1. If `token` is falsy, the function exits without action.
43
+ 2. The token is hashed with SHA‑256 (same method as `validateToken`).
44
+ 3. A database client is created with `createDbClient(token)`.
45
+ 4. The client updates the `revoked_at` column of the matching `user_sessions` row (where `token_hash` matches) to the current ISO timestamp.
46
+ 5. If the update returns an `error`, the function throws that error; otherwise it completes silently.
47
+
48
+ ## Dependencies
49
+ - `crypto` (Node.js built‑in) – used for SHA‑256 hashing.
50
+ - `../db/config` – imports `supabase` (unused in this file) and `createDbClient` for database access.
51
+
52
+ ## Usage Example
53
+ ```js
54
+ const { validateToken, revokeToken } = require('./auth');
55
+
56
+ // Validate a stored session token
57
+ (async () => {
58
+ const token = 'my‑raw‑session‑token';
59
+ const user = await validateToken(token);
60
+
61
+ if (user) {
62
+ console.log('Authenticated user:', user);
63
+ } else {
64
+ console.log('Invalid or expired token');
65
+ }
66
+
67
+ // When the user logs out
68
+ await revokeToken(token);
69
+ console.log('Token revoked');
70
+ })();
71
+ ```
@@ -0,0 +1,37 @@
1
+ ## Overview
2
+ Provides a utility to compute the SHA‑256 hash of a file's contents, returning the hash as a hex string prefixed with `sha256:`.
3
+
4
+ ## Functions
5
+
6
+ ### `computeHash(filePath)`
7
+ Computes the SHA‑256 hash of the file located at `filePath`.
8
+
9
+ | Parameter | Type | Description |
10
+ |-----------|--------|----------------------------|
11
+ | `filePath`| string | Path to the file to hash. |
12
+
13
+ **Returns**: `string` – A hexadecimal SHA‑256 digest prefixed with `sha256:` (e.g., `sha256:ab12cd...`).
14
+
15
+ **Logic**
16
+ 1. Reads the file synchronously using `fs.readFileSync`.
17
+ 2. Creates a SHA‑256 hash object via `crypto.createHash('sha256')`.
18
+ 3. Feeds the file content to the hash with `.update(content)`.
19
+ 4. Produces a hex‑encoded digest with `.digest('hex')`.
20
+ 5. Concatenates the prefix `sha256:` with the digest and returns the result.
21
+
22
+ ## Dependencies
23
+ - `crypto` – `createHash`
24
+ - `fs` – `readFileSync`
25
+
26
+ ## Usage Example
27
+ ```javascript
28
+ // Import the function
29
+ const { computeHash } = require('./hash');
30
+
31
+ // Compute the hash of a file
32
+ const filePath = 'path/to/your/file.txt';
33
+ const hash = computeHash(filePath);
34
+
35
+ console.log(`SHA-256 hash: ${hash}`);
36
+ // Example output: SHA-256 hash: sha256:3a7bd3e2360a...
37
+ ```
@@ -1,15 +1,15 @@
1
- # bin — Documentation
2
-
3
- **Primary language:** javascript
4
- **Total files:** 1
5
- **Analyzed at:** 2026-03-21T11:31:12.941Z
6
-
7
- ## Files
8
-
9
- - [legacyver.js](legacyver.md)
10
-
11
- ## Dependency Graph
12
-
13
- ```mermaid
14
- graph TD
15
- ```
1
+ # Controllers — Documentation
2
+
3
+ **Primary language:** php
4
+ **Total files:** 1
5
+ **Analyzed at:** 2026-02-22T03:45:37.186Z
6
+
7
+ ## Files
8
+
9
+ - [OrderController.php](OrderController.md)
10
+
11
+ ## Dependency Graph
12
+
13
+ ```mermaid
14
+ graph TD
15
+ ```
package/package.json CHANGED
@@ -1,8 +1,16 @@
1
1
  {
2
2
  "name": "legacyver",
3
- "version": "3.2.0",
3
+ "version": "3.4.1",
4
4
  "description": "AI-powered CLI tool to auto-generate technical documentation from legacy/undocumented codebases",
5
5
  "main": "src/index.js",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "git+https://github.com/Dhnillhaq/Legacyver-Node-Package.git"
9
+ },
10
+ "bugs": {
11
+ "url": "https://github.com/Dhnillhaq/Legacyver-Node-Package/issues"
12
+ },
13
+ "homepage": "https://github.com/Dhnillhaq/Legacyver-Node-Package#readme",
6
14
  "bin": {
7
15
  "legacyver": "bin/legacyver.js"
8
16
  },
@@ -27,6 +35,7 @@
27
35
  "author": "",
28
36
  "license": "MIT",
29
37
  "dependencies": {
38
+ "@supabase/supabase-js": "^2.99.3",
30
39
  "chalk": "^4.1.2",
31
40
  "cli-progress": "^3.12.0",
32
41
  "commander": "^11.1.0",
@@ -47,4 +56,4 @@
47
56
  "eslint": "^8.57.0",
48
57
  "vitest": "^1.2.2"
49
58
  }
50
- }
59
+ }
package/src/api/auth.js CHANGED
@@ -1,69 +1,56 @@
1
1
  'use strict';
2
2
 
3
3
  const crypto = require('crypto');
4
- const { Pool } = require('pg');
5
- const dbConfig = require('../db/config');
4
+ const { supabase, createDbClient } = require('../db/config');
6
5
 
7
6
  /**
8
7
  * Validate a CLI session token against app.user_sessions.
9
8
  * Returns user info if valid, null if expired/revoked/not found.
10
9
  *
11
10
  * @param {string} token raw token from ~/.legacyver/session.json
12
- * @param {object} [opts] optional overrides for testing
13
- * @param {object} [opts.pool] pg Pool instance
14
11
  * @returns {Promise<{userId: string, username: string, email: string} | null>}
15
12
  */
16
- async function validateToken(token, opts) {
13
+ async function validateToken(token) {
17
14
  if (!token) return null;
18
15
 
19
- const ownPool = !(opts && opts.pool);
20
- const pool = (opts && opts.pool) || new Pool(dbConfig);
21
- try {
22
- const tokenHash = crypto.createHash('sha256').update(token).digest('hex');
23
-
24
- const result = await pool.query(
25
- `SELECT s.user_id, u.username, u.email
26
- FROM app.user_sessions s
27
- JOIN app.users u ON u.id = s.user_id
28
- WHERE s.token_hash = $1
29
- AND s.expires_at > NOW()
30
- AND s.revoked_at IS NULL`,
31
- [tokenHash]
32
- );
33
-
34
- if (result.rows.length === 0) return null;
35
-
36
- const row = result.rows[0];
37
- return {
38
- userId: String(row.user_id),
39
- username: row.username || 'unknown',
40
- email: row.email || '',
41
- };
42
- } finally {
43
- if (ownPool) await pool.end().catch(() => {});
44
- }
16
+ const tokenHash = crypto.createHash('sha256').update(token).digest('hex');
17
+ const client = createDbClient(token); // Use token to bypass RLS
18
+
19
+ const { data, error } = await client
20
+ .schema('public')
21
+ .from('user_sessions')
22
+ .select('user_id, users!inner(username, email)')
23
+ .eq('token_hash', tokenHash)
24
+ .gt('expires_at', new Date().toISOString())
25
+ .is('revoked_at', null)
26
+ .maybeSingle();
27
+
28
+ if (error || !data) return null;
29
+
30
+ return {
31
+ userId: String(data.user_id),
32
+ username: data.users?.username || 'unknown',
33
+ email: data.users?.email || '',
34
+ };
45
35
  }
46
36
 
47
37
  /**
48
38
  * Revoke a CLI session token (logout).
49
39
  * @param {string} token raw token
50
- * @param {object} [opts] optional overrides for testing
51
- * @param {object} [opts.pool] pg Pool instance
52
40
  */
53
- async function revokeToken(token, opts) {
41
+ async function revokeToken(token) {
54
42
  if (!token) return;
55
43
 
56
- const ownPool = !(opts && opts.pool);
57
- const pool = (opts && opts.pool) || new Pool(dbConfig);
58
- try {
59
- const tokenHash = crypto.createHash('sha256').update(token).digest('hex');
60
- await pool.query(
61
- 'UPDATE app.user_sessions SET revoked_at = NOW() WHERE token_hash = $1',
62
- [tokenHash]
63
- );
64
- } finally {
65
- if (ownPool) await pool.end().catch(() => {});
66
- }
44
+ const tokenHash = crypto.createHash('sha256').update(token).digest('hex');
45
+ const client = createDbClient(token);
46
+
47
+ const { error } = await client
48
+ .schema('public')
49
+ .from('user_sessions')
50
+ .update({ revoked_at: new Date().toISOString() })
51
+ .eq('token_hash', tokenHash);
52
+
53
+ if (error) throw error;
67
54
  }
68
55
 
69
56
  module.exports = { validateToken, revokeToken };
@@ -217,7 +217,23 @@ module.exports = async function analyzeCommand(target, flags) {
217
217
 
218
218
  // Merge cached fragments
219
219
  const allFragments = [...docFragments];
220
- // (cached fragments would be loaded here)
220
+ if (config.incremental && cacheHits.length > 0) {
221
+ const fs = require('fs');
222
+ for (const hit of cacheHits) {
223
+ const cachedInfo = cacheMap[hit.relativePath];
224
+ if (cachedInfo && cachedInfo.docFile) {
225
+ try {
226
+ const content = fs.readFileSync(cachedInfo.docFile, 'utf8');
227
+ allFragments.push({
228
+ ...hit,
229
+ content
230
+ });
231
+ } catch (e) {
232
+ logger.warn(`Failed to read cached file for ${hit.relativePath}`);
233
+ }
234
+ }
235
+ }
236
+ }
221
237
 
222
238
  // ─── Stage 4: Renderer ───────────────────────────────────────────────────
223
239
  const renderSpinner = createSpinner('Rendering output...');
@@ -268,8 +284,11 @@ module.exports = async function analyzeCommand(target, flags) {
268
284
 
269
285
  if (!cloudResult.skipped) {
270
286
  syncSpinner.succeed(`Docs synced to cloud (${cloudResult.pushed} files)`);
287
+ } else if (session.token) {
288
+ syncSpinner.stop(); // Stops spinner gracefully if skipped without error
271
289
  }
272
290
  } catch (syncErr) {
291
+ if (typeof syncSpinner !== 'undefined') syncSpinner.fail('Cloud sync failed');
273
292
  logger.warn('Cloud sync failed: ' + syncErr.message);
274
293
  }
275
294
 
@@ -5,7 +5,7 @@ const crypto = require('crypto');
5
5
  const pc = require('picocolors');
6
6
  const { saveSession, loadSession } = require('../../utils/config');
7
7
 
8
- const WEB_URL = 'https://weci-holic.hackathon.sev-2.com';
8
+ const WEB_URL = 'https://legac.vercel.app';
9
9
 
10
10
  /**
11
11
  * Open a URL in the default browser (cross-platform).
package/src/db/config.js CHANGED
@@ -1,18 +1,43 @@
1
1
  'use strict';
2
2
 
3
+ const { createClient } = require('@supabase/supabase-js');
4
+
3
5
  /**
4
- * PostgreSQL connection config for the Legacyver hackathon database.
5
- * Uses SSL with self-signed certificate (rejectUnauthorized: false).
6
+ * Supabase client for Legacyver CLI.
7
+ *
8
+ * Uses the public anon key — safe to bundle in an npm package.
9
+ * Row Level Security (RLS) on Supabase enforces access control.
6
10
  */
7
- module.exports = {
8
- host: '103.185.52.138',
9
- port: 1185,
10
- user: 'weci_holic',
11
- password: 'f==+HLH_bvzLN2fo82f3x239MZE3@bGF',
12
- database: 'weci_holic',
13
- ssl: { rejectUnauthorized: false },
14
- // Keep pool small — CLI is short-lived
15
- max: 3,
16
- idleTimeoutMillis: 5000,
17
- connectionTimeoutMillis: 10000,
18
- };
11
+ const SUPABASE_URL = 'https://kbsxwyoylwhieoljepxr.supabase.co';
12
+
13
+ // anon/public key — safe to commit, RLS is the gatekeeper
14
+ const SUPABASE_ANON_KEY = process.env.SUPABASE_ANON_KEY || 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Imtic3h3eW95bHdoaWVvbGplcHhyIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NzQxMjU4ODksImV4cCI6MjA4OTcwMTg4OX0.GHX8Id1qunhypkN6WurM4UZUgVwrkD_z3bOIJhW2Y7A';
15
+
16
+ const supabase = createClient(SUPABASE_URL, SUPABASE_ANON_KEY, {
17
+ auth: {
18
+ persistSession: false,
19
+ autoRefreshToken: false,
20
+ detectSessionFromUrl: false,
21
+ },
22
+ });
23
+
24
+ /**
25
+ * Create a Supabase client configured with the CLI token for RLS bypass.
26
+ */
27
+ function createDbClient(token) {
28
+ const headers = {};
29
+ if (token) {
30
+ headers['x-cli-token'] = token;
31
+ }
32
+
33
+ return createClient(SUPABASE_URL, SUPABASE_ANON_KEY, {
34
+ auth: {
35
+ persistSession: false,
36
+ autoRefreshToken: false,
37
+ detectSessionFromUrl: false,
38
+ },
39
+ global: { headers }
40
+ });
41
+ }
42
+
43
+ module.exports = { supabase, createDbClient };
package/src/db/index.js CHANGED
@@ -1,114 +1,103 @@
1
1
  'use strict';
2
2
 
3
- const { Pool } = require('pg');
4
3
  const path = require('path');
5
- const dbConfig = require('./config');
4
+ const { createDbClient } = require('./config');
6
5
  const { loadSession } = require('../utils/config');
7
6
  const { validateToken } = require('../api/auth');
8
7
  const logger = require('../utils/logger');
9
8
 
10
- let _pool = null;
11
-
12
- /**
13
- * Lazy singleton pool — created on first use, ended after push.
14
- */
15
- function getPool() {
16
- if (!_pool) {
17
- _pool = new Pool(dbConfig);
18
- }
19
- return _pool;
20
- }
21
-
22
9
  /**
23
10
  * Find or create a repository for the given user + project path.
24
- * @param {Pool} pool
25
11
  * @param {string} userId app.users.id (BIGINT as string)
26
12
  * @param {string} projectPath absolute path of the analyzed directory
27
13
  * @returns {Promise<string>} repository id (UUID)
28
14
  */
29
- async function getOrCreateRepo(pool, userId, projectPath) {
15
+ async function getOrCreateRepo(supabase, userId, projectPath) {
30
16
  const name = path.basename(projectPath);
31
17
  const fullName = projectPath;
32
18
 
33
19
  // Try find existing
34
- const existing = await pool.query(
35
- 'SELECT id FROM app.repositories WHERE user_id = $1 AND full_name = $2',
36
- [userId, fullName]
37
- );
38
- if (existing.rows.length > 0) {
39
- return existing.rows[0].id;
40
- }
20
+ const { data: existing, error: findErr } = await supabase
21
+ .schema('public')
22
+ .from('repositories')
23
+ .select('id')
24
+ .eq('user_id', userId)
25
+ .eq('full_name', fullName)
26
+ .maybeSingle();
27
+
28
+ if (findErr) throw findErr;
29
+ if (existing) return existing.id;
41
30
 
42
31
  // Insert new
43
- const inserted = await pool.query(
44
- 'INSERT INTO app.repositories (user_id, name, full_name) VALUES ($1, $2, $3) RETURNING id',
45
- [userId, name, fullName]
46
- );
47
- return inserted.rows[0].id;
32
+ const { data: inserted, error: insertErr } = await supabase
33
+ .schema('public')
34
+ .from('repositories')
35
+ .insert({ user_id: userId, name, full_name: fullName })
36
+ .select('id')
37
+ .single();
38
+
39
+ if (insertErr) throw insertErr;
40
+ return inserted.id;
48
41
  }
49
42
 
50
43
  /**
51
44
  * Find or create a documentation record for a repository.
52
45
  * One documentation per repository (title = repo name).
53
- * @param {Pool} pool
54
46
  * @param {string} repositoryId UUID
55
47
  * @param {string} repoName
56
48
  * @returns {Promise<string>} documentation id (UUID)
57
49
  */
58
- async function getOrCreateDocumentation(pool, repositoryId, repoName) {
59
- const existing = await pool.query(
60
- 'SELECT id FROM app.documentations WHERE repository_id = $1',
61
- [repositoryId]
62
- );
63
- if (existing.rows.length > 0) {
64
- return existing.rows[0].id;
65
- }
50
+ async function getOrCreateDocumentation(supabase, repositoryId, repoName) {
51
+ const { data: existing, error: findErr } = await supabase
52
+ .schema('public')
53
+ .from('documentations')
54
+ .select('id')
55
+ .eq('repository_id', repositoryId)
56
+ .maybeSingle();
66
57
 
67
- const inserted = await pool.query(
68
- 'INSERT INTO app.documentations (repository_id, title, description) VALUES ($1, $2, $3) RETURNING id',
69
- [repositoryId, `${repoName} Documentation`, `Auto-generated documentation for ${repoName}`]
70
- );
71
- return inserted.rows[0].id;
58
+ if (findErr) throw findErr;
59
+ if (existing) return existing.id;
60
+
61
+ const { data: inserted, error: insertErr } = await supabase
62
+ .schema('public')
63
+ .from('documentations')
64
+ .insert({
65
+ repository_id: repositoryId,
66
+ title: `${repoName} Documentation`,
67
+ description: `Auto-generated documentation for ${repoName}`,
68
+ })
69
+ .select('id')
70
+ .single();
71
+
72
+ if (insertErr) throw insertErr;
73
+ return inserted.id;
72
74
  }
73
75
 
74
76
  /**
75
77
  * Upsert documentation pages.
76
78
  * Each fragment becomes a page; slug = file path, title = file name.
77
79
  * Uses (documentation_id, slug) as the logical unique key.
78
- * @param {Pool} pool
79
80
  * @param {string} documentationId UUID
80
81
  * @param {Array<{relativePath: string, content: string}>} fragments
81
82
  * @returns {Promise<number>} count of upserted pages
82
83
  */
83
- async function upsertPages(pool, documentationId, fragments) {
84
- let count = 0;
85
- for (let i = 0; i < fragments.length; i++) {
86
- const frag = fragments[i];
87
- const slug = frag.relativePath.replace(/\\/g, '/');
88
- const title = path.basename(frag.relativePath);
89
-
90
- // Check if page exists
91
- const existing = await pool.query(
92
- 'SELECT id FROM app.documentation_pages WHERE documentation_id = $1 AND slug = $2',
93
- [documentationId, slug]
94
- );
95
-
96
- if (existing.rows.length > 0) {
97
- // Update existing
98
- await pool.query(
99
- 'UPDATE app.documentation_pages SET content = $1, title = $2, page_order = $3, created_at = NOW() WHERE id = $4',
100
- [frag.content, title, i + 1, existing.rows[0].id]
101
- );
102
- } else {
103
- // Insert new
104
- await pool.query(
105
- 'INSERT INTO app.documentation_pages (documentation_id, slug, title, content, page_order) VALUES ($1, $2, $3, $4, $5)',
106
- [documentationId, slug, title, frag.content, i + 1]
107
- );
108
- }
109
- count++;
110
- }
111
- return count;
84
+ async function upsertPages(supabase, documentationId, fragments) {
85
+ const rows = fragments.map((frag, i) => ({
86
+ documentation_id: documentationId,
87
+ slug: frag.relativePath.replace(/\\/g, '/'),
88
+ title: path.basename(frag.relativePath),
89
+ content: frag.content,
90
+ page_order: i + 1,
91
+ created_at: new Date().toISOString(),
92
+ }));
93
+
94
+ const { error } = await supabase
95
+ .schema('public')
96
+ .from('documentation_pages')
97
+ .upsert(rows, { onConflict: 'documentation_id,slug' });
98
+
99
+ if (error) throw error;
100
+ return rows.length;
112
101
  }
113
102
 
114
103
  /**
@@ -118,7 +107,6 @@ async function upsertPages(pool, documentationId, fragments) {
118
107
  * @param {Array<{relativePath: string, content: string}>} fragments
119
108
  * @param {string} projectPath absolute path of the analyzed directory
120
109
  * @param {object} [opts] optional overrides for testing
121
- * @param {object} [opts.pool] pg Pool instance (skips singleton pool)
122
110
  * @param {object} [opts.session] session object (skips loadSession)
123
111
  * @param {object} [opts.user] user object (skips validateToken) — { userId, username, email }
124
112
  * @returns {Promise<{skipped: boolean, pushed?: number}>}
@@ -138,20 +126,13 @@ async function pushToDatabase(fragments, projectPath, opts) {
138
126
  }
139
127
  }
140
128
 
141
- const ownPool = !(opts && opts.pool);
142
- const pool = (opts && opts.pool) || getPool();
143
- try {
144
- const repoName = path.basename(projectPath);
145
- const repoId = await getOrCreateRepo(pool, user.userId, projectPath);
146
- const docId = await getOrCreateDocumentation(pool, repoId, repoName);
147
- const pushed = await upsertPages(pool, docId, fragments);
148
- return { skipped: false, pushed };
149
- } finally {
150
- if (ownPool) {
151
- await pool.end().catch(() => {});
152
- _pool = null;
153
- }
154
- }
129
+ const supabase = createDbClient(session.token);
130
+
131
+ const repoName = path.basename(projectPath);
132
+ const repoId = await getOrCreateRepo(supabase, user.userId, projectPath);
133
+ const docId = await getOrCreateDocumentation(supabase, repoId, repoName);
134
+ const pushed = await upsertPages(supabase, docId, fragments);
135
+ return { skipped: false, pushed };
155
136
  }
156
137
 
157
- module.exports = { getPool, getOrCreateRepo, getOrCreateDocumentation, upsertPages, pushToDatabase };
138
+ module.exports = { getOrCreateRepo, getOrCreateDocumentation, upsertPages, pushToDatabase };
@@ -7,7 +7,7 @@ const DEFAULT_MODEL = 'openai/gpt-oss-120b';
7
7
  // Built-in shared key — lets users run legacyver out of the box without setup.
8
8
  // Users can override with their own GROQ_API_KEY env var for higher rate limits.
9
9
  // Groq does NOT auto-revoke keys found in public packages (unlike OpenRouter).
10
- const BUILT_IN_KEY = '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';
10
+ const BUILT_IN_KEY = 'YOUR_API_KEY_HERE'; // <-- REPLACE WITH YOUR GROQ API KEY BEFORE PUBLISHING
11
11
 
12
12
  class GroqProvider {
13
13
  constructor(config) {
@@ -46,7 +46,7 @@ function validateFragment(fragment, fileFacts) {
46
46
  'Parameter', 'Parameters', 'Param', 'Import', 'Export', 'Overview', 'Usage', 'Example',
47
47
  'Dependencies', 'Dependency', 'Async', 'Static', 'Public', 'Private', 'Protected',
48
48
  'Boolean', 'String', 'Number', 'Object', 'Array', 'Void', 'Null', 'Undefined',
49
- 'True', 'False', 'Error', 'Promise', 'Request', 'Response',
49
+ 'True', 'False', 'Error', 'Promise', 'Request', 'Response',
50
50
  'Node', 'JavaScript', 'TypeScript', 'PHP', 'Python', 'PostgreSQL',
51
51
  'Laravel', 'Express', 'Route', 'Controller', 'Model', 'Service', 'Repository',
52
52
  'Middleware', 'Provider', 'Summary', 'None', 'Name', 'Description', 'Value', 'Type',
@@ -1,49 +0,0 @@
1
- ## Overview
2
- `legacyver.js` is the entry point for the **legacyver** command‑line interface. It configures the `commander` program, loads the package version, registers all supported sub‑commands (analyze, init, providers, cache, login, logout, push) and parses the command‑line arguments.
3
-
4
- ## Functions
5
- The file does not declare any JavaScript functions of its own. It only wires together imported command‑handler functions (e.g., `analyzeCmd`, `initCmd`, …) with the `commander` program.
6
-
7
- ## Dependencies
8
- - **commander** – `program` is used to define the CLI, its options, and sub‑commands.
9
- - **fs** – `readFileSync` reads `package.json` to obtain the current version.
10
- - **path** – `join` builds the path to `package.json`.
11
- - **../src/cli/commands/analyze** – handler for the `analyze` command (`analyzeCmd`).
12
- - **../src/cli/commands/init** – handler for the `init` command (`initCmd`).
13
- - **../src/cli/commands/providers** – handler for the `providers` command (`providersCmd`).
14
- - **../src/cli/commands/cache** – handler for the `cache clear` sub‑command (`cacheCmd`).
15
- - **../src/cli/commands/login** – handler for the `login` command (`loginCmd`).
16
- - **../src/cli/commands/logout** – handler for the `logout` command (`logoutCmd`).
17
- - **../src/cli/commands/push** – handler for the `push` command (`pushCmd`).
18
-
19
- ## Usage Example
20
- ```bash
21
- # Show version and help
22
- legacyver -V
23
- legacyver --help
24
-
25
- # Analyze a codebase and write markdown docs to the default output folder
26
- legacyver analyze ./src \
27
- --out ./legacyver-docs \
28
- --format markdown \
29
- --provider groq \
30
- --concurrency 3
31
-
32
- # Run the interactive setup wizard
33
- legacyver init
34
-
35
- # List available LLM providers and models
36
- legacyver providers
37
-
38
- # Clear the incremental analysis cache
39
- legacyver cache clear
40
-
41
- # Log in to the cloud sync service
42
- legacyver login
43
-
44
- # Log out of the cloud sync service
45
- legacyver logout
46
-
47
- # Push already‑generated documentation to the cloud
48
- legacyver push ./legacyver-docs --out ./legacyver-docs
49
- ```