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.
Files changed (68) hide show
  1. package/AGENTS.md +6 -2
  2. package/README.md +21 -15
  3. package/dist/ajv.d.mts +11 -0
  4. package/dist/ajv.d.mts.map +1 -0
  5. package/dist/ajv.mjs +221 -0
  6. package/dist/ajv.mjs.map +1 -0
  7. package/dist/check-BI7U3ZLc.mjs +88 -0
  8. package/dist/check-BI7U3ZLc.mjs.map +1 -0
  9. package/dist/cli.mjs +14 -62
  10. package/dist/cli.mjs.map +1 -1
  11. package/dist/config-Bx1-XXeo.d.mts +189 -0
  12. package/dist/config-Bx1-XXeo.d.mts.map +1 -0
  13. package/dist/config-Cb6o4YnJ.mjs +348 -0
  14. package/dist/config-Cb6o4YnJ.mjs.map +1 -0
  15. package/dist/core.d.mts +27 -125
  16. package/dist/core.d.mts.map +1 -1
  17. package/dist/core.mjs +5 -2
  18. package/dist/diagnostics-BqD1WOy4.mjs +70 -0
  19. package/dist/diagnostics-BqD1WOy4.mjs.map +1 -0
  20. package/dist/engine-VST0hoSk.mjs +111 -0
  21. package/dist/engine-VST0hoSk.mjs.map +1 -0
  22. package/dist/extensions-ClclWslY.d.mts +11 -0
  23. package/dist/extensions-ClclWslY.d.mts.map +1 -0
  24. package/dist/hints-HvSIhuMM.mjs +57 -0
  25. package/dist/hints-HvSIhuMM.mjs.map +1 -0
  26. package/dist/lsp.mjs +40 -91
  27. package/dist/lsp.mjs.map +1 -1
  28. package/dist/renovate.d.mts +5 -0
  29. package/dist/renovate.d.mts.map +1 -0
  30. package/dist/renovate.mjs +11 -0
  31. package/dist/renovate.mjs.map +1 -0
  32. package/dist/schemas-UxCtIdo2.mjs +265 -0
  33. package/dist/schemas-UxCtIdo2.mjs.map +1 -0
  34. package/dist/session-DCFav56x.mjs +113 -0
  35. package/dist/session-DCFav56x.mjs.map +1 -0
  36. package/dist/worker.mjs +80 -0
  37. package/dist/worker.mjs.map +1 -0
  38. package/docs/guide.md +166 -34
  39. package/examples/basic/correctly.config.ts +13 -0
  40. package/package.json +69 -60
  41. package/src/cli/check.ts +41 -11
  42. package/src/cli/main.ts +3 -2
  43. package/src/cli/readable.ts +24 -5
  44. package/src/cli/vsix.ts +1 -0
  45. package/src/core/adapters.ts +54 -0
  46. package/src/core/config.ts +201 -64
  47. package/src/core/engine.ts +118 -218
  48. package/src/core/extensions.ts +144 -0
  49. package/src/core/index.ts +3 -0
  50. package/src/core/parse.ts +26 -2
  51. package/src/core/parsers.ts +22 -0
  52. package/src/core/schema-walk.ts +43 -0
  53. package/src/core/schemas.ts +21 -41
  54. package/src/core/types.ts +33 -6
  55. package/src/extensions/renovate.ts +4 -0
  56. package/src/lsp/hints.ts +22 -12
  57. package/src/lsp/server.ts +9 -2
  58. package/src/lsp/workspace.ts +49 -54
  59. package/src/runtime/session.ts +173 -0
  60. package/src/runtime/worker.ts +99 -0
  61. package/src/validators/ajv.ts +257 -0
  62. package/src/vscode/README.md +1 -1
  63. package/src/vscode/extension.ts +2 -4
  64. package/src/vscode/package.json +9 -2
  65. package/dist/engine-BgWBnb1A.mjs +0 -746
  66. package/dist/engine-BgWBnb1A.mjs.map +0 -1
  67. package/examples/basic/correctly.config.json +0 -10
  68. package/src/core/config.schema.json +0 -47
package/dist/lsp.mjs CHANGED
@@ -1,37 +1,35 @@
1
1
  #!/usr/bin/env node
2
- import { b as diagnostic, d as discoverConfig, g as readText, l as associationFor, m as loadProject, n as diagnosticDetails, p as isIncluded, r as diagnosticViews, t as Engine, u as contains, v as CorrectlyError, y as failure } from "./engine-BgWBnb1A.mjs";
2
+ import { b as diagnostic, h as failure, i as contains, m as CorrectlyError, o as discoverConfig } from "./config-Cb6o4YnJ.mjs";
3
+ import { t as ProjectSession } from "./session-DCFav56x.mjs";
4
+ import { n as diagnosticViews, t as diagnosticDetails } from "./diagnostics-BqD1WOy4.mjs";
5
+ import { t as Hints } from "./hints-HvSIhuMM.mjs";
3
6
  import path from "node:path";
4
7
  import { fileURLToPath, pathToFileURL } from "node:url";
5
8
  import { DidChangeWatchedFilesNotification, LSPErrorCodes, ResponseError, TextDocumentSyncKind, TextDocuments, createConnection } from "vscode-languageserver/node";
6
9
  import { TextDocument } from "vscode-languageserver-textdocument";
7
- import { getLanguageService } from "vscode-json-languageservice";
8
10
  //#region src/lsp/workspace.ts
9
11
  var Workspace = class {
10
12
  onSchema;
11
13
  roots;
12
- read;
13
14
  documents = /* @__PURE__ */ new Map();
14
15
  engines = /* @__PURE__ */ new Map();
15
- controller = new AbortController();
16
16
  generation = 0;
17
- constructor(roots, read = readText) {
17
+ constructor(roots) {
18
18
  this.roots = roots;
19
- this.read = read;
20
19
  }
21
20
  invalidate() {
22
21
  this.generation++;
23
- this.controller.abort();
24
- this.controller = new AbortController();
22
+ for (const session of this.engines.values()) session.dispose();
25
23
  this.engines.clear();
26
24
  }
27
25
  open(document) {
28
26
  this.documents.set(document.uri, document);
29
- const resource = path.basename(fileURLToPath(document.uri)) === "correctly.config.json" || this.isSchema(document.uri);
27
+ const resource = this.isSchema(document.uri) && !this.moduleUris.has(document.uri);
30
28
  if (resource) this.invalidate();
31
29
  return resource;
32
30
  }
33
31
  close(uri) {
34
- const resource = uri.endsWith("/correctly.config.json") || this.isSchema(uri);
32
+ const resource = this.isSchema(uri) && !this.moduleUris.has(uri);
35
33
  this.documents.delete(uri);
36
34
  if (resource) this.invalidate();
37
35
  return resource;
@@ -40,47 +38,42 @@ var Workspace = class {
40
38
  isSchema(uri) {
41
39
  return this.schemaUris.has(uri);
42
40
  }
41
+ moduleUris = /* @__PURE__ */ new Set();
43
42
  resourceChanged(uri) {
44
- return uri.endsWith("/correctly.config.json") || this.isSchema(uri);
43
+ return uri.endsWith(`/correctly.config.ts`) || this.isSchema(uri) || this.moduleUris.has(uri) || /\/(?:package\.json|pnpm-lock\.yaml|package-lock\.json|yarn\.lock)$/.test(uri);
45
44
  }
46
- readBuffer = (uri) => {
47
- const document = this.documents.get(uri);
48
- return document ? Promise.resolve(document.getText()) : this.read(uri);
49
- };
50
45
  async engineFor(uri) {
51
46
  const file = fileURLToPath(uri);
52
47
  const root = this.roots.filter((r) => contains(r, file)).sort((a, b) => b.length - a.length)[0];
53
48
  if (this.roots.length && !root) throw new CorrectlyError("config", `Document is outside the editor workspace: ${file}`);
54
- const virtualConfigs = new Set([...this.documents.keys()].filter((key) => key.endsWith("/correctly.config.json")).map((key) => fileURLToPath(key)));
55
- const configPath = await discoverConfig(path.dirname(file), root, virtualConfigs);
56
- let promise = this.engines.get(configPath);
57
- if (!promise) {
58
- const signal = this.controller.signal;
59
- promise = (async () => {
60
- const project = await loadProject(configPath, this.readBuffer);
61
- const readSchema = async (schema) => {
62
- this.schemaUris.add(schema);
63
- this.onSchema?.(schema);
64
- return this.readBuffer(schema);
65
- };
66
- const engine = new Engine(project, {
67
- read: readSchema,
68
- signal
69
- });
70
- await engine.prepare();
71
- for (const resource of engine.store.resources.values()) this.schemaUris.add(resource.uri);
72
- signal.throwIfAborted();
73
- return engine;
74
- })();
75
- this.engines.set(configPath, promise);
49
+ const configPath = await discoverConfig(path.dirname(file), root);
50
+ let session = this.engines.get(configPath);
51
+ if (!session) {
52
+ session = new ProjectSession(configPath, {
53
+ buffers: [...this.documents].map(([uri, document]) => [uri, document.getText()]),
54
+ onDependency: (dependency, kind) => {
55
+ (kind === "module" ? this.moduleUris : this.schemaUris).add(dependency);
56
+ this.onSchema?.(dependency);
57
+ }
58
+ });
59
+ this.engines.set(configPath, session);
76
60
  }
77
- return promise;
61
+ await session.ready;
62
+ return session;
78
63
  }
79
64
  async validate(document) {
80
65
  try {
81
66
  return await (await this.engineFor(document.uri)).validate(fileURLToPath(document.uri), document.getText());
82
67
  } catch (error) {
83
68
  const problem = failure(error, fileURLToPath(document.uri));
69
+ if (problem.code === "config" && problem.message.startsWith(`No correctly.config.ts found`) && !["json", "jsonc"].includes(document.languageId)) return {
70
+ file: fileURLToPath(document.uri),
71
+ mode: document.languageId,
72
+ association: null,
73
+ coverage: "excluded",
74
+ diagnostics: [],
75
+ failures: []
76
+ };
84
77
  return {
85
78
  file: fileURLToPath(document.uri),
86
79
  mode: document.languageId === "jsonc" ? "jsonc" : "json",
@@ -99,57 +92,6 @@ var Workspace = class {
99
92
  }
100
93
  };
101
94
  //#endregion
102
- //#region src/lsp/hints.ts
103
- function externalSelection(document) {
104
- return new Proxy(document, { get(target, key) {
105
- if (key === "root") return void 0;
106
- const value = Reflect.get(target, key);
107
- return typeof value === "function" ? value.bind(target) : value;
108
- } });
109
- }
110
- var Hints = class {
111
- services = /* @__PURE__ */ new WeakMap();
112
- async service(engine, document) {
113
- const file = fileURLToPath(document.uri);
114
- if (!isIncluded(engine.project, file)) return void 0;
115
- const association = associationFor(engine.project, file);
116
- if (!association?.schema) return void 0;
117
- await engine.validator(association.schema);
118
- let services = this.services.get(engine);
119
- if (!services) {
120
- services = /* @__PURE__ */ new Map();
121
- this.services.set(engine, services);
122
- }
123
- let service = services.get(association.schema);
124
- if (!service) {
125
- service = getLanguageService({
126
- schemaRequestService: async (uri) => JSON.stringify((await engine.store.load(uri)).schema),
127
- workspaceContext: { resolveRelativePath: (relative, resource) => new URL(relative, resource).href }
128
- });
129
- service.configure({
130
- validate: false,
131
- schemas: [{
132
- uri: association.schema,
133
- fileMatch: ["*"]
134
- }]
135
- });
136
- services.set(association.schema, service);
137
- }
138
- return service;
139
- }
140
- async complete(engine, document, position) {
141
- const service = await this.service(engine, document);
142
- return service ? await service.doComplete(document, position, externalSelection(service.parseJSONDocument(document))) : {
143
- isIncomplete: false,
144
- items: []
145
- };
146
- }
147
- async hover(engine, document, position) {
148
- const service = await this.service(engine, document);
149
- return service ? await service.doHover(document, position, externalSelection(service.parseJSONDocument(document))) : null;
150
- }
151
- };
152
- //#endregion
153
95
  //#region src/lsp/server.ts
154
96
  function lspDiagnostics(result, text) {
155
97
  return diagnosticViews([...result.diagnostics, ...result.failures.filter((f) => !result.diagnostics.some((d) => d.code === f.code && d.message === f.message)).map((f) => diagnostic(text, f.code, f.message))]).map((d) => ({
@@ -190,7 +132,11 @@ function startServer() {
190
132
  dynamicWatch = params.capabilities.workspace?.didChangeWatchedFiles?.dynamicRegistration ?? false;
191
133
  return {
192
134
  capabilities: {
193
- textDocumentSync: TextDocumentSyncKind.Incremental,
135
+ textDocumentSync: {
136
+ openClose: true,
137
+ change: TextDocumentSyncKind.Incremental,
138
+ save: { includeText: false }
139
+ },
194
140
  completionProvider: { triggerCharacters: ["\"", ":"] },
195
141
  hoverProvider: true,
196
142
  workspace: { workspaceFolders: {
@@ -205,7 +151,7 @@ function startServer() {
205
151
  };
206
152
  });
207
153
  connection.onInitialized(() => {
208
- if (dynamicWatch) connection.client.register(DidChangeWatchedFilesNotification.type, { watchers: [{ globPattern: "**/*.{json,jsonc}" }] }).catch((error) => connection.console.error(String(error)));
154
+ if (dynamicWatch) connection.client.register(DidChangeWatchedFilesNotification.type, { watchers: [{ globPattern: "**/*" }] }).catch((error) => connection.console.error(String(error)));
209
155
  connection.workspace.onDidChangeWorkspaceFolders((event) => {
210
156
  const removed = new Set(event.removed.map((f) => fileURLToPath(f.uri)));
211
157
  workspace.roots = [...workspace.roots.filter((r) => !removed.has(r)), ...event.added.map((f) => fileURLToPath(f.uri))];
@@ -261,6 +207,9 @@ function startServer() {
261
207
  });
262
208
  if (document.uri.startsWith("file:") && workspace.close(document.uri)) scheduleAll();
263
209
  });
210
+ documents.onDidSave(({ document }) => {
211
+ if (workspace.resourceChanged(document.uri)) refresh();
212
+ });
264
213
  connection.onDidChangeWatchedFiles((params) => {
265
214
  if (params.changes.some((change) => workspace.resourceChanged(change.uri))) refresh();
266
215
  });
package/dist/lsp.mjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"lsp.mjs","names":[],"sources":["../src/lsp/workspace.ts","../src/lsp/hints.ts","../src/lsp/server.ts"],"sourcesContent":["import path from \"node:path\"\nimport { fileURLToPath, pathToFileURL } from \"node:url\"\nimport { TextDocument } from \"vscode-languageserver-textdocument\"\nimport {\n\tcontains,\n\tdiscoverConfig,\n\tloadProject,\n\treadText,\n\ttype ReadText,\n} from \"../core/config.ts\"\nimport { Engine } from \"../core/engine.ts\"\nimport { CorrectlyError, failure, type FileResult } from \"../core/types.ts\"\nimport { diagnostic } from \"../core/parse.ts\"\n\nexport class Workspace {\n\tonSchema: ((uri: string) => void) | undefined\n\troots: string[]\n\tprivate readonly read: ReadText\n\treadonly documents = new Map<string, TextDocument>()\n\tprivate readonly engines = new Map<string, Promise<Engine>>()\n\tprivate controller = new AbortController()\n\tgeneration = 0\n\tconstructor(roots: string[], read: ReadText = readText) {\n\t\tthis.roots = roots\n\t\tthis.read = read\n\t}\n\n\tinvalidate() {\n\t\tthis.generation++\n\t\tthis.controller.abort()\n\t\tthis.controller = new AbortController()\n\t\tthis.engines.clear()\n\t}\n\n\topen(document: TextDocument): boolean {\n\t\tthis.documents.set(document.uri, document)\n\t\tconst resource =\n\t\t\tpath.basename(fileURLToPath(document.uri)) === \"correctly.config.json\" ||\n\t\t\tthis.isSchema(document.uri)\n\t\tif (resource) this.invalidate()\n\t\treturn resource\n\t}\n\tclose(uri: string): boolean {\n\t\tconst resource =\n\t\t\turi.endsWith(\"/correctly.config.json\") || this.isSchema(uri)\n\t\tthis.documents.delete(uri)\n\t\tif (resource) this.invalidate()\n\t\treturn resource\n\t}\n\tprivate readonly schemaUris = new Set<string>()\n\tprivate isSchema(uri: string): boolean {\n\t\treturn this.schemaUris.has(uri)\n\t}\n\tresourceChanged(uri: string): boolean {\n\t\treturn uri.endsWith(\"/correctly.config.json\") || this.isSchema(uri)\n\t}\n\tprivate readonly readBuffer: ReadText = (uri) => {\n\t\tconst document = this.documents.get(uri)\n\t\treturn document ? Promise.resolve(document.getText()) : this.read(uri)\n\t}\n\n\tasync engineFor(uri: string): Promise<Engine> {\n\t\tconst file = fileURLToPath(uri)\n\t\tconst root = this.roots\n\t\t\t.filter((r) => contains(r, file))\n\t\t\t.sort((a, b) => b.length - a.length)[0]\n\t\tif (this.roots.length && !root)\n\t\t\tthrow new CorrectlyError(\n\t\t\t\t\"config\",\n\t\t\t\t`Document is outside the editor workspace: ${file}`,\n\t\t\t)\n\t\tconst virtualConfigs = new Set(\n\t\t\t[...this.documents.keys()]\n\t\t\t\t.filter((key) => key.endsWith(\"/correctly.config.json\"))\n\t\t\t\t.map((key) => fileURLToPath(key)),\n\t\t)\n\t\tconst configPath = await discoverConfig(\n\t\t\tpath.dirname(file),\n\t\t\troot,\n\t\t\tvirtualConfigs,\n\t\t)\n\t\tlet promise = this.engines.get(configPath)\n\t\tif (!promise) {\n\t\t\tconst signal = this.controller.signal\n\t\t\tpromise = (async () => {\n\t\t\t\tconst project = await loadProject(configPath, this.readBuffer)\n\t\t\t\tconst readSchema: ReadText = async (schema) => {\n\t\t\t\t\tthis.schemaUris.add(schema)\n\t\t\t\t\tthis.onSchema?.(schema)\n\t\t\t\t\treturn this.readBuffer(schema)\n\t\t\t\t}\n\t\t\t\tconst engine = new Engine(project, { read: readSchema, signal })\n\t\t\t\tawait engine.prepare()\n\t\t\t\tfor (const resource of engine.store.resources.values())\n\t\t\t\t\tthis.schemaUris.add(resource.uri)\n\t\t\t\tsignal.throwIfAborted()\n\t\t\t\treturn engine\n\t\t\t})()\n\t\t\tthis.engines.set(configPath, promise)\n\t\t}\n\t\treturn promise\n\t}\n\n\tasync validate(document: TextDocument): Promise<FileResult> {\n\t\ttry {\n\t\t\tconst engine = await this.engineFor(document.uri)\n\t\t\treturn await engine.validate(\n\t\t\t\tfileURLToPath(document.uri),\n\t\t\t\tdocument.getText(),\n\t\t\t)\n\t\t} catch (error) {\n\t\t\tconst problem = failure(error, fileURLToPath(document.uri))\n\t\t\treturn {\n\t\t\t\tfile: fileURLToPath(document.uri),\n\t\t\t\tmode: document.languageId === \"jsonc\" ? \"jsonc\" : \"json\",\n\t\t\t\tassociation: null,\n\t\t\t\tcoverage: \"syntax-only\",\n\t\t\t\tdiagnostics: [\n\t\t\t\t\tdiagnostic(document.getText(), problem.code, problem.message),\n\t\t\t\t],\n\t\t\t\tfailures: [problem],\n\t\t\t}\n\t\t}\n\t}\n\n\tdocumentPath(uri: string): string {\n\t\treturn fileURLToPath(uri)\n\t}\n\trootUris(): string[] {\n\t\treturn this.roots.map((root) => pathToFileURL(root).href)\n\t}\n}\n","import {\n\tgetLanguageService,\n\ttype JSONDocument,\n\ttype LanguageService,\n\ttype Position,\n} from \"vscode-json-languageservice\"\nimport type { TextDocument } from \"vscode-languageserver-textdocument\"\nimport { fileURLToPath } from \"node:url\"\nimport { associationFor, isIncluded } from \"../core/config.ts\"\nimport type { Engine } from \"../core/engine.ts\"\n\n// Only the schema-selection accessor is hidden. The original AST (including\n// $schema as ordinary data) is retained by every bound traversal method.\nexport function externalSelection(document: JSONDocument): JSONDocument {\n\treturn new Proxy(document, {\n\t\tget(target, key) {\n\t\t\tif (key === \"root\") return undefined\n\t\t\tconst value: unknown = Reflect.get(target, key)\n\t\t\treturn typeof value === \"function\" ? value.bind(target) : value\n\t\t},\n\t})\n}\n\nexport class Hints {\n\tprivate readonly services = new WeakMap<\n\t\tEngine,\n\t\tMap<string, LanguageService>\n\t>()\n\tprivate async service(\n\t\tengine: Engine,\n\t\tdocument: TextDocument,\n\t): Promise<LanguageService | undefined> {\n\t\tconst file = fileURLToPath(document.uri)\n\t\tif (!isIncluded(engine.project, file)) return undefined\n\t\tconst association = associationFor(engine.project, file)\n\t\tif (!association?.schema) return undefined\n\t\tawait engine.validator(association.schema)\n\t\tlet services = this.services.get(engine)\n\t\tif (!services) {\n\t\t\tservices = new Map()\n\t\t\tthis.services.set(engine, services)\n\t\t}\n\t\tlet service = services.get(association.schema)\n\t\tif (!service) {\n\t\t\tservice = getLanguageService({\n\t\t\t\tschemaRequestService: async (uri) =>\n\t\t\t\t\tJSON.stringify((await engine.store.load(uri)).schema),\n\t\t\t\tworkspaceContext: {\n\t\t\t\t\tresolveRelativePath: (relative, resource) =>\n\t\t\t\t\t\tnew URL(relative, resource).href,\n\t\t\t\t},\n\t\t\t})\n\t\t\t// A per-association service prevents overlapping rules being combined.\n\t\t\tservice.configure({\n\t\t\t\tvalidate: false,\n\t\t\t\tschemas: [{ uri: association.schema, fileMatch: [\"*\"] }],\n\t\t\t})\n\t\t\tservices.set(association.schema, service)\n\t\t}\n\t\treturn service\n\t}\n\tasync complete(engine: Engine, document: TextDocument, position: Position) {\n\t\tconst service = await this.service(engine, document)\n\t\treturn service\n\t\t\t? await service.doComplete(\n\t\t\t\t\tdocument,\n\t\t\t\t\tposition,\n\t\t\t\t\texternalSelection(service.parseJSONDocument(document)),\n\t\t\t\t)\n\t\t\t: { isIncomplete: false, items: [] }\n\t}\n\tasync hover(engine: Engine, document: TextDocument, position: Position) {\n\t\tconst service = await this.service(engine, document)\n\t\treturn service\n\t\t\t? await service.doHover(\n\t\t\t\t\tdocument,\n\t\t\t\t\tposition,\n\t\t\t\t\texternalSelection(service.parseJSONDocument(document)),\n\t\t\t\t)\n\t\t\t: null\n\t}\n}\n","#!/usr/bin/env node\nimport { fileURLToPath, pathToFileURL } from \"node:url\"\nimport path from \"node:path\"\nimport {\n\tcreateConnection,\n\tTextDocuments,\n\tTextDocumentSyncKind,\n\tDidChangeWatchedFilesNotification,\n\tResponseError,\n\tLSPErrorCodes,\n\ttype CancellationToken,\n} from \"vscode-languageserver/node\"\nimport { TextDocument } from \"vscode-languageserver-textdocument\"\nimport { Workspace } from \"./workspace.ts\"\nimport { Hints } from \"./hints.ts\"\nimport type { FileResult } from \"../core/types.ts\"\nimport { diagnostic } from \"../core/parse.ts\"\nimport { diagnosticViews, diagnosticDetails } from \"../core/diagnostics.ts\"\n\nexport function lspDiagnostics(result: FileResult, text: string) {\n\treturn diagnosticViews([\n\t\t...result.diagnostics,\n\t\t...result.failures\n\t\t\t.filter(\n\t\t\t\t(f) =>\n\t\t\t\t\t!result.diagnostics.some(\n\t\t\t\t\t\t(d) => d.code === f.code && d.message === f.message,\n\t\t\t\t\t),\n\t\t\t)\n\t\t\t.map((f) => diagnostic(text, f.code, f.message)),\n\t]).map((d) => ({\n\t\trange: d.range,\n\t\tmessage: d.message,\n\t\tcode: d.code,\n\t\tseverity: 1 as const,\n\t\tsource: \"correctly\",\n\t\tdata: { pointer: d.pointer },\n\t\t...(d.branches.length\n\t\t\t? {\n\t\t\t\t\trelatedInformation: diagnosticDetails(d).map(\n\t\t\t\t\t\t({ label, diagnostic }) => ({\n\t\t\t\t\t\t\tlocation: {\n\t\t\t\t\t\t\t\turi: pathToFileURL(result.file).href,\n\t\t\t\t\t\t\t\trange: diagnostic.range,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tmessage: `${label}: ${diagnostic.message}`,\n\t\t\t\t\t\t}),\n\t\t\t\t\t),\n\t\t\t\t}\n\t\t\t: {}),\n\t}))\n}\n\nexport function startServer() {\n\tconst connection = createConnection()\n\tconst documents = new TextDocuments(TextDocument)\n\tconst workspace = new Workspace([])\n\tconst hints = new Hints()\n\tconst jobs = new Map<\n\t\tstring,\n\t\t{ controller: AbortController; timer: ReturnType<typeof setTimeout> }\n\t>()\n\tlet dynamicWatch = false\n\tconst watchedSchemas = new Set<string>()\n\tworkspace.onSchema = (uri) => {\n\t\tif (!dynamicWatch || !uri.startsWith(\"file:\") || watchedSchemas.has(uri))\n\t\t\treturn\n\t\twatchedSchemas.add(uri)\n\t\tconst file = fileURLToPath(uri)\n\t\tvoid connection.client\n\t\t\t.register(DidChangeWatchedFilesNotification.type, {\n\t\t\t\twatchers: [\n\t\t\t\t\t{\n\t\t\t\t\t\tglobPattern: {\n\t\t\t\t\t\t\tbaseUri: pathToFileURL(path.dirname(file)).href,\n\t\t\t\t\t\t\tpattern: path.basename(file),\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t})\n\t\t\t.catch((error: unknown) => connection.console.error(String(error)))\n\t}\n\tconnection.onInitialize((params) => {\n\t\tworkspace.roots = (\n\t\t\tparams.workspaceFolders?.map((f) => f.uri) ??\n\t\t\t(params.rootUri ? [params.rootUri] : [])\n\t\t)\n\t\t\t.filter((uri) => uri.startsWith(\"file:\"))\n\t\t\t.map((uri) => fileURLToPath(uri))\n\t\tdynamicWatch =\n\t\t\tparams.capabilities.workspace?.didChangeWatchedFiles\n\t\t\t\t?.dynamicRegistration ?? false\n\t\treturn {\n\t\t\tcapabilities: {\n\t\t\t\ttextDocumentSync: TextDocumentSyncKind.Incremental,\n\t\t\t\tcompletionProvider: { triggerCharacters: ['\"', \":\"] },\n\t\t\t\thoverProvider: true,\n\t\t\t\tworkspace: {\n\t\t\t\t\tworkspaceFolders: { supported: true, changeNotifications: true },\n\t\t\t\t},\n\t\t\t},\n\t\t\tserverInfo: { name: \"correctly\", version: \"0.0.0\" },\n\t\t}\n\t})\n\tconnection.onInitialized(() => {\n\t\tif (dynamicWatch)\n\t\t\tvoid connection.client\n\t\t\t\t.register(DidChangeWatchedFilesNotification.type, {\n\t\t\t\t\twatchers: [{ globPattern: \"**/*.{json,jsonc}\" }],\n\t\t\t\t})\n\t\t\t\t.catch((error: unknown) => connection.console.error(String(error)))\n\t\tconnection.workspace.onDidChangeWorkspaceFolders((event) => {\n\t\t\tconst removed = new Set(event.removed.map((f) => fileURLToPath(f.uri)))\n\t\t\tworkspace.roots = [\n\t\t\t\t...workspace.roots.filter((r) => !removed.has(r)),\n\t\t\t\t...event.added.map((f) => fileURLToPath(f.uri)),\n\t\t\t]\n\t\t\trefresh()\n\t\t})\n\t})\n\tfunction schedule(document: TextDocument) {\n\t\tif (!document.uri.startsWith(\"file:\")) return\n\t\tconst previous = jobs.get(document.uri)\n\t\tif (previous) {\n\t\t\tclearTimeout(previous.timer)\n\t\t\tprevious.controller.abort()\n\t\t}\n\t\tconst controller = new AbortController()\n\t\tconst generation = workspace.generation\n\t\tconst timer = setTimeout(() => {\n\t\t\tvoid workspace\n\t\t\t\t.validate(document)\n\t\t\t\t.then((result) => {\n\t\t\t\t\tif (\n\t\t\t\t\t\tcontroller.signal.aborted ||\n\t\t\t\t\t\tworkspace.generation !== generation ||\n\t\t\t\t\t\tdocuments.get(document.uri)?.version !== document.version\n\t\t\t\t\t)\n\t\t\t\t\t\treturn\n\t\t\t\t\treturn connection.sendDiagnostics({\n\t\t\t\t\t\turi: document.uri,\n\t\t\t\t\t\tversion: document.version,\n\t\t\t\t\t\tdiagnostics: lspDiagnostics(result, document.getText()),\n\t\t\t\t\t})\n\t\t\t\t})\n\t\t\t\t.catch((error: unknown) => connection.console.error(String(error)))\n\t\t}, 40)\n\t\tjobs.set(document.uri, { controller, timer })\n\t}\n\tfunction scheduleAll() {\n\t\tfor (const document of documents.all()) schedule(document)\n\t}\n\tfunction refresh() {\n\t\tworkspace.invalidate()\n\t\tscheduleAll()\n\t}\n\tdocuments.onDidChangeContent(({ document }) => {\n\t\tif (!document.uri.startsWith(\"file:\")) return\n\t\tconst resourceChanged = workspace.open(document)\n\t\tif (resourceChanged) scheduleAll()\n\t\telse schedule(document)\n\t})\n\tdocuments.onDidClose(async ({ document }) => {\n\t\tconst job = jobs.get(document.uri)\n\t\tif (job) {\n\t\t\tclearTimeout(job.timer)\n\t\t\tjob.controller.abort()\n\t\t\tjobs.delete(document.uri)\n\t\t}\n\t\tawait connection.sendDiagnostics({ uri: document.uri, diagnostics: [] })\n\t\tif (document.uri.startsWith(\"file:\") && workspace.close(document.uri))\n\t\t\tscheduleAll()\n\t})\n\tconnection.onDidChangeWatchedFiles((params) => {\n\t\t// Cache writes and unrelated JSON files must not trigger schema reloads.\n\t\tif (params.changes.some((change) => workspace.resourceChanged(change.uri)))\n\t\t\trefresh()\n\t})\n\tconnection.onDidChangeConfiguration(refresh)\n\tasync function request<T>(\n\t\turi: string,\n\t\ttoken: CancellationToken,\n\t\taction: (\n\t\t\tengine: Awaited<ReturnType<Workspace[\"engineFor\"]>>,\n\t\t\tdocument: TextDocument,\n\t\t) => Promise<T>,\n\t): Promise<T | null> {\n\t\tconst document = documents.get(uri)\n\t\tif (!document || !uri.startsWith(\"file:\")) return null\n\t\tconst generation = workspace.generation\n\t\tconst cancelled = () =>\n\t\t\ttoken.isCancellationRequested ||\n\t\t\tworkspace.generation !== generation ||\n\t\t\tdocuments.get(uri)?.version !== document.version\n\t\tif (cancelled())\n\t\t\tthrow new ResponseError(\n\t\t\t\tLSPErrorCodes.RequestCancelled,\n\t\t\t\t\"Request cancelled\",\n\t\t\t)\n\t\tconst operation = (async () => {\n\t\t\ttry {\n\t\t\t\tconst engine = await workspace.engineFor(uri)\n\t\t\t\tif (cancelled())\n\t\t\t\t\tthrow new ResponseError(\n\t\t\t\t\t\tLSPErrorCodes.RequestCancelled,\n\t\t\t\t\t\t\"Request cancelled\",\n\t\t\t\t\t)\n\t\t\t\tconst result = await action(engine, document)\n\t\t\t\tif (cancelled())\n\t\t\t\t\tthrow new ResponseError(\n\t\t\t\t\t\tLSPErrorCodes.RequestCancelled,\n\t\t\t\t\t\t\"Request cancelled\",\n\t\t\t\t\t)\n\t\t\t\treturn result\n\t\t\t} catch (error) {\n\t\t\t\tif (error instanceof ResponseError) throw error\n\t\t\t\tif (cancelled())\n\t\t\t\t\tthrow new ResponseError(\n\t\t\t\t\t\tLSPErrorCodes.RequestCancelled,\n\t\t\t\t\t\t\"Request cancelled\",\n\t\t\t\t\t)\n\t\t\t\tconnection.console.error(String(error))\n\t\t\t\treturn null\n\t\t\t}\n\t\t})()\n\t\tlet listener: { dispose(): void } | undefined\n\t\tconst cancellation = new Promise<never>((_resolve, reject) => {\n\t\t\tlistener = token.onCancellationRequested(() =>\n\t\t\t\treject(\n\t\t\t\t\tnew ResponseError(\n\t\t\t\t\t\tLSPErrorCodes.RequestCancelled,\n\t\t\t\t\t\t\"Request cancelled\",\n\t\t\t\t\t),\n\t\t\t\t),\n\t\t\t)\n\t\t})\n\t\ttry {\n\t\t\treturn await Promise.race([operation, cancellation])\n\t\t} finally {\n\t\t\tlistener?.dispose()\n\t\t}\n\t}\n\tconnection.onCompletion((params, token) =>\n\t\trequest(params.textDocument.uri, token, (engine, document) =>\n\t\t\thints.complete(engine, document, params.position),\n\t\t),\n\t)\n\tconnection.onHover((params, token) =>\n\t\trequest(params.textDocument.uri, token, (engine, document) =>\n\t\t\thints.hover(engine, document, params.position),\n\t\t),\n\t)\n\tconnection.onShutdown(() => {\n\t\tworkspace.invalidate()\n\t\tfor (const job of jobs.values()) {\n\t\t\tclearTimeout(job.timer)\n\t\t\tjob.controller.abort()\n\t\t}\n\t\tjobs.clear()\n\t})\n\tdocuments.listen(connection)\n\tconnection.listen()\n}\n\nstartServer()\n"],"mappings":"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"}
1
+ {"version":3,"file":"lsp.mjs","names":[],"sources":["../src/lsp/workspace.ts","../src/lsp/server.ts"],"sourcesContent":["import path from \"node:path\"\nimport { fileURLToPath, pathToFileURL } from \"node:url\"\nimport { TextDocument } from \"vscode-languageserver-textdocument\"\nimport { contains, discoverConfig, CONFIG_NAME } from \"../core/config.ts\"\nimport { ProjectSession } from \"../runtime/session.ts\"\nimport { CorrectlyError, failure, type FileResult } from \"../core/types.ts\"\nimport { diagnostic } from \"../core/parse.ts\"\n\nexport class Workspace {\n\tonSchema: ((uri: string) => void) | undefined\n\troots: string[]\n\treadonly documents = new Map<string, TextDocument>()\n\tprivate readonly engines = new Map<string, ProjectSession>()\n\tgeneration = 0\n\tconstructor(roots: string[]) {\n\t\tthis.roots = roots\n\t}\n\n\tinvalidate() {\n\t\tthis.generation++\n\t\tfor (const session of this.engines.values()) void session.dispose()\n\t\tthis.engines.clear()\n\t}\n\n\topen(document: TextDocument): boolean {\n\t\tthis.documents.set(document.uri, document)\n\t\tconst resource =\n\t\t\tthis.isSchema(document.uri) && !this.moduleUris.has(document.uri)\n\t\tif (resource) this.invalidate()\n\t\treturn resource\n\t}\n\tclose(uri: string): boolean {\n\t\tconst resource = this.isSchema(uri) && !this.moduleUris.has(uri)\n\t\tthis.documents.delete(uri)\n\t\tif (resource) this.invalidate()\n\t\treturn resource\n\t}\n\tprivate readonly schemaUris = new Set<string>()\n\tprivate isSchema(uri: string): boolean {\n\t\treturn this.schemaUris.has(uri)\n\t}\n\tprivate readonly moduleUris = new Set<string>()\n\tresourceChanged(uri: string): boolean {\n\t\treturn (\n\t\t\turi.endsWith(`/${CONFIG_NAME}`) ||\n\t\t\tthis.isSchema(uri) ||\n\t\t\tthis.moduleUris.has(uri) ||\n\t\t\t/\\/(?:package\\.json|pnpm-lock\\.yaml|package-lock\\.json|yarn\\.lock)$/.test(\n\t\t\t\turi,\n\t\t\t)\n\t\t)\n\t}\n\n\tasync engineFor(uri: string): Promise<ProjectSession> {\n\t\tconst file = fileURLToPath(uri)\n\t\tconst root = this.roots\n\t\t\t.filter((r) => contains(r, file))\n\t\t\t.sort((a, b) => b.length - a.length)[0]\n\t\tif (this.roots.length && !root)\n\t\t\tthrow new CorrectlyError(\n\t\t\t\t\"config\",\n\t\t\t\t`Document is outside the editor workspace: ${file}`,\n\t\t\t)\n\t\tconst configPath = await discoverConfig(path.dirname(file), root)\n\t\tlet session = this.engines.get(configPath)\n\t\tif (!session) {\n\t\t\tsession = new ProjectSession(configPath, {\n\t\t\t\tbuffers: [...this.documents].map(([uri, document]) => [\n\t\t\t\t\turi,\n\t\t\t\t\tdocument.getText(),\n\t\t\t\t]),\n\t\t\t\tonDependency: (dependency, kind) => {\n\t\t\t\t\t;(kind === \"module\" ? this.moduleUris : this.schemaUris).add(\n\t\t\t\t\t\tdependency,\n\t\t\t\t\t)\n\t\t\t\t\tthis.onSchema?.(dependency)\n\t\t\t\t},\n\t\t\t})\n\t\t\tthis.engines.set(configPath, session)\n\t\t}\n\t\tawait session.ready\n\t\treturn session\n\t}\n\n\tasync validate(document: TextDocument): Promise<FileResult> {\n\t\ttry {\n\t\t\tconst engine = await this.engineFor(document.uri)\n\t\t\treturn await engine.validate(\n\t\t\t\tfileURLToPath(document.uri),\n\t\t\t\tdocument.getText(),\n\t\t\t)\n\t\t} catch (error) {\n\t\t\tconst problem = failure(error, fileURLToPath(document.uri))\n\t\t\tif (\n\t\t\t\tproblem.code === \"config\" &&\n\t\t\t\tproblem.message.startsWith(`No ${CONFIG_NAME} found`) &&\n\t\t\t\t![\"json\", \"jsonc\"].includes(document.languageId)\n\t\t\t)\n\t\t\t\treturn {\n\t\t\t\t\tfile: fileURLToPath(document.uri),\n\t\t\t\t\tmode: document.languageId,\n\t\t\t\t\tassociation: null,\n\t\t\t\t\tcoverage: \"excluded\",\n\t\t\t\t\tdiagnostics: [],\n\t\t\t\t\tfailures: [],\n\t\t\t\t}\n\n\t\t\treturn {\n\t\t\t\tfile: fileURLToPath(document.uri),\n\t\t\t\tmode: document.languageId === \"jsonc\" ? \"jsonc\" : \"json\",\n\t\t\t\tassociation: null,\n\t\t\t\tcoverage: \"syntax-only\",\n\t\t\t\tdiagnostics: [\n\t\t\t\t\tdiagnostic(document.getText(), problem.code, problem.message),\n\t\t\t\t],\n\t\t\t\tfailures: [problem],\n\t\t\t}\n\t\t}\n\t}\n\n\tdocumentPath(uri: string): string {\n\t\treturn fileURLToPath(uri)\n\t}\n\trootUris(): string[] {\n\t\treturn this.roots.map((root) => pathToFileURL(root).href)\n\t}\n}\n","#!/usr/bin/env node\nimport { fileURLToPath, pathToFileURL } from \"node:url\"\nimport path from \"node:path\"\nimport {\n\tcreateConnection,\n\tTextDocuments,\n\tTextDocumentSyncKind,\n\tDidChangeWatchedFilesNotification,\n\tResponseError,\n\tLSPErrorCodes,\n\ttype CancellationToken,\n} from \"vscode-languageserver/node\"\nimport { TextDocument } from \"vscode-languageserver-textdocument\"\nimport { Workspace } from \"./workspace.ts\"\nimport { Hints } from \"./hints.ts\"\nimport type { FileResult } from \"../core/types.ts\"\nimport { diagnostic } from \"../core/parse.ts\"\nimport { diagnosticViews, diagnosticDetails } from \"../core/diagnostics.ts\"\n\nexport function lspDiagnostics(result: FileResult, text: string) {\n\treturn diagnosticViews([\n\t\t...result.diagnostics,\n\t\t...result.failures\n\t\t\t.filter(\n\t\t\t\t(f) =>\n\t\t\t\t\t!result.diagnostics.some(\n\t\t\t\t\t\t(d) => d.code === f.code && d.message === f.message,\n\t\t\t\t\t),\n\t\t\t)\n\t\t\t.map((f) => diagnostic(text, f.code, f.message)),\n\t]).map((d) => ({\n\t\trange: d.range,\n\t\tmessage: d.message,\n\t\tcode: d.code,\n\t\tseverity: 1 as const,\n\t\tsource: \"correctly\",\n\t\tdata: { pointer: d.pointer },\n\t\t...(d.branches.length\n\t\t\t? {\n\t\t\t\t\trelatedInformation: diagnosticDetails(d).map(\n\t\t\t\t\t\t({ label, diagnostic }) => ({\n\t\t\t\t\t\t\tlocation: {\n\t\t\t\t\t\t\t\turi: pathToFileURL(result.file).href,\n\t\t\t\t\t\t\t\trange: diagnostic.range,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tmessage: `${label}: ${diagnostic.message}`,\n\t\t\t\t\t\t}),\n\t\t\t\t\t),\n\t\t\t\t}\n\t\t\t: {}),\n\t}))\n}\n\nexport function startServer() {\n\tconst connection = createConnection()\n\tconst documents = new TextDocuments(TextDocument)\n\tconst workspace = new Workspace([])\n\tconst hints = new Hints()\n\tconst jobs = new Map<\n\t\tstring,\n\t\t{ controller: AbortController; timer: ReturnType<typeof setTimeout> }\n\t>()\n\tlet dynamicWatch = false\n\tconst watchedSchemas = new Set<string>()\n\tworkspace.onSchema = (uri) => {\n\t\tif (!dynamicWatch || !uri.startsWith(\"file:\") || watchedSchemas.has(uri))\n\t\t\treturn\n\t\twatchedSchemas.add(uri)\n\t\tconst file = fileURLToPath(uri)\n\t\tvoid connection.client\n\t\t\t.register(DidChangeWatchedFilesNotification.type, {\n\t\t\t\twatchers: [\n\t\t\t\t\t{\n\t\t\t\t\t\tglobPattern: {\n\t\t\t\t\t\t\tbaseUri: pathToFileURL(path.dirname(file)).href,\n\t\t\t\t\t\t\tpattern: path.basename(file),\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t})\n\t\t\t.catch((error: unknown) => connection.console.error(String(error)))\n\t}\n\tconnection.onInitialize((params) => {\n\t\tworkspace.roots = (\n\t\t\tparams.workspaceFolders?.map((f) => f.uri) ??\n\t\t\t(params.rootUri ? [params.rootUri] : [])\n\t\t)\n\t\t\t.filter((uri) => uri.startsWith(\"file:\"))\n\t\t\t.map((uri) => fileURLToPath(uri))\n\t\tdynamicWatch =\n\t\t\tparams.capabilities.workspace?.didChangeWatchedFiles\n\t\t\t\t?.dynamicRegistration ?? false\n\t\treturn {\n\t\t\tcapabilities: {\n\t\t\t\ttextDocumentSync: {\n\t\t\t\t\topenClose: true,\n\t\t\t\t\tchange: TextDocumentSyncKind.Incremental,\n\t\t\t\t\tsave: { includeText: false },\n\t\t\t\t},\n\t\t\t\tcompletionProvider: { triggerCharacters: ['\"', \":\"] },\n\t\t\t\thoverProvider: true,\n\t\t\t\tworkspace: {\n\t\t\t\t\tworkspaceFolders: { supported: true, changeNotifications: true },\n\t\t\t\t},\n\t\t\t},\n\t\t\tserverInfo: { name: \"correctly\", version: \"0.0.0\" },\n\t\t}\n\t})\n\tconnection.onInitialized(() => {\n\t\tif (dynamicWatch)\n\t\t\tvoid connection.client\n\t\t\t\t.register(DidChangeWatchedFilesNotification.type, {\n\t\t\t\t\twatchers: [{ globPattern: \"**/*\" }],\n\t\t\t\t})\n\t\t\t\t.catch((error: unknown) => connection.console.error(String(error)))\n\t\tconnection.workspace.onDidChangeWorkspaceFolders((event) => {\n\t\t\tconst removed = new Set(event.removed.map((f) => fileURLToPath(f.uri)))\n\t\t\tworkspace.roots = [\n\t\t\t\t...workspace.roots.filter((r) => !removed.has(r)),\n\t\t\t\t...event.added.map((f) => fileURLToPath(f.uri)),\n\t\t\t]\n\t\t\trefresh()\n\t\t})\n\t})\n\tfunction schedule(document: TextDocument) {\n\t\tif (!document.uri.startsWith(\"file:\")) return\n\t\tconst previous = jobs.get(document.uri)\n\t\tif (previous) {\n\t\t\tclearTimeout(previous.timer)\n\t\t\tprevious.controller.abort()\n\t\t}\n\t\tconst controller = new AbortController()\n\t\tconst generation = workspace.generation\n\t\tconst timer = setTimeout(() => {\n\t\t\tvoid workspace\n\t\t\t\t.validate(document)\n\t\t\t\t.then((result) => {\n\t\t\t\t\tif (\n\t\t\t\t\t\tcontroller.signal.aborted ||\n\t\t\t\t\t\tworkspace.generation !== generation ||\n\t\t\t\t\t\tdocuments.get(document.uri)?.version !== document.version\n\t\t\t\t\t)\n\t\t\t\t\t\treturn\n\t\t\t\t\treturn connection.sendDiagnostics({\n\t\t\t\t\t\turi: document.uri,\n\t\t\t\t\t\tversion: document.version,\n\t\t\t\t\t\tdiagnostics: lspDiagnostics(result, document.getText()),\n\t\t\t\t\t})\n\t\t\t\t})\n\t\t\t\t.catch((error: unknown) => connection.console.error(String(error)))\n\t\t}, 40)\n\t\tjobs.set(document.uri, { controller, timer })\n\t}\n\tfunction scheduleAll() {\n\t\tfor (const document of documents.all()) schedule(document)\n\t}\n\tfunction refresh() {\n\t\tworkspace.invalidate()\n\t\tscheduleAll()\n\t}\n\tdocuments.onDidChangeContent(({ document }) => {\n\t\tif (!document.uri.startsWith(\"file:\")) return\n\t\tconst resourceChanged = workspace.open(document)\n\t\tif (resourceChanged) scheduleAll()\n\t\telse schedule(document)\n\t})\n\tdocuments.onDidClose(async ({ document }) => {\n\t\tconst job = jobs.get(document.uri)\n\t\tif (job) {\n\t\t\tclearTimeout(job.timer)\n\t\t\tjob.controller.abort()\n\t\t\tjobs.delete(document.uri)\n\t\t}\n\t\tawait connection.sendDiagnostics({ uri: document.uri, diagnostics: [] })\n\t\tif (document.uri.startsWith(\"file:\") && workspace.close(document.uri))\n\t\t\tscheduleAll()\n\t})\n\tdocuments.onDidSave(({ document }) => {\n\t\tif (workspace.resourceChanged(document.uri)) refresh()\n\t})\n\tconnection.onDidChangeWatchedFiles((params) => {\n\t\t// Cache writes and unrelated JSON files must not trigger schema reloads.\n\t\tif (params.changes.some((change) => workspace.resourceChanged(change.uri)))\n\t\t\trefresh()\n\t})\n\tconnection.onDidChangeConfiguration(refresh)\n\tasync function request<T>(\n\t\turi: string,\n\t\ttoken: CancellationToken,\n\t\taction: (\n\t\t\tengine: Awaited<ReturnType<Workspace[\"engineFor\"]>>,\n\t\t\tdocument: TextDocument,\n\t\t) => Promise<T>,\n\t): Promise<T | null> {\n\t\tconst document = documents.get(uri)\n\t\tif (!document || !uri.startsWith(\"file:\")) return null\n\t\tconst generation = workspace.generation\n\t\tconst cancelled = () =>\n\t\t\ttoken.isCancellationRequested ||\n\t\t\tworkspace.generation !== generation ||\n\t\t\tdocuments.get(uri)?.version !== document.version\n\t\tif (cancelled())\n\t\t\tthrow new ResponseError(\n\t\t\t\tLSPErrorCodes.RequestCancelled,\n\t\t\t\t\"Request cancelled\",\n\t\t\t)\n\t\tconst operation = (async () => {\n\t\t\ttry {\n\t\t\t\tconst engine = await workspace.engineFor(uri)\n\t\t\t\tif (cancelled())\n\t\t\t\t\tthrow new ResponseError(\n\t\t\t\t\t\tLSPErrorCodes.RequestCancelled,\n\t\t\t\t\t\t\"Request cancelled\",\n\t\t\t\t\t)\n\t\t\t\tconst result = await action(engine, document)\n\t\t\t\tif (cancelled())\n\t\t\t\t\tthrow new ResponseError(\n\t\t\t\t\t\tLSPErrorCodes.RequestCancelled,\n\t\t\t\t\t\t\"Request cancelled\",\n\t\t\t\t\t)\n\t\t\t\treturn result\n\t\t\t} catch (error) {\n\t\t\t\tif (error instanceof ResponseError) throw error\n\t\t\t\tif (cancelled())\n\t\t\t\t\tthrow new ResponseError(\n\t\t\t\t\t\tLSPErrorCodes.RequestCancelled,\n\t\t\t\t\t\t\"Request cancelled\",\n\t\t\t\t\t)\n\t\t\t\tconnection.console.error(String(error))\n\t\t\t\treturn null\n\t\t\t}\n\t\t})()\n\t\tlet listener: { dispose(): void } | undefined\n\t\tconst cancellation = new Promise<never>((_resolve, reject) => {\n\t\t\tlistener = token.onCancellationRequested(() =>\n\t\t\t\treject(\n\t\t\t\t\tnew ResponseError(\n\t\t\t\t\t\tLSPErrorCodes.RequestCancelled,\n\t\t\t\t\t\t\"Request cancelled\",\n\t\t\t\t\t),\n\t\t\t\t),\n\t\t\t)\n\t\t})\n\t\ttry {\n\t\t\treturn await Promise.race([operation, cancellation])\n\t\t} finally {\n\t\t\tlistener?.dispose()\n\t\t}\n\t}\n\tconnection.onCompletion((params, token) =>\n\t\trequest(params.textDocument.uri, token, (engine, document) =>\n\t\t\thints.complete(engine, document, params.position),\n\t\t),\n\t)\n\tconnection.onHover((params, token) =>\n\t\trequest(params.textDocument.uri, token, (engine, document) =>\n\t\t\thints.hover(engine, document, params.position),\n\t\t),\n\t)\n\tconnection.onShutdown(() => {\n\t\tworkspace.invalidate()\n\t\tfor (const job of jobs.values()) {\n\t\t\tclearTimeout(job.timer)\n\t\t\tjob.controller.abort()\n\t\t}\n\t\tjobs.clear()\n\t})\n\tdocuments.listen(connection)\n\tconnection.listen()\n}\n\nstartServer()\n"],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ import { t as AjvExtension } from "./extensions-ClclWslY.mjs";
2
+ //#region src/extensions/renovate.d.ts
3
+ export declare function renovate(): AjvExtension;
4
+ //#endregion
5
+ //# sourceMappingURL=renovate.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"renovate.d.mts","names":[],"sources":["../src/extensions/renovate.ts"],"mappings":";;wBACgB,YAAY"}
@@ -0,0 +1,11 @@
1
+ //#region src/extensions/renovate.ts
2
+ function renovate() {
3
+ return {
4
+ id: "renovate",
5
+ annotations: ["x-renovate-version"]
6
+ };
7
+ }
8
+ //#endregion
9
+ export { renovate };
10
+
11
+ //# sourceMappingURL=renovate.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"renovate.mjs","names":[],"sources":["../src/extensions/renovate.ts"],"sourcesContent":["import type { AjvExtension } from \"../core/extensions.ts\"\nexport function renovate(): AjvExtension {\n\treturn { id: \"renovate\", annotations: [\"x-renovate-version\"] }\n}\n"],"mappings":";AACA,SAAgB,WAAyB;CACxC,OAAO;EAAE,IAAI;EAAY,aAAa,CAAC,oBAAoB;CAAE;AAC9D"}
@@ -0,0 +1,265 @@
1
+ import { C as pointer, S as parseDocument, d as readText, m as CorrectlyError } from "./config-Cb6o4YnJ.mjs";
2
+ import { mkdir, readFile, rename, writeFile } from "node:fs/promises";
3
+ import path from "node:path";
4
+ import { fileURLToPath } from "node:url";
5
+ import { createHash, randomUUID } from "node:crypto";
6
+ //#region src/core/schema-walk.ts
7
+ /** Only schema positions: defaults, enums, examples and other data stay opaque. */
8
+ function* schemaChildren(schema) {
9
+ for (const key of [
10
+ "additionalItems",
11
+ "additionalProperties",
12
+ "contains",
13
+ "propertyNames",
14
+ "not",
15
+ "if",
16
+ "then",
17
+ "else",
18
+ "unevaluatedProperties",
19
+ "unevaluatedItems",
20
+ "contentSchema"
21
+ ]) if (schema[key] !== void 0) yield [pointer([key]), schema[key]];
22
+ for (const key of [
23
+ "properties",
24
+ "patternProperties",
25
+ "definitions",
26
+ "$defs",
27
+ "dependentSchemas",
28
+ "dependencies"
29
+ ]) {
30
+ const map = schema[key];
31
+ if (typeof map === "object" && map !== null && !Array.isArray(map)) for (const [name, child] of Object.entries(map)) yield [pointer([key, name]), child];
32
+ }
33
+ for (const key of [
34
+ "allOf",
35
+ "anyOf",
36
+ "oneOf",
37
+ "prefixItems",
38
+ "items"
39
+ ]) {
40
+ const children = schema[key];
41
+ if (Array.isArray(children)) for (const [index, child] of children.entries()) yield [pointer([key, index]), child];
42
+ else if (key === "items" && children !== void 0) yield [pointer([key]), children];
43
+ }
44
+ }
45
+ //#endregion
46
+ //#region src/core/schemas.ts
47
+ const DIALECTS = {
48
+ draft7: "http://json-schema.org/draft-07/schema#",
49
+ "2020-12": "https://json-schema.org/draft/2020-12/schema"
50
+ };
51
+ const VOCABULARIES = new Set([
52
+ "core",
53
+ "applicator",
54
+ "unevaluated",
55
+ "validation",
56
+ "meta-data",
57
+ "format-annotation",
58
+ "content"
59
+ ].map((name) => `https://json-schema.org/draft/2020-12/vocab/${name}`));
60
+ function isObject(value) {
61
+ return typeof value === "object" && value !== null && !Array.isArray(value);
62
+ }
63
+ function dialect(value, inherited) {
64
+ if (value === void 0) return inherited;
65
+ if (typeof value !== "string") throw new CorrectlyError("schema", "Schema $schema must be a string");
66
+ const id = value.replace(/#$/, "");
67
+ if (id === "http://json-schema.org/draft-07/schema" || id === "https://json-schema.org/draft-07/schema") return "draft7";
68
+ if (id === DIALECTS["2020-12"]) return "2020-12";
69
+ throw new CorrectlyError("unsupported-dialect", `Unsupported schema dialect: ${value}; supported: draft 7, draft 2020-12`);
70
+ }
71
+ var SchemaStore = class {
72
+ options;
73
+ resources = /* @__PURE__ */ new Map();
74
+ pending = /* @__PURE__ */ new Map();
75
+ cacheDir;
76
+ offline;
77
+ timeoutMs;
78
+ maxBytes;
79
+ maxRequests;
80
+ read;
81
+ request;
82
+ requests = 0;
83
+ constructor(project, options = {}) {
84
+ this.options = options;
85
+ const remote = project.config.remote;
86
+ this.cacheDir = path.resolve(project.root, remote?.cacheDir ?? ".correctly-cache");
87
+ this.offline = options.offline ?? remote?.offline ?? false;
88
+ this.timeoutMs = remote?.timeoutMs ?? 5e3;
89
+ this.maxBytes = remote?.maxBytes ?? 2097152;
90
+ this.maxRequests = remote?.maxRequests ?? 64;
91
+ this.read = options.read ?? readText;
92
+ this.request = options.fetch ?? fetch;
93
+ }
94
+ async load(value, inherited = "draft7") {
95
+ this.options.signal?.throwIfAborted();
96
+ const url = new URL(value);
97
+ url.hash = "";
98
+ const uri = url.href;
99
+ const known = this.resources.get(uri);
100
+ if (known) return known;
101
+ let promise = this.pending.get(uri);
102
+ if (!promise) {
103
+ promise = this.loadResource(uri, inherited);
104
+ this.pending.set(uri, promise);
105
+ }
106
+ return promise;
107
+ }
108
+ async loadResource(uri, inherited) {
109
+ if (++this.requests > this.maxRequests) throw new CorrectlyError("schema-limit", `Schema resource limit (${this.maxRequests}) exceeded`);
110
+ let text;
111
+ const url = new URL(uri);
112
+ try {
113
+ if (url.protocol === "file:") text = await this.read(uri);
114
+ else if (url.protocol === "https:" || url.protocol === "http:") text = await this.remote(uri);
115
+ else throw new CorrectlyError("schema", `Unsupported schema protocol: ${url.protocol}`);
116
+ } catch (error) {
117
+ if (error instanceof CorrectlyError) throw error;
118
+ throw new CorrectlyError("schema-load", `Cannot load schema ${uri}: ${error instanceof Error ? error.message : String(error)}`);
119
+ }
120
+ this.options.signal?.throwIfAborted();
121
+ if (Buffer.byteLength(text) > this.maxBytes) throw new CorrectlyError("schema-limit", `Schema ${uri} exceeds ${this.maxBytes} bytes`);
122
+ const parsed = parseDocument(text, "json");
123
+ if (parsed.diagnostics.length) {
124
+ const d = parsed.diagnostics[0];
125
+ throw new CorrectlyError("schema", `Invalid schema JSON at ${uri}:${d.range.start.line + 1}:${d.range.start.character + 1}: ${d.message}`);
126
+ }
127
+ if (typeof parsed.value !== "boolean" && !isObject(parsed.value)) throw new CorrectlyError("schema", `Schema must be an object or boolean: ${uri}`);
128
+ const rootDialect = dialect(isObject(parsed.value) ? parsed.value.$schema : void 0, inherited);
129
+ const schema = parsed.value;
130
+ const resource = {
131
+ uri,
132
+ schema,
133
+ dialect: rootDialect
134
+ };
135
+ this.normalize(schema, uri, rootDialect, resource);
136
+ if (isObject(schema) && schema.$id === void 0) schema.$id = uri;
137
+ this.register(uri, resource);
138
+ return resource;
139
+ }
140
+ register(uri, resource) {
141
+ const previous = this.resources.get(uri);
142
+ if (previous && previous.schema !== resource.schema) throw new CorrectlyError("schema", `Duplicate schema identifier: ${uri}`);
143
+ this.resources.set(uri, resource);
144
+ }
145
+ normalize(schema, base, expected, root, location = "") {
146
+ if (!isObject(schema)) return;
147
+ const ownDialect = dialect(schema.$schema, expected);
148
+ if (ownDialect !== expected) throw new CorrectlyError("unsupported-dialect", `Mixed schema dialects at ${base} are not supported`);
149
+ if (schema.$schema !== void 0) schema.$schema = DIALECTS[ownDialect];
150
+ if (schema.$async !== void 0) throw new CorrectlyError("schema", `Asynchronous data validation is not supported: ${base}`);
151
+ if (schema.$vocabulary !== void 0) {
152
+ if (!isObject(schema.$vocabulary)) throw new CorrectlyError("schema", `$vocabulary must be an object: ${base}`);
153
+ for (const [vocabulary, required] of Object.entries(schema.$vocabulary)) {
154
+ if (typeof required !== "boolean") throw new CorrectlyError("schema", `$vocabulary values must be boolean: ${base}`);
155
+ if (required && !VOCABULARIES.has(vocabulary)) throw new CorrectlyError("unsupported-vocabulary", `Unsupported required vocabulary: ${vocabulary}`);
156
+ }
157
+ }
158
+ let scope = base;
159
+ if (schema.$id !== void 0) {
160
+ if (typeof schema.$id !== "string") throw new CorrectlyError("schema", `$id must be a string: ${base}`);
161
+ scope = new URL(schema.$id, base).href;
162
+ schema.$id = scope;
163
+ this.register(scope.replace(/#$/, ""), {
164
+ ...root,
165
+ schema,
166
+ pointer: location
167
+ });
168
+ }
169
+ for (const anchor of [schema.$anchor, schema.$dynamicAnchor]) if (typeof anchor === "string") this.register(new URL(`#${anchor}`, scope).href, {
170
+ ...root,
171
+ schema,
172
+ pointer: location
173
+ });
174
+ for (const [path, child] of schemaChildren(schema)) this.normalize(child, scope, expected, root, location + path);
175
+ }
176
+ async remote(uri) {
177
+ const cacheFile = path.join(this.cacheDir, `${createHash("sha256").update(uri).digest("hex")}.json`);
178
+ let cached;
179
+ try {
180
+ const raw = await readFile(cacheFile, "utf8");
181
+ const entry = JSON.parse(raw);
182
+ if (!isObject(entry) || entry.uri !== uri || typeof entry.text !== "string") throw new Error("invalid cache entry");
183
+ cached = entry;
184
+ } catch (error) {
185
+ if (error.code !== "ENOENT") throw new CorrectlyError("schema-cache", `Cannot read schema cache ${cacheFile}: ${String(error)}`);
186
+ }
187
+ if (this.offline) {
188
+ if (!cached) throw new CorrectlyError("offline-miss", `Schema is not cached in offline mode: ${uri}`);
189
+ return cached.text;
190
+ }
191
+ const timeout = AbortSignal.timeout(this.timeoutMs);
192
+ const signal = this.options.signal ? AbortSignal.any([this.options.signal, timeout]) : timeout;
193
+ const headers = {};
194
+ if (cached?.etag) headers["If-None-Match"] = cached.etag;
195
+ if (cached?.lastModified) headers["If-Modified-Since"] = cached.lastModified;
196
+ let current = uri;
197
+ for (let redirects = 0; redirects <= 5; redirects++) {
198
+ const response = await this.request(current, {
199
+ signal,
200
+ headers,
201
+ redirect: "manual"
202
+ });
203
+ if ([
204
+ 301,
205
+ 302,
206
+ 303,
207
+ 307,
208
+ 308
209
+ ].includes(response.status)) {
210
+ await response.body?.cancel();
211
+ const location = response.headers.get("location");
212
+ if (!location) throw new CorrectlyError("schema-load", `Redirect without location: ${current}`);
213
+ current = new URL(location, current).href;
214
+ if (!["http:", "https:"].includes(new URL(current).protocol)) throw new CorrectlyError("schema-load", `Unsupported redirect: ${current}`);
215
+ continue;
216
+ }
217
+ if (response.status === 304 && cached) {
218
+ await response.body?.cancel();
219
+ return cached.text;
220
+ }
221
+ if (!response.ok || !response.body) {
222
+ await response.body?.cancel();
223
+ throw new CorrectlyError("schema-load", `HTTP ${response.status} loading schema ${uri}`);
224
+ }
225
+ const reader = response.body.getReader();
226
+ const chunks = [];
227
+ let size = 0;
228
+ try {
229
+ while (true) {
230
+ const { value, done } = await reader.read();
231
+ if (done) break;
232
+ size += value.byteLength;
233
+ if (size > this.maxBytes) throw new CorrectlyError("schema-limit", `Schema ${uri} exceeds ${this.maxBytes} bytes`);
234
+ chunks.push(value);
235
+ }
236
+ } finally {
237
+ await reader.cancel();
238
+ }
239
+ const text = Buffer.concat(chunks).toString("utf8");
240
+ const entry = {
241
+ uri,
242
+ text
243
+ };
244
+ const etag = response.headers.get("etag");
245
+ const lastModified = response.headers.get("last-modified");
246
+ if (etag) entry.etag = etag;
247
+ if (lastModified) entry.lastModified = lastModified;
248
+ signal.throwIfAborted();
249
+ await mkdir(this.cacheDir, { recursive: true });
250
+ const temporary = `${cacheFile}.${randomUUID()}.tmp`;
251
+ await writeFile(temporary, JSON.stringify(entry));
252
+ await rename(temporary, cacheFile);
253
+ return text;
254
+ }
255
+ throw new CorrectlyError("schema-limit", `Too many redirects loading schema ${uri}`);
256
+ }
257
+ };
258
+ function localSchemaPath(uri) {
259
+ const url = new URL(uri);
260
+ return url.protocol === "file:" ? fileURLToPath(url) : void 0;
261
+ }
262
+ //#endregion
263
+ export { schemaChildren as i, SchemaStore as n, localSchemaPath as r, DIALECTS as t };
264
+
265
+ //# sourceMappingURL=schemas-UxCtIdo2.mjs.map