doc-detective-common 3.6.0-dev.2 → 3.6.1-dev.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/.c8rc.json +8 -0
- package/.claude/skills/tdd-coverage/SKILL.md +161 -0
- package/AGENTS.md +47 -0
- package/CLAUDE.md +38 -0
- package/coverage-thresholds.json +8 -0
- package/package.json +7 -2
- package/scripts/check-coverage-ratchet.js +118 -0
- package/src/resolvePaths.js +10 -9
- package/src/validate.js +21 -12
package/.c8rc.json
ADDED
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
# TDD and Coverage Skill
|
|
2
|
+
|
|
3
|
+
**Type:** Rigid (follow exactly)
|
|
4
|
+
|
|
5
|
+
## When to Use
|
|
6
|
+
|
|
7
|
+
Use this skill when:
|
|
8
|
+
- Creating new functionality
|
|
9
|
+
- Modifying existing code
|
|
10
|
+
- Fixing bugs
|
|
11
|
+
- Refactoring
|
|
12
|
+
|
|
13
|
+
## Mandatory Process
|
|
14
|
+
|
|
15
|
+
### 1. Test First (TDD)
|
|
16
|
+
|
|
17
|
+
Before writing or modifying any implementation code:
|
|
18
|
+
|
|
19
|
+
1. **Write the test(s)** that describe the expected behavior
|
|
20
|
+
2. **Run the test** - it should FAIL (red)
|
|
21
|
+
3. **Write the implementation** to make the test pass
|
|
22
|
+
4. **Run the test** - it should PASS (green)
|
|
23
|
+
5. **Refactor** if needed, keeping tests passing
|
|
24
|
+
|
|
25
|
+
### 2. Coverage Verification
|
|
26
|
+
|
|
27
|
+
After any code change:
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
# Run tests with coverage
|
|
31
|
+
npm run test:coverage
|
|
32
|
+
|
|
33
|
+
# Verify coverage hasn't decreased
|
|
34
|
+
npm run test:coverage:ratchet
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
**Coverage must not decrease.** If ratchet check fails:
|
|
38
|
+
1. Add tests for uncovered code
|
|
39
|
+
2. Re-run coverage until ratchet passes
|
|
40
|
+
|
|
41
|
+
### 3. Coverage Thresholds
|
|
42
|
+
|
|
43
|
+
Current thresholds are in `coverage-thresholds.json`. These values must only increase:
|
|
44
|
+
|
|
45
|
+
| Metric | Threshold |
|
|
46
|
+
|--------|-----------|
|
|
47
|
+
| Lines | 100% |
|
|
48
|
+
| Statements | 100% |
|
|
49
|
+
| Functions | 100% |
|
|
50
|
+
| Branches | 100% |
|
|
51
|
+
|
|
52
|
+
### 4. Test Location
|
|
53
|
+
|
|
54
|
+
| Code | Test File |
|
|
55
|
+
|------|-----------|
|
|
56
|
+
| `src/validate.js` | `test/validate.test.js` |
|
|
57
|
+
| `src/resolvePaths.js` | `test/resolvePaths.test.js` |
|
|
58
|
+
| `src/files.js` | `test/files.test.js` |
|
|
59
|
+
| Schema validation | `test/schema.test.js` |
|
|
60
|
+
|
|
61
|
+
### 5. Test Structure Pattern
|
|
62
|
+
|
|
63
|
+
```javascript
|
|
64
|
+
const sinon = require("sinon");
|
|
65
|
+
|
|
66
|
+
(async () => {
|
|
67
|
+
const { expect } = await import("chai");
|
|
68
|
+
const { functionUnderTest } = require("../src/module");
|
|
69
|
+
|
|
70
|
+
describe("functionUnderTest", function () {
|
|
71
|
+
describe("input validation", function () {
|
|
72
|
+
it("should throw error when required param missing", function () {
|
|
73
|
+
expect(() => functionUnderTest()).to.throw();
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
describe("happy path", function () {
|
|
78
|
+
it("should return expected result for valid input", function () {
|
|
79
|
+
const result = functionUnderTest({ validInput: true });
|
|
80
|
+
expect(result).to.deep.equal(expectedOutput);
|
|
81
|
+
});
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
describe("edge cases", function () {
|
|
85
|
+
it("should handle boundary condition", function () {
|
|
86
|
+
// test edge case
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
})();
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### 6. Checklist
|
|
94
|
+
|
|
95
|
+
Before completing any code change:
|
|
96
|
+
|
|
97
|
+
- [ ] Tests written BEFORE implementation (or for existing code: tests added)
|
|
98
|
+
- [ ] All tests pass (`npm test`)
|
|
99
|
+
- [ ] Coverage hasn't decreased (`npm run test:coverage:ratchet`)
|
|
100
|
+
- [ ] New code has corresponding test coverage
|
|
101
|
+
- [ ] Error paths are tested (not just happy paths)
|
|
102
|
+
|
|
103
|
+
## Commands Reference
|
|
104
|
+
|
|
105
|
+
```bash
|
|
106
|
+
# Run all tests
|
|
107
|
+
npm test
|
|
108
|
+
|
|
109
|
+
# Run tests with coverage report
|
|
110
|
+
npm run test:coverage
|
|
111
|
+
|
|
112
|
+
# Run coverage ratchet check
|
|
113
|
+
npm run test:coverage:ratchet
|
|
114
|
+
|
|
115
|
+
# Generate HTML coverage report
|
|
116
|
+
npm run test:coverage:html
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
## Common Patterns
|
|
120
|
+
|
|
121
|
+
### Testing async functions
|
|
122
|
+
|
|
123
|
+
```javascript
|
|
124
|
+
it("should handle async operation", async function () {
|
|
125
|
+
const result = await asyncFunction();
|
|
126
|
+
expect(result).to.exist;
|
|
127
|
+
});
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
### Mocking with Sinon
|
|
131
|
+
|
|
132
|
+
```javascript
|
|
133
|
+
const stub = sinon.stub(fs, "readFileSync").returns("mock content");
|
|
134
|
+
try {
|
|
135
|
+
const result = functionUnderTest();
|
|
136
|
+
expect(result).to.equal("expected");
|
|
137
|
+
} finally {
|
|
138
|
+
stub.restore();
|
|
139
|
+
}
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
### Testing error handling
|
|
143
|
+
|
|
144
|
+
```javascript
|
|
145
|
+
it("should throw on invalid input", function () {
|
|
146
|
+
expect(() => functionUnderTest(null)).to.throw(/error message/);
|
|
147
|
+
});
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
### Testing transformations
|
|
151
|
+
|
|
152
|
+
```javascript
|
|
153
|
+
it("should transform v2 object to v3", function () {
|
|
154
|
+
const result = transformToSchemaKey({
|
|
155
|
+
currentSchema: "schema_v2",
|
|
156
|
+
targetSchema: "schema_v3",
|
|
157
|
+
object: v2Object,
|
|
158
|
+
});
|
|
159
|
+
expect(result.newProperty).to.equal(expectedValue);
|
|
160
|
+
});
|
|
161
|
+
```
|
package/AGENTS.md
CHANGED
|
@@ -106,6 +106,8 @@ When creating new schema version (e.g., v4):
|
|
|
106
106
|
**Test structure (Mocha + Chai):**
|
|
107
107
|
- `test/schema.test.js`: Validates all schema examples (auto-generated from schemas)
|
|
108
108
|
- `test/files.test.js`: Unit tests for `readFile()` with Sinon stubs
|
|
109
|
+
- `test/validate.test.js`: Tests for `validate()` and `transformToSchemaKey()`
|
|
110
|
+
- `test/resolvePaths.test.js`: Tests for path resolution
|
|
109
111
|
|
|
110
112
|
**Run tests:** `npm test` (or `mocha`)
|
|
111
113
|
|
|
@@ -115,6 +117,51 @@ const result = validate({ schemaKey: "step_v3", object: example });
|
|
|
115
117
|
assert.ok(result.valid, `Validation failed: ${result.errors}`);
|
|
116
118
|
```
|
|
117
119
|
|
|
120
|
+
### Testing Requirements (CRITICAL)
|
|
121
|
+
|
|
122
|
+
**TDD is mandatory for this project.** All code changes must follow test-driven development:
|
|
123
|
+
|
|
124
|
+
1. **Write tests first** - before any implementation
|
|
125
|
+
2. **Run tests** - verify they fail (red)
|
|
126
|
+
3. **Write implementation** - make tests pass
|
|
127
|
+
4. **Run tests** - verify they pass (green)
|
|
128
|
+
5. **Check coverage** - must not decrease
|
|
129
|
+
|
|
130
|
+
**Coverage enforcement:**
|
|
131
|
+
|
|
132
|
+
```bash
|
|
133
|
+
# Run tests with coverage
|
|
134
|
+
npm run test:coverage
|
|
135
|
+
|
|
136
|
+
# Verify coverage baseline (CI enforces this)
|
|
137
|
+
npm run test:coverage:ratchet
|
|
138
|
+
|
|
139
|
+
# Generate HTML report for detailed analysis
|
|
140
|
+
npm run test:coverage:html
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
**Current coverage thresholds (enforced by CI):**
|
|
144
|
+
|
|
145
|
+
| Metric | Threshold |
|
|
146
|
+
|--------|-----------|
|
|
147
|
+
| Lines | 100% |
|
|
148
|
+
| Statements | 100% |
|
|
149
|
+
| Functions | 100% |
|
|
150
|
+
| Branches | 100% |
|
|
151
|
+
|
|
152
|
+
**Coverage ratchet:** Thresholds in `coverage-thresholds.json` can only increase. CI fails if coverage decreases.
|
|
153
|
+
|
|
154
|
+
**Test file mapping:**
|
|
155
|
+
|
|
156
|
+
| Source | Test File |
|
|
157
|
+
|--------|-----------|
|
|
158
|
+
| `src/validate.js` | `test/validate.test.js` |
|
|
159
|
+
| `src/resolvePaths.js` | `test/resolvePaths.test.js` |
|
|
160
|
+
| `src/files.js` | `test/files.test.js` |
|
|
161
|
+
| Schema examples | `test/schema.test.js` |
|
|
162
|
+
|
|
163
|
+
**AI Tooling:** See `.claude/skills/tdd-coverage/SKILL.md` for detailed TDD workflow.
|
|
164
|
+
|
|
118
165
|
### Version Management & CI/CD Workflows
|
|
119
166
|
|
|
120
167
|
#### Auto Dev Release (`.github/workflows/auto-dev-release.yml`)
|
package/CLAUDE.md
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# Claude Code Configuration
|
|
2
|
+
|
|
3
|
+
This file is a pointer for Claude Code and similar AI assistants.
|
|
4
|
+
|
|
5
|
+
## Primary Documentation
|
|
6
|
+
|
|
7
|
+
See **[AGENTS.md](./AGENTS.md)** for complete project guidelines, architecture, and development workflows.
|
|
8
|
+
|
|
9
|
+
## Quick Reference
|
|
10
|
+
|
|
11
|
+
### Testing (CRITICAL)
|
|
12
|
+
|
|
13
|
+
**All code changes require TDD:**
|
|
14
|
+
1. Write tests first
|
|
15
|
+
2. Verify tests fail
|
|
16
|
+
3. Write implementation
|
|
17
|
+
4. Verify tests pass
|
|
18
|
+
5. Check coverage: `npm run test:coverage:ratchet`
|
|
19
|
+
|
|
20
|
+
**Coverage must never decrease.**
|
|
21
|
+
|
|
22
|
+
### Available Commands
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
npm test # Run tests
|
|
26
|
+
npm run test:coverage # Tests + coverage report
|
|
27
|
+
npm run test:coverage:ratchet # Verify coverage baseline
|
|
28
|
+
npm run build # Build schemas
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
### Key Files
|
|
32
|
+
|
|
33
|
+
| Purpose | Location |
|
|
34
|
+
|---------|----------|
|
|
35
|
+
| Project guidelines | `AGENTS.md` |
|
|
36
|
+
| TDD/Coverage skill | `.claude/skills/tdd-coverage/SKILL.md` |
|
|
37
|
+
| Coverage config | `.c8rc.json` |
|
|
38
|
+
| Coverage baseline | `coverage-thresholds.json` |
|
package/package.json
CHANGED
|
@@ -1,13 +1,17 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "doc-detective-common",
|
|
3
|
-
"version": "3.6.
|
|
3
|
+
"version": "3.6.1-dev.1",
|
|
4
4
|
"description": "Shared components for Doc Detective projects.",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"scripts": {
|
|
7
7
|
"dereferenceSchemas": "node ./src/schemas/dereferenceSchemas.js",
|
|
8
8
|
"build": "npm run dereferenceSchemas",
|
|
9
9
|
"postbuild": "npm run test",
|
|
10
|
-
"test": "mocha"
|
|
10
|
+
"test": "mocha",
|
|
11
|
+
"test:coverage": "c8 mocha",
|
|
12
|
+
"test:coverage:html": "c8 --reporter=html mocha",
|
|
13
|
+
"test:coverage:check": "c8 check-coverage",
|
|
14
|
+
"test:coverage:ratchet": "node scripts/check-coverage-ratchet.js"
|
|
11
15
|
},
|
|
12
16
|
"repository": {
|
|
13
17
|
"type": "git",
|
|
@@ -20,6 +24,7 @@
|
|
|
20
24
|
},
|
|
21
25
|
"homepage": "https://github.com/doc-detective/doc-detective-common#readme",
|
|
22
26
|
"devDependencies": {
|
|
27
|
+
"c8": "^10.1.3",
|
|
23
28
|
"chai": "^6.2.2",
|
|
24
29
|
"mocha": "^11.7.5",
|
|
25
30
|
"sinon": "^21.0.1"
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Coverage Ratchet Script
|
|
5
|
+
*
|
|
6
|
+
* Compares current coverage against baseline thresholds.
|
|
7
|
+
* Fails if any metric has decreased.
|
|
8
|
+
*
|
|
9
|
+
* Usage: node scripts/check-coverage-ratchet.js
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
const fs = require('fs');
|
|
13
|
+
const path = require('path');
|
|
14
|
+
|
|
15
|
+
const THRESHOLDS_FILE = path.join(__dirname, '..', 'coverage-thresholds.json');
|
|
16
|
+
const COVERAGE_SUMMARY_FILE = path.join(__dirname, '..', 'coverage', 'coverage-summary.json');
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Load and parse JSON from the given path, exiting the process with code 1 if the file is missing or cannot be parsed.
|
|
20
|
+
* @param {string} filePath - Filesystem path to the JSON file.
|
|
21
|
+
* @param {string} description - Human-readable name for the file used in error messages.
|
|
22
|
+
* @returns {Object} The parsed JSON object.
|
|
23
|
+
*/
|
|
24
|
+
function loadJSON(filePath, description) {
|
|
25
|
+
if (!fs.existsSync(filePath)) {
|
|
26
|
+
console.error(`Error: ${description} not found at ${filePath}`);
|
|
27
|
+
console.error(`Run 'npm run test:coverage' first to generate coverage data.`);
|
|
28
|
+
process.exit(1);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
try {
|
|
32
|
+
return JSON.parse(fs.readFileSync(filePath, 'utf8'));
|
|
33
|
+
} catch (error) {
|
|
34
|
+
console.error(`Error parsing ${description}: ${error.message}`);
|
|
35
|
+
process.exit(1);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Compares current test coverage against the stored baseline thresholds and enforces the coverage ratchet.
|
|
41
|
+
*
|
|
42
|
+
* Loads baseline thresholds and the current coverage summary, prints a per-metric table of baseline vs current values and statuses, and enforces policy:
|
|
43
|
+
* - Exits with code 1 if any metric has decreased relative to the baseline.
|
|
44
|
+
* - If one or more metrics have improved, prints suggested threshold updates.
|
|
45
|
+
* - Exits with code 0 when all metrics meet or exceed their baselines.
|
|
46
|
+
*/
|
|
47
|
+
function main() {
|
|
48
|
+
// Load baseline thresholds
|
|
49
|
+
const thresholds = loadJSON(THRESHOLDS_FILE, 'Coverage thresholds file');
|
|
50
|
+
|
|
51
|
+
// Load current coverage
|
|
52
|
+
const coverageSummary = loadJSON(COVERAGE_SUMMARY_FILE, 'Coverage summary');
|
|
53
|
+
|
|
54
|
+
const current = coverageSummary.total;
|
|
55
|
+
const metrics = ['lines', 'statements', 'functions', 'branches'];
|
|
56
|
+
|
|
57
|
+
let failed = false;
|
|
58
|
+
const results = [];
|
|
59
|
+
|
|
60
|
+
console.log('\n=== Coverage Ratchet Check ===\n');
|
|
61
|
+
console.log('Metric | Baseline | Current | Status');
|
|
62
|
+
console.log('------------|----------|----------|--------');
|
|
63
|
+
|
|
64
|
+
for (const metric of metrics) {
|
|
65
|
+
const baseline = thresholds[metric];
|
|
66
|
+
const currentValue = current[metric].pct;
|
|
67
|
+
const diff = (currentValue - baseline).toFixed(2);
|
|
68
|
+
|
|
69
|
+
let status;
|
|
70
|
+
if (currentValue < baseline) {
|
|
71
|
+
status = `FAIL (${diff}%)`;
|
|
72
|
+
failed = true;
|
|
73
|
+
} else if (currentValue > baseline) {
|
|
74
|
+
status = `PASS (+${diff}%)`;
|
|
75
|
+
} else {
|
|
76
|
+
status = 'PASS';
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const baselineStr = `${baseline.toFixed(2)}%`.padEnd(8);
|
|
80
|
+
const currentStr = `${currentValue.toFixed(2)}%`.padEnd(8);
|
|
81
|
+
const metricStr = metric.padEnd(11);
|
|
82
|
+
|
|
83
|
+
console.log(`${metricStr} | ${baselineStr} | ${currentStr} | ${status}`);
|
|
84
|
+
|
|
85
|
+
results.push({
|
|
86
|
+
metric,
|
|
87
|
+
baseline,
|
|
88
|
+
current: currentValue,
|
|
89
|
+
passed: currentValue >= baseline
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
console.log('');
|
|
94
|
+
|
|
95
|
+
if (failed) {
|
|
96
|
+
console.error('Coverage ratchet check FAILED!');
|
|
97
|
+
console.error('Coverage has decreased from the baseline.');
|
|
98
|
+
console.error('Please add tests to restore coverage before committing.');
|
|
99
|
+
process.exit(1);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// Check if we can bump thresholds
|
|
103
|
+
const canBump = results.filter(r => r.current > r.baseline);
|
|
104
|
+
if (canBump.length > 0) {
|
|
105
|
+
console.log('Coverage has improved! Consider updating thresholds:');
|
|
106
|
+
console.log('');
|
|
107
|
+
for (const r of canBump) {
|
|
108
|
+
console.log(` "${r.metric}": ${r.current.toFixed(2)}`);
|
|
109
|
+
}
|
|
110
|
+
console.log('');
|
|
111
|
+
console.log(`Update ${THRESHOLDS_FILE} to lock in the new baseline.`);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
console.log('Coverage ratchet check PASSED!');
|
|
115
|
+
process.exit(0);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
main();
|
package/src/resolvePaths.js
CHANGED
|
@@ -5,19 +5,18 @@ const { validate } = require("./validate");
|
|
|
5
5
|
exports.resolvePaths = resolvePaths;
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
|
-
*
|
|
8
|
+
* Convert recognized relative path properties in a config or spec object to absolute paths.
|
|
9
9
|
*
|
|
10
|
-
* Traverses the provided object
|
|
10
|
+
* Traverses the provided object (recursing into nested objects and arrays), resolving fields that represent filesystem paths according to the provided config.relativePathBase and reference filePath. On top-level calls the function infers whether the object is a config or spec via schema validation; for nested calls objectType must be provided.
|
|
11
11
|
*
|
|
12
|
-
* @async
|
|
13
12
|
* @param {Object} options - Options for path resolution.
|
|
14
|
-
* @param {Object} options.config - Configuration
|
|
13
|
+
* @param {Object} options.config - Configuration containing settings such as `relativePathBase`.
|
|
15
14
|
* @param {Object} options.object - The config or spec object whose path properties will be resolved.
|
|
16
|
-
* @param {string} options.filePath - Reference file
|
|
17
|
-
* @param {boolean} [options.nested=false] -
|
|
18
|
-
* @param {string} [options.objectType] -
|
|
19
|
-
* @returns {
|
|
20
|
-
* @throws {Error} If the object
|
|
15
|
+
* @param {string} options.filePath - Reference file or directory used to resolve relative paths.
|
|
16
|
+
* @param {boolean} [options.nested=false] - True when invoked recursively for nested objects.
|
|
17
|
+
* @param {string} [options.objectType] - 'config' or 'spec'; required for nested invocations to select which properties to resolve.
|
|
18
|
+
* @returns {Object} The same object with applicable path properties converted to absolute paths.
|
|
19
|
+
* @throws {Error} If the top-level object matches neither config nor spec schema, or if `objectType` is missing for nested calls.
|
|
21
20
|
*/
|
|
22
21
|
async function resolvePaths({
|
|
23
22
|
config,
|
|
@@ -223,6 +222,7 @@ async function resolvePaths({
|
|
|
223
222
|
}
|
|
224
223
|
|
|
225
224
|
// If called directly, resolve paths in the provided object
|
|
225
|
+
/* c8 ignore start */
|
|
226
226
|
if (require.main === module) {
|
|
227
227
|
(async () => {
|
|
228
228
|
// Example usage
|
|
@@ -250,3 +250,4 @@ if (require.main === module) {
|
|
|
250
250
|
console.log(JSON.stringify(object, null, 2));
|
|
251
251
|
})();
|
|
252
252
|
}
|
|
253
|
+
/* c8 ignore stop */
|
package/src/validate.js
CHANGED
|
@@ -148,6 +148,7 @@ function validate({ schemaKey, object, addDefaults = true }) {
|
|
|
148
148
|
if (result.valid) {
|
|
149
149
|
validationObject = transformedObject;
|
|
150
150
|
object = transformedObject;
|
|
151
|
+
/* c8 ignore start - Defensive: transformToSchemaKey validates internally, so this is unreachable */
|
|
151
152
|
} else if (check.errors) {
|
|
152
153
|
const errors = check.errors.map(
|
|
153
154
|
(error) =>
|
|
@@ -158,6 +159,7 @@ function validate({ schemaKey, object, addDefaults = true }) {
|
|
|
158
159
|
result.errors = errors.join(", ");
|
|
159
160
|
return result;
|
|
160
161
|
}
|
|
162
|
+
/* c8 ignore stop */
|
|
161
163
|
}
|
|
162
164
|
}
|
|
163
165
|
if (addDefaults) {
|
|
@@ -170,18 +172,14 @@ function validate({ schemaKey, object, addDefaults = true }) {
|
|
|
170
172
|
}
|
|
171
173
|
|
|
172
174
|
/**
|
|
173
|
-
*
|
|
175
|
+
* Transform an object from one schema key to another and return a validated instance of the target schema.
|
|
174
176
|
*
|
|
175
|
-
* @param {Object} params
|
|
176
|
-
* @param {string} params.currentSchema -
|
|
177
|
-
* @param {string} params.targetSchema -
|
|
178
|
-
* @param {Object} params.object - The object to transform.
|
|
179
|
-
* @returns {Object} The transformed object
|
|
180
|
-
*
|
|
181
|
-
* @throws {Error} If transformation between the specified schemas is not supported, or if the transformed object fails validation.
|
|
182
|
-
*
|
|
183
|
-
* @remark
|
|
184
|
-
* Supports deep and recursive transformations for complex schema types, including steps, configs, contexts, OpenAPI integrations, specs, and tests. Throws if the schemas are incompatible or if the resulting object does not conform to the target schema.
|
|
177
|
+
* @param {Object} params - Function parameters.
|
|
178
|
+
* @param {string} params.currentSchema - Schema key representing the object's current version.
|
|
179
|
+
* @param {string} params.targetSchema - Schema key to transform the object into.
|
|
180
|
+
* @param {Object} params.object - The source object to transform.
|
|
181
|
+
* @returns {Object} The transformed object conforming to the target schema.
|
|
182
|
+
* @throws {Error} If transformation between the specified schemas is not supported or if the transformed object fails validation.
|
|
185
183
|
*/
|
|
186
184
|
function transformToSchemaKey({
|
|
187
185
|
currentSchema = "",
|
|
@@ -445,6 +443,8 @@ function transformToSchemaKey({
|
|
|
445
443
|
schemaKey: "config_v3",
|
|
446
444
|
object: transformedObject,
|
|
447
445
|
});
|
|
446
|
+
// Defensive: transformation always produces valid config_v3, unreachable
|
|
447
|
+
/* c8 ignore next 3 */
|
|
448
448
|
if (!result.valid) {
|
|
449
449
|
throw new Error(`Invalid object: ${result.errors}`);
|
|
450
450
|
}
|
|
@@ -528,6 +528,8 @@ function transformToSchemaKey({
|
|
|
528
528
|
schemaKey: "spec_v3",
|
|
529
529
|
object: transformedObject,
|
|
530
530
|
});
|
|
531
|
+
// Defensive: nested transforms validate; this is unreachable
|
|
532
|
+
/* c8 ignore next 3 */
|
|
531
533
|
if (!result.valid) {
|
|
532
534
|
throw new Error(`Invalid object: ${result.errors}`);
|
|
533
535
|
}
|
|
@@ -570,18 +572,25 @@ function transformToSchemaKey({
|
|
|
570
572
|
schemaKey: "test_v3",
|
|
571
573
|
object: transformedObject,
|
|
572
574
|
});
|
|
575
|
+
// Defensive: nested transforms validate; this is unreachable
|
|
576
|
+
/* c8 ignore next 3 */
|
|
573
577
|
if (!result.valid) {
|
|
574
578
|
throw new Error(`Invalid object: ${result.errors}`);
|
|
575
579
|
}
|
|
576
580
|
return result.object;
|
|
577
581
|
}
|
|
582
|
+
/* c8 ignore next - Dead code: incompatible schemas throw at line 197-200 */
|
|
578
583
|
return null;
|
|
579
584
|
}
|
|
580
585
|
|
|
581
586
|
// If called directly, validate an example object
|
|
587
|
+
/* c8 ignore start */
|
|
582
588
|
if (require.main === module) {
|
|
583
|
-
const example =
|
|
589
|
+
const example = {
|
|
590
|
+
path: "/User/manny/projects/doc-detective/static/images/image.png",
|
|
591
|
+
};
|
|
584
592
|
|
|
585
593
|
const result = validate({ schemaKey: "screenshot_v3", object: example });
|
|
586
594
|
console.log(JSON.stringify(result, null, 2));
|
|
587
595
|
}
|
|
596
|
+
/* c8 ignore stop */
|