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.
- package/legacyver-docs/OrderController.md +80 -0
- package/legacyver-docs/SUMMARY.md +3 -3
- package/legacyver-docs/auth.md +71 -0
- package/legacyver-docs/hash.md +37 -0
- package/legacyver-docs/index.md +15 -15
- package/package.json +11 -2
- package/src/api/auth.js +32 -45
- package/src/cli/commands/analyze.js +20 -1
- package/src/cli/commands/login.js +1 -1
- package/src/db/config.js +39 -14
- package/src/db/index.js +69 -88
- package/src/llm/providers/groq.js +1 -1
- package/src/llm/validator.js +1 -1
- package/legacyver-docs/legacyver.md +0 -49
|
@@ -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
|
-
* [
|
|
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
|
+
```
|
package/legacyver-docs/index.md
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
#
|
|
2
|
-
|
|
3
|
-
**Primary language:**
|
|
4
|
-
**Total files:** 1
|
|
5
|
-
**Analyzed at:** 2026-
|
|
6
|
-
|
|
7
|
-
## Files
|
|
8
|
-
|
|
9
|
-
- [
|
|
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.
|
|
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 {
|
|
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
|
|
13
|
+
async function validateToken(token) {
|
|
17
14
|
if (!token) return null;
|
|
18
15
|
|
|
19
|
-
const
|
|
20
|
-
const
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
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
|
|
41
|
+
async function revokeToken(token) {
|
|
54
42
|
if (!token) return;
|
|
55
43
|
|
|
56
|
-
const
|
|
57
|
-
const
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
);
|
|
64
|
-
|
|
65
|
-
|
|
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
|
-
|
|
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://
|
|
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
|
-
*
|
|
5
|
-
*
|
|
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
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
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
|
|
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(
|
|
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
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
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
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
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(
|
|
59
|
-
const existing = await
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
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
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
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(
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
)
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
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
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
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 = {
|
|
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 = '
|
|
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) {
|
package/src/llm/validator.js
CHANGED
|
@@ -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
|
-
|
|
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
|
-
```
|