doc-detective 4.29.0 → 4.30.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.
- package/bin/doc-detective-lsp.js +6 -0
- package/dist/cli.js +58 -35
- package/dist/cli.js.map +1 -1
- package/dist/common/src/schemas/schemas.json +56 -56
- package/dist/common/src/validate.d.ts +16 -1
- package/dist/common/src/validate.d.ts.map +1 -1
- package/dist/common/src/validate.js +113 -13
- package/dist/common/src/validate.js.map +1 -1
- package/dist/core/config.d.ts +66 -1
- package/dist/core/config.d.ts.map +1 -1
- package/dist/core/config.js +121 -68
- package/dist/core/config.js.map +1 -1
- package/dist/core/detectTests.d.ts.map +1 -1
- package/dist/core/detectTests.js +105 -23
- package/dist/core/detectTests.js.map +1 -1
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +53 -14
- package/dist/core/index.js.map +1 -1
- package/dist/core/openapi.d.ts.map +1 -1
- package/dist/core/openapi.js +22 -5
- package/dist/core/openapi.js.map +1 -1
- package/dist/core/resolveTests.d.ts.map +1 -1
- package/dist/core/resolveTests.js +33 -2
- package/dist/core/resolveTests.js.map +1 -1
- package/dist/core/tests/findStrategies.d.ts.map +1 -1
- package/dist/core/tests/findStrategies.js +83 -10
- package/dist/core/tests/findStrategies.js.map +1 -1
- package/dist/core/tests/saveScreenshot.d.ts.map +1 -1
- package/dist/core/tests/saveScreenshot.js +101 -59
- package/dist/core/tests/saveScreenshot.js.map +1 -1
- package/dist/core/utils.d.ts.map +1 -1
- package/dist/core/utils.js +13 -4
- package/dist/core/utils.js.map +1 -1
- package/dist/index.cjs +356 -196
- package/dist/lsp/command.d.ts +12 -0
- package/dist/lsp/command.d.ts.map +1 -0
- package/dist/lsp/command.js +24 -0
- package/dist/lsp/command.js.map +1 -0
- package/dist/lsp/completion.d.ts +27 -0
- package/dist/lsp/completion.d.ts.map +1 -0
- package/dist/lsp/completion.js +114 -0
- package/dist/lsp/completion.js.map +1 -0
- package/dist/lsp/diagnostics.d.ts +21 -0
- package/dist/lsp/diagnostics.d.ts.map +1 -0
- package/dist/lsp/diagnostics.js +137 -0
- package/dist/lsp/diagnostics.js.map +1 -0
- package/dist/lsp/gate.d.ts +30 -0
- package/dist/lsp/gate.d.ts.map +1 -0
- package/dist/lsp/gate.js +80 -0
- package/dist/lsp/gate.js.map +1 -0
- package/dist/lsp/hover.d.ts +9 -0
- package/dist/lsp/hover.d.ts.map +1 -0
- package/dist/lsp/hover.js +56 -0
- package/dist/lsp/hover.js.map +1 -0
- package/dist/lsp/inline.d.ts +25 -0
- package/dist/lsp/inline.d.ts.map +1 -0
- package/dist/lsp/inline.js +208 -0
- package/dist/lsp/inline.js.map +1 -0
- package/dist/lsp/json/positions.d.ts +51 -0
- package/dist/lsp/json/positions.d.ts.map +1 -0
- package/dist/lsp/json/positions.js +101 -0
- package/dist/lsp/json/positions.js.map +1 -0
- package/dist/lsp/messages.d.ts +27 -0
- package/dist/lsp/messages.d.ts.map +1 -0
- package/dist/lsp/messages.js +33 -0
- package/dist/lsp/messages.js.map +1 -0
- package/dist/lsp/model.d.ts +27 -0
- package/dist/lsp/model.d.ts.map +1 -0
- package/dist/lsp/model.js +49 -0
- package/dist/lsp/model.js.map +1 -0
- package/dist/lsp/registry.d.ts +61 -0
- package/dist/lsp/registry.d.ts.map +1 -0
- package/dist/lsp/registry.js +124 -0
- package/dist/lsp/registry.js.map +1 -0
- package/dist/lsp/server.d.ts +40 -0
- package/dist/lsp/server.d.ts.map +1 -0
- package/dist/lsp/server.js +64 -0
- package/dist/lsp/server.js.map +1 -0
- package/dist/lsp/yaml/positions.d.ts +32 -0
- package/dist/lsp/yaml/positions.d.ts.map +1 -0
- package/dist/lsp/yaml/positions.js +92 -0
- package/dist/lsp/yaml/positions.js.map +1 -0
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +30 -5
- package/dist/utils.js.map +1 -1
- package/package.json +6 -2
- package/scripts/postinstall.js +144 -1
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { schemas } from "../common/src/schemas/index.js";
|
|
2
|
+
/** Collect the candidate sub-schemas of a schema: itself plus anyOf/oneOf/allOf. */
|
|
3
|
+
export function branchesOf(schema) {
|
|
4
|
+
if (!schema || typeof schema !== "object")
|
|
5
|
+
return [];
|
|
6
|
+
const out = [schema];
|
|
7
|
+
for (const key of ["anyOf", "oneOf", "allOf"]) {
|
|
8
|
+
if (Array.isArray(schema[key]))
|
|
9
|
+
out.push(...schema[key]);
|
|
10
|
+
}
|
|
11
|
+
return out;
|
|
12
|
+
}
|
|
13
|
+
/** Dig for a human description across a schema and its immediate branches. */
|
|
14
|
+
export function findDescription(schema) {
|
|
15
|
+
for (const branch of branchesOf(schema)) {
|
|
16
|
+
if (typeof branch.description === "string" && branch.description.length > 0) {
|
|
17
|
+
return branch.description;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
return undefined;
|
|
21
|
+
}
|
|
22
|
+
/** Map a JSON-Schema scalar type onto the snippet-relevant primitive kind. */
|
|
23
|
+
function normalizePrimitive(type) {
|
|
24
|
+
if (type === "string")
|
|
25
|
+
return "string";
|
|
26
|
+
if (type === "number" || type === "integer")
|
|
27
|
+
return "number";
|
|
28
|
+
if (type === "boolean")
|
|
29
|
+
return "boolean";
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* The scalar kind a schema (or a branch) accepts in its compact form, or null.
|
|
34
|
+
* The first primitive type encountered across branches wins.
|
|
35
|
+
*/
|
|
36
|
+
export function primitiveKindOf(schema) {
|
|
37
|
+
for (const branch of branchesOf(schema)) {
|
|
38
|
+
const type = branch.type;
|
|
39
|
+
const types = typeof type === "string" ? [type] : Array.isArray(type) ? type : [];
|
|
40
|
+
for (const t of types) {
|
|
41
|
+
const kind = normalizePrimitive(t);
|
|
42
|
+
if (kind)
|
|
43
|
+
return kind;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
/** Does the schema (or a branch) accept a primitive scalar value? */
|
|
49
|
+
export function acceptsPrimitive(schema) {
|
|
50
|
+
return primitiveKindOf(schema) !== null;
|
|
51
|
+
}
|
|
52
|
+
/** Merge the `properties` maps found across a schema's object branches. */
|
|
53
|
+
export function collectFields(schema) {
|
|
54
|
+
const seen = new Map();
|
|
55
|
+
for (const branch of branchesOf(schema)) {
|
|
56
|
+
const props = branch.properties;
|
|
57
|
+
if (!props || typeof props !== "object")
|
|
58
|
+
continue;
|
|
59
|
+
for (const [name, propSchema] of Object.entries(props)) {
|
|
60
|
+
if (name === "$schema" || seen.has(name))
|
|
61
|
+
continue;
|
|
62
|
+
seen.set(name, {
|
|
63
|
+
name,
|
|
64
|
+
description: findDescription(propSchema),
|
|
65
|
+
primitiveKind: primitiveKindOf(propSchema),
|
|
66
|
+
enumValues: extractEnum(propSchema),
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
return [...seen.values()];
|
|
71
|
+
}
|
|
72
|
+
/** Pull an enum list from a schema or its branches, if any. */
|
|
73
|
+
export function extractEnum(schema) {
|
|
74
|
+
for (const branch of branchesOf(schema)) {
|
|
75
|
+
if (Array.isArray(branch.enum))
|
|
76
|
+
return branch.enum.slice();
|
|
77
|
+
}
|
|
78
|
+
return undefined;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* The `allOf` element of a `step_v3` anyOf branch that carries the action: the
|
|
82
|
+
* one whose `required` names exactly the action key (never `$schema`).
|
|
83
|
+
*/
|
|
84
|
+
function actionPartOf(branch) {
|
|
85
|
+
const parts = Array.isArray(branch.allOf) ? branch.allOf : [branch];
|
|
86
|
+
return parts.find((p) => Array.isArray(p.required) &&
|
|
87
|
+
p.required.length === 1 &&
|
|
88
|
+
p.required[0] !== "$schema");
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Build the action registry from the live `step_v3` schema. Derived at runtime
|
|
92
|
+
* (not a build step) so a new action added to the schemas automatically appears
|
|
93
|
+
* in completion/hover — the anti-drift test pins that guarantee.
|
|
94
|
+
*/
|
|
95
|
+
export function buildRegistry(step = schemas["step_v3"]) {
|
|
96
|
+
const actions = [];
|
|
97
|
+
for (const branch of step.anyOf || []) {
|
|
98
|
+
const part = actionPartOf(branch);
|
|
99
|
+
/* c8 ignore next - every step_v3 anyOf branch has an action part; defensive */
|
|
100
|
+
if (!part)
|
|
101
|
+
continue;
|
|
102
|
+
const key = part.required[0];
|
|
103
|
+
const propSchema = part.properties?.[key];
|
|
104
|
+
const primitiveKind = primitiveKindOf(propSchema);
|
|
105
|
+
actions.push({
|
|
106
|
+
key,
|
|
107
|
+
title: part.title || key,
|
|
108
|
+
description: findDescription(propSchema),
|
|
109
|
+
acceptsPrimitive: primitiveKind !== null,
|
|
110
|
+
primitiveKind,
|
|
111
|
+
fields: collectFields(propSchema),
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
const byKey = new Map(actions.map((a) => [a.key, a]));
|
|
115
|
+
return { actions, byKey };
|
|
116
|
+
}
|
|
117
|
+
/** Lazily-built shared registry (the schemas are static within a process). */
|
|
118
|
+
let cached;
|
|
119
|
+
export function getRegistry() {
|
|
120
|
+
if (!cached)
|
|
121
|
+
cached = buildRegistry();
|
|
122
|
+
return cached;
|
|
123
|
+
}
|
|
124
|
+
//# sourceMappingURL=registry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.js","sourceRoot":"","sources":["../../src/lsp/registry.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { type InitializeResult } from "vscode-languageserver/node.js";
|
|
2
|
+
import type { CompletionItem, CompletionParams, Hover, HoverParams } from "vscode-languageserver";
|
|
3
|
+
import { TextDocument } from "vscode-languageserver-textdocument";
|
|
4
|
+
/**
|
|
5
|
+
* The subset of a `Connection` the handlers touch. Narrowed to an interface so
|
|
6
|
+
* tests can drive the wiring with a fake instead of a live stdio transport.
|
|
7
|
+
*/
|
|
8
|
+
export interface DiagnosticsConnection {
|
|
9
|
+
onInitialize(handler: () => InitializeResult): void;
|
|
10
|
+
sendDiagnostics(params: {
|
|
11
|
+
uri: string;
|
|
12
|
+
diagnostics: unknown[];
|
|
13
|
+
}): void;
|
|
14
|
+
onCompletion(handler: (params: CompletionParams) => CompletionItem[]): void;
|
|
15
|
+
onHover(handler: (params: HoverParams) => Hover | null): void;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* The subset of a `TextDocuments` manager the handlers subscribe to.
|
|
19
|
+
*/
|
|
20
|
+
export interface DocumentEvents {
|
|
21
|
+
onDidChangeContent(handler: (event: {
|
|
22
|
+
document: TextDocument;
|
|
23
|
+
}) => void): void;
|
|
24
|
+
onDidClose(handler: (event: {
|
|
25
|
+
document: TextDocument;
|
|
26
|
+
}) => void): void;
|
|
27
|
+
get(uri: string): TextDocument | undefined;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Wire language-server behavior onto a connection + document manager. Pure of
|
|
31
|
+
* any transport, so a unit test can pass fakes, capture the registered
|
|
32
|
+
* callbacks, and assert what gets published — no real process required.
|
|
33
|
+
*/
|
|
34
|
+
export declare function registerHandlers(connection: DiagnosticsConnection, documents: DocumentEvents): void;
|
|
35
|
+
/**
|
|
36
|
+
* Boot the language server over stdio. Called by the `doc-detective lsp`
|
|
37
|
+
* subcommand.
|
|
38
|
+
*/
|
|
39
|
+
export declare function startServer(): void;
|
|
40
|
+
//# sourceMappingURL=server.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/lsp/server.ts"],"names":[],"mappings":"AAAA,OAAO,EAQL,KAAK,gBAAgB,EACtB,MAAM,+BAA+B,CAAC;AACvC,OAAO,KAAK,EACV,cAAc,EACd,gBAAgB,EAChB,KAAK,EACL,WAAW,EACZ,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,YAAY,EAAE,MAAM,oCAAoC,CAAC;AAKlE;;;GAGG;AACH,MAAM,WAAW,qBAAqB;IACpC,YAAY,CAAC,OAAO,EAAE,MAAM,gBAAgB,GAAG,IAAI,CAAC;IACpD,eAAe,CAAC,MAAM,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,OAAO,EAAE,CAAA;KAAE,GAAG,IAAI,CAAC;IACvE,YAAY,CACV,OAAO,EAAE,CAAC,MAAM,EAAE,gBAAgB,KAAK,cAAc,EAAE,GACtD,IAAI,CAAC;IACR,OAAO,CAAC,OAAO,EAAE,CAAC,MAAM,EAAE,WAAW,KAAK,KAAK,GAAG,IAAI,GAAG,IAAI,CAAC;CAC/D;AAED;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,kBAAkB,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE;QAAE,QAAQ,EAAE,YAAY,CAAA;KAAE,KAAK,IAAI,GAAG,IAAI,CAAC;IAC/E,UAAU,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE;QAAE,QAAQ,EAAE,YAAY,CAAA;KAAE,KAAK,IAAI,GAAG,IAAI,CAAC;IACvE,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,YAAY,GAAG,SAAS,CAAC;CAC5C;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAC9B,UAAU,EAAE,qBAAqB,EACjC,SAAS,EAAE,cAAc,GACxB,IAAI,CAsCN;AAKD;;;GAGG;AACH,wBAAgB,WAAW,IAAI,IAAI,CAclC"}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { createConnection, TextDocuments, ProposedFeatures, StreamMessageReader, StreamMessageWriter, TextDocumentSyncKind, } from "vscode-languageserver/node.js";
|
|
2
|
+
import { TextDocument } from "vscode-languageserver-textdocument";
|
|
3
|
+
import { computeDiagnostics } from "./diagnostics.js";
|
|
4
|
+
import { computeCompletions } from "./completion.js";
|
|
5
|
+
import { computeHover } from "./hover.js";
|
|
6
|
+
/**
|
|
7
|
+
* Wire language-server behavior onto a connection + document manager. Pure of
|
|
8
|
+
* any transport, so a unit test can pass fakes, capture the registered
|
|
9
|
+
* callbacks, and assert what gets published — no real process required.
|
|
10
|
+
*/
|
|
11
|
+
export function registerHandlers(connection, documents) {
|
|
12
|
+
connection.onInitialize(() => ({
|
|
13
|
+
capabilities: {
|
|
14
|
+
// Full sync keeps Phase 1 simple; jsonc-parser reparses the whole buffer
|
|
15
|
+
// on each change anyway, so incremental patching buys nothing yet.
|
|
16
|
+
textDocumentSync: TextDocumentSyncKind.Full,
|
|
17
|
+
completionProvider: { triggerCharacters: ['"', "{"] },
|
|
18
|
+
hoverProvider: true,
|
|
19
|
+
},
|
|
20
|
+
}));
|
|
21
|
+
const publish = (document) => {
|
|
22
|
+
connection.sendDiagnostics({
|
|
23
|
+
uri: document.uri,
|
|
24
|
+
diagnostics: computeDiagnostics(document),
|
|
25
|
+
});
|
|
26
|
+
};
|
|
27
|
+
// Fires on open and on every edit — the single hook we need for live
|
|
28
|
+
// diagnostics.
|
|
29
|
+
documents.onDidChangeContent((event) => publish(event.document));
|
|
30
|
+
// Clear diagnostics when a document closes so stale squiggles don't linger.
|
|
31
|
+
documents.onDidClose((event) => connection.sendDiagnostics({ uri: event.document.uri, diagnostics: [] }));
|
|
32
|
+
connection.onCompletion((params) => {
|
|
33
|
+
const document = documents.get(params.textDocument.uri);
|
|
34
|
+
if (!document)
|
|
35
|
+
return [];
|
|
36
|
+
return computeCompletions(document, params.position);
|
|
37
|
+
});
|
|
38
|
+
connection.onHover((params) => {
|
|
39
|
+
const document = documents.get(params.textDocument.uri);
|
|
40
|
+
if (!document)
|
|
41
|
+
return null;
|
|
42
|
+
return computeHover(document, params.position);
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
/* c8 ignore start - real stdio bootstrap: no injectable seam; the pure wiring
|
|
46
|
+
in registerHandlers is unit-tested, and the whole server is exercised
|
|
47
|
+
end-to-end by the spawned protocol test. */
|
|
48
|
+
/**
|
|
49
|
+
* Boot the language server over stdio. Called by the `doc-detective lsp`
|
|
50
|
+
* subcommand.
|
|
51
|
+
*/
|
|
52
|
+
export function startServer() {
|
|
53
|
+
// Explicit stdio streams so the server works no matter how it's launched —
|
|
54
|
+
// the `doc-detective lsp` subcommand OR the bare `doc-detective-lsp` bin.
|
|
55
|
+
// (createConnection(ProposedFeatures.all) alone sniffs argv for `--stdio`,
|
|
56
|
+
// which the bare bin doesn't pass.)
|
|
57
|
+
const connection = createConnection(ProposedFeatures.all, new StreamMessageReader(process.stdin), new StreamMessageWriter(process.stdout));
|
|
58
|
+
const documents = new TextDocuments(TextDocument);
|
|
59
|
+
registerHandlers(connection, documents);
|
|
60
|
+
documents.listen(connection);
|
|
61
|
+
connection.listen();
|
|
62
|
+
}
|
|
63
|
+
/* c8 ignore stop */
|
|
64
|
+
//# sourceMappingURL=server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/lsp/server.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,gBAAgB,EAChB,aAAa,EACb,gBAAgB,EAChB,mBAAmB,EACnB,mBAAmB,EACnB,oBAAoB,GAGrB,MAAM,+BAA+B,CAAC;AAOvC,OAAO,EAAE,YAAY,EAAE,MAAM,oCAAoC,CAAC;AAClE,OAAO,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AACtD,OAAO,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AACrD,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAwB1C;;;;GAIG;AACH,MAAM,UAAU,gBAAgB,CAC9B,UAAiC,EACjC,SAAyB;IAEzB,UAAU,CAAC,YAAY,CAAC,GAAG,EAAE,CAAC,CAAC;QAC7B,YAAY,EAAE;YACZ,yEAAyE;YACzE,mEAAmE;YACnE,gBAAgB,EAAE,oBAAoB,CAAC,IAAI;YAC3C,kBAAkB,EAAE,EAAE,iBAAiB,EAAE,CAAC,GAAG,EAAE,GAAG,CAAC,EAAE;YACrD,aAAa,EAAE,IAAI;SACpB;KACF,CAAC,CAAC,CAAC;IAEJ,MAAM,OAAO,GAAG,CAAC,QAAsB,EAAE,EAAE;QACzC,UAAU,CAAC,eAAe,CAAC;YACzB,GAAG,EAAE,QAAQ,CAAC,GAAG;YACjB,WAAW,EAAE,kBAAkB,CAAC,QAAQ,CAAC;SAC1C,CAAC,CAAC;IACL,CAAC,CAAC;IAEF,qEAAqE;IACrE,eAAe;IACf,SAAS,CAAC,kBAAkB,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC;IAEjE,4EAA4E;IAC5E,SAAS,CAAC,UAAU,CAAC,CAAC,KAAK,EAAE,EAAE,CAC7B,UAAU,CAAC,eAAe,CAAC,EAAE,GAAG,EAAE,KAAK,CAAC,QAAQ,CAAC,GAAG,EAAE,WAAW,EAAE,EAAE,EAAE,CAAC,CACzE,CAAC;IAEF,UAAU,CAAC,YAAY,CAAC,CAAC,MAAM,EAAE,EAAE;QACjC,MAAM,QAAQ,GAAG,SAAS,CAAC,GAAG,CAAC,MAAM,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC;QACxD,IAAI,CAAC,QAAQ;YAAE,OAAO,EAAE,CAAC;QACzB,OAAO,kBAAkB,CAAC,QAAQ,EAAE,MAAM,CAAC,QAAQ,CAAC,CAAC;IACvD,CAAC,CAAC,CAAC;IAEH,UAAU,CAAC,OAAO,CAAC,CAAC,MAAM,EAAE,EAAE;QAC5B,MAAM,QAAQ,GAAG,SAAS,CAAC,GAAG,CAAC,MAAM,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC;QACxD,IAAI,CAAC,QAAQ;YAAE,OAAO,IAAI,CAAC;QAC3B,OAAO,YAAY,CAAC,QAAQ,EAAE,MAAM,CAAC,QAAQ,CAAC,CAAC;IACjD,CAAC,CAAC,CAAC;AACL,CAAC;AAED;;8CAE8C;AAC9C;;;GAGG;AACH,MAAM,UAAU,WAAW;IACzB,2EAA2E;IAC3E,0EAA0E;IAC1E,2EAA2E;IAC3E,oCAAoC;IACpC,MAAM,UAAU,GAAe,gBAAgB,CAC7C,gBAAgB,CAAC,GAAG,EACpB,IAAI,mBAAmB,CAAC,OAAO,CAAC,KAAK,CAAC,EACtC,IAAI,mBAAmB,CAAC,OAAO,CAAC,MAAM,CAAC,CACxC,CAAC;IACF,MAAM,SAAS,GAAG,IAAI,aAAa,CAAC,YAAY,CAAC,CAAC;IAClD,gBAAgB,CAAC,UAAU,EAAE,SAAS,CAAC,CAAC;IACxC,SAAS,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;IAC7B,UAAU,CAAC,MAAM,EAAE,CAAC;AACtB,CAAC;AACD,oBAAoB"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import YAML from "yaml";
|
|
2
|
+
import type { OffsetRange, ActionKeyedStep } from "../json/positions.js";
|
|
3
|
+
/** Result of parsing YAML: the document (for node lookups) plus its value. */
|
|
4
|
+
export interface YamlParse {
|
|
5
|
+
doc: YAML.Document.Parsed;
|
|
6
|
+
value: any;
|
|
7
|
+
/** Set when `doc.toJS()` threw (e.g. an alias-bomb exceeding maxAliasCount). */
|
|
8
|
+
valueError?: string;
|
|
9
|
+
}
|
|
10
|
+
/** Parse YAML into a position-preserving document plus its plain JS value. */
|
|
11
|
+
export declare function parseYamlTree(text: string): YamlParse;
|
|
12
|
+
/** A YAML syntax error reduced to an offset span and message. */
|
|
13
|
+
export interface YamlSyntaxError {
|
|
14
|
+
range: OffsetRange;
|
|
15
|
+
message: string;
|
|
16
|
+
}
|
|
17
|
+
/** Collect YAML syntax errors as offset spans (empty when the doc is clean). */
|
|
18
|
+
export declare function yamlSyntaxErrors(doc: YAML.Document.Parsed): YamlSyntaxError[];
|
|
19
|
+
/**
|
|
20
|
+
* Resolve the source span for an AJV `instancePath` in a YAML document. Walks
|
|
21
|
+
* up to the nearest present ancestor (like the JSON resolver) so a
|
|
22
|
+
* `required`-style error still anchors somewhere. Returns null only when even
|
|
23
|
+
* the document contents can't be located.
|
|
24
|
+
*/
|
|
25
|
+
export declare function rangeForInstancePathYaml(doc: YAML.Document.Parsed, instancePath: string): OffsetRange | null;
|
|
26
|
+
/**
|
|
27
|
+
* Find every step written as a map carrying an `action` key inside a `steps`
|
|
28
|
+
* sequence — the YAML twin of the JSON detector. Returns the offending `action`
|
|
29
|
+
* key span and the step's JSON-Pointer.
|
|
30
|
+
*/
|
|
31
|
+
export declare function findActionKeyedStepsYaml(doc: YAML.Document.Parsed): ActionKeyedStep[];
|
|
32
|
+
//# sourceMappingURL=positions.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"positions.d.ts","sourceRoot":"","sources":["../../../src/lsp/yaml/positions.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,KAAK,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAGzE,8EAA8E;AAC9E,MAAM,WAAW,SAAS;IACxB,GAAG,EAAE,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC;IAC1B,KAAK,EAAE,GAAG,CAAC;IACX,gFAAgF;IAChF,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,8EAA8E;AAC9E,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,CAarD;AAED,iEAAiE;AACjE,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,WAAW,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,gFAAgF;AAChF,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,IAAI,CAAC,QAAQ,CAAC,MAAM,GAAG,eAAe,EAAE,CAK7E;AASD;;;;;GAKG;AACH,wBAAgB,wBAAwB,CACtC,GAAG,EAAE,IAAI,CAAC,QAAQ,CAAC,MAAM,EACzB,YAAY,EAAE,MAAM,GACnB,WAAW,GAAG,IAAI,CAUpB;AAED;;;;GAIG;AACH,wBAAgB,wBAAwB,CACtC,GAAG,EAAE,IAAI,CAAC,QAAQ,CAAC,MAAM,GACxB,eAAe,EAAE,CAqCnB"}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import YAML from "yaml";
|
|
2
|
+
import { instancePathToSegments, pointerFromPath } from "../json/positions.js";
|
|
3
|
+
/** Parse YAML into a position-preserving document plus its plain JS value. */
|
|
4
|
+
export function parseYamlTree(text) {
|
|
5
|
+
const doc = YAML.parseDocument(text, { keepSourceTokens: true });
|
|
6
|
+
// `toJS()` resolves aliases and can throw on a pathological document (the
|
|
7
|
+
// "billion laughs" alias bomb the yaml lib guards with maxAliasCount). Catch
|
|
8
|
+
// it so a malicious/degenerate buffer can't crash the server — surface it as
|
|
9
|
+
// a document-level error instead.
|
|
10
|
+
try {
|
|
11
|
+
return { doc, value: doc.toJS() };
|
|
12
|
+
}
|
|
13
|
+
catch (error) {
|
|
14
|
+
/* c8 ignore next - the yaml lib always throws an Error with a message; the String() fallback guards a hypothetical non-Error throw */
|
|
15
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
16
|
+
return { doc, value: undefined, valueError: message };
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
/** Collect YAML syntax errors as offset spans (empty when the doc is clean). */
|
|
20
|
+
export function yamlSyntaxErrors(doc) {
|
|
21
|
+
return doc.errors.map((error) => {
|
|
22
|
+
const [start, end] = error.pos;
|
|
23
|
+
return { range: { start, end }, message: error.message };
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
/** Node range → OffsetRange, using [start, valueEnd] for a tight span. */
|
|
27
|
+
function nodeRange(node) {
|
|
28
|
+
const range = node?.range;
|
|
29
|
+
if (!Array.isArray(range))
|
|
30
|
+
return null;
|
|
31
|
+
return { start: range[0], end: range[1] };
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Resolve the source span for an AJV `instancePath` in a YAML document. Walks
|
|
35
|
+
* up to the nearest present ancestor (like the JSON resolver) so a
|
|
36
|
+
* `required`-style error still anchors somewhere. Returns null only when even
|
|
37
|
+
* the document contents can't be located.
|
|
38
|
+
*/
|
|
39
|
+
export function rangeForInstancePathYaml(doc, instancePath) {
|
|
40
|
+
const segments = instancePathToSegments(instancePath);
|
|
41
|
+
for (let end = segments.length; end >= 0; end--) {
|
|
42
|
+
const node = end === 0 ? doc.contents : doc.getIn(segments.slice(0, end), true);
|
|
43
|
+
const range = nodeRange(node);
|
|
44
|
+
if (range)
|
|
45
|
+
return range;
|
|
46
|
+
}
|
|
47
|
+
// Reached only for an empty document (no contents node has a range).
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Find every step written as a map carrying an `action` key inside a `steps`
|
|
52
|
+
* sequence — the YAML twin of the JSON detector. Returns the offending `action`
|
|
53
|
+
* key span and the step's JSON-Pointer.
|
|
54
|
+
*/
|
|
55
|
+
export function findActionKeyedStepsYaml(doc) {
|
|
56
|
+
const found = [];
|
|
57
|
+
const visit = (node, path) => {
|
|
58
|
+
if (YAML.isMap(node)) {
|
|
59
|
+
for (const pair of node.items) {
|
|
60
|
+
const key = pair.key?.value;
|
|
61
|
+
if (key === "steps" && YAML.isSeq(pair.value)) {
|
|
62
|
+
collectSteps(pair.value, [...path, "steps"]);
|
|
63
|
+
}
|
|
64
|
+
if (pair.value)
|
|
65
|
+
visit(pair.value, [...path, key]);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
else if (YAML.isSeq(node)) {
|
|
69
|
+
node.items.forEach((item, index) => visit(item, [...path, index]));
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
const collectSteps = (seq, seqPath) => {
|
|
73
|
+
seq.items.forEach((item, index) => {
|
|
74
|
+
if (!YAML.isMap(item))
|
|
75
|
+
return;
|
|
76
|
+
const actionPair = item.items.find((p) => p.key?.value === "action");
|
|
77
|
+
if (actionPair) {
|
|
78
|
+
const range = nodeRange(actionPair.key);
|
|
79
|
+
if (range) {
|
|
80
|
+
found.push({
|
|
81
|
+
keyRange: range,
|
|
82
|
+
pointer: pointerFromPath([...seqPath, index]),
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
};
|
|
88
|
+
if (doc.contents)
|
|
89
|
+
visit(doc.contents, []);
|
|
90
|
+
return found;
|
|
91
|
+
}
|
|
92
|
+
//# sourceMappingURL=positions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"positions.js","sourceRoot":"","sources":["../../../src/lsp/yaml/positions.ts"],"names":[],"mappings":"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"}
|
package/dist/utils.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAcA,OAAO,EACL,UAAU,EACV,OAAO,EACP,SAAS,EACT,aAAa,EACb,YAAY,EACZ,OAAO,EACP,cAAc,EACd,GAAG,EACH,eAAe,EACf,uBAAuB,EACvB,aAAa,EACb,SAAS,EACT,gBAAgB,EAChB,gBAAgB,GACjB,CAAC;AASF,iBAAS,gBAAgB,IAAI,OAAO,CAKnC;AAQD,iBAAS,eAAe,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,GAAE,GAAQ,GAAG,OAAO,CAMjE;AAED,iBAAS,GAAG,CAAC,OAAO,EAAE,GAAG,EAAE,KAAK,GAAE,MAAe,EAAE,MAAM,GAAE,GAAQ,QASlE;AAMD,iBAAS,UAAU,CAAC,IAAI,EAAE,GAAG,GAAG,GAAG,CAiGlC;AAGD,iBAAS,OAAO,CAAC,IAAI,EAAE,GAAG,GAAG,GAAG,CAG/B;AAGD,iBAAe,uBAAuB,CAAC,MAAM,GAAE,GAAQ;;;UAyEtD;AA8CD,iBAAe,SAAS,CAAC,EAAE,UAAU,EAAE,IAAI,EAAE,EAAE;IAAE,UAAU,CAAC,EAAE,GAAG,CAAC;IAAC,IAAI,EAAE,GAAG,CAAA;CAAE,gBAwM7E;AAgCD,QAAA,MAAM,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,MAAM,EAAE,GAAG,EAAE,UAAU,EAAE,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,OAAO,EAAE,GAAG,KAAK,OAAO,CAAC,GAAG,CAAC,
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAcA,OAAO,EACL,UAAU,EACV,OAAO,EACP,SAAS,EACT,aAAa,EACb,YAAY,EACZ,OAAO,EACP,cAAc,EACd,GAAG,EACH,eAAe,EACf,uBAAuB,EACvB,aAAa,EACb,SAAS,EACT,gBAAgB,EAChB,gBAAgB,GACjB,CAAC;AASF,iBAAS,gBAAgB,IAAI,OAAO,CAKnC;AAQD,iBAAS,eAAe,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,GAAE,GAAQ,GAAG,OAAO,CAMjE;AAED,iBAAS,GAAG,CAAC,OAAO,EAAE,GAAG,EAAE,KAAK,GAAE,MAAe,EAAE,MAAM,GAAE,GAAQ,QASlE;AAMD,iBAAS,UAAU,CAAC,IAAI,EAAE,GAAG,GAAG,GAAG,CAiGlC;AAGD,iBAAS,OAAO,CAAC,IAAI,EAAE,GAAG,GAAG,GAAG,CAG/B;AAGD,iBAAe,uBAAuB,CAAC,MAAM,GAAE,GAAQ;;;UAyEtD;AA8CD,iBAAe,SAAS,CAAC,EAAE,UAAU,EAAE,IAAI,EAAE,EAAE;IAAE,UAAU,CAAC,EAAE,GAAG,CAAC;IAAC,IAAI,EAAE,GAAG,CAAA;CAAE,gBAwM7E;AAgCD,QAAA,MAAM,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,MAAM,EAAE,GAAG,EAAE,UAAU,EAAE,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,OAAO,EAAE,GAAG,KAAK,OAAO,CAAC,GAAG,CAAC,CA+lBzG,CAAC;AAGF,iBAAS,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,gBAAgB,EAAE,GAAG,WAM5D;AAED,iBAAe,aAAa,CAAC,EAAE,SAAS,EAAE,OAAO,EAAE,EAAE;IAAE,SAAS,EAAE,GAAG,CAAC;IAAC,OAAO,EAAE,GAAG,CAAA;CAAE,iBA4DpF;AAED,iBAAe,aAAa,CAAC,MAAM,EAAE,GAAG,YAAK,EAAE,UAAU,EAAE,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,OAAO,GAAE,GAAQ,kBA0E9F;AAGD,iBAAe,YAAY,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,EAAE;;;;GAwCvD;AAED,iBAAS,OAAO,SAuBf;AAGD,iBAAS,cAAc,QA+GtB"}
|
package/dist/utils.js
CHANGED
|
@@ -454,7 +454,12 @@ const reporters = {
|
|
|
454
454
|
const outputExtensions = [".json"];
|
|
455
455
|
// Normalize output path
|
|
456
456
|
outputPath = path.resolve(outputPath);
|
|
457
|
-
|
|
457
|
+
// Reuse the results JSON serialized once by outputResults when present
|
|
458
|
+
// (byte-identical to serializing `results` here); fall back for direct
|
|
459
|
+
// callers that don't pass it.
|
|
460
|
+
const data = typeof options.resultsJson === "string"
|
|
461
|
+
? options.resultsJson
|
|
462
|
+
: JSON.stringify(results, null, 2);
|
|
458
463
|
let outputFile = "";
|
|
459
464
|
let outputDir = "";
|
|
460
465
|
let reportType = "doc-detective-results";
|
|
@@ -564,10 +569,17 @@ const reporters = {
|
|
|
564
569
|
// The run folder name IS the runId under the `runs/<id>` layout.
|
|
565
570
|
runId: path.basename(runDir),
|
|
566
571
|
};
|
|
572
|
+
// When we archive the unmodified `results` (stamped-runDir case), reuse the
|
|
573
|
+
// JSON outputResults already serialized. When we rewrote runId/runDir into a
|
|
574
|
+
// fresh copy, that object differs from `results`, so serialize it directly —
|
|
575
|
+
// keeping the written file byte-identical to before this optimization.
|
|
576
|
+
const persistedJson = useStampedRunDir && typeof options.resultsJson === "string"
|
|
577
|
+
? options.resultsJson
|
|
578
|
+
: JSON.stringify(persistedResults, null, 2);
|
|
567
579
|
const outputFile = path.resolve(runDir, `${reportType}.json`);
|
|
568
580
|
try {
|
|
569
581
|
fs.mkdirSync(runDir, { recursive: true });
|
|
570
|
-
fs.writeFileSync(outputFile,
|
|
582
|
+
fs.writeFileSync(outputFile, persistedJson);
|
|
571
583
|
// Archive a human-readable HTML report beside the JSON, so the run folder
|
|
572
584
|
// is a complete shareable artifact without the standalone `html` reporter.
|
|
573
585
|
// Best-effort and isolated: an HTML failure must not break the JSON
|
|
@@ -978,7 +990,6 @@ async function reportResults({ apiConfig, results }) {
|
|
|
978
990
|
try {
|
|
979
991
|
const url = `${apiConfig.url}/contexts`;
|
|
980
992
|
const payload = { contexts };
|
|
981
|
-
console.log(payload);
|
|
982
993
|
const response = await axios.post(url, payload, {
|
|
983
994
|
headers: {
|
|
984
995
|
"x-runner-token": apiConfig.token,
|
|
@@ -1021,15 +1032,29 @@ async function outputResults(config = {}, outputPath, results, options = {}) {
|
|
|
1021
1032
|
return reporter;
|
|
1022
1033
|
});
|
|
1023
1034
|
}
|
|
1035
|
+
// Serialize the canonical results tree ONCE and share the string with the
|
|
1036
|
+
// JSON-writing reporters (json + runFolder) instead of each calling
|
|
1037
|
+
// JSON.stringify on the full tree independently. Only computed when a
|
|
1038
|
+
// JSON-writing reporter is actually active, so a terminal-only run pays
|
|
1039
|
+
// nothing. Each reporter still falls back to serializing itself when the
|
|
1040
|
+
// shared string is absent (so they stay correct if called directly), and
|
|
1041
|
+
// runFolder only reuses it when it archives the unmodified `results` (the
|
|
1042
|
+
// stamped-runDir case) — a rewritten runId/runDir copy is serialized fresh,
|
|
1043
|
+
// keeping every written file byte-identical to before.
|
|
1044
|
+
const writesJson = activeReporters.includes("jsonReporter") ||
|
|
1045
|
+
activeReporters.includes("runFolderReporter");
|
|
1046
|
+
const reporterOptions = writesJson
|
|
1047
|
+
? { ...options, resultsJson: JSON.stringify(results, null, 2) }
|
|
1048
|
+
: options;
|
|
1024
1049
|
// Execute each reporter
|
|
1025
1050
|
const reporterPromises = activeReporters.map((reporter) => {
|
|
1026
1051
|
if (typeof reporter === "function") {
|
|
1027
1052
|
// Direct function reference
|
|
1028
|
-
return reporter(config, outputPath, results,
|
|
1053
|
+
return reporter(config, outputPath, results, reporterOptions);
|
|
1029
1054
|
}
|
|
1030
1055
|
else if (typeof reporter === "string" && reporters[reporter]) {
|
|
1031
1056
|
// String reference to built-in or registered reporter
|
|
1032
|
-
return reporters[reporter](config, outputPath, results,
|
|
1057
|
+
return reporters[reporter](config, outputPath, results, reporterOptions);
|
|
1033
1058
|
}
|
|
1034
1059
|
else if (typeof reporter === "string" && !reporters[reporter]) {
|
|
1035
1060
|
console.error(`Reporter "${reporter}" not found. Available reporters: ${Object.keys(reporters).join(", ")}`);
|