correctly 0.0.0 → 0.1.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/AGENTS.md +6 -2
- package/README.md +21 -15
- package/dist/ajv.d.mts +11 -0
- package/dist/ajv.d.mts.map +1 -0
- package/dist/ajv.mjs +221 -0
- package/dist/ajv.mjs.map +1 -0
- package/dist/check-BI7U3ZLc.mjs +88 -0
- package/dist/check-BI7U3ZLc.mjs.map +1 -0
- package/dist/cli.mjs +14 -62
- package/dist/cli.mjs.map +1 -1
- package/dist/config-Bx1-XXeo.d.mts +189 -0
- package/dist/config-Bx1-XXeo.d.mts.map +1 -0
- package/dist/config-Cb6o4YnJ.mjs +348 -0
- package/dist/config-Cb6o4YnJ.mjs.map +1 -0
- package/dist/core.d.mts +27 -125
- package/dist/core.d.mts.map +1 -1
- package/dist/core.mjs +5 -2
- package/dist/diagnostics-BqD1WOy4.mjs +70 -0
- package/dist/diagnostics-BqD1WOy4.mjs.map +1 -0
- package/dist/engine-VST0hoSk.mjs +111 -0
- package/dist/engine-VST0hoSk.mjs.map +1 -0
- package/dist/extensions-ClclWslY.d.mts +11 -0
- package/dist/extensions-ClclWslY.d.mts.map +1 -0
- package/dist/hints-HvSIhuMM.mjs +57 -0
- package/dist/hints-HvSIhuMM.mjs.map +1 -0
- package/dist/lsp.mjs +40 -91
- package/dist/lsp.mjs.map +1 -1
- package/dist/renovate.d.mts +5 -0
- package/dist/renovate.d.mts.map +1 -0
- package/dist/renovate.mjs +11 -0
- package/dist/renovate.mjs.map +1 -0
- package/dist/schemas-UxCtIdo2.mjs +265 -0
- package/dist/schemas-UxCtIdo2.mjs.map +1 -0
- package/dist/session-DCFav56x.mjs +113 -0
- package/dist/session-DCFav56x.mjs.map +1 -0
- package/dist/worker.mjs +80 -0
- package/dist/worker.mjs.map +1 -0
- package/docs/guide.md +166 -34
- package/examples/basic/correctly.config.ts +13 -0
- package/package.json +69 -60
- package/src/cli/check.ts +41 -11
- package/src/cli/main.ts +3 -2
- package/src/cli/readable.ts +24 -5
- package/src/cli/vsix.ts +1 -0
- package/src/core/adapters.ts +54 -0
- package/src/core/config.ts +201 -64
- package/src/core/engine.ts +118 -218
- package/src/core/extensions.ts +144 -0
- package/src/core/index.ts +3 -0
- package/src/core/parse.ts +26 -2
- package/src/core/parsers.ts +22 -0
- package/src/core/schema-walk.ts +43 -0
- package/src/core/schemas.ts +21 -41
- package/src/core/types.ts +33 -6
- package/src/extensions/renovate.ts +4 -0
- package/src/lsp/hints.ts +22 -12
- package/src/lsp/server.ts +9 -2
- package/src/lsp/workspace.ts +49 -54
- package/src/runtime/session.ts +173 -0
- package/src/runtime/worker.ts +99 -0
- package/src/validators/ajv.ts +257 -0
- package/src/vscode/README.md +1 -1
- package/src/vscode/extension.ts +2 -4
- package/src/vscode/package.json +9 -2
- package/dist/engine-BgWBnb1A.mjs +0 -746
- package/dist/engine-BgWBnb1A.mjs.map +0 -1
- package/examples/basic/correctly.config.json +0 -10
- package/src/core/config.schema.json +0 -47
package/AGENTS.md
CHANGED
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
# Correctly consumer guidance
|
|
2
2
|
|
|
3
|
-
- Keep schema associations in `correctly.config.
|
|
3
|
+
- Keep schema associations in `correctly.config.ts`; document `$schema` fields do not select schemas.
|
|
4
4
|
- Run `correctly check` in CI and use the extension or `correctly-lsp --stdio` for editor assistance.
|
|
5
5
|
- JSON is strict. JSONC permits comments and trailing commas. Duplicate keys are always errors.
|
|
6
6
|
- Resolve patterns and local schema paths from the configuration directory. Last matching association wins.
|
|
7
|
+
- Compose parser and validator adapters directly; install `@correctlyjs/schemars` and import `schemars` from `@correctlyjs/schemars/ajv` and opt into the extension on each Ajv validator, for example `extensions: [schemars({ version: "0.8.22" })]`; an exact reviewed release or a closed range within one compatibility era is required; consult the extension package's guide for supported eras, format inventory, and precision. Renovate metadata uses the separate `renovate()` extension.
|
|
8
|
+
- Missing format implementations fail with `extension-required`; never disable format validation to work around them.
|
|
7
9
|
- Use explicit offline mode for reproducible cached validation. Missing schemas and offline cache misses fail.
|
|
8
|
-
- See `docs/guide.md` for detailed semantics. Formatting belongs to your formatter;
|
|
10
|
+
- See `docs/guide.md` for detailed semantics. Formatting belongs to your formatter; built-in parsers cover JSON/JSONC. Custom parsers must specify value semantics and source mapping.
|
|
11
|
+
|
|
12
|
+
- Use Comline-loaded TypeScript configuration and project-local dependencies. Save executable configuration/imports to reload; do not evaluate unsaved TypeScript. The VSIX requires workspace trust.
|
package/README.md
CHANGED
|
@@ -1,24 +1,30 @@
|
|
|
1
1
|
# Correctly
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Compose parsers and validators in one TypeScript configuration for CI and editors.
|
|
4
4
|
|
|
5
|
-
```
|
|
6
|
-
{
|
|
7
|
-
|
|
8
|
-
|
|
5
|
+
```ts
|
|
6
|
+
import { defineConfig, json } from "correctly"
|
|
7
|
+
import { ajv } from "correctly/validators/ajv"
|
|
8
|
+
import { schemars } from "@correctlyjs/schemars/ajv"
|
|
9
|
+
|
|
10
|
+
export default defineConfig({
|
|
11
|
+
associations: [
|
|
9
12
|
{
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
}
|
|
13
|
+
files: ["turbo.json"],
|
|
14
|
+
parse: json(),
|
|
15
|
+
validate: ajv({
|
|
16
|
+
schema: "https://turbo.build/schema.json",
|
|
17
|
+
extensions: [schemars({ version: ">=0.8.15 <=0.8.22" })],
|
|
18
|
+
}),
|
|
19
|
+
},
|
|
20
|
+
],
|
|
21
|
+
})
|
|
16
22
|
```
|
|
17
23
|
|
|
18
|
-
|
|
24
|
+
Install `correctly` and `@correctlyjs/schemars` in your project and save this as `correctly.config.ts`, then run `correctly check`. Comline loads the configuration and its imported implementations. Exit codes are 0 for success, 1 for document errors, and 2 for configuration/schema/execution failures. Use `correctly check --format json` for a versioned report or `correctly check --offline` for cached remote schemas.
|
|
19
25
|
|
|
20
|
-
The VS Code extension
|
|
26
|
+
The VS Code extension and `correctly-lsp --stdio` use the same runtime for diagnostics, completion and hover. Executable configuration runs only in trusted workspaces and reloads after saving; data and schema buffers validate as you type.
|
|
21
27
|
|
|
22
|
-
|
|
28
|
+
Built-in JSON/JSONC parsers and an Ajv adapter support drafts 7 and 2020-12. Schema extensions are explicit per validator. Unsupported formats produce actionable extension requirements. Documents' `$schema` properties remain ordinary data. Correctly does not coerce values, insert defaults, remove properties, format, or fix documents.
|
|
23
29
|
|
|
24
|
-
See the [guide](docs/guide.md) for
|
|
30
|
+
See the [guide](docs/guide.md) for migration, adapter contracts, reviewed Schemars eras and precision, discovery, cache policy, diagnostics, and editor behavior.
|
package/dist/ajv.d.mts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { C as Validator } from "./config-Bx1-XXeo.mjs";
|
|
2
|
+
import { t as AjvExtension } from "./extensions-ClclWslY.mjs";
|
|
3
|
+
//#region src/validators/ajv.d.ts
|
|
4
|
+
export type AjvOptions = {
|
|
5
|
+
schema: string;
|
|
6
|
+
extensions?: readonly AjvExtension[];
|
|
7
|
+
};
|
|
8
|
+
export declare function ajv(options: AjvOptions): Validator;
|
|
9
|
+
//#endregion
|
|
10
|
+
export type { AjvExtension };
|
|
11
|
+
//# sourceMappingURL=ajv.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ajv.d.mts","names":[],"sources":["../src/validators/ajv.ts"],"mappings":";;;YA0NY;EACX;EACA,sBAAsB;;wBAEP,IAAI,SAAS,aAAa"}
|
package/dist/ajv.mjs
ADDED
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
import { C as pointer, b as diagnostic, f as schemaUri, g as isCorrectlyError, m as CorrectlyError } from "./config-Cb6o4YnJ.mjs";
|
|
2
|
+
import { r as validationContexts } from "./diagnostics-BqD1WOy4.mjs";
|
|
3
|
+
import { i as schemaChildren, n as SchemaStore, t as DIALECTS } from "./schemas-UxCtIdo2.mjs";
|
|
4
|
+
import { Ajv } from "ajv";
|
|
5
|
+
import { Ajv2020 } from "ajv/dist/2020.js";
|
|
6
|
+
import addFormats from "ajv-formats";
|
|
7
|
+
//#region src/core/extensions.ts
|
|
8
|
+
const knownExtensions = [{
|
|
9
|
+
id: "@correctlyjs/schemars",
|
|
10
|
+
formats: [
|
|
11
|
+
"int",
|
|
12
|
+
"uint",
|
|
13
|
+
"int8",
|
|
14
|
+
"uint8",
|
|
15
|
+
"int16",
|
|
16
|
+
"uint16",
|
|
17
|
+
"int32",
|
|
18
|
+
"uint32",
|
|
19
|
+
"int64",
|
|
20
|
+
"uint64",
|
|
21
|
+
"int128",
|
|
22
|
+
"uint128",
|
|
23
|
+
"float",
|
|
24
|
+
"double",
|
|
25
|
+
"ip",
|
|
26
|
+
"phone",
|
|
27
|
+
"partial-date-time"
|
|
28
|
+
],
|
|
29
|
+
guidance: `For a Schemars-generated schema, install @correctlyjs/schemars and import { schemars } from "@correctlyjs/schemars/ajv". Select the schema's reviewed Schemars version with schemars({ version: "..." }) in this Ajv validator's extensions in correctly.config.ts.`
|
|
30
|
+
}];
|
|
31
|
+
function installExtensions(ajv, extensions) {
|
|
32
|
+
const formats = /* @__PURE__ */ new Set(), annotations = /* @__PURE__ */ new Set();
|
|
33
|
+
const identifiers = /* @__PURE__ */ new Set();
|
|
34
|
+
for (const extension of extensions) {
|
|
35
|
+
if (!extension || typeof extension !== "object" || typeof extension.id !== "string" || !extension.id) throw new CorrectlyError("extension", "Pass extension implementations to ajv({ extensions: [...] }); string identifiers are not implementations.");
|
|
36
|
+
const id = extension.id;
|
|
37
|
+
if (identifiers.has(id)) throw new CorrectlyError("extension", `Duplicate extension identifier: ${id}`);
|
|
38
|
+
identifiers.add(id);
|
|
39
|
+
for (const [name, definition] of Object.entries(extension.formats ?? {})) {
|
|
40
|
+
if (formats.has(name)) throw new CorrectlyError("extension", `Enabled extensions both define format ${JSON.stringify(name)}.`);
|
|
41
|
+
if (definition.async || typeof definition.validate !== "function") throw new CorrectlyError("extension", `Extension ${id} must provide a synchronous validator for format ${JSON.stringify(name)}.`);
|
|
42
|
+
formats.add(name);
|
|
43
|
+
const validate = definition.validate;
|
|
44
|
+
ajv.addFormat(name, {
|
|
45
|
+
...definition,
|
|
46
|
+
validate: (value) => {
|
|
47
|
+
const valid = validate(value);
|
|
48
|
+
if (typeof valid !== "boolean") throw new CorrectlyError("extension", `Extension ${id} returned a non-boolean result for format ${JSON.stringify(name)}. Validators must be synchronous.`);
|
|
49
|
+
return valid;
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
for (const keyword of extension.annotations ?? []) {
|
|
54
|
+
if (annotations.has(keyword) || ajv.RULES.keywords[keyword]) throw new CorrectlyError("extension", `Extension ${id} conflicts with keyword ${JSON.stringify(keyword)}.`);
|
|
55
|
+
annotations.add(keyword);
|
|
56
|
+
ajv.addKeyword({
|
|
57
|
+
keyword,
|
|
58
|
+
valid: true
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
function assertFormats(ajv, schema, uri, registry, location = "") {
|
|
64
|
+
if (typeof schema !== "object" || schema === null || Array.isArray(schema)) return;
|
|
65
|
+
const object = schema;
|
|
66
|
+
if (typeof object.format === "string" && (!Object.hasOwn(ajv.formats, object.format) || ajv.formats[object.format] === true)) {
|
|
67
|
+
const format = object.format;
|
|
68
|
+
const extension = registry.find((entry) => entry.formats.includes(format));
|
|
69
|
+
const hint = extension?.guidance ?? `Pass an extension implementing ${JSON.stringify(format)} to this Ajv validator's extensions in correctly.config.ts.`;
|
|
70
|
+
throw new CorrectlyError("extension-required", `This schema needs an extension.\nFormat ${JSON.stringify(format)} has no enabled validator; validation cannot proceed.\nSchema: ${uri}#${location}/format\n${hint}`, {
|
|
71
|
+
schemaUri: uri,
|
|
72
|
+
schemaPointer: `${location}/format`,
|
|
73
|
+
format,
|
|
74
|
+
...extension ? { suggestedExtension: extension.id } : {}
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
for (const [path, child] of schemaChildren(object)) assertFormats(ajv, child, uri, registry, location + path);
|
|
78
|
+
}
|
|
79
|
+
//#endregion
|
|
80
|
+
//#region src/validators/ajv.ts
|
|
81
|
+
const ANNOTATIONS = [
|
|
82
|
+
"markdownDescription",
|
|
83
|
+
"enumDescriptions",
|
|
84
|
+
"markdownEnumDescriptions",
|
|
85
|
+
"defaultSnippets",
|
|
86
|
+
"errorMessage",
|
|
87
|
+
"patternErrorMessage",
|
|
88
|
+
"deprecationMessage",
|
|
89
|
+
"suggestSortText",
|
|
90
|
+
"doNotSuggest",
|
|
91
|
+
"allowComments",
|
|
92
|
+
"allowTrailingCommas",
|
|
93
|
+
"deprecated",
|
|
94
|
+
"$vocabulary"
|
|
95
|
+
];
|
|
96
|
+
function registerRootAnchors(ajv, schema, uri) {
|
|
97
|
+
if (typeof schema !== "object" || schema === null) return;
|
|
98
|
+
const object = schema;
|
|
99
|
+
for (const anchor of [object.$anchor, object.$dynamicAnchor]) if (typeof anchor === "string") ajv.refs[new URL(`#${anchor}`, object.$id ?? uri).href] = object.$id ?? uri;
|
|
100
|
+
}
|
|
101
|
+
function validationMessage(error) {
|
|
102
|
+
if (error.keyword === "anyOf") return "must match at least one alternative; none matched";
|
|
103
|
+
if (error.keyword === "if" && ["then", "else"].includes(error.params.failingKeyword)) return `must satisfy the ${error.params.failingKeyword} branch of the conditional schema`;
|
|
104
|
+
if (error.keyword === "uniqueItems") {
|
|
105
|
+
const { i, j } = error.params;
|
|
106
|
+
if (Number.isInteger(i) && Number.isInteger(j)) return `duplicates item at ${error.instancePath}${pointer([Math.min(i, j)])}; array items must be unique`;
|
|
107
|
+
}
|
|
108
|
+
if (error.keyword === "enum" && Array.isArray(error.params.allowedValues)) return `must be one of: ${error.params.allowedValues.map((value) => JSON.stringify(value)).join(", ")}`;
|
|
109
|
+
if (error.keyword === "const" && Object.hasOwn(error.params, "allowedValue")) return `must equal ${JSON.stringify(error.params.allowedValue)}`;
|
|
110
|
+
if (error.keyword === "oneOf") {
|
|
111
|
+
if (error.params.passingSchemas === null) return "must match exactly one alternative; none matched";
|
|
112
|
+
if (Array.isArray(error.params.passingSchemas)) return `must match exactly one alternative; multiple matched (alternatives ${error.params.passingSchemas.map((index) => index + 1).join(", ")})`;
|
|
113
|
+
}
|
|
114
|
+
return error.message ?? "Schema violation";
|
|
115
|
+
}
|
|
116
|
+
function validationDiagnostic(text, parsed, error) {
|
|
117
|
+
let jsonPointer = error.instancePath;
|
|
118
|
+
if (error.keyword === "uniqueItems" && Number.isInteger(error.params.i) && Number.isInteger(error.params.j)) jsonPointer += pointer([Math.max(error.params.i, error.params.j)]);
|
|
119
|
+
const property = error.params.additionalProperty ?? error.params.unevaluatedProperty ?? error.params.missingProperty ?? error.params.propertyName ?? error.propertyName;
|
|
120
|
+
if (typeof property === "string") jsonPointer += pointer([property]);
|
|
121
|
+
const node = parsed.locate(jsonPointer, error.keyword === "additionalProperties" || error.keyword === "unevaluatedProperties" || error.keyword === "propertyNames" || typeof error.propertyName === "string");
|
|
122
|
+
const rangeNode = error.keyword === "required" && error.propertyName === void 0 ? parsed.locate(error.instancePath) : node;
|
|
123
|
+
const length = error.keyword === "required" && error.propertyName === void 0 ? 1 : rangeNode?.length;
|
|
124
|
+
return diagnostic(text, `schema/${error.keyword}`, `${jsonPointer || "/"}: ${validationMessage(error)}`, jsonPointer, rangeNode?.offset ?? 0, length ?? 1);
|
|
125
|
+
}
|
|
126
|
+
async function compile(store, uri, extensions) {
|
|
127
|
+
try {
|
|
128
|
+
const resource = await store.load(uri);
|
|
129
|
+
const options = {
|
|
130
|
+
allErrors: true,
|
|
131
|
+
strictSchema: true,
|
|
132
|
+
strictTypes: false,
|
|
133
|
+
strictTuples: false,
|
|
134
|
+
strictRequired: false,
|
|
135
|
+
coerceTypes: false,
|
|
136
|
+
useDefaults: false,
|
|
137
|
+
removeAdditional: false,
|
|
138
|
+
validateSchema: true,
|
|
139
|
+
loadSchema: async (ref) => {
|
|
140
|
+
const loaded = await store.load(ref, resource.dialect);
|
|
141
|
+
if (loaded.dialect !== resource.dialect) throw new CorrectlyError("unsupported-dialect", `Mixed schema dialects: ${uri} references ${ref}`);
|
|
142
|
+
assertFormats(ajv, loaded.schema, loaded.uri, knownExtensions, loaded.pointer);
|
|
143
|
+
registerRootAnchors(ajv, loaded.schema, loaded.uri);
|
|
144
|
+
return loaded.schema;
|
|
145
|
+
}
|
|
146
|
+
};
|
|
147
|
+
const ajv = resource.dialect === "2020-12" ? new Ajv2020(options) : new Ajv(options);
|
|
148
|
+
addFormats(ajv);
|
|
149
|
+
for (const name of [
|
|
150
|
+
"int32",
|
|
151
|
+
"int64",
|
|
152
|
+
"float",
|
|
153
|
+
"double"
|
|
154
|
+
]) delete ajv.formats[name];
|
|
155
|
+
for (const keyword of ANNOTATIONS) if (!ajv.RULES.keywords[keyword]) ajv.addKeyword({
|
|
156
|
+
keyword,
|
|
157
|
+
valid: true
|
|
158
|
+
});
|
|
159
|
+
if (resource.dialect === "2020-12") ajv.addKeyword({
|
|
160
|
+
keyword: "$anchor",
|
|
161
|
+
schemaType: "string",
|
|
162
|
+
valid: true
|
|
163
|
+
});
|
|
164
|
+
installExtensions(ajv, extensions);
|
|
165
|
+
assertFormats(ajv, resource.schema, resource.uri, knownExtensions, resource.pointer);
|
|
166
|
+
ajv.addSchema(resource.schema, resource.uri);
|
|
167
|
+
registerRootAnchors(ajv, resource.schema, resource.uri);
|
|
168
|
+
return await ajv.compileAsync({
|
|
169
|
+
$schema: DIALECTS[resource.dialect],
|
|
170
|
+
$ref: uri
|
|
171
|
+
});
|
|
172
|
+
} catch (error) {
|
|
173
|
+
if (isCorrectlyError(error)) throw error;
|
|
174
|
+
throw new CorrectlyError("schema", `Cannot compile schema ${uri}: ${error instanceof Error ? error.message : String(error)}`);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
const storeKey = Symbol.for("correctly/ajv/schema-store@1");
|
|
178
|
+
function schemaStore(context) {
|
|
179
|
+
let store = context.services.get(storeKey);
|
|
180
|
+
if (!store) {
|
|
181
|
+
store = new SchemaStore({
|
|
182
|
+
root: context.root,
|
|
183
|
+
config: context.remote ? { remote: context.remote } : {}
|
|
184
|
+
}, {
|
|
185
|
+
read: context.read,
|
|
186
|
+
...context.fetch ? { fetch: context.fetch } : {},
|
|
187
|
+
...context.signal ? { signal: context.signal } : {}
|
|
188
|
+
});
|
|
189
|
+
context.services.set(storeKey, store);
|
|
190
|
+
}
|
|
191
|
+
return store;
|
|
192
|
+
}
|
|
193
|
+
function ajv(options) {
|
|
194
|
+
const extensions = [...options.extensions ?? []];
|
|
195
|
+
return {
|
|
196
|
+
id: "ajv",
|
|
197
|
+
accepts: ["json"],
|
|
198
|
+
schema: options.schema,
|
|
199
|
+
extensions,
|
|
200
|
+
register: async (context) => {
|
|
201
|
+
await schemaStore(context).load(schemaUri(options.schema, context.root));
|
|
202
|
+
},
|
|
203
|
+
prepare: async (context) => {
|
|
204
|
+
const store = schemaStore(context);
|
|
205
|
+
const uri = schemaUri(options.schema, context.root);
|
|
206
|
+
const validate = await compile(store, uri, extensions);
|
|
207
|
+
return {
|
|
208
|
+
validate: ({ text, parsed }) => validate(parsed.value) ? [] : validationContexts(validate.errors ?? [], (validate.errors ?? []).map((error) => validationDiagnostic(text, parsed, error))),
|
|
209
|
+
editor: {
|
|
210
|
+
kind: "json-schema",
|
|
211
|
+
uri,
|
|
212
|
+
readSchema: async (uri) => JSON.stringify((await store.load(uri)).schema)
|
|
213
|
+
}
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
//#endregion
|
|
219
|
+
export { ajv };
|
|
220
|
+
|
|
221
|
+
//# sourceMappingURL=ajv.mjs.map
|
package/dist/ajv.mjs.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ajv.mjs","names":[],"sources":["../src/core/extensions.ts","../src/validators/ajv.ts"],"sourcesContent":["import type { Ajv, FormatDefinition } from \"ajv\"\nimport { CorrectlyError } from \"./types.ts\"\nimport { schemaChildren } from \"./schema-walk.ts\"\n\n/** Synchronous assertions and annotation-only keywords. Registration is explicit. */\nexport type AjvExtension = {\n\tid: string\n\tformats?: Readonly<\n\t\tRecord<string, FormatDefinition<string> | FormatDefinition<number>>\n\t>\n\tannotations?: readonly string[]\n}\n\ntype ExtensionSuggestion = {\n\tid: string\n\tformats: readonly string[]\n\tguidance: string\n}\n// Discovery metadata only: implementations are installed by the consuming project.\nexport const knownExtensions: readonly ExtensionSuggestion[] = [\n\t{\n\t\tid: \"@correctlyjs/schemars\",\n\t\tformats: [\n\t\t\t\"int\",\n\t\t\t\"uint\",\n\t\t\t\"int8\",\n\t\t\t\"uint8\",\n\t\t\t\"int16\",\n\t\t\t\"uint16\",\n\t\t\t\"int32\",\n\t\t\t\"uint32\",\n\t\t\t\"int64\",\n\t\t\t\"uint64\",\n\t\t\t\"int128\",\n\t\t\t\"uint128\",\n\t\t\t\"float\",\n\t\t\t\"double\",\n\t\t\t\"ip\",\n\t\t\t\"phone\",\n\t\t\t\"partial-date-time\",\n\t\t],\n\t\tguidance: `For a Schemars-generated schema, install @correctlyjs/schemars and import { schemars } from \"@correctlyjs/schemars/ajv\". Select the schema's reviewed Schemars version with schemars({ version: \"...\" }) in this Ajv validator's extensions in correctly.config.ts.`,\n\t},\n]\n\nexport function installExtensions(\n\tajv: Ajv,\n\textensions: readonly AjvExtension[],\n): void {\n\tconst formats = new Set<string>(),\n\t\tannotations = new Set<string>()\n\tconst identifiers = new Set<string>()\n\tfor (const extension of extensions) {\n\t\tif (\n\t\t\t!extension ||\n\t\t\ttypeof extension !== \"object\" ||\n\t\t\ttypeof extension.id !== \"string\" ||\n\t\t\t!extension.id\n\t\t)\n\t\t\tthrow new CorrectlyError(\n\t\t\t\t\"extension\",\n\t\t\t\t\"Pass extension implementations to ajv({ extensions: [...] }); string identifiers are not implementations.\",\n\t\t\t)\n\t\tconst id = extension.id\n\t\tif (identifiers.has(id))\n\t\t\tthrow new CorrectlyError(\n\t\t\t\t\"extension\",\n\t\t\t\t`Duplicate extension identifier: ${id}`,\n\t\t\t)\n\t\tidentifiers.add(id)\n\t\tfor (const [name, definition] of Object.entries(extension.formats ?? {})) {\n\t\t\tif (formats.has(name))\n\t\t\t\tthrow new CorrectlyError(\n\t\t\t\t\t\"extension\",\n\t\t\t\t\t`Enabled extensions both define format ${JSON.stringify(name)}.`,\n\t\t\t\t)\n\t\t\tif (definition.async || typeof definition.validate !== \"function\")\n\t\t\t\tthrow new CorrectlyError(\n\t\t\t\t\t\"extension\",\n\t\t\t\t\t`Extension ${id} must provide a synchronous validator for format ${JSON.stringify(name)}.`,\n\t\t\t\t)\n\t\t\tformats.add(name)\n\t\t\tconst validate = definition.validate as (\n\t\t\t\tvalue: string | number,\n\t\t\t) => unknown\n\t\t\tajv.addFormat(name, {\n\t\t\t\t...definition,\n\t\t\t\tvalidate: (value: string | number) => {\n\t\t\t\t\tconst valid = validate(value)\n\t\t\t\t\tif (typeof valid !== \"boolean\")\n\t\t\t\t\t\tthrow new CorrectlyError(\n\t\t\t\t\t\t\t\"extension\",\n\t\t\t\t\t\t\t`Extension ${id} returned a non-boolean result for format ${JSON.stringify(name)}. Validators must be synchronous.`,\n\t\t\t\t\t\t)\n\t\t\t\t\treturn valid\n\t\t\t\t},\n\t\t\t})\n\t\t}\n\t\tfor (const keyword of extension.annotations ?? []) {\n\t\t\tif (annotations.has(keyword) || ajv.RULES.keywords[keyword])\n\t\t\t\tthrow new CorrectlyError(\n\t\t\t\t\t\"extension\",\n\t\t\t\t\t`Extension ${id} conflicts with keyword ${JSON.stringify(keyword)}.`,\n\t\t\t\t)\n\t\t\tannotations.add(keyword)\n\t\t\tajv.addKeyword({ keyword, valid: true })\n\t\t}\n\t}\n}\n\nexport function assertFormats(\n\tajv: Ajv,\n\tschema: unknown,\n\turi: string,\n\tregistry: readonly ExtensionSuggestion[],\n\tlocation = \"\",\n): void {\n\tif (typeof schema !== \"object\" || schema === null || Array.isArray(schema))\n\t\treturn\n\tconst object = schema as Record<string, unknown>\n\tif (\n\t\ttypeof object.format === \"string\" &&\n\t\t(!Object.hasOwn(ajv.formats, object.format) ||\n\t\t\tajv.formats[object.format] === true)\n\t) {\n\t\tconst format = object.format\n\t\tconst extension = registry.find((entry) => entry.formats.includes(format))\n\t\tconst hint =\n\t\t\textension?.guidance ??\n\t\t\t`Pass an extension implementing ${JSON.stringify(format)} to this Ajv validator's extensions in correctly.config.ts.`\n\t\tthrow new CorrectlyError(\n\t\t\t\"extension-required\",\n\t\t\t`This schema needs an extension.\\nFormat ${JSON.stringify(format)} has no enabled validator; validation cannot proceed.\\nSchema: ${uri}#${location}/format\\n${hint}`,\n\t\t\t{\n\t\t\t\tschemaUri: uri,\n\t\t\t\tschemaPointer: `${location}/format`,\n\t\t\t\tformat,\n\t\t\t\t...(extension ? { suggestedExtension: extension.id } : {}),\n\t\t\t},\n\t\t)\n\t}\n\tfor (const [path, child] of schemaChildren(object))\n\t\tassertFormats(ajv, child, uri, registry, location + path)\n}\n","import {\n\tAjv,\n\ttype ErrorObject,\n\ttype ValidateFunction,\n\ttype SchemaObject,\n} from \"ajv\"\nimport { Ajv2020 } from \"ajv/dist/2020.js\"\nimport addFormats from \"ajv-formats\"\nimport { schemaUri } from \"../core/config.ts\"\nimport { diagnostic, pointer, type ParsedDocument } from \"../core/parse.ts\"\nimport { DIALECTS, SchemaStore } from \"../core/schemas.ts\"\nimport { validationContexts } from \"../core/diagnostics.ts\"\nimport {\n\tknownExtensions,\n\tinstallExtensions,\n\tassertFormats,\n\ttype AjvExtension,\n} from \"../core/extensions.ts\"\nimport {\n\tCorrectlyError,\n\tisCorrectlyError,\n\ttype Diagnostic,\n} from \"../core/types.ts\"\nimport type { Validator, ValidationContext } from \"../core/adapters.ts\"\n\nconst ANNOTATIONS = [\n\t\"markdownDescription\",\n\t\"enumDescriptions\",\n\t\"markdownEnumDescriptions\",\n\t\"defaultSnippets\",\n\t\"errorMessage\",\n\t\"patternErrorMessage\",\n\t\"deprecationMessage\",\n\t\"suggestSortText\",\n\t\"doNotSuggest\",\n\t\"allowComments\",\n\t\"allowTrailingCommas\",\n\t\"deprecated\",\n\t\"$vocabulary\",\n]\n\nfunction registerRootAnchors(ajv: Ajv, schema: unknown, uri: string) {\n\tif (typeof schema !== \"object\" || schema === null) return\n\tconst object = schema as SchemaObject\n\tfor (const anchor of [object.$anchor, object.$dynamicAnchor]) {\n\t\tif (typeof anchor === \"string\")\n\t\t\tajv.refs[new URL(`#${anchor}`, object.$id ?? uri).href] =\n\t\t\t\tobject.$id ?? uri\n\t}\n}\n\nfunction validationMessage(error: ErrorObject): string {\n\tif (error.keyword === \"anyOf\")\n\t\treturn \"must match at least one alternative; none matched\"\n\tif (\n\t\terror.keyword === \"if\" &&\n\t\t[\"then\", \"else\"].includes(error.params.failingKeyword as string)\n\t)\n\t\treturn `must satisfy the ${error.params.failingKeyword} branch of the conditional schema`\n\tif (error.keyword === \"uniqueItems\") {\n\t\tconst { i, j } = error.params\n\t\tif (Number.isInteger(i) && Number.isInteger(j))\n\t\t\treturn `duplicates item at ${error.instancePath}${pointer([Math.min(i, j)])}; array items must be unique`\n\t}\n\tif (error.keyword === \"enum\" && Array.isArray(error.params.allowedValues))\n\t\treturn `must be one of: ${error.params.allowedValues.map((value: unknown) => JSON.stringify(value)).join(\", \")}`\n\tif (error.keyword === \"const\" && Object.hasOwn(error.params, \"allowedValue\"))\n\t\treturn `must equal ${JSON.stringify(error.params.allowedValue)}`\n\tif (error.keyword === \"oneOf\") {\n\t\tif (error.params.passingSchemas === null)\n\t\t\treturn \"must match exactly one alternative; none matched\"\n\t\tif (Array.isArray(error.params.passingSchemas))\n\t\t\treturn `must match exactly one alternative; multiple matched (alternatives ${error.params.passingSchemas.map((index: number) => index + 1).join(\", \")})`\n\t}\n\treturn error.message ?? \"Schema violation\"\n}\n\nfunction validationDiagnostic(\n\ttext: string,\n\tparsed: ParsedDocument,\n\terror: ErrorObject,\n): Diagnostic {\n\tlet jsonPointer = error.instancePath\n\tif (\n\t\terror.keyword === \"uniqueItems\" &&\n\t\tNumber.isInteger(error.params.i) &&\n\t\tNumber.isInteger(error.params.j)\n\t)\n\t\tjsonPointer += pointer([Math.max(error.params.i, error.params.j)])\n\tconst property: unknown =\n\t\terror.params.additionalProperty ??\n\t\terror.params.unevaluatedProperty ??\n\t\terror.params.missingProperty ??\n\t\terror.params.propertyName ??\n\t\terror.propertyName\n\tif (typeof property === \"string\") jsonPointer += pointer([property])\n\tconst node = parsed.locate(\n\t\tjsonPointer,\n\t\terror.keyword === \"additionalProperties\" ||\n\t\t\terror.keyword === \"unevaluatedProperties\" ||\n\t\t\terror.keyword === \"propertyNames\" ||\n\t\t\ttypeof error.propertyName === \"string\",\n\t)\n\tconst rangeNode =\n\t\terror.keyword === \"required\" && error.propertyName === undefined\n\t\t\t? parsed.locate(error.instancePath)\n\t\t\t: node\n\tconst length =\n\t\terror.keyword === \"required\" && error.propertyName === undefined\n\t\t\t? 1\n\t\t\t: rangeNode?.length\n\treturn diagnostic(\n\t\ttext,\n\t\t`schema/${error.keyword}`,\n\t\t`${jsonPointer || \"/\"}: ${validationMessage(error)}`,\n\t\tjsonPointer,\n\t\trangeNode?.offset ?? 0,\n\t\tlength ?? 1,\n\t)\n}\n\nasync function compile(\n\tstore: SchemaStore,\n\turi: string,\n\textensions: readonly AjvExtension[],\n): Promise<ValidateFunction> {\n\ttry {\n\t\tconst resource = await store.load(uri)\n\t\tconst options = {\n\t\t\tallErrors: true,\n\t\t\tstrictSchema: true,\n\t\t\tstrictTypes: false,\n\t\t\tstrictTuples: false,\n\t\t\tstrictRequired: false,\n\t\t\tcoerceTypes: false,\n\t\t\tuseDefaults: false,\n\t\t\tremoveAdditional: false,\n\t\t\tvalidateSchema: true,\n\t\t\tloadSchema: async (ref: string) => {\n\t\t\t\tconst loaded = await store.load(ref, resource.dialect)\n\t\t\t\tif (loaded.dialect !== resource.dialect)\n\t\t\t\t\tthrow new CorrectlyError(\n\t\t\t\t\t\t\"unsupported-dialect\",\n\t\t\t\t\t\t`Mixed schema dialects: ${uri} references ${ref}`,\n\t\t\t\t\t)\n\t\t\t\tassertFormats(\n\t\t\t\t\tajv,\n\t\t\t\t\tloaded.schema,\n\t\t\t\t\tloaded.uri,\n\t\t\t\t\tknownExtensions,\n\t\t\t\t\tloaded.pointer,\n\t\t\t\t)\n\t\t\t\tregisterRootAnchors(ajv, loaded.schema, loaded.uri)\n\t\t\t\treturn loaded.schema as SchemaObject\n\t\t\t},\n\t\t}\n\t\tconst ajv =\n\t\t\tresource.dialect === \"2020-12\" ? new Ajv2020(options) : new Ajv(options)\n\t\t// CommonJS package exports retain a callable default at runtime.\n\t\tconst formats = addFormats as unknown as (instance: Ajv) => void\n\t\tformats(ajv)\n\t\t// ajv-formats also ships OpenAPI numeric names. Their width semantics\n\t\t// belong to an explicitly selected extension, not the baseline.\n\t\tfor (const name of [\"int32\", \"int64\", \"float\", \"double\"])\n\t\t\tdelete ajv.formats[name]\n\t\tfor (const keyword of ANNOTATIONS)\n\t\t\tif (!ajv.RULES.keywords[keyword]) ajv.addKeyword({ keyword, valid: true })\n\t\t// Ajv indexes plain anchors during reference resolution, but does not\n\t\t// register the annotation keyword in its strict vocabulary.\n\t\tif (resource.dialect === \"2020-12\")\n\t\t\tajv.addKeyword({\n\t\t\t\tkeyword: \"$anchor\",\n\t\t\t\tschemaType: \"string\",\n\t\t\t\tvalid: true,\n\t\t\t})\n\t\tinstallExtensions(ajv, extensions)\n\t\tassertFormats(\n\t\t\tajv,\n\t\t\tresource.schema,\n\t\t\tresource.uri,\n\t\t\tknownExtensions,\n\t\t\tresource.pointer,\n\t\t)\n\t\tajv.addSchema(resource.schema, resource.uri)\n\t\tregisterRootAnchors(ajv, resource.schema, resource.uri)\n\t\tconst validate = await ajv.compileAsync({\n\t\t\t$schema: DIALECTS[resource.dialect],\n\t\t\t$ref: uri,\n\t\t})\n\t\treturn validate as ValidateFunction\n\t} catch (error) {\n\t\tif (isCorrectlyError(error)) throw error\n\t\tthrow new CorrectlyError(\n\t\t\t\"schema\",\n\t\t\t`Cannot compile schema ${uri}: ${error instanceof Error ? error.message : String(error)}`,\n\t\t)\n\t}\n}\n\nconst storeKey = Symbol.for(\"correctly/ajv/schema-store@1\")\nfunction schemaStore(context: ValidationContext): SchemaStore {\n\tlet store = context.services.get(storeKey) as SchemaStore | undefined\n\tif (!store) {\n\t\tstore = new SchemaStore(\n\t\t\t{\n\t\t\t\troot: context.root,\n\t\t\t\tconfig: context.remote ? { remote: context.remote } : {},\n\t\t\t},\n\t\t\t{\n\t\t\t\tread: context.read,\n\t\t\t\t...(context.fetch ? { fetch: context.fetch } : {}),\n\t\t\t\t...(context.signal ? { signal: context.signal } : {}),\n\t\t\t},\n\t\t)\n\t\tcontext.services.set(storeKey, store)\n\t}\n\treturn store\n}\nexport type AjvOptions = {\n\tschema: string\n\textensions?: readonly AjvExtension[]\n}\nexport function ajv(options: AjvOptions): Validator {\n\tconst extensions = [...(options.extensions ?? [])]\n\treturn {\n\t\tid: \"ajv\",\n\t\taccepts: [\"json\"],\n\t\tschema: options.schema,\n\t\textensions,\n\t\tregister: async (context) => {\n\t\t\tawait schemaStore(context).load(schemaUri(options.schema, context.root))\n\t\t},\n\t\tprepare: async (context) => {\n\t\t\tconst store = schemaStore(context)\n\t\t\tconst uri = schemaUri(options.schema, context.root)\n\t\t\tconst validate = await compile(store, uri, extensions)\n\t\t\treturn {\n\t\t\t\tvalidate: ({ text, parsed }) =>\n\t\t\t\t\tvalidate(parsed.value)\n\t\t\t\t\t\t? []\n\t\t\t\t\t\t: validationContexts(\n\t\t\t\t\t\t\t\tvalidate.errors ?? [],\n\t\t\t\t\t\t\t\t(validate.errors ?? []).map((error) =>\n\t\t\t\t\t\t\t\t\tvalidationDiagnostic(text, parsed, error),\n\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t),\n\t\t\t\teditor: {\n\t\t\t\t\tkind: \"json-schema\",\n\t\t\t\t\turi,\n\t\t\t\t\treadSchema: async (uri) =>\n\t\t\t\t\t\tJSON.stringify((await store.load(uri)).schema),\n\t\t\t\t},\n\t\t\t}\n\t\t},\n\t}\n}\nexport type { AjvExtension } from \"../core/extensions.ts\"\n"],"mappings":"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"}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { c as isIncluded, h as failure, o as discoverConfig, s as exclusions } from "./config-Cb6o4YnJ.mjs";
|
|
2
|
+
import { t as ProjectSession } from "./session-DCFav56x.mjs";
|
|
3
|
+
import { readFile } from "node:fs/promises";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import { glob } from "tinyglobby";
|
|
6
|
+
//#region src/cli/check.ts
|
|
7
|
+
function emptyReport() {
|
|
8
|
+
return {
|
|
9
|
+
reportVersion: 2,
|
|
10
|
+
config: null,
|
|
11
|
+
files: [],
|
|
12
|
+
failures: [],
|
|
13
|
+
summary: {
|
|
14
|
+
checked: 0,
|
|
15
|
+
validated: 0,
|
|
16
|
+
schemaCovered: 0,
|
|
17
|
+
syntaxOnly: 0,
|
|
18
|
+
invalid: 0,
|
|
19
|
+
failures: 0
|
|
20
|
+
},
|
|
21
|
+
exitCode: 0
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
async function check(options = {}) {
|
|
25
|
+
const report = emptyReport();
|
|
26
|
+
let session;
|
|
27
|
+
try {
|
|
28
|
+
const cwd = path.resolve(options.cwd ?? process.cwd());
|
|
29
|
+
const configPath = options.config ? path.resolve(cwd, options.config) : await discoverConfig(cwd);
|
|
30
|
+
report.config = configPath;
|
|
31
|
+
session = new ProjectSession(configPath, options.offline === void 0 ? {} : { offline: options.offline });
|
|
32
|
+
const result = await session.check({
|
|
33
|
+
cwd,
|
|
34
|
+
...options.files ? { files: options.files } : {},
|
|
35
|
+
sources: Boolean(options.onRead)
|
|
36
|
+
});
|
|
37
|
+
for (const [file, text] of result.sources) options.onRead?.(file, text);
|
|
38
|
+
return result.report;
|
|
39
|
+
} catch (error) {
|
|
40
|
+
report.failures.push(failure(error));
|
|
41
|
+
report.summary.failures = 1;
|
|
42
|
+
report.exitCode = 2;
|
|
43
|
+
return report;
|
|
44
|
+
} finally {
|
|
45
|
+
await session?.dispose();
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
async function checkProject(engine, options) {
|
|
49
|
+
const report = emptyReport();
|
|
50
|
+
const project = engine.project;
|
|
51
|
+
report.config = project.configPath;
|
|
52
|
+
try {
|
|
53
|
+
const cwd = path.resolve(options.cwd ?? process.cwd());
|
|
54
|
+
await engine.prepare();
|
|
55
|
+
const files = options.files?.length ? options.files.map((f) => path.resolve(cwd, f)) : await glob(project.config.files ?? ["**/*.json", "**/*.jsonc"], {
|
|
56
|
+
cwd: project.root,
|
|
57
|
+
ignore: exclusions(project),
|
|
58
|
+
absolute: true,
|
|
59
|
+
dot: true,
|
|
60
|
+
onlyFiles: true
|
|
61
|
+
});
|
|
62
|
+
for (const file of [...new Set(files)].sort()) try {
|
|
63
|
+
const included = isIncluded(project, file);
|
|
64
|
+
const text = included ? await readFile(file, "utf8") : "";
|
|
65
|
+
if (included) options.onRead?.(file, text);
|
|
66
|
+
report.files.push(await engine.validate(file, text));
|
|
67
|
+
} catch (error) {
|
|
68
|
+
report.failures.push(failure(error, file));
|
|
69
|
+
}
|
|
70
|
+
} catch (error) {
|
|
71
|
+
report.failures.push(failure(error));
|
|
72
|
+
}
|
|
73
|
+
const checked = report.files.filter((f) => f.coverage !== "excluded");
|
|
74
|
+
report.summary = {
|
|
75
|
+
checked: checked.length,
|
|
76
|
+
validated: checked.filter((f) => f.coverage === "schema" || f.coverage === "validated").length,
|
|
77
|
+
schemaCovered: checked.filter((f) => f.coverage === "schema").length,
|
|
78
|
+
syntaxOnly: checked.filter((f) => f.coverage === "syntax-only").length,
|
|
79
|
+
invalid: checked.filter((f) => f.diagnostics.length > 0).length,
|
|
80
|
+
failures: report.failures.length + report.files.reduce((n, f) => n + f.failures.length, 0)
|
|
81
|
+
};
|
|
82
|
+
report.exitCode = report.summary.failures ? 2 : report.summary.invalid ? 1 : 0;
|
|
83
|
+
return report;
|
|
84
|
+
}
|
|
85
|
+
//#endregion
|
|
86
|
+
export { checkProject as n, check as t };
|
|
87
|
+
|
|
88
|
+
//# sourceMappingURL=check-BI7U3ZLc.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"check-BI7U3ZLc.mjs","names":[],"sources":["../src/cli/check.ts"],"sourcesContent":["import { readFile } from \"node:fs/promises\"\nimport path from \"node:path\"\nimport { glob } from \"tinyglobby\"\nimport { discoverConfig, exclusions, isIncluded } from \"../core/config.ts\"\nimport { ProjectSession } from \"../runtime/session.ts\"\nimport { Engine } from \"../core/engine.ts\"\nimport { failure, type Report } from \"../core/types.ts\"\n\nexport type CheckOptions = {\n\tcwd?: string\n\tconfig?: string\n\toffline?: boolean\n\tfiles?: string[]\n\tonRead?: (file: string, text: string) => void\n}\nfunction emptyReport(): Report {\n\tconst report: Report = {\n\t\treportVersion: 2,\n\t\tconfig: null,\n\t\tfiles: [],\n\t\tfailures: [],\n\t\tsummary: {\n\t\t\tchecked: 0,\n\t\t\tvalidated: 0,\n\t\t\tschemaCovered: 0,\n\t\t\tsyntaxOnly: 0,\n\t\t\tinvalid: 0,\n\t\t\tfailures: 0,\n\t\t},\n\t\texitCode: 0,\n\t}\n\treturn report\n}\nexport async function check(options: CheckOptions = {}): Promise<Report> {\n\tconst report = emptyReport()\n\tlet session: ProjectSession | undefined\n\ttry {\n\t\tconst cwd = path.resolve(options.cwd ?? process.cwd())\n\t\tconst configPath = options.config\n\t\t\t? path.resolve(cwd, options.config)\n\t\t\t: await discoverConfig(cwd)\n\t\treport.config = configPath\n\t\tsession = new ProjectSession(\n\t\t\tconfigPath,\n\t\t\toptions.offline === undefined ? {} : { offline: options.offline },\n\t\t)\n\t\tconst result = await session.check({\n\t\t\tcwd,\n\t\t\t...(options.files ? { files: options.files } : {}),\n\t\t\tsources: Boolean(options.onRead),\n\t\t})\n\t\tfor (const [file, text] of result.sources) options.onRead?.(file, text)\n\t\treturn result.report\n\t} catch (error) {\n\t\treport.failures.push(failure(error))\n\t\treport.summary.failures = 1\n\t\treport.exitCode = 2\n\t\treturn report\n\t} finally {\n\t\tawait session?.dispose()\n\t}\n}\nexport async function checkProject(\n\tengine: Engine,\n\toptions: CheckOptions,\n): Promise<Report> {\n\tconst report = emptyReport()\n\tconst project = engine.project\n\treport.config = project.configPath\n\n\ttry {\n\t\tconst cwd = path.resolve(options.cwd ?? process.cwd())\n\t\tawait engine.prepare()\n\t\tconst files = options.files?.length\n\t\t\t? options.files.map((f) => path.resolve(cwd, f))\n\t\t\t: await glob(project.config.files ?? [\"**/*.json\", \"**/*.jsonc\"], {\n\t\t\t\t\tcwd: project.root,\n\t\t\t\t\tignore: exclusions(project),\n\t\t\t\t\tabsolute: true,\n\t\t\t\t\tdot: true,\n\t\t\t\t\tonlyFiles: true,\n\t\t\t\t})\n\t\tfor (const file of [...new Set(files)].sort()) {\n\t\t\ttry {\n\t\t\t\tconst included = isIncluded(project, file)\n\t\t\t\tconst text = included ? await readFile(file, \"utf8\") : \"\"\n\t\t\t\tif (included) options.onRead?.(file, text)\n\t\t\t\treport.files.push(await engine.validate(file, text))\n\t\t\t} catch (error) {\n\t\t\t\treport.failures.push(failure(error, file))\n\t\t\t}\n\t\t}\n\t} catch (error) {\n\t\treport.failures.push(failure(error))\n\t}\n\tconst checked = report.files.filter((f) => f.coverage !== \"excluded\")\n\treport.summary = {\n\t\tchecked: checked.length,\n\t\tvalidated: checked.filter(\n\t\t\t(f) => f.coverage === \"schema\" || f.coverage === \"validated\",\n\t\t).length,\n\t\tschemaCovered: checked.filter((f) => f.coverage === \"schema\").length,\n\t\tsyntaxOnly: checked.filter((f) => f.coverage === \"syntax-only\").length,\n\t\tinvalid: checked.filter((f) => f.diagnostics.length > 0).length,\n\t\tfailures:\n\t\t\treport.failures.length +\n\t\t\treport.files.reduce((n, f) => n + f.failures.length, 0),\n\t}\n\treport.exitCode = report.summary.failures ? 2 : report.summary.invalid ? 1 : 0\n\treturn report\n}\n\nexport { readableReport } from \"./readable.ts\"\n"],"mappings":"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"}
|
package/dist/cli.mjs
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
2
|
+
import { h as failure } from "./config-Cb6o4YnJ.mjs";
|
|
3
|
+
import { n as diagnosticViews } from "./diagnostics-BqD1WOy4.mjs";
|
|
4
|
+
import { t as check } from "./check-BI7U3ZLc.mjs";
|
|
3
5
|
import { parseArgs, styleText } from "node:util";
|
|
4
|
-
import { readFile } from "node:fs/promises";
|
|
5
6
|
import path from "node:path";
|
|
6
|
-
import { glob } from "tinyglobby";
|
|
7
7
|
import { fileURLToPath } from "node:url";
|
|
8
8
|
//#region src/cli/readable.ts
|
|
9
9
|
function createStyler(color) {
|
|
@@ -70,6 +70,10 @@ function excerpt(source, diagnostic, diagnosedLines, styler) {
|
|
|
70
70
|
width
|
|
71
71
|
};
|
|
72
72
|
}
|
|
73
|
+
function renderFailure(error, styler, prefix = "", continuation = "") {
|
|
74
|
+
const [title, ...details] = error.message.split("\n");
|
|
75
|
+
return [`${prefix}${styler.bad(error.code)}: ${title}`, ...details.map((line) => `${styler.dim(continuation + "│")} ${line}`)].join("\n");
|
|
76
|
+
}
|
|
73
77
|
function renderDiagnostic(issue, prefix, last, source, diagnosedLines, styler, shownRegions = /* @__PURE__ */ new Set()) {
|
|
74
78
|
const branch = last ? "└─" : "├─";
|
|
75
79
|
const continuation = `${prefix}${last ? " " : "│ "}`;
|
|
@@ -99,7 +103,7 @@ function fileSection(group, options, styler) {
|
|
|
99
103
|
const { mode, coverage, association } = group.result;
|
|
100
104
|
const format = mode.toUpperCase();
|
|
101
105
|
const schema = association?.schema;
|
|
102
|
-
const description = coverage === "excluded" ? format : schema ? `${format} against ${association.name} (${displaySchema(cwd, schema)})` : `${format} syntax only (${association ? `association: ${association.name}` : "no schema association"})`;
|
|
106
|
+
const description = coverage === "excluded" ? format : schema ? `${format} against ${association.name} (${displaySchema(cwd, schema)})` : association?.validator ? `${format} validated by ${association.validator}` : `${format} syntax only (${association ? `association: ${association.name}` : "no schema association"})`;
|
|
103
107
|
header += ` ${styler.dim(`· ${description}`)}`;
|
|
104
108
|
}
|
|
105
109
|
const output = [header];
|
|
@@ -112,7 +116,7 @@ function fileSection(group, options, styler) {
|
|
|
112
116
|
const last = index === issues.length - 1;
|
|
113
117
|
const branch = last ? "└─" : "├─";
|
|
114
118
|
if ("range" in issue) output.push(...renderDiagnostic(issue, "", last, source, diagnosedLines, styler));
|
|
115
|
-
else output.push(`${styler.dim(branch)}
|
|
119
|
+
else output.push(renderFailure(issue, styler, `${styler.dim(branch)} `, last ? " " : "│ "));
|
|
116
120
|
if (!last) output.push(styler.dim("│"));
|
|
117
121
|
}
|
|
118
122
|
return output.join("\n");
|
|
@@ -142,7 +146,7 @@ function readableReport(report, options = {}) {
|
|
|
142
146
|
group.failures.push(error);
|
|
143
147
|
}
|
|
144
148
|
const output = [...groups.values()].toSorted((a, b) => a.file < b.file ? -1 : a.file > b.file ? 1 : 0).map((group) => fileSection(group, options, styler));
|
|
145
|
-
for (const error of globalFailures) output.push(
|
|
149
|
+
for (const error of globalFailures) output.push(renderFailure(error, styler));
|
|
146
150
|
const errors = report.files.reduce((count, file) => count + diagnosticViews(file.diagnostics).length, 0);
|
|
147
151
|
const s = report.summary;
|
|
148
152
|
const status = report.exitCode === 2 ? `▲ Check failed with ${countLabel(s.failures, "failure")}` : report.exitCode === 1 ? `▲ Check found ${countLabel(errors, "error")} in ${countLabel(s.invalid, "file")}` : "✓ Check passed";
|
|
@@ -151,69 +155,16 @@ function readableReport(report, options = {}) {
|
|
|
151
155
|
"",
|
|
152
156
|
(report.exitCode === 2 ? styler.bad : report.exitCode === 1 ? styler.warning : styler.success)(status),
|
|
153
157
|
"",
|
|
154
|
-
`${s.checked} checked, ${s.schemaCovered} schema-covered, ${s.syntaxOnly} syntax-only, ${s.invalid} invalid, ${s.failures} failures`
|
|
158
|
+
`${s.checked} checked, ${s.validated} validated, ${s.schemaCovered} schema-covered, ${s.syntaxOnly} syntax-only, ${s.invalid} invalid, ${s.failures} failures`
|
|
155
159
|
].join("\n"));
|
|
156
160
|
return output.join("\n\n");
|
|
157
161
|
}
|
|
158
162
|
//#endregion
|
|
159
|
-
//#region src/cli/check.ts
|
|
160
|
-
async function check(options = {}) {
|
|
161
|
-
const report = {
|
|
162
|
-
reportVersion: 1,
|
|
163
|
-
config: null,
|
|
164
|
-
files: [],
|
|
165
|
-
failures: [],
|
|
166
|
-
summary: {
|
|
167
|
-
checked: 0,
|
|
168
|
-
schemaCovered: 0,
|
|
169
|
-
syntaxOnly: 0,
|
|
170
|
-
invalid: 0,
|
|
171
|
-
failures: 0
|
|
172
|
-
},
|
|
173
|
-
exitCode: 0
|
|
174
|
-
};
|
|
175
|
-
try {
|
|
176
|
-
const cwd = path.resolve(options.cwd ?? process.cwd());
|
|
177
|
-
const configPath = options.config ? path.resolve(cwd, options.config) : await discoverConfig(cwd);
|
|
178
|
-
report.config = configPath;
|
|
179
|
-
const project = await loadProject(configPath);
|
|
180
|
-
const engine = new Engine(project, options.offline === void 0 ? {} : { offline: options.offline });
|
|
181
|
-
await engine.prepare();
|
|
182
|
-
const files = options.files?.length ? options.files.map((f) => path.resolve(cwd, f)) : await glob(project.config.files ?? ["**/*.json", "**/*.jsonc"], {
|
|
183
|
-
cwd: project.root,
|
|
184
|
-
ignore: exclusions(project),
|
|
185
|
-
absolute: true,
|
|
186
|
-
dot: true,
|
|
187
|
-
onlyFiles: true
|
|
188
|
-
});
|
|
189
|
-
for (const file of [...new Set(files)].sort()) try {
|
|
190
|
-
const included = isIncluded(project, file);
|
|
191
|
-
const text = included ? await readFile(file, "utf8") : "";
|
|
192
|
-
if (included) options.onRead?.(file, text);
|
|
193
|
-
report.files.push(await engine.validate(file, text));
|
|
194
|
-
} catch (error) {
|
|
195
|
-
report.failures.push(failure(error, file));
|
|
196
|
-
}
|
|
197
|
-
} catch (error) {
|
|
198
|
-
report.failures.push(failure(error));
|
|
199
|
-
}
|
|
200
|
-
const checked = report.files.filter((f) => f.coverage !== "excluded");
|
|
201
|
-
report.summary = {
|
|
202
|
-
checked: checked.length,
|
|
203
|
-
schemaCovered: checked.filter((f) => f.coverage === "schema").length,
|
|
204
|
-
syntaxOnly: checked.filter((f) => f.coverage === "syntax-only").length,
|
|
205
|
-
invalid: checked.filter((f) => f.diagnostics.length > 0).length,
|
|
206
|
-
failures: report.failures.length + report.files.reduce((n, f) => n + f.failures.length, 0)
|
|
207
|
-
};
|
|
208
|
-
report.exitCode = report.summary.failures ? 2 : report.summary.invalid ? 1 : 0;
|
|
209
|
-
return report;
|
|
210
|
-
}
|
|
211
|
-
//#endregion
|
|
212
163
|
//#region src/cli/main.ts
|
|
213
164
|
const HELP = `correctly check [files...] [--config path] [--offline] [--format readable|json]
|
|
214
165
|
correctly-lsp --stdio
|
|
215
166
|
|
|
216
|
-
Validate JSON/JSONC using associations in correctly.config.
|
|
167
|
+
Validate JSON/JSONC using associations in correctly.config.ts.
|
|
217
168
|
Exit 0: valid; 1: document errors; 2: configuration, schema, or execution errors.
|
|
218
169
|
Formatting does not affect validation. See the package guide for configuration.
|
|
219
170
|
`;
|
|
@@ -266,12 +217,13 @@ async function run(args, cwd = process.cwd()) {
|
|
|
266
217
|
};
|
|
267
218
|
} catch (error) {
|
|
268
219
|
const report = {
|
|
269
|
-
reportVersion:
|
|
220
|
+
reportVersion: 2,
|
|
270
221
|
config: null,
|
|
271
222
|
files: [],
|
|
272
223
|
failures: [failure(error)],
|
|
273
224
|
summary: {
|
|
274
225
|
checked: 0,
|
|
226
|
+
validated: 0,
|
|
275
227
|
schemaCovered: 0,
|
|
276
228
|
syntaxOnly: 0,
|
|
277
229
|
invalid: 0,
|