@devilsdev/rag-pipeline-utils 1.0.0 → 2.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.
Files changed (72) hide show
  1. package/.codecov.yml +21 -0
  2. package/.dockerignore +14 -14
  3. package/.eslintignore +5 -0
  4. package/.eslintrc.json +21 -0
  5. package/.github/workflows/ci.yml +64 -42
  6. package/.husky/pre-commit +5 -0
  7. package/CHANGELOG.md +46 -0
  8. package/Dockerfile +22 -22
  9. package/LICENSE +674 -674
  10. package/README.md +117 -116
  11. package/__tests__/fixtures/.ragrc.json +8 -0
  12. package/__tests__/fixtures/invalid-ragrc.json +4 -0
  13. package/__tests__/fixtures/sample-eval-dataset.json +14 -14
  14. package/__tests__/fixtures/sample.pdf +1 -0
  15. package/__tests__/fixtures/src/mocks/openai-embedder.js +16 -0
  16. package/__tests__/fixtures/src/mocks/openai-llm.js +9 -0
  17. package/__tests__/fixtures/src/mocks/pdf-loader.js +17 -0
  18. package/__tests__/fixtures/src/mocks/pinecone-retriever.js +26 -0
  19. package/__tests__/fixtures/valid-ragrc.json +16 -0
  20. package/__tests__/integration/cli/config-flow.test.js +98 -44
  21. package/__tests__/integration/config/load-config.test.js +67 -52
  22. package/__tests__/unit/config/validate-schema.test.js +121 -0
  23. package/__tests__/unit/core/plugin-registry.test.js +55 -55
  24. package/__tests__/unit/reranker/llm-reranker.test.js +18 -42
  25. package/__tests__/unit/reranker/reranker.enriched.test.js +19 -0
  26. package/__tests__/unit/reranker/reranker.fallback.test.js +15 -0
  27. package/__tests__/unit/reranker/reranker.snapshot.test.js +15 -17
  28. package/__tests__/unit/reranker/reranker.structured-output.test.js +17 -0
  29. package/bin/cli.js +62 -168
  30. package/docs-site/docs/Architecture.md +76 -76
  31. package/docs-site/docs/CLI.md +69 -69
  32. package/docs-site/docs/Evaluation.md +61 -61
  33. package/docs-site/docs/FAQ.md +50 -50
  34. package/docs-site/docs/Plugins.md +61 -61
  35. package/docs-site/docs/Usage.md +72 -72
  36. package/jest.config.js +20 -13
  37. package/package.json +20 -3
  38. package/plugin.config.json +14 -0
  39. package/public/src/App.jsx +57 -57
  40. package/release.config.js +36 -23
  41. package/scripts/banner-injector.js +89 -0
  42. package/scripts/ci-runner.js +51 -0
  43. package/scripts/repair-fixtures.js +30 -0
  44. package/scripts/setup.js +31 -0
  45. package/scripts/test-global-setup.js +18 -0
  46. package/scripts/verify-fixture-mocks.js +45 -0
  47. package/scripts/verify-fixtures.js +36 -0
  48. package/server.js +31 -31
  49. package/src/config/load-config.js +33 -35
  50. package/src/config/load-plugin-config.js +56 -0
  51. package/src/config/validate-plugin-schema.js +28 -0
  52. package/src/config/validate-schema.js +107 -49
  53. package/src/core/create-pipeline.js +96 -82
  54. package/src/core/plugin-contracts.js +21 -0
  55. package/src/core/plugin-registry.js +48 -61
  56. package/src/dag/dag-engine.js +69 -69
  57. package/src/evaluate/evaluator.js +44 -44
  58. package/src/evaluate/scoring.js +73 -73
  59. package/src/loader/csv-loader.js +30 -30
  60. package/src/loader/directory-loader.js +37 -37
  61. package/src/loader/html-loader.js +57 -57
  62. package/src/loader/markdown-loader.js +58 -58
  63. package/src/mocks/openai-embedder.js +30 -0
  64. package/src/mocks/openai-llm.js +18 -0
  65. package/src/mocks/pdf-loader.js +17 -0
  66. package/src/mocks/pinecone-retriever.js +25 -0
  67. package/src/reranker/llm-reranker.js +34 -49
  68. package/src/utils/ci/diagnostic-reporter.js +36 -0
  69. package/src/utils/logger.js +29 -29
  70. package/src/utils/retry.js +42 -42
  71. package/src/utils/validate-plugin-contract.js +36 -0
  72. package/.github/workflows/release.yml +0 -21
@@ -1,72 +1,72 @@
1
- # Usage
2
-
3
- This guide covers how to use `@yourorg/rag-pipeline-utils` programmatically via API and through the CLI.
4
-
5
- ---
6
-
7
- ## šŸ“¦ Installation
8
-
9
- ```bash
10
- npm install @yourorg/rag-pipeline-utils
11
- ```
12
-
13
- ---
14
-
15
- ## šŸ”§ Programmatic API
16
-
17
- Import and instantiate a RAG pipeline:
18
-
19
- ```js
20
- import { createRagPipeline } from '@yourorg/rag-pipeline-utils';
21
-
22
- const pipeline = createRagPipeline({
23
- loader: 'markdown',
24
- embedder: 'openai',
25
- retriever: 'pinecone',
26
- llm: 'openai-gpt-4',
27
- useReranker: true
28
- });
29
-
30
- const answer = await pipeline.query("What is retrieval-augmented generation?");
31
- console.log(answer);
32
- ```
33
-
34
- ---
35
-
36
- ## šŸ” Pipeline Methods
37
-
38
- - `pipeline.ingest(path: string)` → Load, chunk, embed, and store vectors
39
- - `pipeline.query(prompt: string)` → Retrieve context, rerank (optional), and call LLM
40
-
41
- ---
42
-
43
- ## 🧩 Plugin Configuration
44
-
45
- You can customize the pipeline by registering plugins:
46
-
47
- ```js
48
- registry.register('loader', 'custom', new MyCustomLoader());
49
- registry.register('retriever', 'opensearch', new MyOpenSearchRetriever());
50
- ```
51
-
52
- ---
53
-
54
- ## āš™ļø Configuration via `.ragrc.json`
55
-
56
- Create a JSON file at your root:
57
-
58
- ```json
59
- {
60
- "loader": "directory",
61
- "embedder": "openai",
62
- "retriever": "pinecone",
63
- "llm": "openai-gpt-4",
64
- "useReranker": true
65
- }
66
- ```
67
-
68
- Used automatically when no CLI args are passed.
69
-
70
- ---
71
-
72
- Next → [CLI](./CLI.md)
1
+ # Usage
2
+
3
+ This guide covers how to use `@yourorg/rag-pipeline-utils` programmatically via API and through the CLI.
4
+
5
+ ---
6
+
7
+ ## šŸ“¦ Installation
8
+
9
+ ```bash
10
+ npm install @yourorg/rag-pipeline-utils
11
+ ```
12
+
13
+ ---
14
+
15
+ ## šŸ”§ Programmatic API
16
+
17
+ Import and instantiate a RAG pipeline:
18
+
19
+ ```js
20
+ import { createRagPipeline } from '@yourorg/rag-pipeline-utils';
21
+
22
+ const pipeline = createRagPipeline({
23
+ loader: 'markdown',
24
+ embedder: 'openai',
25
+ retriever: 'pinecone',
26
+ llm: 'openai-gpt-4',
27
+ useReranker: true
28
+ });
29
+
30
+ const answer = await pipeline.query("What is retrieval-augmented generation?");
31
+ console.log(answer);
32
+ ```
33
+
34
+ ---
35
+
36
+ ## šŸ” Pipeline Methods
37
+
38
+ - `pipeline.ingest(path: string)` → Load, chunk, embed, and store vectors
39
+ - `pipeline.query(prompt: string)` → Retrieve context, rerank (optional), and call LLM
40
+
41
+ ---
42
+
43
+ ## 🧩 Plugin Configuration
44
+
45
+ You can customize the pipeline by registering plugins:
46
+
47
+ ```js
48
+ registry.register('loader', 'custom', new MyCustomLoader());
49
+ registry.register('retriever', 'opensearch', new MyOpenSearchRetriever());
50
+ ```
51
+
52
+ ---
53
+
54
+ ## āš™ļø Configuration via `.ragrc.json`
55
+
56
+ Create a JSON file at your root:
57
+
58
+ ```json
59
+ {
60
+ "loader": "directory",
61
+ "embedder": "openai",
62
+ "retriever": "pinecone",
63
+ "llm": "openai-gpt-4",
64
+ "useReranker": true
65
+ }
66
+ ```
67
+
68
+ Used automatically when no CLI args are passed.
69
+
70
+ ---
71
+
72
+ Next → [CLI](./CLI.md)
package/jest.config.js CHANGED
@@ -1,14 +1,21 @@
1
- /**
2
- * Version: 0.1.0
3
- * Path: /jest.config.js
4
- * Description: Jest configuration for native ESM test support
5
- * Author: Ali Kahwaji
6
- */
7
-
8
- export default {
9
- testEnvironment: 'node',
10
- transform: {}, // Native ESM — no Babel used
11
- roots: ['<rootDir>/__tests__'],
12
- verbose: true
13
- };
1
+ /**
2
+ * Version: 0.1.0
3
+ * Path: /jest.config.js
4
+ * Description: Jest configuration for native ESM test support
5
+ * Author: Ali Kahwaji
6
+ */
7
+
8
+ // jest.config.js
9
+
10
+ export default {
11
+ verbose: true,
12
+ testEnvironment: 'node',
13
+ transform: {},
14
+ collectCoverage: true,
15
+ coverageDirectory: 'coverage',
16
+ moduleFileExtensions: ['js', 'json'],
17
+ testMatch: ['**/__tests__/**/*.test.js'],
18
+ roots: ['<rootDir>/__tests__'],
19
+ };
20
+
14
21
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@devilsdev/rag-pipeline-utils",
3
- "version": "1.0.0",
3
+ "version": "2.0.0",
4
4
  "description": "A modular toolkit for building RAG (Retrieval-Augmented Generation) pipelines in Node.js",
5
5
  "main": "src/core/create-pipeline.js",
6
6
  "bin": {
@@ -8,10 +8,22 @@
8
8
  },
9
9
  "type": "module",
10
10
  "scripts": {
11
- "test": "cross-env NODE_OPTIONS=--experimental-vm-modules jest --coverage",
11
+ "validate-fixtures": "node ./scripts/verify-fixtures.js",
12
+ "setup": "node ./scripts/setup.js",
13
+ "test": "node ./scripts/setup.js && cross-env NODE_OPTIONS=--experimental-vm-modules jest --coverage",
12
14
  "test:watch": "jest --watch",
13
15
  "lint": "eslint .",
14
- "prepare": "husky install"
16
+ "prepare": "husky install",
17
+ "lint-staged": "lint-staged",
18
+ "postinstall": "husky install",
19
+ "verify:mocks": "node scripts/verify-fixture-mocks.js",
20
+ "ci": "node scripts/ci-runner.js"
21
+ },
22
+ "lint-staged": {
23
+ "**/*.{js,ts}": [
24
+ "npm run lint",
25
+ "npm run validate-fixtures"
26
+ ]
15
27
  },
16
28
  "repository": {
17
29
  "type": "git",
@@ -37,21 +49,26 @@
37
49
  },
38
50
  "devDependencies": {
39
51
  "@semantic-release/changelog": "^6.0.3",
52
+ "@semantic-release/commit-analyzer": "^13.0.1",
40
53
  "@semantic-release/git": "^10.0.1",
41
54
  "@semantic-release/github": "^9.2.6",
42
55
  "@semantic-release/npm": "^11.0.3",
56
+ "@semantic-release/release-notes-generator": "^14.0.3",
43
57
  "@types/jest": "^29.5.14",
58
+ "codecov": "^3.8.2",
44
59
  "cross-env": "^7.0.3",
45
60
  "esbuild": "^0.25.2",
46
61
  "eslint": "^8.57.1",
47
62
  "husky": "^8.0.3",
48
63
  "jest": "^29.7.0",
64
+ "lint-staged": "^15.5.1",
49
65
  "prettier": "^3.5.3",
50
66
  "rimraf": "^6.0.1",
51
67
  "semantic-release": "^21.1.2",
52
68
  "ts-jest": "^29.3.2"
53
69
  },
54
70
  "dependencies": {
71
+ "ajv": "^8.17.1",
55
72
  "axios": "^1.8.4",
56
73
  "chalk": "^5.4.1",
57
74
  "commander": "^13.1.0",
@@ -0,0 +1,14 @@
1
+ {
2
+ "loader": {
3
+ "pdf": "./src/mocks/pdf-loader.js"
4
+ },
5
+ "embedder": {
6
+ "openai": "./src/mocks/openai-embedder.js"
7
+ },
8
+ "retriever": {
9
+ "pinecone": "./src/mocks/pinecone-retriever.js"
10
+ },
11
+ "llm": {
12
+ "openai-gpt-4": "./src/mocks/openai-llm.js"
13
+ }
14
+ }
@@ -1,57 +1,57 @@
1
- /**
2
- * Version: 0.1.0
3
- * Path: /public/src/App.jsx
4
- * Description: Evaluation results dashboard UI
5
- * Author: Ali Kahwaji
6
- */
7
-
8
- import { useEffect, useState } from 'react';
9
-
10
- export default function App() {
11
- const [results, setResults] = useState([]);
12
- const [summary, setSummary] = useState({ passRate: 0, avgBLEU: 0, avgROUGE: 0 });
13
-
14
- useEffect(() => {
15
- fetch('/results/sample-eval.json')
16
- .then(res => res.json())
17
- .then(data => {
18
- setResults(data);
19
-
20
- const total = data.length;
21
- const passCount = data.filter(r => r.success).length;
22
- const avgBLEU = data.reduce((s, r) => s + r.scores.bleu, 0) / total;
23
- const avgROUGE = data.reduce((s, r) => s + r.scores.rouge, 0) / total;
24
-
25
- setSummary({
26
- passRate: (passCount / total * 100).toFixed(1),
27
- avgBLEU: avgBLEU.toFixed(2),
28
- avgROUGE: avgROUGE.toFixed(2)
29
- });
30
- });
31
- }, []);
32
-
33
- return (
34
- <main className="max-w-6xl mx-auto p-6">
35
- <h1 className="text-3xl font-bold mb-4">RAG Evaluation Dashboard</h1>
36
-
37
- <div className="mb-6 p-4 border rounded shadow bg-white">
38
- <h2 className="text-xl font-semibold mb-2">Summary</h2>
39
- <p> Pass Rate: <strong>{summary.passRate}%</strong></p>
40
- <p> Avg BLEU: <strong>{summary.avgBLEU}</strong></p>
41
- <p> Avg ROUGE: <strong>{summary.avgROUGE}</strong></p>
42
- </div>
43
-
44
- {results.map((res, i) => (
45
- <div key={i} className="mb-4 p-4 border-l-4 rounded bg-white shadow border-l-gray-300">
46
- <h3 className="font-bold text-lg">Prompt {i + 1}</h3>
47
- <p className="mt-2"><strong>Prompt:</strong> {res.prompt}</p>
48
- <p><strong>Expected:</strong> {res.expected}</p>
49
- <p><strong>Actual:</strong> {res.actual}</p>
50
- <p> BLEU: <strong>{res.scores.bleu.toFixed(2)}</strong></p>
51
- <p> ROUGE: <strong>{res.scores.rouge.toFixed(2)}</strong></p>
52
- <p>Pass: <strong className={res.success ? 'text-green-600' : 'text-red-600'}>{res.success ? 'Yes' : 'No'}</strong></p>
53
- </div>
54
- ))}
55
- </main>
56
- );
57
- }
1
+ /**
2
+ * Version: 0.1.0
3
+ * Path: /public/src/App.jsx
4
+ * Description: Evaluation results dashboard UI
5
+ * Author: Ali Kahwaji
6
+ */
7
+
8
+ import { useEffect, useState } from 'react';
9
+
10
+ export default function App() {
11
+ const [results, setResults] = useState([]);
12
+ const [summary, setSummary] = useState({ passRate: 0, avgBLEU: 0, avgROUGE: 0 });
13
+
14
+ useEffect(() => {
15
+ fetch('/results/sample-eval.json')
16
+ .then(res => res.json())
17
+ .then(data => {
18
+ setResults(data);
19
+
20
+ const total = data.length;
21
+ const passCount = data.filter(r => r.success).length;
22
+ const avgBLEU = data.reduce((s, r) => s + r.scores.bleu, 0) / total;
23
+ const avgROUGE = data.reduce((s, r) => s + r.scores.rouge, 0) / total;
24
+
25
+ setSummary({
26
+ passRate: (passCount / total * 100).toFixed(1),
27
+ avgBLEU: avgBLEU.toFixed(2),
28
+ avgROUGE: avgROUGE.toFixed(2)
29
+ });
30
+ });
31
+ }, []);
32
+
33
+ return (
34
+ <main className="max-w-6xl mx-auto p-6">
35
+ <h1 className="text-3xl font-bold mb-4">RAG Evaluation Dashboard</h1>
36
+
37
+ <div className="mb-6 p-4 border rounded shadow bg-white">
38
+ <h2 className="text-xl font-semibold mb-2">Summary</h2>
39
+ <p> Pass Rate: <strong>{summary.passRate}%</strong></p>
40
+ <p> Avg BLEU: <strong>{summary.avgBLEU}</strong></p>
41
+ <p> Avg ROUGE: <strong>{summary.avgROUGE}</strong></p>
42
+ </div>
43
+
44
+ {results.map((res, i) => (
45
+ <div key={i} className="mb-4 p-4 border-l-4 rounded bg-white shadow border-l-gray-300">
46
+ <h3 className="font-bold text-lg">Prompt {i + 1}</h3>
47
+ <p className="mt-2"><strong>Prompt:</strong> {res.prompt}</p>
48
+ <p><strong>Expected:</strong> {res.expected}</p>
49
+ <p><strong>Actual:</strong> {res.actual}</p>
50
+ <p> BLEU: <strong>{res.scores.bleu.toFixed(2)}</strong></p>
51
+ <p> ROUGE: <strong>{res.scores.rouge.toFixed(2)}</strong></p>
52
+ <p>Pass: <strong className={res.success ? 'text-green-600' : 'text-red-600'}>{res.success ? 'Yes' : 'No'}</strong></p>
53
+ </div>
54
+ ))}
55
+ </main>
56
+ );
57
+ }
package/release.config.js CHANGED
@@ -1,23 +1,36 @@
1
- /**
2
- * Version: 0.1.0
3
- * Path: /release.config.js
4
- * Description: Semantic release configuration for automated changelog, versioning, GitHub and npm publishing
5
- * Author: Ali Kahwaji
6
- */
7
-
8
- export default {
9
- branches: ['main'],
10
- plugins: [
11
- '@semantic-release/commit-analyzer',
12
- '@semantic-release/release-notes-generator',
13
- ['@semantic-release/changelog', {
14
- changelogFile: 'CHANGELOG.md'
15
- }],
16
- '@semantic-release/npm',
17
- ['@semantic-release/git', {
18
- assets: ['CHANGELOG.md', 'package.json'],
19
- message: 'chore(release): ${nextRelease.version} [skip ci]\n\n${nextRelease.notes}'
20
- }],
21
- '@semantic-release/github'
22
- ]
23
- };
1
+ /**
2
+ * Version: 0.1.0
3
+ * Description: Semantic Release configuration for CI/CD automation with changelog, versioning, GitHub releases, and npm publishing.
4
+ * Author: Ali Kahwaji
5
+ */
6
+
7
+ export default {
8
+ branches: ['main'],
9
+ repositoryUrl: 'https://github.com/DevilsDev/rag-pipeline-utils',
10
+
11
+ plugins: [
12
+ '@semantic-release/commit-analyzer',
13
+ '@semantic-release/release-notes-generator',
14
+ [
15
+ '@semantic-release/changelog',
16
+ {
17
+ changelogFile: 'CHANGELOG.md',
18
+ changelogTitle: '# Changelog',
19
+ },
20
+ ],
21
+ [
22
+ '@semantic-release/npm',
23
+ {
24
+ npmPublish: true,
25
+ },
26
+ ],
27
+ [
28
+ '@semantic-release/git',
29
+ {
30
+ assets: ['package.json', 'package-lock.json', 'CHANGELOG.md'],
31
+ message: 'chore(release): ${nextRelease.version} [skip ci]\n\n${nextRelease.notes}',
32
+ },
33
+ ],
34
+ '@semantic-release/github',
35
+ ],
36
+ };
@@ -0,0 +1,89 @@
1
+ /**
2
+ * File: scripts/banner-injector.js
3
+ * Description: Script to programmatically inject file-level banners into specified project files
4
+ * Author: Ali Kahwaji
5
+ * Version: 1.0.0
6
+ */
7
+
8
+ import fs from 'fs';
9
+ import path from 'path';
10
+ import glob from 'glob';
11
+
12
+ const banners = {
13
+ '__tests__/unit/reranker/llm-reranker.test.js': `/**
14
+ * File: __tests__/unit/reranker/llm-reranker.test.js
15
+ * Description: Unit and integration tests with mocks for RAG pipeline
16
+ * Author: Ali Kahwaji
17
+ * Version: 1.0.4
18
+ */\n\n`,
19
+
20
+ '__tests__/unit/reranker/reranker.snapshot.test.js': `/**
21
+ * File: __tests__/unit/reranker/reranker.snapshot.test.js
22
+ * Description: Snapshot tests for LLM reranker
23
+ * Author: Ali Kahwaji
24
+ * Version: 1.0.4
25
+ */\n\n`,
26
+
27
+ '__tests__/unit/core/plugin-registry.test.js': `/**
28
+ * File: __tests__/unit/core/plugin-registry.test.js
29
+ * Description: Unit and integration tests with mocks for RAG pipeline
30
+ * Author: Ali Kahwaji
31
+ * Version: 1.0.4
32
+ */\n\n`,
33
+
34
+ '__tests__/integration/config/load-config.test.js': `/**
35
+ * File: __tests__/integration/config/load-config.test.js
36
+ * Description: Unit and integration tests with mocks for RAG pipeline
37
+ * Author: Ali Kahwaji
38
+ * Version: 1.0.4
39
+ */\n\n`,
40
+
41
+ '__tests__/integration/cli/config-flow.test.js': `/**
42
+ * File: __tests__/integration/cli/config-flow.test.js
43
+ * Description: Unit and integration tests with mocks for RAG pipeline
44
+ * Author: Ali Kahwaji
45
+ * Version: 1.0.4
46
+ */\n\n`,
47
+
48
+ 'src/mocks/pdf-loader.js': `/**
49
+ * File: src/mocks/pdf-loader.js
50
+ * Description: Unit and integration tests with mocks for RAG pipeline
51
+ * Author: Ali Kahwaji
52
+ * Version: 1.0.4
53
+ */\n\n`,
54
+
55
+ 'src/mocks/openai-embedder.js': `/**
56
+ * File: src/mocks/openai-embedder.js
57
+ * Description: Unit and integration tests with mocks for RAG pipeline
58
+ * Author: Ali Kahwaji
59
+ * Version: 1.0.4
60
+ */\n\n`,
61
+
62
+ 'src/mocks/pinecone-retriever.js': `/**
63
+ * File: src/mocks/pinecone-retriever.js
64
+ * Description: Unit and integration tests with mocks for RAG pipeline
65
+ * Author: Ali Kahwaji
66
+ * Version: 1.0.4
67
+ */\n\n`
68
+ };
69
+
70
+ Object.entries(banners).forEach(([filePath, banner]) => {
71
+ const resolvedPath = path.resolve(filePath);
72
+
73
+ if (!fs.existsSync(resolvedPath)) {
74
+ console.warn(`[SKIPPED] ${filePath} does not exist.`);
75
+ return;
76
+ }
77
+
78
+ const originalContent = fs.readFileSync(resolvedPath, 'utf-8');
79
+
80
+ // Skip if banner already present
81
+ if (originalContent.startsWith('/**')) {
82
+ console.log(`[SKIPPED] ${filePath} already contains a banner.`);
83
+ return;
84
+ }
85
+
86
+ const newContent = banner + originalContent;
87
+ fs.writeFileSync(resolvedPath, newContent, 'utf-8');
88
+ console.log(`[INJECTED] Banner added to ${filePath}`);
89
+ });
@@ -0,0 +1,51 @@
1
+ /**
2
+ * Version: 1.1.0
3
+ * Description: CI runner for linting, mock validation, testing and coverage
4
+ * Author: Ali Kahwaji
5
+ * File: scripts/ci-runner.js
6
+ */
7
+
8
+ import { execSync } from 'child_process';
9
+ import path from 'path';
10
+ import { pathToFileURL } from 'url';
11
+ import fs from 'fs';
12
+
13
+ // Entry helpers
14
+ const ROOT = path.resolve();
15
+ const resolveScript = (relPath) => pathToFileURL(path.resolve(ROOT, relPath)).href;
16
+
17
+ const run = (label, command) => {
18
+ console.log(`\nšŸ”§ Running: ${label}`);
19
+ try {
20
+ execSync(command, { stdio: 'inherit', cwd: ROOT });
21
+ console.log(`āœ… ${label} passed`);
22
+ } catch (err) {
23
+ console.error(`āŒ ${label} failed`);
24
+ process.exit(1);
25
+ }
26
+ };
27
+
28
+ const runDynamicModule = async (label, modulePath) => {
29
+ console.log(`\nšŸ” Validating: ${label}`);
30
+ try {
31
+ const mod = await import(resolveScript(modulePath));
32
+ if (typeof mod.default === 'function') await mod.default();
33
+ console.log(`āœ… ${label} passed`);
34
+ } catch (err) {
35
+ console.error(`āŒ ${label} failed:\n${err.message}`);
36
+ process.exit(1);
37
+ }
38
+ };
39
+
40
+ (async () => {
41
+ // Step 1: Lint
42
+ run('Lint', 'npx eslint . --ext .js,.ts');
43
+
44
+ // Step 2: Verify mocks for required interface methods
45
+ await runDynamicModule('Fixture Mock Interface Validation', 'scripts/verify-fixture-mocks.js');
46
+
47
+ // Step 3: Test and coverage
48
+ run('Unit + Integration Tests w/ Coverage', 'cross-env NODE_OPTIONS=--experimental-vm-modules jest --coverage');
49
+
50
+ console.log('\nšŸŽ‰ CI runner completed successfully.\n');
51
+ })();
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Version: 1.0.0
3
+ * Description: Auto-repairs mock plugin fixtures for integration tests
4
+ * Author: Ali Kahwaji
5
+ */
6
+
7
+ import { mkdirSync, copyFileSync, existsSync } from 'fs';
8
+ import { resolve, join } from 'path';
9
+
10
+ const MOCKS = [
11
+ 'pdf-loader.js',
12
+ 'openai-embedder.js',
13
+ 'pinecone-retriever.js',
14
+ 'openai-llm.js'
15
+ ];
16
+
17
+ const SRC_FIXTURES = resolve('__tests__/fixtures/src/mocks');
18
+ const DEST_FIXTURES = resolve('__tests__/__temp__/cli-config-fallback/src/mocks');
19
+
20
+ mkdirSync(DEST_FIXTURES, { recursive: true });
21
+
22
+ MOCKS.forEach((file) => {
23
+ const src = join(SRC_FIXTURES, file);
24
+ const dest = join(DEST_FIXTURES, file);
25
+ if (!existsSync(src)) throw new Error(`[FIXTURE MISSING] ${src}`);
26
+ copyFileSync(src, dest);
27
+ console.log(`[COPIED] ${file}`);
28
+ });
29
+
30
+ console.log('āœ… Fixture mocks successfully repaired.');
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Version: 1.1.0
3
+ * Description: Test setup script to ensure fixtures exist and validate mocks before running tests.
4
+ * Author: Ali Kahwaji
5
+ */
6
+
7
+ import fs from 'fs';
8
+ import path from 'path';
9
+ import { fileURLToPath } from 'url';
10
+ import { execSync } from 'child_process';
11
+
12
+ // Resolve __dirname in ES module scope
13
+ const __filename = fileURLToPath(import.meta.url);
14
+ const __dirname = path.dirname(__filename);
15
+
16
+ const fixturePath = path.resolve(__dirname, '../__tests__/fixtures/sample.pdf');
17
+
18
+ // Ensure PDF test fixture
19
+ if (!fs.existsSync(fixturePath)) {
20
+ fs.mkdirSync(path.dirname(fixturePath), { recursive: true });
21
+ fs.writeFileSync(fixturePath, 'Dummy PDF content for test');
22
+ console.log('[setup] Created sample.pdf fixture');
23
+ }
24
+
25
+ // Validate plugin fixture presence
26
+ try {
27
+ execSync('node ./scripts/verify-fixtures.js', { stdio: 'inherit' });
28
+ } catch (err) {
29
+ console.error('[setup] Fixture verification failed. Please check missing mocks.');
30
+ process.exit(1);
31
+ }
@@ -0,0 +1,18 @@
1
+ // __tests__/setup/global.js
2
+ import fs from 'node:fs';
3
+ import path from 'node:path';
4
+ import { fileURLToPath } from 'node:url';
5
+
6
+ // __dirname polyfill for ESM
7
+ const __filename = fileURLToPath(import.meta.url);
8
+ const __dirname = path.dirname(__filename);
9
+
10
+ const pdfPath = path.resolve(__dirname, '../fixtures/sample.pdf');
11
+
12
+ // Ensure the fixtures directory exists
13
+ fs.mkdirSync(path.dirname(pdfPath), { recursive: true });
14
+
15
+ // Create a minimal valid PDF file if it doesn't exist
16
+ if (!fs.existsSync(pdfPath)) {
17
+ fs.writeFileSync(pdfPath, '%PDF-1.4\n%EOF');
18
+ }