@ontrails/warden 1.0.0-beta.13 → 1.0.0-beta.14
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/.turbo/turbo-lint.log +1 -1
- package/CHANGELOG.md +12 -0
- package/README.md +12 -10
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +11 -3
- package/dist/cli.js.map +1 -1
- package/dist/draft.d.ts +5 -0
- package/dist/draft.d.ts.map +1 -0
- package/dist/draft.js +16 -0
- package/dist/draft.js.map +1 -0
- package/dist/drift.d.ts +9 -6
- package/dist/drift.d.ts.map +1 -1
- package/dist/drift.js +49 -15
- package/dist/drift.js.map +1 -1
- package/dist/formatters.d.ts +2 -1
- package/dist/formatters.d.ts.map +1 -1
- package/dist/formatters.js +15 -4
- package/dist/formatters.js.map +1 -1
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -1
- package/dist/rules/ast.d.ts +7 -0
- package/dist/rules/ast.d.ts.map +1 -1
- package/dist/rules/ast.js +22 -0
- package/dist/rules/ast.js.map +1 -1
- package/dist/rules/draft-file-marking.d.ts +6 -0
- package/dist/rules/draft-file-marking.d.ts.map +1 -0
- package/dist/rules/draft-file-marking.js +65 -0
- package/dist/rules/draft-file-marking.js.map +1 -0
- package/dist/rules/draft-visible-debt.d.ts +12 -0
- package/dist/rules/draft-visible-debt.d.ts.map +1 -0
- package/dist/rules/draft-visible-debt.js +45 -0
- package/dist/rules/draft-visible-debt.js.map +1 -0
- package/dist/rules/index.d.ts +2 -0
- package/dist/rules/index.d.ts.map +1 -1
- package/dist/rules/index.js +6 -0
- package/dist/rules/index.js.map +1 -1
- package/dist/rules/provision-exists.d.ts.map +1 -1
- package/dist/rules/provision-exists.js +2 -1
- package/dist/rules/provision-exists.js.map +1 -1
- package/dist/rules/valid-detour-refs.d.ts.map +1 -1
- package/dist/rules/valid-detour-refs.js +3 -2
- package/dist/rules/valid-detour-refs.js.map +1 -1
- package/package.json +4 -4
- package/src/__tests__/cli.test.ts +114 -1
- package/src/__tests__/drift.test.ts +74 -4
- package/src/__tests__/formatters.test.ts +2 -2
- package/src/cli.ts +11 -3
- package/src/draft.ts +22 -0
- package/src/drift.ts +60 -18
- package/src/formatters.ts +15 -4
- package/src/index.ts +21 -0
- package/src/rules/ast.ts +38 -0
- package/src/rules/draft-file-marking.ts +112 -0
- package/src/rules/draft-visible-debt.ts +62 -0
- package/src/rules/index.ts +6 -0
- package/src/rules/provision-exists.ts +3 -1
- package/src/rules/valid-detour-refs.ts +4 -2
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { isDraftId } from '@ontrails/core';
|
|
2
|
+
import { isDraftMarkedFile } from '../draft.js';
|
|
3
|
+
import { findStringLiterals, offsetToLine, parse } from './ast.js';
|
|
4
|
+
const messageForMissingMarker = (draftId) => `Draft id "${draftId}" appears in source, but the file is not draft-marked. ` +
|
|
5
|
+
'Rename it with an _draft. prefix or a .draft. trailing segment.';
|
|
6
|
+
const makeDiagnostic = (sourceCode, filePath, start, message, severity) => ({
|
|
7
|
+
filePath,
|
|
8
|
+
line: offsetToLine(sourceCode, start),
|
|
9
|
+
message,
|
|
10
|
+
rule: 'draft-file-marking',
|
|
11
|
+
severity,
|
|
12
|
+
});
|
|
13
|
+
const draftMissingMarkerDiagnostic = (sourceCode, filePath, ast) => {
|
|
14
|
+
const draftMatches = findStringLiterals(ast, (value) => isDraftId(value));
|
|
15
|
+
if (!draftMatches.length || isDraftMarkedFile(filePath)) {
|
|
16
|
+
return null;
|
|
17
|
+
}
|
|
18
|
+
const [first] = draftMatches;
|
|
19
|
+
if (!first) {
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
return makeDiagnostic(sourceCode, filePath, first.start, messageForMissingMarker(first.value), 'error');
|
|
23
|
+
};
|
|
24
|
+
const draftMarkedWithoutIdsDiagnostic = (filePath, ast) => {
|
|
25
|
+
if (findStringLiterals(ast, (value) => isDraftId(value)).length > 0) {
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
if (!isDraftMarkedFile(filePath)) {
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
return {
|
|
32
|
+
filePath,
|
|
33
|
+
line: 1,
|
|
34
|
+
message: 'File is draft-marked but no longer contains draft ids. Remove the draft filename marker or finish the promotion cleanup.',
|
|
35
|
+
rule: 'draft-file-marking',
|
|
36
|
+
severity: 'warn',
|
|
37
|
+
};
|
|
38
|
+
};
|
|
39
|
+
const collectDraftFileMarkingDiagnostics = (sourceCode, filePath, ast) => {
|
|
40
|
+
const missingMarkerDiagnostic = draftMissingMarkerDiagnostic(sourceCode, filePath, ast);
|
|
41
|
+
if (missingMarkerDiagnostic) {
|
|
42
|
+
return [missingMarkerDiagnostic];
|
|
43
|
+
}
|
|
44
|
+
const markedWithoutIdsDiagnostic = draftMarkedWithoutIdsDiagnostic(filePath, ast);
|
|
45
|
+
if (markedWithoutIdsDiagnostic) {
|
|
46
|
+
return [markedWithoutIdsDiagnostic];
|
|
47
|
+
}
|
|
48
|
+
return [];
|
|
49
|
+
};
|
|
50
|
+
/**
|
|
51
|
+
* Ensures files containing draft ids are visibly marked as draft-bearing files.
|
|
52
|
+
*/
|
|
53
|
+
export const draftFileMarking = {
|
|
54
|
+
check(sourceCode, filePath) {
|
|
55
|
+
const ast = parse(filePath, sourceCode);
|
|
56
|
+
if (!ast) {
|
|
57
|
+
return [];
|
|
58
|
+
}
|
|
59
|
+
return collectDraftFileMarkingDiagnostics(sourceCode, filePath, ast);
|
|
60
|
+
},
|
|
61
|
+
description: 'Require draft-bearing files to use _draft.* or *.draft.* filename markers.',
|
|
62
|
+
name: 'draft-file-marking',
|
|
63
|
+
severity: 'error',
|
|
64
|
+
};
|
|
65
|
+
//# sourceMappingURL=draft-file-marking.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"draft-file-marking.js","sourceRoot":"","sources":["../../src/rules/draft-file-marking.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { WardenRule } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Warns when draft ids are still present so the debt stays visible during
|
|
4
|
+
* review even when the file is correctly marked.
|
|
5
|
+
*
|
|
6
|
+
* Severity is intentionally `warn`, not `error`. The hard rejection gate for
|
|
7
|
+
* draft state leaking into established outputs is `validateEstablishedTopo` at
|
|
8
|
+
* runtime — it blocks topo export, trailhead projection, and lockfile writes.
|
|
9
|
+
* This rule surfaces the debt for human reviewers without duplicating that gate.
|
|
10
|
+
*/
|
|
11
|
+
export declare const draftVisibleDebt: WardenRule;
|
|
12
|
+
//# sourceMappingURL=draft-visible-debt.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"draft-visible-debt.d.ts","sourceRoot":"","sources":["../../src/rules/draft-visible-debt.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAoB,UAAU,EAAE,MAAM,YAAY,CAAC;AAoC/D;;;;;;;;GAQG;AACH,eAAO,MAAM,gBAAgB,EAAE,UAa9B,CAAC"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { isDraftId } from '@ontrails/core';
|
|
2
|
+
import { findStringLiterals, offsetToLine, parse } from './ast.js';
|
|
3
|
+
const createDiagnostic = (sourceCode, filePath, match) => ({
|
|
4
|
+
filePath,
|
|
5
|
+
line: offsetToLine(sourceCode, match.start),
|
|
6
|
+
message: `Draft id "${match.value}" is still visible debt. ` +
|
|
7
|
+
'Established trailheads, lock export, and OpenAPI generation will reject it until it is promoted.',
|
|
8
|
+
rule: 'draft-visible-debt',
|
|
9
|
+
severity: 'warn',
|
|
10
|
+
});
|
|
11
|
+
const collectDraftVisibleDebtDiagnostics = (sourceCode, filePath, ast) => {
|
|
12
|
+
const seen = new Set();
|
|
13
|
+
const diagnostics = [];
|
|
14
|
+
for (const match of findStringLiterals(ast, (value) => isDraftId(value))) {
|
|
15
|
+
const key = `${match.value}:${String(match.start)}`;
|
|
16
|
+
if (seen.has(key)) {
|
|
17
|
+
continue;
|
|
18
|
+
}
|
|
19
|
+
seen.add(key);
|
|
20
|
+
diagnostics.push(createDiagnostic(sourceCode, filePath, match));
|
|
21
|
+
}
|
|
22
|
+
return diagnostics;
|
|
23
|
+
};
|
|
24
|
+
/**
|
|
25
|
+
* Warns when draft ids are still present so the debt stays visible during
|
|
26
|
+
* review even when the file is correctly marked.
|
|
27
|
+
*
|
|
28
|
+
* Severity is intentionally `warn`, not `error`. The hard rejection gate for
|
|
29
|
+
* draft state leaking into established outputs is `validateEstablishedTopo` at
|
|
30
|
+
* runtime — it blocks topo export, trailhead projection, and lockfile writes.
|
|
31
|
+
* This rule surfaces the debt for human reviewers without duplicating that gate.
|
|
32
|
+
*/
|
|
33
|
+
export const draftVisibleDebt = {
|
|
34
|
+
check(sourceCode, filePath) {
|
|
35
|
+
const ast = parse(filePath, sourceCode);
|
|
36
|
+
if (!ast) {
|
|
37
|
+
return [];
|
|
38
|
+
}
|
|
39
|
+
return collectDraftVisibleDebtDiagnostics(sourceCode, filePath, ast);
|
|
40
|
+
},
|
|
41
|
+
description: 'Warn when draft ids remain in source so the debt stays visible during review.',
|
|
42
|
+
name: 'draft-visible-debt',
|
|
43
|
+
severity: 'warn',
|
|
44
|
+
};
|
|
45
|
+
//# sourceMappingURL=draft-visible-debt.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"draft-visible-debt.js","sourceRoot":"","sources":["../../src/rules/draft-visible-debt.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAE3C,OAAO,EAAE,kBAAkB,EAAE,YAAY,EAAE,KAAK,EAAE,MAAM,UAAU,CAAC;AAGnE,MAAM,gBAAgB,GAAG,CACvB,UAAkB,EAClB,QAAgB,EAChB,KAAuC,EACrB,EAAE,CAAC,CAAC;IACtB,QAAQ;IACR,IAAI,EAAE,YAAY,CAAC,UAAU,EAAE,KAAK,CAAC,KAAK,CAAC;IAC3C,OAAO,EACL,aAAa,KAAK,CAAC,KAAK,2BAA2B;QACnD,kGAAkG;IACpG,IAAI,EAAE,oBAAoB;IAC1B,QAAQ,EAAE,MAAM;CACjB,CAAC,CAAC;AAEH,MAAM,kCAAkC,GAAG,CACzC,UAAkB,EAClB,QAAgB,EAChB,GAA0C,EACtB,EAAE;IACtB,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;IAC/B,MAAM,WAAW,GAAuB,EAAE,CAAC;IAE3C,KAAK,MAAM,KAAK,IAAI,kBAAkB,CAAC,GAAG,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC;QACzE,MAAM,GAAG,GAAG,GAAG,KAAK,CAAC,KAAK,IAAI,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC;QACpD,IAAI,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC;YAClB,SAAS;QACX,CAAC;QACD,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QACd,WAAW,CAAC,IAAI,CAAC,gBAAgB,CAAC,UAAU,EAAE,QAAQ,EAAE,KAAK,CAAC,CAAC,CAAC;IAClE,CAAC;IAED,OAAO,WAAW,CAAC;AACrB,CAAC,CAAC;AAEF;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAe;IAC1C,KAAK,CAAC,UAAkB,EAAE,QAAgB;QACxC,MAAM,GAAG,GAAG,KAAK,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAC;QACxC,IAAI,CAAC,GAAG,EAAE,CAAC;YACT,OAAO,EAAE,CAAC;QACZ,CAAC;QAED,OAAO,kCAAkC,CAAC,UAAU,EAAE,QAAQ,EAAE,GAAG,CAAC,CAAC;IACvE,CAAC;IACD,WAAW,EACT,+EAA+E;IACjF,IAAI,EAAE,oBAAoB;IAC1B,QAAQ,EAAE,MAAM;CACjB,CAAC"}
|
package/dist/rules/index.d.ts
CHANGED
|
@@ -3,6 +3,8 @@ export type { ProjectAwareWardenRule, ProjectContext, WardenDiagnostic, WardenRu
|
|
|
3
3
|
export { noThrowInImplementation } from './no-throw-in-implementation.js';
|
|
4
4
|
export { contextNoTrailheadTypes } from './context-no-trailhead-types.js';
|
|
5
5
|
export { crossDeclarations } from './cross-declarations.js';
|
|
6
|
+
export { draftFileMarking } from './draft-file-marking.js';
|
|
7
|
+
export { draftVisibleDebt } from './draft-visible-debt.js';
|
|
6
8
|
export { validDetourRefs } from './valid-detour-refs.js';
|
|
7
9
|
export { noDirectImplInRoute } from './no-direct-impl-in-route.js';
|
|
8
10
|
export { noDirectImplementationCall } from './no-direct-implementation-call.js';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/rules/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/rules/index.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAI7C,YAAY,EACV,sBAAsB,EACtB,cAAc,EACd,gBAAgB,EAChB,UAAU,EACV,cAAc,GACf,MAAM,YAAY,CAAC;AAEpB,OAAO,EAAE,uBAAuB,EAAE,MAAM,iCAAiC,CAAC;AAC1E,OAAO,EAAE,uBAAuB,EAAE,MAAM,iCAAiC,CAAC;AAC1E,OAAO,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAC5D,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAC3D,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAC3D,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,EAAE,mBAAmB,EAAE,MAAM,8BAA8B,CAAC;AACnE,OAAO,EAAE,0BAA0B,EAAE,MAAM,oCAAoC,CAAC;AAChF,OAAO,EAAE,sBAAsB,EAAE,MAAM,gCAAgC,CAAC;AACxE,OAAO,EAAE,2BAA2B,EAAE,MAAM,oCAAoC,CAAC;AACjF,OAAO,EAAE,qBAAqB,EAAE,MAAM,gCAAgC,CAAC;AACvE,OAAO,EAAE,qBAAqB,EAAE,MAAM,8BAA8B,CAAC;AACrE,OAAO,EAAE,qBAAqB,EAAE,MAAM,6BAA6B,CAAC;AACpE,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAE7D,qDAAqD;AACrD,eAAO,MAAM,WAAW,EAAE,WAAW,CAAC,MAAM,EAAE,UAAU,CAmBtD,CAAC"}
|
package/dist/rules/index.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { contextNoTrailheadTypes } from './context-no-trailhead-types.js';
|
|
2
2
|
import { crossDeclarations } from './cross-declarations.js';
|
|
3
|
+
import { draftFileMarking } from './draft-file-marking.js';
|
|
4
|
+
import { draftVisibleDebt } from './draft-visible-debt.js';
|
|
3
5
|
import { implementationReturnsResult } from './implementation-returns-result.js';
|
|
4
6
|
import { noDirectImplInRoute } from './no-direct-impl-in-route.js';
|
|
5
7
|
import { noDirectImplementationCall } from './no-direct-implementation-call.js';
|
|
@@ -14,6 +16,8 @@ import { validDetourRefs } from './valid-detour-refs.js';
|
|
|
14
16
|
export { noThrowInImplementation } from './no-throw-in-implementation.js';
|
|
15
17
|
export { contextNoTrailheadTypes } from './context-no-trailhead-types.js';
|
|
16
18
|
export { crossDeclarations } from './cross-declarations.js';
|
|
19
|
+
export { draftFileMarking } from './draft-file-marking.js';
|
|
20
|
+
export { draftVisibleDebt } from './draft-visible-debt.js';
|
|
17
21
|
export { validDetourRefs } from './valid-detour-refs.js';
|
|
18
22
|
export { noDirectImplInRoute } from './no-direct-impl-in-route.js';
|
|
19
23
|
export { noDirectImplementationCall } from './no-direct-implementation-call.js';
|
|
@@ -29,6 +33,8 @@ export const wardenRules = new Map([
|
|
|
29
33
|
[noThrowInImplementation.name, noThrowInImplementation],
|
|
30
34
|
[contextNoTrailheadTypes.name, contextNoTrailheadTypes],
|
|
31
35
|
[crossDeclarations.name, crossDeclarations],
|
|
36
|
+
[draftFileMarking.name, draftFileMarking],
|
|
37
|
+
[draftVisibleDebt.name, draftVisibleDebt],
|
|
32
38
|
[provisionDeclarations.name, provisionDeclarations],
|
|
33
39
|
[provisionExists.name, provisionExists],
|
|
34
40
|
[preferSchemaInference.name, preferSchemaInference],
|
package/dist/rules/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/rules/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,uBAAuB,EAAE,MAAM,iCAAiC,CAAC;AAC1E,OAAO,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAC5D,OAAO,EAAE,2BAA2B,EAAE,MAAM,oCAAoC,CAAC;AACjF,OAAO,EAAE,mBAAmB,EAAE,MAAM,8BAA8B,CAAC;AACnE,OAAO,EAAE,0BAA0B,EAAE,MAAM,oCAAoC,CAAC;AAChF,OAAO,EAAE,sBAAsB,EAAE,MAAM,gCAAgC,CAAC;AACxE,OAAO,EAAE,qBAAqB,EAAE,MAAM,gCAAgC,CAAC;AACvE,OAAO,EAAE,uBAAuB,EAAE,MAAM,iCAAiC,CAAC;AAC1E,OAAO,EAAE,qBAAqB,EAAE,MAAM,8BAA8B,CAAC;AACrE,OAAO,EAAE,qBAAqB,EAAE,MAAM,6BAA6B,CAAC;AACpE,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAExD,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAC7D,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAUzD,OAAO,EAAE,uBAAuB,EAAE,MAAM,iCAAiC,CAAC;AAC1E,OAAO,EAAE,uBAAuB,EAAE,MAAM,iCAAiC,CAAC;AAC1E,OAAO,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAC5D,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,EAAE,mBAAmB,EAAE,MAAM,8BAA8B,CAAC;AACnE,OAAO,EAAE,0BAA0B,EAAE,MAAM,oCAAoC,CAAC;AAChF,OAAO,EAAE,sBAAsB,EAAE,MAAM,gCAAgC,CAAC;AACxE,OAAO,EAAE,2BAA2B,EAAE,MAAM,oCAAoC,CAAC;AACjF,OAAO,EAAE,qBAAqB,EAAE,MAAM,gCAAgC,CAAC;AACvE,OAAO,EAAE,qBAAqB,EAAE,MAAM,8BAA8B,CAAC;AACrE,OAAO,EAAE,qBAAqB,EAAE,MAAM,6BAA6B,CAAC;AACpE,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAE7D,qDAAqD;AACrD,MAAM,CAAC,MAAM,WAAW,GAAoC,IAAI,GAAG,CAGjE;IACA,CAAC,uBAAuB,CAAC,IAAI,EAAE,uBAAuB,CAAC;IACvD,CAAC,uBAAuB,CAAC,IAAI,EAAE,uBAAuB,CAAC;IACvD,CAAC,iBAAiB,CAAC,IAAI,EAAE,iBAAiB,CAAC;IAC3C,CAAC,qBAAqB,CAAC,IAAI,EAAE,qBAAqB,CAAC;IACnD,CAAC,eAAe,CAAC,IAAI,EAAE,eAAe,CAAC;IACvC,CAAC,qBAAqB,CAAC,IAAI,EAAE,qBAAqB,CAAC;IACnD,CAAC,iBAAiB,CAAC,IAAI,EAAE,iBAAiB,CAAC;IAC3C,CAAC,eAAe,CAAC,IAAI,EAAE,eAAe,CAAC;IACvC,CAAC,0BAA0B,CAAC,IAAI,EAAE,0BAA0B,CAAC;IAC7D,CAAC,sBAAsB,CAAC,IAAI,EAAE,sBAAsB,CAAC;IACrD,CAAC,2BAA2B,CAAC,IAAI,EAAE,2BAA2B,CAAC;IAC/D,CAAC,qBAAqB,CAAC,IAAI,EAAE,qBAAqB,CAAC;IACnD,CAAC,mBAAmB,CAAC,IAAI,EAAE,mBAAmB,CAAC;CAChD,CAAC,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/rules/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"provision-exists.d.ts","sourceRoot":"","sources":["../../src/rules/provision-exists.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"provision-exists.d.ts","sourceRoot":"","sources":["../../src/rules/provision-exists.ts"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EACV,sBAAsB,EAGvB,MAAM,YAAY,CAAC;AAiIpB;;GAEG;AACH,eAAO,MAAM,eAAe,EAAE,sBA+B7B,CAAC"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { isDraftId } from '@ontrails/core';
|
|
1
2
|
import { collectNamedProvisionIds, collectProvisionDefinitionIds, extractFirstStringArg, findConfigProperty, findTrailDefinitions, getStringValue, identifierName, isStringLiteral, offsetToLine, parse, } from './ast.js';
|
|
2
3
|
import { isTestFile } from './scan.js';
|
|
3
4
|
const isProvisionCall = (node) => node.type === 'CallExpression' &&
|
|
@@ -41,7 +42,7 @@ const buildMissingProvisionDiagnostic = (trailId, provisionId, filePath, line) =
|
|
|
41
42
|
const reportMissingProvisions = (def, sourceCode, provisionIdsByName, filePath, knownProvisionIds, diagnostics) => {
|
|
42
43
|
const line = offsetToLine(sourceCode, def.start);
|
|
43
44
|
for (const provisionId of extractDeclaredProvisionIds(def.config, provisionIdsByName)) {
|
|
44
|
-
if (!knownProvisionIds.has(provisionId)) {
|
|
45
|
+
if (!knownProvisionIds.has(provisionId) && !isDraftId(provisionId)) {
|
|
45
46
|
diagnostics.push(buildMissingProvisionDiagnostic(def.id, provisionId, filePath, line));
|
|
46
47
|
}
|
|
47
48
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"provision-exists.js","sourceRoot":"","sources":["../../src/rules/provision-exists.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,wBAAwB,EACxB,6BAA6B,EAC7B,qBAAqB,EACrB,kBAAkB,EAClB,oBAAoB,EACpB,cAAc,EACd,cAAc,EACd,eAAe,EACf,YAAY,EACZ,KAAK,GACN,MAAM,UAAU,CAAC;AAElB,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAOvC,MAAM,eAAe,GAAG,CAAC,IAAa,EAAW,EAAE,CACjD,IAAI,CAAC,IAAI,KAAK,gBAAgB;IAC9B,cAAc,CAAE,IAAwC,CAAC,MAAM,CAAC;QAC9D,WAAW,CAAC;AAEhB,MAAM,oBAAoB,GAAG,CAAC,MAAe,EAAsB,EAAE;IACnE,MAAM,cAAc,GAAG,kBAAkB,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;IAChE,IAAI,CAAC,cAAc,EAAE,CAAC;QACpB,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,MAAM,SAAS,GAAG,cAAc,CAAC,KAAK,CAAC;IACvC,IAAI,CAAC,SAAS,IAAK,SAAqB,CAAC,IAAI,KAAK,iBAAiB,EAAE,CAAC;QACpE,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,MAAM,QAAQ,GAAI,SAAqB,CAAC,UAAU,CAErC,CAAC;IACd,OAAO,QAAQ,IAAI,EAAE,CAAC;AACxB,CAAC,CAAC;AAEF,MAAM,0BAA0B,GAAG,CACjC,OAAgB,EAChB,kBAA+C,EAChC,EAAE;IACjB,IAAI,OAAO,CAAC,IAAI,KAAK,YAAY,EAAE,CAAC;QAClC,MAAM,IAAI,GAAG,cAAc,CAAC,OAAO,CAAC,CAAC;QACrC,OAAO,IAAI,CAAC,CAAC,CAAC,CAAC,kBAAkB,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IAC9D,CAAC;IAED,IAAI,eAAe,CAAC,OAAO,CAAC,EAAE,CAAC;QAC7B,OAAO,cAAc,CAAC,OAAO,CAAC,CAAC;IACjC,CAAC;IAED,OAAO,eAAe,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,qBAAqB,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;AAC1E,CAAC,CAAC;AAEF,MAAM,2BAA2B,GAAG,CAClC,MAAe,EACf,kBAA+C,EAC5B,EAAE,CAAC;IACtB,GAAG,IAAI,GAAG,CACR,oBAAoB,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;QAC/C,MAAM,EAAE,GAAG,0BAA0B,CAAC,OAAO,EAAE,kBAAkB,CAAC,CAAC;QACnE,OAAO,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IACxB,CAAC,CAAC,CACH;CACF,CAAC;AAEF,MAAM,+BAA+B,GAAG,CACtC,OAAe,EACf,WAAmB,EACnB,QAAgB,EAChB,IAAY,EACM,EAAE,CAAC,CAAC;IACtB,QAAQ;IACR,IAAI;IACJ,OAAO,EAAE,UAAU,OAAO,yBAAyB,WAAW,wCAAwC;IACtG,IAAI,EAAE,kBAAkB;IACxB,QAAQ,EAAE,OAAO;CAClB,CAAC,CAAC;AAEH,MAAM,uBAAuB,GAAG,CAC9B,GAAmD,EACnD,UAAkB,EAClB,kBAA+C,EAC/C,QAAgB,EAChB,iBAAsC,EACtC,WAA+B,EACzB,EAAE;IACR,MAAM,IAAI,GAAG,YAAY,CAAC,UAAU,EAAE,GAAG,CAAC,KAAK,CAAC,CAAC;IACjD,KAAK,MAAM,WAAW,IAAI,2BAA2B,CACnD,GAAG,CAAC,MAAM,EACV,kBAAkB,CACnB,EAAE,CAAC;QACF,IAAI,CAAC,iBAAiB,CAAC,GAAG,CAAC,WAAW,CAAC,EAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"provision-exists.js","sourceRoot":"","sources":["../../src/rules/provision-exists.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"valid-detour-refs.d.ts","sourceRoot":"","sources":["../../src/rules/valid-detour-refs.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"valid-detour-refs.d.ts","sourceRoot":"","sources":["../../src/rules/valid-detour-refs.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACV,sBAAsB,EAGvB,MAAM,YAAY,CAAC;AAoKpB;;GAEG;AACH,eAAO,MAAM,eAAe,EAAE,sBAc7B,CAAC"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { isDraftId } from '@ontrails/core';
|
|
1
2
|
import { collectTrailIds } from './specs.js';
|
|
2
3
|
const trackBraces = (line, state) => {
|
|
3
4
|
for (const ch of line) {
|
|
@@ -28,7 +29,7 @@ const findMissingDetourTargets = (text, knownIds) => {
|
|
|
28
29
|
const missing = [];
|
|
29
30
|
for (const m of text.matchAll(/target\s*:\s*["'`]([^"'`]+)["'`]/g)) {
|
|
30
31
|
const [, id] = m;
|
|
31
|
-
if (id && !knownIds.has(id)) {
|
|
32
|
+
if (id && !knownIds.has(id) && !isDraftId(id)) {
|
|
32
33
|
missing.push(id);
|
|
33
34
|
}
|
|
34
35
|
}
|
|
@@ -39,7 +40,7 @@ const findMissingPlainDetours = (text, knownIds) => {
|
|
|
39
40
|
const cleaned = text.replaceAll(/target\s*:\s*["'`][^"'`]+["'`]/g, '');
|
|
40
41
|
for (const m of cleaned.matchAll(/["'`]([^"'`]+)["'`]/g)) {
|
|
41
42
|
const [, id] = m;
|
|
42
|
-
if (id && id.includes('.') && !knownIds.has(id)) {
|
|
43
|
+
if (id && id.includes('.') && !knownIds.has(id) && !isDraftId(id)) {
|
|
43
44
|
missing.push(id);
|
|
44
45
|
}
|
|
45
46
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"valid-detour-refs.js","sourceRoot":"","sources":["../../src/rules/valid-detour-refs.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAY7C,MAAM,WAAW,GAAG,CAAC,IAAY,EAAE,KAAiB,EAAQ,EAAE;IAC5D,KAAK,MAAM,EAAE,IAAI,IAAI,EAAE,CAAC;QACtB,IAAI,EAAE,KAAK,GAAG,EAAE,CAAC;YACf,KAAK,CAAC,KAAK,IAAI,CAAC,CAAC;YACjB,KAAK,CAAC,KAAK,GAAG,IAAI,CAAC;QACrB,CAAC;QACD,IAAI,EAAE,KAAK,GAAG,EAAE,CAAC;YACf,KAAK,CAAC,KAAK,IAAI,CAAC,CAAC;QACnB,CAAC;IACH,CAAC;AACH,CAAC,CAAC;AAEF,MAAM,gBAAgB,GAAG,CAAC,KAAwB,EAAE,KAAa,EAAU,EAAE;IAC3E,IAAI,IAAI,GAAG,EAAE,CAAC;IACd,KAAK,IAAI,CAAC,GAAG,KAAK,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,IAAI,CAAC,GAAG,KAAK,GAAG,EAAE,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;QAC/D,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;QACtB,IAAI,CAAC,IAAI,EAAE,CAAC;YACV,SAAS;QACX,CAAC;QACD,IAAI,IAAI,GAAG,IAAI,IAAI,CAAC;QACpB,IAAI,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;YACvB,MAAM;QACR,CAAC;IACH,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC,CAAC;AAEF,MAAM,wBAAwB,GAAG,CAC/B,IAAY,EACZ,QAA6B,EACnB,EAAE;IACZ,MAAM,OAAO,GAAa,EAAE,CAAC;IAC7B,KAAK,MAAM,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,mCAAmC,CAAC,EAAE,CAAC;QACnE,MAAM,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC;QACjB,IAAI,EAAE,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"valid-detour-refs.js","sourceRoot":"","sources":["../../src/rules/valid-detour-refs.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAE3C,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAY7C,MAAM,WAAW,GAAG,CAAC,IAAY,EAAE,KAAiB,EAAQ,EAAE;IAC5D,KAAK,MAAM,EAAE,IAAI,IAAI,EAAE,CAAC;QACtB,IAAI,EAAE,KAAK,GAAG,EAAE,CAAC;YACf,KAAK,CAAC,KAAK,IAAI,CAAC,CAAC;YACjB,KAAK,CAAC,KAAK,GAAG,IAAI,CAAC;QACrB,CAAC;QACD,IAAI,EAAE,KAAK,GAAG,EAAE,CAAC;YACf,KAAK,CAAC,KAAK,IAAI,CAAC,CAAC;QACnB,CAAC;IACH,CAAC;AACH,CAAC,CAAC;AAEF,MAAM,gBAAgB,GAAG,CAAC,KAAwB,EAAE,KAAa,EAAU,EAAE;IAC3E,IAAI,IAAI,GAAG,EAAE,CAAC;IACd,KAAK,IAAI,CAAC,GAAG,KAAK,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,IAAI,CAAC,GAAG,KAAK,GAAG,EAAE,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;QAC/D,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;QACtB,IAAI,CAAC,IAAI,EAAE,CAAC;YACV,SAAS;QACX,CAAC;QACD,IAAI,IAAI,GAAG,IAAI,IAAI,CAAC;QACpB,IAAI,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;YACvB,MAAM;QACR,CAAC;IACH,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC,CAAC;AAEF,MAAM,wBAAwB,GAAG,CAC/B,IAAY,EACZ,QAA6B,EACnB,EAAE;IACZ,MAAM,OAAO,GAAa,EAAE,CAAC;IAC7B,KAAK,MAAM,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,mCAAmC,CAAC,EAAE,CAAC;QACnE,MAAM,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC;QACjB,IAAI,EAAE,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC,EAAE,CAAC;YAC9C,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACnB,CAAC;IACH,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC,CAAC;AAEF,MAAM,uBAAuB,GAAG,CAC9B,IAAY,EACZ,QAA6B,EACnB,EAAE;IACZ,MAAM,OAAO,GAAa,EAAE,CAAC;IAC7B,MAAM,OAAO,GAAG,IAAI,CAAC,UAAU,CAAC,iCAAiC,EAAE,EAAE,CAAC,CAAC;IACvE,KAAK,MAAM,CAAC,IAAI,OAAO,CAAC,QAAQ,CAAC,sBAAsB,CAAC,EAAE,CAAC;QACzD,MAAM,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC;QACjB,IAAI,EAAE,IAAI,EAAE,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC,EAAE,CAAC;YAClE,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACnB,CAAC;IACH,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC,CAAC;AAEF,MAAM,qBAAqB,GAAG,CAC5B,IAAY,EACZ,QAA6B,EACnB,EAAE,CAAC;IACb,GAAG,wBAAwB,CAAC,IAAI,EAAE,QAAQ,CAAC;IAC3C,GAAG,uBAAuB,CAAC,IAAI,EAAE,QAAQ,CAAC;CAC3C,CAAC;AAEF,MAAM,2BAA2B,GAAG,CAClC,QAAgB,EAChB,CAAS,EACT,KAAwB,EACxB,OAAe,EACf,OAAe,EACf,QAAgB,EAChB,QAA6B,EAC7B,WAA+B,EACzB,EAAE;IACR,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;QACpC,OAAO;IACT,CAAC;IACD,KAAK,MAAM,QAAQ,IAAI,qBAAqB,CAC1C,gBAAgB,CAAC,KAAK,EAAE,CAAC,CAAC,EAC1B,QAAQ,CACT,EAAE,CAAC;QACF,WAAW,CAAC,IAAI,CAAC;YACf,QAAQ;YACR,IAAI,EAAE,OAAO;YACb,OAAO,EAAE,UAAU,OAAO,2BAA2B,QAAQ,yBAAyB;YACtF,IAAI,EAAE,mBAAmB;YACzB,QAAQ,EAAE,OAAO;SAClB,CAAC,CAAC;IACL,CAAC;AACH,CAAC,CAAC;AAEF,MAAM,gBAAgB,GAAG,CACvB,KAAwB,EACxB,UAAkB,EAClB,OAAe,EACf,QAAgB,EAChB,QAA6B,EAC7B,WAA+B,EACzB,EAAE;IACR,MAAM,UAAU,GAAe,EAAE,KAAK,EAAE,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC;IAC1D,KAAK,IAAI,CAAC,GAAG,UAAU,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,IAAI,CAAC,GAAG,UAAU,GAAG,GAAG,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;QAC1E,MAAM,QAAQ,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;QAC1B,IAAI,CAAC,QAAQ,EAAE,CAAC;YACd,SAAS;QACX,CAAC;QACD,WAAW,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAC;QAClC,2BAA2B,CACzB,QAAQ,EACR,CAAC,EACD,KAAK,EACL,OAAO,EACP,UAAU,GAAG,CAAC,EACd,QAAQ,EACR,QAAQ,EACR,WAAW,CACZ,CAAC;QACF,IAAI,UAAU,CAAC,KAAK,IAAI,UAAU,CAAC,KAAK,IAAI,CAAC,EAAE,CAAC;YAC9C,MAAM;QACR,CAAC;IACH,CAAC;AACH,CAAC,CAAC;AAEF,MAAM,WAAW,GAAG,CAClB,IAAY,EACZ,CAAS,EACT,KAAwB,EACxB,QAAgB,EAChB,QAA6B,EAC7B,WAA+B,EACzB,EAAE;IACR,MAAM,UAAU,GAAG,IAAI,CAAC,KAAK,CAAC,oCAAoC,CAAC,CAAC;IACpE,IAAI,CAAC,UAAU,EAAE,CAAC;QAChB,OAAO;IACT,CAAC;IACD,MAAM,CAAC,EAAE,OAAO,CAAC,GAAG,UAAU,CAAC;IAC/B,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,OAAO;IACT,CAAC;IACD,gBAAgB,CAAC,KAAK,EAAE,CAAC,EAAE,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,WAAW,CAAC,CAAC;AACvE,CAAC,CAAC;AAEF,MAAM,eAAe,GAAG,CACtB,UAAkB,EAClB,QAAgB,EAChB,QAA6B,EACA,EAAE;IAC/B,MAAM,WAAW,GAAuB,EAAE,CAAC;IAC3C,MAAM,KAAK,GAAG,UAAU,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IACrC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;QACzC,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;QACtB,IAAI,IAAI,EAAE,CAAC;YACT,WAAW,CAAC,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,WAAW,CAAC,CAAC;QAC/D,CAAC;IACH,CAAC;IACD,OAAO,WAAW,CAAC;AACrB,CAAC,CAAC;AAEF;;GAEG;AACH,MAAM,CAAC,MAAM,eAAe,GAA2B;IACrD,KAAK,CAAC,UAAkB,EAAE,QAAgB;QACxC,OAAO,eAAe,CAAC,UAAU,EAAE,QAAQ,EAAE,eAAe,CAAC,UAAU,CAAC,CAAC,CAAC;IAC5E,CAAC;IACD,gBAAgB,CACd,UAAkB,EAClB,QAAgB,EAChB,OAAuB;QAEvB,OAAO,eAAe,CAAC,UAAU,EAAE,QAAQ,EAAE,OAAO,CAAC,aAAa,CAAC,CAAC;IACtE,CAAC;IACD,WAAW,EAAE,8DAA8D;IAC3E,IAAI,EAAE,mBAAmB;IACzB,QAAQ,EAAE,OAAO;CAClB,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ontrails/warden",
|
|
3
|
-
"version": "1.0.0-beta.
|
|
3
|
+
"version": "1.0.0-beta.14",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"exports": {
|
|
6
6
|
".": "./src/index.ts",
|
|
@@ -14,13 +14,13 @@
|
|
|
14
14
|
"clean": "rm -rf dist *.tsbuildinfo"
|
|
15
15
|
},
|
|
16
16
|
"devDependencies": {
|
|
17
|
-
"@ontrails/testing": "^1.0.0-beta.
|
|
17
|
+
"@ontrails/testing": "^1.0.0-beta.13",
|
|
18
18
|
"@oxc-project/types": "^0.122.0",
|
|
19
19
|
"oxc-parser": "^0.121.0",
|
|
20
20
|
"zod": "^4.3.5"
|
|
21
21
|
},
|
|
22
22
|
"peerDependencies": {
|
|
23
|
-
"@ontrails/core": "^1.0.0-beta.
|
|
24
|
-
"@ontrails/schema": "^1.0.0-beta.
|
|
23
|
+
"@ontrails/core": "^1.0.0-beta.13",
|
|
24
|
+
"@ontrails/schema": "^1.0.0-beta.13"
|
|
25
25
|
}
|
|
26
26
|
}
|
|
@@ -5,6 +5,24 @@ import { join } from 'node:path';
|
|
|
5
5
|
|
|
6
6
|
import { formatWardenReport, runWarden } from '../cli.js';
|
|
7
7
|
|
|
8
|
+
const isDraftFileMarking = (rule: string): boolean =>
|
|
9
|
+
rule === 'draft-file-marking';
|
|
10
|
+
|
|
11
|
+
const isDraftFileMarkingError = (diagnostic: {
|
|
12
|
+
rule: string;
|
|
13
|
+
severity?: string;
|
|
14
|
+
}): boolean =>
|
|
15
|
+
isDraftFileMarking(diagnostic.rule) && diagnostic.severity === 'error';
|
|
16
|
+
|
|
17
|
+
const isDraftFileMarkingWarn = (diagnostic: {
|
|
18
|
+
rule: string;
|
|
19
|
+
severity?: string;
|
|
20
|
+
}): boolean =>
|
|
21
|
+
isDraftFileMarking(diagnostic.rule) && diagnostic.severity === 'warn';
|
|
22
|
+
|
|
23
|
+
const isDraftVisibleDebt = (rule: string): boolean =>
|
|
24
|
+
rule === 'draft-visible-debt';
|
|
25
|
+
|
|
8
26
|
const makeTempDir = (): string => {
|
|
9
27
|
const dir = join(
|
|
10
28
|
tmpdir(),
|
|
@@ -146,6 +164,83 @@ describe('runWarden', () => {
|
|
|
146
164
|
rmSync(dir, { force: true, recursive: true });
|
|
147
165
|
}
|
|
148
166
|
});
|
|
167
|
+
|
|
168
|
+
test('requires draft-bearing files to be visibly marked', async () => {
|
|
169
|
+
const dir = makeTempDir();
|
|
170
|
+
try {
|
|
171
|
+
writeFileSync(
|
|
172
|
+
join(dir, 'draft-id.ts'),
|
|
173
|
+
`trail("_draft.entity.prepare", {
|
|
174
|
+
blaze: async () => Result.ok({ ok: true }),
|
|
175
|
+
input: z.object({})
|
|
176
|
+
})`
|
|
177
|
+
);
|
|
178
|
+
|
|
179
|
+
const report = await runWarden({ rootDir: dir });
|
|
180
|
+
|
|
181
|
+
const hasDraftFileMarking = report.diagnostics.some((diagnostic) =>
|
|
182
|
+
isDraftFileMarking(diagnostic.rule)
|
|
183
|
+
);
|
|
184
|
+
const hasDraftVisibleDebt = report.diagnostics.some((diagnostic) =>
|
|
185
|
+
isDraftVisibleDebt(diagnostic.rule)
|
|
186
|
+
);
|
|
187
|
+
|
|
188
|
+
expect(hasDraftFileMarking).toBe(true);
|
|
189
|
+
expect(hasDraftVisibleDebt).toBe(true);
|
|
190
|
+
} finally {
|
|
191
|
+
rmSync(dir, { force: true, recursive: true });
|
|
192
|
+
}
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
test('allows correctly marked draft files while keeping the debt visible', async () => {
|
|
196
|
+
const dir = makeTempDir();
|
|
197
|
+
try {
|
|
198
|
+
writeFileSync(
|
|
199
|
+
join(dir, '_draft.entity.ts'),
|
|
200
|
+
`trail("_draft.entity.prepare", {
|
|
201
|
+
blaze: async () => Result.ok({ ok: true }),
|
|
202
|
+
input: z.object({})
|
|
203
|
+
})`
|
|
204
|
+
);
|
|
205
|
+
|
|
206
|
+
const report = await runWarden({ rootDir: dir });
|
|
207
|
+
|
|
208
|
+
const hasDraftFileMarkingError = report.diagnostics.some((diagnostic) =>
|
|
209
|
+
isDraftFileMarkingError(diagnostic)
|
|
210
|
+
);
|
|
211
|
+
const hasDraftVisibleDebt = report.diagnostics.some((diagnostic) =>
|
|
212
|
+
isDraftVisibleDebt(diagnostic.rule)
|
|
213
|
+
);
|
|
214
|
+
|
|
215
|
+
expect(hasDraftFileMarkingError).toBe(false);
|
|
216
|
+
expect(hasDraftVisibleDebt).toBe(true);
|
|
217
|
+
} finally {
|
|
218
|
+
rmSync(dir, { force: true, recursive: true });
|
|
219
|
+
}
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
test('warns when a draft-marked file no longer contains draft ids', async () => {
|
|
223
|
+
const dir = makeTempDir();
|
|
224
|
+
try {
|
|
225
|
+
writeFileSync(
|
|
226
|
+
join(dir, 'entity.draft.ts'),
|
|
227
|
+
`trail("entity.prepare", {
|
|
228
|
+
blaze: async () => Result.ok({ ok: true }),
|
|
229
|
+
input: z.object({})
|
|
230
|
+
})`
|
|
231
|
+
);
|
|
232
|
+
|
|
233
|
+
const report = await runWarden({ rootDir: dir });
|
|
234
|
+
|
|
235
|
+
const hasDraftFileMarkingWarn = report.diagnostics.some((diagnostic) =>
|
|
236
|
+
isDraftFileMarkingWarn(diagnostic)
|
|
237
|
+
);
|
|
238
|
+
|
|
239
|
+
expect(hasDraftFileMarkingWarn).toBe(true);
|
|
240
|
+
} finally {
|
|
241
|
+
rmSync(dir, { force: true, recursive: true });
|
|
242
|
+
}
|
|
243
|
+
});
|
|
149
244
|
});
|
|
150
245
|
|
|
151
246
|
describe('formatWardenReport', () => {
|
|
@@ -192,7 +287,25 @@ describe('formatWardenReport', () => {
|
|
|
192
287
|
passed: false,
|
|
193
288
|
warnCount: 0,
|
|
194
289
|
});
|
|
195
|
-
expect(output).toContain('
|
|
290
|
+
expect(output).toContain('trails.lock is stale');
|
|
196
291
|
expect(output).toContain('Result: FAIL');
|
|
197
292
|
});
|
|
293
|
+
|
|
294
|
+
test('formats a report with blocked established exports', () => {
|
|
295
|
+
const output = formatWardenReport({
|
|
296
|
+
diagnostics: [],
|
|
297
|
+
drift: {
|
|
298
|
+
blockedReason:
|
|
299
|
+
'Established topo validation failed with 1 draft issue(s)',
|
|
300
|
+
committedHash: null,
|
|
301
|
+
currentHash: 'blocked',
|
|
302
|
+
stale: true,
|
|
303
|
+
},
|
|
304
|
+
errorCount: 0,
|
|
305
|
+
passed: false,
|
|
306
|
+
warnCount: 0,
|
|
307
|
+
});
|
|
308
|
+
expect(output).toContain('Drift: blocked');
|
|
309
|
+
expect(output).toContain('established exports blocked');
|
|
310
|
+
});
|
|
198
311
|
});
|
|
@@ -3,8 +3,17 @@ import { mkdirSync, rmSync, writeFileSync } from 'node:fs';
|
|
|
3
3
|
import { join } from 'node:path';
|
|
4
4
|
import { tmpdir } from 'node:os';
|
|
5
5
|
|
|
6
|
-
import { trail, topo, Result } from '@ontrails/core';
|
|
7
|
-
import {
|
|
6
|
+
import { createTopoStore, trail, topo, Result } from '@ontrails/core';
|
|
7
|
+
import { persistEstablishedTopoSave } from '@ontrails/core/internal/topo-store';
|
|
8
|
+
import {
|
|
9
|
+
openWriteTrailsDb,
|
|
10
|
+
resolveTrailsDir,
|
|
11
|
+
} from '@ontrails/core/internal/trails-db';
|
|
12
|
+
import {
|
|
13
|
+
hashTrailheadMap,
|
|
14
|
+
generateTrailheadMap,
|
|
15
|
+
writeTrailheadLock,
|
|
16
|
+
} from '@ontrails/schema';
|
|
8
17
|
import { z } from 'zod';
|
|
9
18
|
|
|
10
19
|
import { checkDrift } from '../drift.js';
|
|
@@ -24,6 +33,28 @@ const createTempDir = (): string => {
|
|
|
24
33
|
return dir;
|
|
25
34
|
};
|
|
26
35
|
|
|
36
|
+
const committedLockDir = (dir: string): string => {
|
|
37
|
+
const trailsDir = resolveTrailsDir({ rootDir: dir });
|
|
38
|
+
mkdirSync(trailsDir, { recursive: true });
|
|
39
|
+
return trailsDir;
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
const seedSavedTopo = (dir: string): string | undefined => {
|
|
43
|
+
const db = openWriteTrailsDb({ rootDir: dir });
|
|
44
|
+
try {
|
|
45
|
+
const result = persistEstablishedTopoSave(db, makeTopo(), {
|
|
46
|
+
createdAt: '2026-04-03T15:00:00.000Z',
|
|
47
|
+
});
|
|
48
|
+
if (result.isErr()) {
|
|
49
|
+
throw result.error;
|
|
50
|
+
}
|
|
51
|
+
} finally {
|
|
52
|
+
db.close();
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return createTopoStore({ rootDir: dir }).exports.get()?.trailheadHash;
|
|
56
|
+
};
|
|
57
|
+
|
|
27
58
|
describe('checkDrift', () => {
|
|
28
59
|
test('returns stale: false when no topo is provided', async () => {
|
|
29
60
|
const result = await checkDrift('/tmp');
|
|
@@ -43,12 +74,28 @@ describe('checkDrift', () => {
|
|
|
43
74
|
}
|
|
44
75
|
});
|
|
45
76
|
|
|
77
|
+
test('uses the latest saved topo hash when no live topo is provided', async () => {
|
|
78
|
+
const dir = createTempDir();
|
|
79
|
+
try {
|
|
80
|
+
const expectedHash = seedSavedTopo(dir);
|
|
81
|
+
const result = await checkDrift(dir);
|
|
82
|
+
|
|
83
|
+
expect(result.stale).toBe(false);
|
|
84
|
+
expect(result.currentHash).toBe(expectedHash);
|
|
85
|
+
} finally {
|
|
86
|
+
rmSync(dir, { force: true, recursive: true });
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
|
|
46
90
|
test('returns stale: false when lock matches current hash', async () => {
|
|
47
91
|
const dir = createTempDir();
|
|
48
92
|
try {
|
|
49
93
|
const tp = makeTopo();
|
|
50
94
|
const hash = hashTrailheadMap(generateTrailheadMap(tp));
|
|
51
|
-
|
|
95
|
+
await writeTrailheadLock(
|
|
96
|
+
{ hash, version: 1 },
|
|
97
|
+
{ dir: committedLockDir(dir) }
|
|
98
|
+
);
|
|
52
99
|
|
|
53
100
|
const result = await checkDrift(dir, tp);
|
|
54
101
|
expect(result.stale).toBe(false);
|
|
@@ -62,7 +109,10 @@ describe('checkDrift', () => {
|
|
|
62
109
|
test('returns stale: true when lock does not match', async () => {
|
|
63
110
|
const dir = createTempDir();
|
|
64
111
|
try {
|
|
65
|
-
writeFileSync(
|
|
112
|
+
writeFileSync(
|
|
113
|
+
join(committedLockDir(dir), 'trailhead.lock'),
|
|
114
|
+
'outdated-hash\n'
|
|
115
|
+
);
|
|
66
116
|
|
|
67
117
|
const result = await checkDrift(dir, makeTopo());
|
|
68
118
|
expect(result.stale).toBe(true);
|
|
@@ -71,4 +121,24 @@ describe('checkDrift', () => {
|
|
|
71
121
|
rmSync(dir, { force: true, recursive: true });
|
|
72
122
|
}
|
|
73
123
|
});
|
|
124
|
+
|
|
125
|
+
test('blocks drift calculation when draft state remains in the topo', async () => {
|
|
126
|
+
const dir = createTempDir();
|
|
127
|
+
try {
|
|
128
|
+
const draftTrail = trail('test.hello', {
|
|
129
|
+
blaze: () => Result.ok({ greeting: 'hi' }),
|
|
130
|
+
crosses: ['_draft.test.prepare'],
|
|
131
|
+
input: z.object({ name: z.string() }),
|
|
132
|
+
output: z.object({ greeting: z.string() }),
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
const result = await checkDrift(dir, topo('test-app', { draftTrail }));
|
|
136
|
+
|
|
137
|
+
expect(result.stale).toBe(true);
|
|
138
|
+
expect(result.blockedReason).toContain('draft');
|
|
139
|
+
expect(result.currentHash).toBe('blocked');
|
|
140
|
+
} finally {
|
|
141
|
+
rmSync(dir, { force: true, recursive: true });
|
|
142
|
+
}
|
|
143
|
+
});
|
|
74
144
|
});
|
|
@@ -67,7 +67,7 @@ describe('formatGitHubAnnotations', () => {
|
|
|
67
67
|
|
|
68
68
|
test('emits drift as a single ::error annotation', () => {
|
|
69
69
|
const output = formatGitHubAnnotations(reportWithDrift);
|
|
70
|
-
expect(output).toContain('::error::drift:
|
|
70
|
+
expect(output).toContain('::error::drift: trails.lock is stale');
|
|
71
71
|
});
|
|
72
72
|
|
|
73
73
|
test('produces one line per diagnostic', () => {
|
|
@@ -148,7 +148,7 @@ describe('formatSummary', () => {
|
|
|
148
148
|
test('includes drift section when stale', () => {
|
|
149
149
|
const output = formatSummary(reportWithDrift);
|
|
150
150
|
expect(output).toContain('### Drift');
|
|
151
|
-
expect(output).toContain('
|
|
151
|
+
expect(output).toContain('trails.lock is stale');
|
|
152
152
|
});
|
|
153
153
|
|
|
154
154
|
test('omits drift section when clean', () => {
|
package/src/cli.ts
CHANGED
|
@@ -293,7 +293,10 @@ export const runWarden = async (
|
|
|
293
293
|
diagnostics: allDiagnostics,
|
|
294
294
|
drift,
|
|
295
295
|
errorCount,
|
|
296
|
-
passed:
|
|
296
|
+
passed:
|
|
297
|
+
errorCount === 0 &&
|
|
298
|
+
!(drift?.stale ?? false) &&
|
|
299
|
+
drift?.blockedReason === undefined,
|
|
297
300
|
warnCount,
|
|
298
301
|
};
|
|
299
302
|
};
|
|
@@ -327,8 +330,11 @@ const formatDriftSection = (drift: DriftResult | null): string[] => {
|
|
|
327
330
|
if (drift === null) {
|
|
328
331
|
return [];
|
|
329
332
|
}
|
|
333
|
+
if (drift.blockedReason !== undefined) {
|
|
334
|
+
return [`Drift: blocked (${drift.blockedReason})`, ''];
|
|
335
|
+
}
|
|
330
336
|
const label = drift.stale
|
|
331
|
-
? 'Drift:
|
|
337
|
+
? 'Drift: trails.lock is stale (regenerate with `trails topo export`)'
|
|
332
338
|
: 'Drift: clean';
|
|
333
339
|
return [label, ''];
|
|
334
340
|
};
|
|
@@ -344,7 +350,9 @@ const formatResultLine = (report: WardenReport): string => {
|
|
|
344
350
|
if (report.errorCount > 0) {
|
|
345
351
|
parts.push(`${report.errorCount} errors`);
|
|
346
352
|
}
|
|
347
|
-
if (report.drift?.
|
|
353
|
+
if (report.drift?.blockedReason !== undefined) {
|
|
354
|
+
parts.push('established exports blocked');
|
|
355
|
+
} else if (report.drift?.stale) {
|
|
348
356
|
parts.push('drift detected');
|
|
349
357
|
}
|
|
350
358
|
return `Result: FAIL (${parts.join(', ')})`;
|
package/src/draft.ts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { basename } from 'node:path';
|
|
2
|
+
|
|
3
|
+
export const DRAFT_FILE_PREFIX = '_draft.';
|
|
4
|
+
export const DRAFT_FILE_SEGMENT = '.draft.';
|
|
5
|
+
|
|
6
|
+
const DRAFT_TRAILING_SEGMENT = /\.draft(?=\.[^.]+$)/;
|
|
7
|
+
|
|
8
|
+
export const isDraftMarkedFile = (filePath: string): boolean => {
|
|
9
|
+
const fileName = basename(filePath);
|
|
10
|
+
return (
|
|
11
|
+
fileName.startsWith(DRAFT_FILE_PREFIX) ||
|
|
12
|
+
fileName.includes(DRAFT_FILE_SEGMENT)
|
|
13
|
+
);
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export const stripDraftFileMarkers = (fileName: string): string => {
|
|
17
|
+
if (fileName.startsWith(DRAFT_FILE_PREFIX)) {
|
|
18
|
+
return fileName.slice(DRAFT_FILE_PREFIX.length);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
return fileName.replace(DRAFT_TRAILING_SEGMENT, '');
|
|
22
|
+
};
|