@oml/server 0.14.0 → 0.14.2

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 (48) hide show
  1. package/out/auth/feature-gate.d.ts +23 -0
  2. package/out/auth/feature-gate.js +172 -0
  3. package/out/auth/feature-gate.js.map +1 -0
  4. package/out/auth/feature-policy.d.ts +13 -0
  5. package/out/auth/feature-policy.js +38 -0
  6. package/out/auth/feature-policy.js.map +1 -0
  7. package/out/cli.d.ts +1 -0
  8. package/{src/cli.ts → out/cli.js} +45 -55
  9. package/out/cli.js.map +1 -0
  10. package/out/index.d.ts +7 -0
  11. package/{src/index.ts → out/index.js} +1 -1
  12. package/out/index.js.map +1 -0
  13. package/out/lsp/diagram-server.d.ts +6 -0
  14. package/out/lsp/diagram-server.js +36 -0
  15. package/out/lsp/diagram-server.js.map +1 -0
  16. package/out/lsp/language-server.d.ts +15 -0
  17. package/{src/lsp/language-server.ts → out/lsp/language-server.js} +95 -120
  18. package/out/lsp/language-server.js.map +1 -0
  19. package/out/lsp/protocol/browser-fs-protocol.d.ts +14 -0
  20. package/{src/lsp/protocol/browser-fs-protocol.ts → out/lsp/protocol/browser-fs-protocol.js} +2 -14
  21. package/out/lsp/protocol/browser-fs-protocol.js.map +1 -0
  22. package/{src/lsp/protocol/reasoner-protocol.ts → out/lsp/protocol/reasoner-protocol.d.ts} +10 -24
  23. package/out/lsp/protocol/reasoner-protocol.js +12 -0
  24. package/out/lsp/protocol/reasoner-protocol.js.map +1 -0
  25. package/out/lsp/providers/browser-fs-provider.d.ts +21 -0
  26. package/{src/lsp/providers/browser-fs-provider.ts → out/lsp/providers/browser-fs-provider.js} +22 -34
  27. package/out/lsp/providers/browser-fs-provider.js.map +1 -0
  28. package/out/lsp/providers/hybrid-fs-provider.d.ts +28 -0
  29. package/{src/lsp/providers/hybrid-fs-provider.ts → out/lsp/providers/hybrid-fs-provider.js} +22 -44
  30. package/out/lsp/providers/hybrid-fs-provider.js.map +1 -0
  31. package/out/rest/export.d.ts +16 -0
  32. package/{src/rest/export.ts → out/rest/export.js} +14 -35
  33. package/out/rest/export.js.map +1 -0
  34. package/out/rest/routes.d.ts +47 -0
  35. package/{src/rest/routes.ts → out/rest/routes.js} +40 -35
  36. package/out/rest/routes.js.map +1 -0
  37. package/out/rest/server.d.ts +17 -0
  38. package/{src/rest/server.ts → out/rest/server.js} +232 -485
  39. package/out/rest/server.js.map +1 -0
  40. package/out/rest/template.d.ts +18 -0
  41. package/{src/rest/template.ts → out/rest/template.js} +21 -80
  42. package/out/rest/template.js.map +1 -0
  43. package/out/rest/validation.d.ts +49 -0
  44. package/{src/rest/validation.ts → out/rest/validation.js} +59 -163
  45. package/out/rest/validation.js.map +1 -0
  46. package/package.json +9 -4
  47. package/src/lsp/diagram-server.ts +0 -48
  48. package/tsconfig.json +0 -22
@@ -1,5 +1,4 @@
1
1
  // Copyright (c) 2026 Modelware. All rights reserved.
2
-
3
2
  import * as http from 'node:http';
4
3
  import * as fsSync from 'node:fs';
5
4
  import * as fs from 'node:fs/promises';
@@ -10,57 +9,24 @@ import { PassThrough } from 'node:stream';
10
9
  import { URL, pathToFileURL } from 'node:url';
11
10
  import { URI } from 'langium';
12
11
  import { NodeFileSystem } from 'langium/node';
13
- import { createConnection, type Connection } from 'vscode-languageserver/node.js';
12
+ import { createConnection } from 'vscode-languageserver/node.js';
14
13
  import { DataFactory, Writer } from 'n3';
15
14
  import uFuzzy from '@leeoniya/ufuzzy';
16
- import {
17
- MarkdownHandlerRegistry,
18
- MarkdownPreviewRuntime,
19
- buildTemplateCatalog as buildNavigationTemplateCatalog,
20
- extractLeadingFrontMatter,
21
- resolveTemplateForNavigation,
22
- renderTemplate,
23
- type TemplateInvocation,
24
- MarkdownExecutor,
25
- type MdBlockKind,
26
- type MdExecutableBlock,
27
- type MdBlockExecutionResult,
28
- } from '@oml/markdown';
15
+ import { MarkdownHandlerRegistry, MarkdownPreviewRuntime, buildTemplateCatalog as buildNavigationTemplateCatalog, extractLeadingFrontMatter, resolveTemplateForNavigation, renderTemplate, MarkdownExecutor, } from '@oml/markdown';
29
16
  import { STATIC_MARKDOWN_RUNTIME_BUNDLE_FILE, STATIC_MARKDOWN_RUNTIME_CSS } from '@oml/markdown/static';
30
- import {
31
- applyOmlUpdate,
32
- collectOntologyMembers,
33
- getIriForNode,
34
- getOntologyModelIndex,
35
- iriFragment,
36
- isDescription,
37
- isOntology,
38
- isVocabulary,
39
- tokenizeForFuzzy,
40
- type OmlEditRequest,
41
- type OmlEditResponse,
42
- type OmlFuzzyIndexedEntry,
43
- } from '@oml/language';
17
+ import { applyOmlUpdate, collectOntologyMembers, getIriForNode, getOntologyModelIndex, iriFragment, isDescription, isOntology, isVocabulary, tokenizeForFuzzy, } from '@oml/language';
44
18
  import { detectSparqlKind } from '@oml/owl';
45
- import { exportAssertedWorkspace, exportWorkspace, type RestExportContext } from './export.js';
46
- import { startOmlLanguageServer, type OmlLanguageServerRuntime } from '../lsp/language-server.js';
47
- import { createOpenApiSpec, dispatchRestRoute } from './routes.js';
48
- import {
49
- buildTemplateCatalog,
50
- expandTemplateComposeBlocks,
51
- findFilesByExtension,
52
- frontMatterString,
53
- isTemplateMarkdownFile,
54
- normalizeContextOntologyIri,
55
- } from './template.js';
56
- import { lintWorkspace, reasonWorkspace, type RestLintResult, type RestValidationContext, validateWorkspace } from './validation.js';
57
-
19
+ import { exportAssertedWorkspace, exportWorkspace } from './export.js';
20
+ import { startOmlLanguageServer } from '../lsp/language-server.js';
21
+ import { createOpenApiSpec, dispatchRestOperation, resolveRestOperationId } from './routes.js';
22
+ import { buildTemplateCatalog, expandTemplateComposeBlocks, findFilesByExtension, frontMatterString, isTemplateMarkdownFile, normalizeContextOntologyIri, } from './template.js';
23
+ import { lintWorkspace, reasonWorkspace, validateWorkspace } from './validation.js';
24
+ import { OmlAccessError, requiredFeatureForRestOperation, } from '../auth/feature-policy.js';
58
25
  const JSON_CONTENT_TYPE = 'application/json; charset=utf-8';
59
26
  const HTML_CONTENT_TYPE = 'text/html; charset=utf-8';
60
27
  const DEFAULT_BODY_LIMIT_BYTES = 2 * 1024 * 1024;
61
- const DEFAULT_REQUEST_TIMEOUT_MS = 30_000;
62
-
63
- const SUPPORTED_MD_BLOCK_KINDS = new Set<MdBlockKind>([
28
+ const DEFAULT_REQUEST_TIMEOUT_MS = 30000;
29
+ const SUPPORTED_MD_BLOCK_KINDS = new Set([
64
30
  'table',
65
31
  'tree',
66
32
  'graph',
@@ -71,22 +37,6 @@ const SUPPORTED_MD_BLOCK_KINDS = new Set<MdBlockKind>([
71
37
  'matrix',
72
38
  'table-editor'
73
39
  ]);
74
-
75
- export interface OmlRestServerOptions {
76
- host: string;
77
- port: number;
78
- workspaceRoot?: string;
79
- watchWorkspace?: boolean;
80
- requestTimeoutMs?: number;
81
- authToken?: string;
82
- runtime?: OmlLanguageServerRuntime;
83
- }
84
-
85
- interface WorkspaceModelFileEntry {
86
- path: string;
87
- uri: string;
88
- }
89
-
90
40
  const BUILT_IN_ONTOLOGIES = new Set([
91
41
  'http://www.w3.org/2001/XMLSchema',
92
42
  'http://www.w3.org/1999/02/22-rdf-syntax-ns',
@@ -95,37 +45,31 @@ const BUILT_IN_ONTOLOGIES = new Set([
95
45
  'http://www.w3.org/2003/11/swrl',
96
46
  'http://www.w3.org/2003/11/swrlb',
97
47
  ]);
98
-
99
-
100
- function jsonResponse(res: http.ServerResponse, status: number, payload: unknown): void {
48
+ function jsonResponse(res, status, payload) {
101
49
  const body = JSON.stringify(payload);
102
50
  res.statusCode = status;
103
51
  res.setHeader('content-type', JSON_CONTENT_TYPE);
104
52
  res.setHeader('content-length', Buffer.byteLength(body, 'utf-8'));
105
53
  res.end(body);
106
54
  }
107
-
108
- function htmlResponse(res: http.ServerResponse, status: number, body: string): void {
109
- res.statusCode = status;
110
- res.setHeader('content-type', HTML_CONTENT_TYPE);
111
- res.setHeader('content-length', Buffer.byteLength(body, 'utf-8'));
112
- res.end(body);
55
+ function htmlResponse(res, status, body) {
56
+ res.statusCode = status;
57
+ res.setHeader('content-type', HTML_CONTENT_TYPE);
58
+ res.setHeader('content-length', Buffer.byteLength(body, 'utf-8'));
59
+ res.end(body);
113
60
  }
114
-
115
- function escapeHtml(value: string): string {
116
- return value
117
- .replace(/&/g, '&amp;')
118
- .replace(/</g, '&lt;')
119
- .replace(/>/g, '&gt;')
120
- .replace(/"/g, '&quot;')
121
- .replace(/'/g, '&#39;');
61
+ function escapeHtml(value) {
62
+ return value
63
+ .replace(/&/g, '&amp;')
64
+ .replace(/</g, '&lt;')
65
+ .replace(/>/g, '&gt;')
66
+ .replace(/"/g, '&quot;')
67
+ .replace(/'/g, '&#39;');
122
68
  }
123
-
124
- async function listWorkspaceModelFiles(workspaceRoot: string): Promise<WorkspaceModelFileEntry[]> {
125
- const entries: WorkspaceModelFileEntry[] = [];
69
+ async function listWorkspaceModelFiles(workspaceRoot) {
70
+ const entries = [];
126
71
  const ignoredNames = new Set(['.git', 'node_modules', 'dist', 'build', 'out']);
127
-
128
- const visit = async (currentDir: string): Promise<void> => {
72
+ const visit = async (currentDir) => {
129
73
  const children = await fs.readdir(currentDir, { withFileTypes: true });
130
74
  for (const child of children) {
131
75
  const childPath = path.join(currentDir, child.name);
@@ -146,13 +90,11 @@ async function listWorkspaceModelFiles(workspaceRoot: string): Promise<Workspace
146
90
  });
147
91
  }
148
92
  };
149
-
150
93
  await visit(workspaceRoot);
151
94
  entries.sort((left, right) => left.path.localeCompare(right.path));
152
95
  return entries;
153
96
  }
154
-
155
- function createSparqlWorkbenchPage(defaultWorkspaceRoot: string): string {
97
+ function createSparqlWorkbenchPage(defaultWorkspaceRoot) {
156
98
  const escapedWorkspaceRoot = escapeHtml(defaultWorkspaceRoot);
157
99
  return `<!DOCTYPE html>
158
100
  <html lang="en">
@@ -991,9 +933,8 @@ LIMIT 25</textarea>
991
933
  </body>
992
934
  </html>`;
993
935
  }
994
-
995
- async function readJsonBody(req: http.IncomingMessage): Promise<Record<string, unknown>> {
996
- const chunks: Buffer[] = [];
936
+ async function readJsonBody(req) {
937
+ const chunks = [];
997
938
  let total = 0;
998
939
  for await (const chunk of req) {
999
940
  const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
@@ -1007,36 +948,30 @@ async function readJsonBody(req: http.IncomingMessage): Promise<Record<string, u
1007
948
  return {};
1008
949
  }
1009
950
  const parsed = JSON.parse(Buffer.concat(chunks).toString('utf-8'));
1010
- return typeof parsed === 'object' && parsed !== null ? parsed as Record<string, unknown> : {};
951
+ return typeof parsed === 'object' && parsed !== null ? parsed : {};
1011
952
  }
1012
-
1013
- function withTimeout<T>(promise: Promise<T>, timeoutMs: number, method: string): Promise<T> {
1014
- return new Promise<T>((resolve, reject) => {
953
+ function withTimeout(promise, timeoutMs, method) {
954
+ return new Promise((resolve, reject) => {
1015
955
  const timer = setTimeout(() => {
1016
956
  reject(new Error(`LSP request timeout for '${method}' after ${timeoutMs}ms.`));
1017
957
  }, timeoutMs);
1018
- void promise.then(
1019
- (value) => {
1020
- clearTimeout(timer);
1021
- resolve(value);
1022
- },
1023
- (error: unknown) => {
1024
- clearTimeout(timer);
1025
- reject(error);
1026
- },
1027
- );
958
+ void promise.then((value) => {
959
+ clearTimeout(timer);
960
+ resolve(value);
961
+ }, (error) => {
962
+ clearTimeout(timer);
963
+ reject(error);
964
+ });
1028
965
  });
1029
966
  }
1030
-
1031
- function normalizeOntologyNamespace(value: string | undefined): string | undefined {
967
+ function normalizeOntologyNamespace(value) {
1032
968
  if (!value) {
1033
969
  return undefined;
1034
970
  }
1035
971
  const normalized = value.replace(/^<|>$/g, '').replace(/[\/#]+$/, '');
1036
972
  return normalized.length > 0 ? normalized : undefined;
1037
973
  }
1038
-
1039
- function resolveOutputPathFromOntologyIriString(ontologyIri: string, format: 'ttl' | 'trig' | 'nt' | 'nq' | 'n3'): string {
974
+ function resolveOutputPathFromOntologyIriString(ontologyIri, format) {
1040
975
  const iri = new URL(ontologyIri);
1041
976
  const rawPath = iri.pathname.replace(/\/+$/, '');
1042
977
  const pathSegments = rawPath.split('/').filter(Boolean);
@@ -1046,12 +981,7 @@ function resolveOutputPathFromOntologyIriString(ontologyIri: string, format: 'tt
1046
981
  : pathSegments.slice(0, -1);
1047
982
  return path.join(...directorySegments, `${fileStem}.${format}`);
1048
983
  }
1049
-
1050
- async function serializeQuads(
1051
- quads: any[],
1052
- format: 'ttl' | 'trig' | 'nt' | 'nq' | 'n3',
1053
- pretty = false,
1054
- ): Promise<string> {
984
+ async function serializeQuads(quads, format, pretty = false) {
1055
985
  const writer = new Writer({
1056
986
  format: format === 'ttl'
1057
987
  ? 'text/turtle'
@@ -1062,7 +992,7 @@ async function serializeQuads(
1062
992
  : (format === 'nq' ? 'N-Quads' : 'application/n3'))),
1063
993
  });
1064
994
  writer.addQuads(quads);
1065
- const serialized = await new Promise<string>((resolve, reject) => {
995
+ const serialized = await new Promise((resolve, reject) => {
1066
996
  writer.end((error, result) => {
1067
997
  if (error) {
1068
998
  reject(error);
@@ -1073,13 +1003,12 @@ async function serializeQuads(
1073
1003
  });
1074
1004
  return pretty ? prettyPrintRdf(serialized, format) : serialized;
1075
1005
  }
1076
-
1077
- function prettyPrintRdf(serialized: string, format: 'ttl' | 'trig' | 'nt' | 'nq' | 'n3'): string {
1006
+ function prettyPrintRdf(serialized, format) {
1078
1007
  if (format !== 'ttl' && format !== 'trig') {
1079
1008
  return serialized;
1080
1009
  }
1081
1010
  const lines = serialized.split('\n');
1082
- const out: string[] = [];
1011
+ const out = [];
1083
1012
  for (let i = 0; i < lines.length; i += 1) {
1084
1013
  const line = lines[i];
1085
1014
  out.push(line);
@@ -1092,19 +1021,8 @@ function prettyPrintRdf(serialized: string, format: 'ttl' | 'trig' | 'nt' | 'nq'
1092
1021
  }
1093
1022
  return out.join('\n');
1094
1023
  }
1095
-
1096
- function toSparqlRowDto(row: Map<string, any>): Record<string, {
1097
- termType: 'NamedNode' | 'Literal' | 'BlankNode';
1098
- value: string;
1099
- datatype?: string;
1100
- language?: string;
1101
- }> {
1102
- const result: Record<string, {
1103
- termType: 'NamedNode' | 'Literal' | 'BlankNode';
1104
- value: string;
1105
- datatype?: string;
1106
- language?: string;
1107
- }> = {};
1024
+ function toSparqlRowDto(row) {
1025
+ const result = {};
1108
1026
  for (const [key, term] of row.entries()) {
1109
1027
  if (!term) {
1110
1028
  continue;
@@ -1118,29 +1036,14 @@ function toSparqlRowDto(row: Map<string, any>): Record<string, {
1118
1036
  }
1119
1037
  return result;
1120
1038
  }
1121
-
1122
- function isRecord(value: unknown): value is Record<string, unknown> {
1039
+ function isRecord(value) {
1123
1040
  return typeof value === 'object' && value !== null && !Array.isArray(value);
1124
1041
  }
1125
-
1126
- type RenderedBlockResult = MdBlockExecutionResult & {
1127
- options?: Record<string, unknown>;
1128
- };
1129
-
1130
- function toMdBlockKind(language: string): MdBlockKind | undefined {
1131
- return SUPPORTED_MD_BLOCK_KINDS.has(language as MdBlockKind) ? (language as MdBlockKind) : undefined;
1042
+ function toMdBlockKind(language) {
1043
+ return SUPPORTED_MD_BLOCK_KINDS.has(language) ? language : undefined;
1132
1044
  }
1133
-
1134
- function toExecutableBlocks(codeBlocks: ReadonlyArray<{
1135
- id: string;
1136
- language: string;
1137
- content: string;
1138
- meta?: string;
1139
- options?: Record<string, unknown>;
1140
- lineStart: number;
1141
- lineEnd: number;
1142
- }>): MdExecutableBlock[] {
1143
- const executable: MdExecutableBlock[] = [];
1045
+ function toExecutableBlocks(codeBlocks) {
1046
+ const executable = [];
1144
1047
  for (const block of codeBlocks) {
1145
1048
  const kind = toMdBlockKind(block.language);
1146
1049
  if (!kind) {
@@ -1158,8 +1061,7 @@ function toExecutableBlocks(codeBlocks: ReadonlyArray<{
1158
1061
  }
1159
1062
  return executable;
1160
1063
  }
1161
-
1162
- function splitHref(href: string): { path: string; query: string; fragment: string } {
1064
+ function splitHref(href) {
1163
1065
  const hashIndex = href.indexOf('#');
1164
1066
  const pathAndQuery = hashIndex >= 0 ? href.slice(0, hashIndex) : href;
1165
1067
  const fragment = hashIndex >= 0 ? href.slice(hashIndex) : '';
@@ -1170,8 +1072,7 @@ function splitHref(href: string): { path: string; query: string; fragment: strin
1170
1072
  fragment
1171
1073
  };
1172
1074
  }
1173
-
1174
- function resolveWorkspacePath(workspaceRoot: string, hrefPath: string): string | undefined {
1075
+ function resolveWorkspacePath(workspaceRoot, hrefPath) {
1175
1076
  const relative = hrefPath.slice('workspace:/'.length).replace(/^\/+/, '');
1176
1077
  const resolved = path.resolve(workspaceRoot, relative);
1177
1078
  const relativeToWorkspace = path.relative(workspaceRoot, resolved);
@@ -1180,27 +1081,17 @@ function resolveWorkspacePath(workspaceRoot: string, hrefPath: string): string |
1180
1081
  }
1181
1082
  return resolved;
1182
1083
  }
1183
-
1184
- function toRelativeWebPath(fromDir: string, toFile: string): string {
1084
+ function toRelativeWebPath(fromDir, toFile) {
1185
1085
  const relative = path.relative(fromDir, toFile).split(path.sep).join('/');
1186
1086
  if (!relative || relative === '.') {
1187
1087
  return '.';
1188
1088
  }
1189
1089
  return relative.startsWith('.') ? relative : `./${relative}`;
1190
1090
  }
1191
-
1192
- function isImagePath(filePath: string): boolean {
1091
+ function isImagePath(filePath) {
1193
1092
  return /\.(png|jpe?g|gif|svg|webp|bmp|ico|avif)$/i.test(filePath);
1194
1093
  }
1195
-
1196
- function resolveAssetTargetPath(
1197
- resolvedFile: string,
1198
- context: {
1199
- workspaceRoot: string;
1200
- inputRoot: string;
1201
- outputRoot: string;
1202
- }
1203
- ): string {
1094
+ function resolveAssetTargetPath(resolvedFile, context) {
1204
1095
  const workspaceRelative = path.relative(context.workspaceRoot, resolvedFile);
1205
1096
  const markdownRelative = path.relative(context.inputRoot, resolvedFile);
1206
1097
  const isInsideMarkdownRoot = !markdownRelative.startsWith('..') && !path.isAbsolute(markdownRelative);
@@ -1211,18 +1102,7 @@ function resolveAssetTargetPath(
1211
1102
  const outputRelative = isInsideMarkdownRoot ? markdownRelative : workspaceRelative;
1212
1103
  return path.join(context.outputRoot, outputRelative);
1213
1104
  }
1214
-
1215
- function rewriteHref(
1216
- href: string,
1217
- context: {
1218
- workspaceRoot: string;
1219
- inputRoot: string;
1220
- inputFile: string;
1221
- outputRoot: string;
1222
- outputFile: string;
1223
- },
1224
- workspaceAssets: Set<string>
1225
- ): string {
1105
+ function rewriteHref(href, context, workspaceAssets) {
1226
1106
  const parts = splitHref(href);
1227
1107
  if (!parts.path) {
1228
1108
  return href;
@@ -1240,10 +1120,7 @@ function rewriteHref(
1240
1120
  workspaceAssets.add(resolved);
1241
1121
  }
1242
1122
  const targetInOutput = resolved.toLowerCase().endsWith('.md')
1243
- ? path.join(
1244
- context.outputRoot,
1245
- path.relative(context.inputRoot, resolved).replace(/\.md$/i, '.html')
1246
- )
1123
+ ? path.join(context.outputRoot, path.relative(context.inputRoot, resolved).replace(/\.md$/i, '.html'))
1247
1124
  : resolveAssetTargetPath(resolved, context);
1248
1125
  const rewrittenPath = toRelativeWebPath(path.dirname(context.outputFile), targetInOutput);
1249
1126
  return `${rewrittenPath}${parts.query}${parts.fragment}`;
@@ -1259,10 +1136,7 @@ function rewriteHref(
1259
1136
  workspaceAssets.add(resolved);
1260
1137
  }
1261
1138
  const targetInOutput = resolved.toLowerCase().endsWith('.md')
1262
- ? path.join(
1263
- context.outputRoot,
1264
- path.relative(context.inputRoot, resolved).replace(/\.md$/i, '.html')
1265
- )
1139
+ ? path.join(context.outputRoot, path.relative(context.inputRoot, resolved).replace(/\.md$/i, '.html'))
1266
1140
  : resolveAssetTargetPath(resolved, context);
1267
1141
  const rewrittenPath = toRelativeWebPath(path.dirname(context.outputFile), targetInOutput);
1268
1142
  return `${rewrittenPath}${parts.query}${parts.fragment}`;
@@ -1273,38 +1147,15 @@ function rewriteHref(
1273
1147
  : trimmedPath;
1274
1148
  return `${rewrittenPath}${parts.query}${parts.fragment}`;
1275
1149
  }
1276
-
1277
- function rewriteRenderedLinks(
1278
- content: string,
1279
- context: {
1280
- workspaceRoot: string;
1281
- inputRoot: string;
1282
- inputFile: string;
1283
- outputRoot: string;
1284
- outputFile: string;
1285
- }
1286
- ): {
1287
- html: string;
1288
- workspaceAssets: Set<string>;
1289
- } {
1290
- const workspaceAssets = new Set<string>();
1291
- const html = content.replace(/(\b(?:href|src)\s*=\s*)(["'])([^"']+)\2/gi, (_match, prefix: string, quote: string, rawHref: string) => {
1150
+ function rewriteRenderedLinks(content, context) {
1151
+ const workspaceAssets = new Set();
1152
+ const html = content.replace(/(\b(?:href|src)\s*=\s*)(["'])([^"']+)\2/gi, (_match, prefix, quote, rawHref) => {
1292
1153
  const rewritten = rewriteHref(rawHref, context, workspaceAssets);
1293
1154
  return `${prefix}${quote}${rewritten}${quote}`;
1294
1155
  });
1295
1156
  return { html, workspaceAssets };
1296
1157
  }
1297
-
1298
- function rewriteAssetReference(
1299
- rawValue: string,
1300
- context: {
1301
- workspaceRoot: string;
1302
- inputRoot: string;
1303
- sourceFile: string;
1304
- outputRoot: string;
1305
- outputFile: string;
1306
- }
1307
- ): { rewritten: string; sourceAsset?: string } {
1158
+ function rewriteAssetReference(rawValue, context) {
1308
1159
  const trimmed = rawValue.trim();
1309
1160
  if (!trimmed || trimmed.startsWith('#') || trimmed.startsWith('data:') || trimmed.startsWith('//')) {
1310
1161
  return { rewritten: rawValue };
@@ -1312,16 +1163,18 @@ function rewriteAssetReference(
1312
1163
  if (/^[a-z][a-z0-9+.-]*:/i.test(trimmed) && !trimmed.startsWith('workspace:/')) {
1313
1164
  return { rewritten: rawValue };
1314
1165
  }
1315
- let resolved: string | undefined;
1166
+ let resolved;
1316
1167
  if (trimmed.startsWith('workspace:/')) {
1317
1168
  resolved = resolveWorkspacePath(context.workspaceRoot, trimmed);
1318
- } else if (trimmed.startsWith('/')) {
1169
+ }
1170
+ else if (trimmed.startsWith('/')) {
1319
1171
  const candidate = path.resolve(context.workspaceRoot, trimmed.replace(/^\/+/, ''));
1320
1172
  const relative = path.relative(context.workspaceRoot, candidate);
1321
1173
  if (!relative.startsWith('..') && !path.isAbsolute(relative)) {
1322
1174
  resolved = candidate;
1323
1175
  }
1324
- } else {
1176
+ }
1177
+ else {
1325
1178
  const candidate = path.resolve(path.dirname(context.sourceFile), trimmed);
1326
1179
  const relative = path.relative(context.workspaceRoot, candidate);
1327
1180
  if (!relative.startsWith('..') && !path.isAbsolute(relative)) {
@@ -1341,18 +1194,7 @@ function rewriteAssetReference(
1341
1194
  sourceAsset: resolved,
1342
1195
  };
1343
1196
  }
1344
-
1345
- function rewriteBlockResultAssetPaths(
1346
- result: RenderedBlockResult,
1347
- context: {
1348
- workspaceRoot: string;
1349
- inputRoot: string;
1350
- sourceFile: string;
1351
- outputRoot: string;
1352
- outputFile: string;
1353
- },
1354
- workspaceAssets: Set<string>,
1355
- ): RenderedBlockResult {
1197
+ function rewriteBlockResultAssetPaths(result, context, workspaceAssets) {
1356
1198
  if (!result.options || result.kind !== 'diagram') {
1357
1199
  return result;
1358
1200
  }
@@ -1366,7 +1208,7 @@ function rewriteBlockResultAssetPaths(
1366
1208
  return entry;
1367
1209
  }
1368
1210
  let entryChanged = false;
1369
- const icon = { ...entry.style.icon } as Record<string, unknown>;
1211
+ const icon = { ...entry.style.icon };
1370
1212
  for (const key of ['href', 'xlinkHref', 'xlink:href']) {
1371
1213
  const value = icon[key];
1372
1214
  if (typeof value !== 'string') {
@@ -1404,26 +1246,21 @@ function rewriteBlockResultAssetPaths(
1404
1246
  },
1405
1247
  };
1406
1248
  }
1407
-
1408
- function sanitizeBlockId(value: string): string {
1249
+ function sanitizeBlockId(value) {
1409
1250
  const trimmed = value.trim();
1410
1251
  if (!trimmed) {
1411
1252
  return 'block';
1412
1253
  }
1413
1254
  return trimmed.replace(/[^a-zA-Z0-9._-]/g, '_');
1414
1255
  }
1415
-
1416
- async function writeBlockArtifacts(outputFile: string, results: ReadonlyArray<RenderedBlockResult>): Promise<{
1417
- count: number;
1418
- manifest: Array<{ blockId: string; path: string }>;
1419
- }> {
1256
+ async function writeBlockArtifacts(outputFile, results) {
1420
1257
  if (results.length === 0) {
1421
1258
  return { count: 0, manifest: [] };
1422
1259
  }
1423
1260
  const blockDirName = `${path.basename(outputFile, '.html')}.blocks`;
1424
1261
  const blockDir = path.join(path.dirname(outputFile), blockDirName);
1425
1262
  await fs.mkdir(blockDir, { recursive: true });
1426
- const manifest: Array<{ blockId: string; path: string }> = [];
1263
+ const manifest = [];
1427
1264
  for (const result of results) {
1428
1265
  const safeId = sanitizeBlockId(result.blockId);
1429
1266
  const fileName = `${safeId}.json`;
@@ -1436,33 +1273,29 @@ async function writeBlockArtifacts(outputFile: string, results: ReadonlyArray<Re
1436
1273
  }
1437
1274
  return { count: results.length, manifest };
1438
1275
  }
1439
-
1440
- async function loadStaticRuntimeBundle(): Promise<string> {
1276
+ async function loadStaticRuntimeBundle() {
1441
1277
  const require = createRequire(import.meta.url);
1442
1278
  const staticEntry = require.resolve('@oml/markdown/static');
1443
1279
  const bundlePath = path.join(path.dirname(staticEntry), STATIC_MARKDOWN_RUNTIME_BUNDLE_FILE);
1444
1280
  try {
1445
1281
  return await fs.readFile(bundlePath, 'utf-8');
1446
- } catch {
1282
+ }
1283
+ catch {
1447
1284
  throw new Error(`Unable to load markdown static runtime bundle at '${bundlePath}'.`);
1448
1285
  }
1449
1286
  }
1450
-
1451
- async function loadCodeBlockStylesheet(): Promise<string> {
1287
+ async function loadCodeBlockStylesheet() {
1452
1288
  const require = createRequire(import.meta.url);
1453
1289
  const staticEntry = require.resolve('@oml/markdown/static');
1454
1290
  const stylesheetPath = path.resolve(path.dirname(staticEntry), '..', '..', 'src', 'static', 'markdown-webview.css');
1455
1291
  try {
1456
1292
  return await fs.readFile(stylesheetPath, 'utf-8');
1457
- } catch {
1293
+ }
1294
+ catch {
1458
1295
  throw new Error(`Unable to load markdown webview stylesheet at '${stylesheetPath}'.`);
1459
1296
  }
1460
1297
  }
1461
-
1462
- async function writeStaticAssets(outputRoot: string): Promise<{
1463
- runtimeScriptFile: string;
1464
- stylesheetFile: string;
1465
- }> {
1298
+ async function writeStaticAssets(outputRoot) {
1466
1299
  const runtimeBundle = await loadStaticRuntimeBundle();
1467
1300
  const sourceStylesheet = await loadCodeBlockStylesheet();
1468
1301
  const mergedStylesheet = `${sourceStylesheet}\n\n${STATIC_MARKDOWN_RUNTIME_CSS}\n`;
@@ -1475,8 +1308,7 @@ async function writeStaticAssets(outputRoot: string): Promise<{
1475
1308
  await fs.writeFile(stylesheetFile, mergedStylesheet, 'utf-8');
1476
1309
  return { runtimeScriptFile: runtimeFile, stylesheetFile };
1477
1310
  }
1478
-
1479
- function escapeAttribute(value: string): string {
1311
+ function escapeAttribute(value) {
1480
1312
  return value
1481
1313
  .replace(/&/g, '&amp;')
1482
1314
  .replace(/</g, '&lt;')
@@ -1484,12 +1316,10 @@ function escapeAttribute(value: string): string {
1484
1316
  .replace(/"/g, '&quot;')
1485
1317
  .replace(/'/g, '&#39;');
1486
1318
  }
1487
-
1488
- function escapeJsonForScript(value: string): string {
1319
+ function escapeJsonForScript(value) {
1489
1320
  return value.replace(/</g, '\\u003c');
1490
1321
  }
1491
-
1492
- function normalizeWikiPathKey(raw: string): string {
1322
+ function normalizeWikiPathKey(raw) {
1493
1323
  const normalized = raw.replace(/\\/g, '/').replace(/^\/+/, '').replace(/\/+/g, '/');
1494
1324
  if (!normalized) {
1495
1325
  return '';
@@ -1497,9 +1327,8 @@ function normalizeWikiPathKey(raw: string): string {
1497
1327
  const withoutHtml = normalized.replace(/\.html$/i, '');
1498
1328
  return withoutHtml.replace(/^\.\//, '').toLowerCase();
1499
1329
  }
1500
-
1501
- function buildWikiPageIndex(markdownFiles: ReadonlyArray<string>, inputRoot: string, outputRoot: string): Map<string, string> {
1502
- const index = new Map<string, string>();
1330
+ function buildWikiPageIndex(markdownFiles, inputRoot, outputRoot) {
1331
+ const index = new Map();
1503
1332
  for (const markdownFile of markdownFiles) {
1504
1333
  const relativeInput = path.relative(inputRoot, markdownFile).split(path.sep).join('/');
1505
1334
  const normalizedRelative = normalizeWikiPathKey(relativeInput.replace(/\.md$/i, ''));
@@ -1511,23 +1340,17 @@ function buildWikiPageIndex(markdownFiles: ReadonlyArray<string>, inputRoot: str
1511
1340
  }
1512
1341
  return index;
1513
1342
  }
1514
-
1515
- function buildWikiLinkHrefMapForPage(
1516
- wikiPageIndex: ReadonlyMap<string, string>,
1517
- outputFile: string
1518
- ): Record<string, string> {
1519
- const map: Record<string, string> = {};
1343
+ function buildWikiLinkHrefMapForPage(wikiPageIndex, outputFile) {
1344
+ const map = {};
1520
1345
  for (const [key, targetFile] of wikiPageIndex.entries()) {
1521
1346
  map[key] = toRelativeWebPath(path.dirname(outputFile), targetFile);
1522
1347
  }
1523
1348
  return map;
1524
1349
  }
1525
-
1526
- function normalizeIri(value: string): string {
1350
+ function normalizeIri(value) {
1527
1351
  return value.trim().replace(/^<|>$/g, '');
1528
1352
  }
1529
-
1530
- function inferOntologyIriFromMemberIri(memberIri: string): string | undefined {
1353
+ function inferOntologyIriFromMemberIri(memberIri) {
1531
1354
  const normalized = normalizeIri(memberIri);
1532
1355
  if (!normalized) {
1533
1356
  return undefined;
@@ -1543,13 +1366,13 @@ function inferOntologyIriFromMemberIri(memberIri: string): string | undefined {
1543
1366
  }
1544
1367
  return normalized.replace(/[\/#]+$/, '');
1545
1368
  }
1546
-
1547
- function memberIriToOutputFile(outputRoot: string, memberIri: string): string | undefined {
1369
+ function memberIriToOutputFile(outputRoot, memberIri) {
1548
1370
  const normalized = normalizeIri(memberIri);
1549
- let parsed: URL;
1371
+ let parsed;
1550
1372
  try {
1551
1373
  parsed = new URL(normalized);
1552
- } catch {
1374
+ }
1375
+ catch {
1553
1376
  return undefined;
1554
1377
  }
1555
1378
  const host = (parsed.hostname || '').trim().toLowerCase();
@@ -1569,11 +1392,7 @@ function memberIriToOutputFile(outputRoot: string, memberIri: string): string |
1569
1392
  : pathname.split('/').slice(0, -1).join('/');
1570
1393
  return path.join(outputRoot, host, basePath, `${safeMember}.html`);
1571
1394
  }
1572
-
1573
- async function queryMemberTypes(
1574
- reasoningService: any,
1575
- modelUri: string,
1576
- ): Promise<Map<string, string[]>> {
1395
+ async function queryMemberTypes(reasoningService, modelUri) {
1577
1396
  const queryResult = await reasoningService.getSparqlService().query(modelUri, `
1578
1397
  PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
1579
1398
  SELECT DISTINCT ?member ?type
@@ -1585,44 +1404,29 @@ async function queryMemberTypes(
1585
1404
  }
1586
1405
  `);
1587
1406
  if (!queryResult?.success || !Array.isArray(queryResult.rows)) {
1588
- return new Map<string, string[]>();
1407
+ return new Map();
1589
1408
  }
1590
- const byMember = new Map<string, Set<string>>();
1591
- for (const row of queryResult.rows as Array<Map<string, { value: string } | undefined>>) {
1409
+ const byMember = new Map();
1410
+ for (const row of queryResult.rows) {
1592
1411
  const member = normalizeIri(row.get('member')?.value ?? '');
1593
1412
  const type = normalizeIri(row.get('type')?.value ?? '');
1594
1413
  if (!member || !type) {
1595
1414
  continue;
1596
1415
  }
1597
- const existing = byMember.get(member) ?? new Set<string>();
1416
+ const existing = byMember.get(member) ?? new Set();
1598
1417
  existing.add(type);
1599
1418
  byMember.set(member, existing);
1600
1419
  }
1601
- return new Map<string, string[]>(
1602
- Array.from(byMember.entries()).map(([member, types]) => [member, Array.from(types)])
1603
- );
1420
+ return new Map(Array.from(byMember.entries()).map(([member, types]) => [member, Array.from(types)]));
1604
1421
  }
1605
-
1606
- function buildIriAliasMapForPage(
1607
- aliasesByIri: ReadonlyMap<string, string>,
1608
- outputFile: string
1609
- ): Record<string, string> {
1610
- const aliases: Record<string, string> = {};
1422
+ function buildIriAliasMapForPage(aliasesByIri, outputFile) {
1423
+ const aliases = {};
1611
1424
  for (const [iri, absoluteTarget] of aliasesByIri.entries()) {
1612
1425
  aliases[iri] = toRelativeWebPath(path.dirname(outputFile), absoluteTarget);
1613
1426
  }
1614
1427
  return aliases;
1615
1428
  }
1616
-
1617
- function wrapHtml(
1618
- content: string,
1619
- runtimeScriptPath: string,
1620
- stylesheetPath: string,
1621
- blockManifest: Array<{ blockId: string; path: string }>,
1622
- blockResults: ReadonlyArray<RenderedBlockResult>,
1623
- wikiLinkHrefByKey: Record<string, string>,
1624
- iriAliasByIri: Record<string, string>,
1625
- ): string {
1429
+ function wrapHtml(content, runtimeScriptPath, stylesheetPath, blockManifest, blockResults, wikiLinkHrefByKey, iriAliasByIri) {
1626
1430
  const escapedManifest = escapeJsonForScript(JSON.stringify(blockManifest));
1627
1431
  const inlineResults = Object.fromEntries(blockResults.map((result) => [result.blockId, result]));
1628
1432
  const escapedInlineResults = escapeJsonForScript(JSON.stringify(inlineResults));
@@ -1650,33 +1454,21 @@ ${content}
1650
1454
  </html>
1651
1455
  `;
1652
1456
  }
1653
-
1654
1457
  class InMemoryJsonRpcLspClient {
1655
- private readonly workspaceRoot: string;
1656
- private readonly requestTimeoutMs: number;
1657
- private readonly clientConnection?: Connection;
1658
- private readonly serverConnection?: Connection;
1659
- private readonly clientToServer?: PassThrough;
1660
- private readonly serverToClient?: PassThrough;
1661
- private initPromise?: Promise<void>;
1662
- private readonly runtime: OmlLanguageServerRuntime;
1663
- private readonly useExternalRuntime: boolean;
1664
- private readonly watchWorkspace: boolean;
1665
- private readonly watchers: Set<fsSync.FSWatcher> = new Set();
1666
- private watcherFlushTimer: NodeJS.Timeout | undefined;
1667
- private watcherActive = false;
1668
- private refreshInFlight = false;
1669
- private refreshQueued = false;
1670
- private initialWorkspaceSyncCompleted = false;
1671
-
1672
- constructor(workspaceRoot: string, requestTimeoutMs: number, watchWorkspace: boolean, runtime?: OmlLanguageServerRuntime) {
1458
+ constructor(workspaceRoot, requestTimeoutMs, watchWorkspace, runtime) {
1459
+ this.watchers = new Set();
1460
+ this.watcherActive = false;
1461
+ this.refreshInFlight = false;
1462
+ this.refreshQueued = false;
1463
+ this.initialWorkspaceSyncCompleted = false;
1673
1464
  this.workspaceRoot = workspaceRoot;
1674
1465
  this.requestTimeoutMs = requestTimeoutMs;
1675
1466
  this.useExternalRuntime = runtime !== undefined;
1676
1467
  this.watchWorkspace = runtime ? false : watchWorkspace;
1677
1468
  if (runtime) {
1678
1469
  this.runtime = runtime;
1679
- } else {
1470
+ }
1471
+ else {
1680
1472
  this.clientToServer = new PassThrough();
1681
1473
  this.serverToClient = new PassThrough();
1682
1474
  this.serverConnection = createConnection(this.clientToServer, this.serverToClient);
@@ -1693,20 +1485,18 @@ class InMemoryJsonRpcLspClient {
1693
1485
  this.startWorkspaceWatcher();
1694
1486
  }
1695
1487
  }
1696
-
1697
- private getWorkspaceOmlDocuments(): any[] {
1488
+ getWorkspaceOmlDocuments() {
1698
1489
  const documents = this.runtime.shared.workspace.LangiumDocuments;
1699
1490
  const allDocs = documents.all ?? [];
1700
- const iterable: any[] = Array.isArray(allDocs)
1491
+ const iterable = Array.isArray(allDocs)
1701
1492
  ? allDocs
1702
- : (typeof allDocs?.toArray === 'function' ? allDocs.toArray() : Array.from(allDocs as Iterable<any>));
1493
+ : (typeof allDocs?.toArray === 'function' ? allDocs.toArray() : Array.from(allDocs));
1703
1494
  return iterable
1704
1495
  .filter((doc) => String(doc?.uri ?? '').trim().toLowerCase().endsWith('.oml'))
1705
1496
  .sort((left, right) => String(left?.uri ?? '').localeCompare(String(right?.uri ?? '')));
1706
1497
  }
1707
-
1708
- async lintWorkspace(params: Record<string, unknown> = {}): Promise<RestLintResult> {
1709
- const context: RestValidationContext = {
1498
+ async lintWorkspace(params = {}) {
1499
+ const context = {
1710
1500
  workspaceRoot: this.workspaceRoot,
1711
1501
  runtime: this.runtime,
1712
1502
  ensureInitialized: () => this.ensureInitialized(),
@@ -1716,8 +1506,7 @@ class InMemoryJsonRpcLspClient {
1716
1506
  };
1717
1507
  return await lintWorkspace(context, params);
1718
1508
  }
1719
-
1720
- async queryWorkspace(params: Record<string, unknown>): Promise<Record<string, unknown>> {
1509
+ async queryWorkspace(params) {
1721
1510
  await this.ensureInitialized();
1722
1511
  await this.ensureWorkspaceCurrent();
1723
1512
  const modelUri = typeof params.modelUri === 'string' ? params.modelUri.trim() : '';
@@ -1731,7 +1520,7 @@ class InMemoryJsonRpcLspClient {
1731
1520
  };
1732
1521
  }
1733
1522
  try {
1734
- const reasoningService = this.runtime.Oml.reasoning.ReasoningService as any;
1523
+ const reasoningService = this.runtime.Oml.reasoning.ReasoningService;
1735
1524
  const kind = detectSparqlKind(sparql);
1736
1525
  await reasoningService.ensureQueryContext(modelUri);
1737
1526
  const sparqlService = reasoningService.getSparqlService();
@@ -1741,7 +1530,7 @@ class InMemoryJsonRpcLspClient {
1741
1530
  success: result.success,
1742
1531
  kind,
1743
1532
  warnings: result.warnings ?? [],
1744
- rows: result.rows.map((row: Map<string, any>) => toSparqlRowDto(row)),
1533
+ rows: result.rows.map((row) => toSparqlRowDto(row)),
1745
1534
  error: result.error,
1746
1535
  };
1747
1536
  }
@@ -1761,7 +1550,7 @@ class InMemoryJsonRpcLspClient {
1761
1550
  success: result.success,
1762
1551
  kind,
1763
1552
  warnings: result.warnings ?? [],
1764
- quads: result.quads.map((quad: any) => ({
1553
+ quads: result.quads.map((quad) => ({
1765
1554
  subject: quad.subject.value,
1766
1555
  predicate: quad.predicate.value,
1767
1556
  object: quad.object.value,
@@ -1776,7 +1565,8 @@ class InMemoryJsonRpcLspClient {
1776
1565
  warnings: [],
1777
1566
  error: 'Unsupported or unknown SPARQL query kind.',
1778
1567
  };
1779
- } catch (error) {
1568
+ }
1569
+ catch (error) {
1780
1570
  return {
1781
1571
  success: false,
1782
1572
  kind: detectSparqlKind(sparql),
@@ -1785,18 +1575,12 @@ class InMemoryJsonRpcLspClient {
1785
1575
  };
1786
1576
  }
1787
1577
  }
1788
-
1789
- async updateWorkspace(params: Record<string, unknown>): Promise<OmlEditResponse> {
1578
+ async updateWorkspace(params) {
1790
1579
  await this.ensureInitialized();
1791
1580
  await this.ensureWorkspaceCurrent();
1792
- return await applyOmlUpdate(
1793
- this.runtime.shared,
1794
- params as OmlEditRequest,
1795
- (message) => this.logError(message),
1796
- );
1581
+ return await applyOmlUpdate(this.runtime.shared, params, (message) => this.logError(message));
1797
1582
  }
1798
-
1799
- async fuzzySearchWorkspace(params: Record<string, unknown>): Promise<Record<string, unknown>> {
1583
+ async fuzzySearchWorkspace(params) {
1800
1584
  await this.ensureInitialized();
1801
1585
  await this.ensureWorkspaceCurrent();
1802
1586
  try {
@@ -1805,14 +1589,14 @@ class InMemoryJsonRpcLspClient {
1805
1589
  return { success: true, candidates: [] };
1806
1590
  }
1807
1591
  const limit = Math.max(1, Math.min(50, typeof params.limit === 'number' ? params.limit : 12));
1808
- const ontologyIndex = getOntologyModelIndex(this.runtime.shared as any);
1809
- const langiumDocuments: any = this.runtime.shared.workspace.LangiumDocuments;
1592
+ const ontologyIndex = getOntologyModelIndex(this.runtime.shared);
1593
+ const langiumDocuments = this.runtime.shared.workspace.LangiumDocuments;
1810
1594
  const allDocs = langiumDocuments.all ?? [];
1811
- const iterable: any[] = Array.isArray(allDocs)
1595
+ const iterable = Array.isArray(allDocs)
1812
1596
  ? allDocs
1813
- : (typeof allDocs?.toArray === 'function' ? allDocs.toArray() : Array.from(allDocs as Iterable<any>));
1814
- const modelUris: string[] = [];
1815
- const candidateByIri = new Map<string, { iri: string; label?: string }>();
1597
+ : (typeof allDocs?.toArray === 'function' ? allDocs.toArray() : Array.from(allDocs));
1598
+ const modelUris = [];
1599
+ const candidateByIri = new Map();
1816
1600
  for (const doc of iterable) {
1817
1601
  const root = doc?.parseResult?.value;
1818
1602
  if (!root || !isOntology(root) || (!isVocabulary(root) && !isDescription(root))) {
@@ -1833,8 +1617,7 @@ class InMemoryJsonRpcLspClient {
1833
1617
  entry.label = label;
1834
1618
  candidateByIri.set(iri, entry);
1835
1619
  }
1836
-
1837
- const indexedEntries: OmlFuzzyIndexedEntry[] = [...candidateByIri.values()].map((entry) => ({
1620
+ const indexedEntries = [...candidateByIri.values()].map((entry) => ({
1838
1621
  iri: entry.iri,
1839
1622
  label: entry.label,
1840
1623
  fragment: iriFragment(entry.iri),
@@ -1850,11 +1633,11 @@ class InMemoryJsonRpcLspClient {
1850
1633
  const info = uf.info(filtered, haystack, text);
1851
1634
  const order = info ? uf.sort(info, haystack, text) : null;
1852
1635
  const ranked = (order ?? filtered.map((_, index) => index))
1853
- .map((index) => filtered[index]!)
1636
+ .map((index) => filtered[index])
1854
1637
  .slice(0, limit);
1855
1638
  const queryTokens = tokenizeForFuzzy(text).slice(0, 6);
1856
1639
  const candidates = ranked.map((entryIndex, index) => {
1857
- const entry = indexedEntries[entryIndex]!;
1640
+ const entry = indexedEntries[entryIndex];
1858
1641
  const fragment = entry.fragment.toLowerCase();
1859
1642
  const lowerLabel = (entry.label ?? '').toLowerCase();
1860
1643
  const lowerInput = text.toLowerCase();
@@ -1887,7 +1670,8 @@ class InMemoryJsonRpcLspClient {
1887
1670
  };
1888
1671
  });
1889
1672
  return { success: true, candidates };
1890
- } catch (error) {
1673
+ }
1674
+ catch (error) {
1891
1675
  return {
1892
1676
  success: false,
1893
1677
  candidates: [],
@@ -1895,26 +1679,21 @@ class InMemoryJsonRpcLspClient {
1895
1679
  };
1896
1680
  }
1897
1681
  }
1898
-
1899
- private async writeWorkspaceAssertedOwl(
1900
- outputDir: string,
1901
- format: 'ttl' | 'trig' | 'nt' | 'nq' | 'n3',
1902
- pretty: boolean,
1903
- ): Promise<Array<{ modelUri: string; ontologyIri: string; owlPath: string }>> {
1904
- const entries: Array<{ modelUri: string; ontologyIri: string; owlPath: string }> = [];
1682
+ async writeWorkspaceAssertedOwl(outputDir, format, pretty) {
1683
+ const entries = [];
1905
1684
  const docs = this.getWorkspaceOmlDocuments();
1906
- const reasoningService = this.runtime.Oml.reasoning.ReasoningService as any;
1685
+ const reasoningService = this.runtime.Oml.reasoning.ReasoningService;
1907
1686
  const store = reasoningService.getStore().getStore();
1908
1687
  for (const doc of docs) {
1909
1688
  const modelUri = String(doc?.uri ?? '').trim();
1910
- const root = doc?.parseResult?.value as { namespace?: string } | undefined;
1689
+ const root = doc?.parseResult?.value;
1911
1690
  const ontologyIri = normalizeOntologyNamespace(root?.namespace)?.replace(/[\/#]+$/, '');
1912
1691
  if (!ontologyIri || BUILT_IN_ONTOLOGIES.has(ontologyIri)) {
1913
1692
  continue;
1914
1693
  }
1915
1694
  await reasoningService.ensureQueryContext(modelUri);
1916
1695
  const quads = store.getQuads(null, null, null, DataFactory.namedNode(modelUri))
1917
- .map((quad: any) => DataFactory.quad(quad.subject, quad.predicate, quad.object));
1696
+ .map((quad) => DataFactory.quad(quad.subject, quad.predicate, quad.object));
1918
1697
  const owlPath = path.join(outputDir, resolveOutputPathFromOntologyIriString(ontologyIri, format));
1919
1698
  await fs.mkdir(path.dirname(owlPath), { recursive: true });
1920
1699
  await fs.writeFile(owlPath, await serializeQuads(quads, format, pretty), 'utf-8');
@@ -1923,9 +1702,8 @@ class InMemoryJsonRpcLspClient {
1923
1702
  entries.sort((left, right) => left.ontologyIri.localeCompare(right.ontologyIri));
1924
1703
  return entries;
1925
1704
  }
1926
-
1927
- async reasonWorkspace(params: Record<string, unknown>): Promise<Record<string, unknown>> {
1928
- const context: RestValidationContext = {
1705
+ async reasonWorkspace(params) {
1706
+ const context = {
1929
1707
  workspaceRoot: this.workspaceRoot,
1930
1708
  runtime: this.runtime,
1931
1709
  ensureInitialized: () => this.ensureInitialized(),
@@ -1935,9 +1713,8 @@ class InMemoryJsonRpcLspClient {
1935
1713
  };
1936
1714
  return await reasonWorkspace(context, params);
1937
1715
  }
1938
-
1939
- async validateWorkspace(params: Record<string, unknown>): Promise<Record<string, unknown>> {
1940
- const context: RestValidationContext = {
1716
+ async validateWorkspace(params) {
1717
+ const context = {
1941
1718
  workspaceRoot: this.workspaceRoot,
1942
1719
  runtime: this.runtime,
1943
1720
  ensureInitialized: () => this.ensureInitialized(),
@@ -1947,9 +1724,8 @@ class InMemoryJsonRpcLspClient {
1947
1724
  };
1948
1725
  return await validateWorkspace(context, params);
1949
1726
  }
1950
-
1951
- async exportWorkspace(params: Record<string, unknown>): Promise<Record<string, unknown>> {
1952
- const context: RestExportContext = {
1727
+ async exportWorkspace(params) {
1728
+ const context = {
1953
1729
  workspaceRoot: this.workspaceRoot,
1954
1730
  ensureInitialized: () => this.ensureInitialized(),
1955
1731
  ensureWorkspaceCurrent: () => this.ensureWorkspaceCurrent(),
@@ -1958,13 +1734,13 @@ class InMemoryJsonRpcLspClient {
1958
1734
  };
1959
1735
  return await exportWorkspace(context, params);
1960
1736
  }
1961
-
1962
- async close(): Promise<void> {
1737
+ async close() {
1963
1738
  this.stopWorkspaceWatcher();
1964
1739
  if (!this.useExternalRuntime) {
1965
1740
  try {
1966
1741
  this.clientConnection?.sendNotification('exit');
1967
- } catch {
1742
+ }
1743
+ catch {
1968
1744
  // Ignore close-time notification failures.
1969
1745
  }
1970
1746
  this.clientConnection?.dispose();
@@ -1973,24 +1749,21 @@ class InMemoryJsonRpcLspClient {
1973
1749
  this.serverToClient?.destroy();
1974
1750
  }
1975
1751
  }
1976
-
1977
- private logError(message: string): void {
1752
+ logError(message) {
1978
1753
  if (this.clientConnection?.console?.error) {
1979
1754
  this.clientConnection.console.error(message);
1980
1755
  return;
1981
1756
  }
1982
1757
  console.error(`[oml-rest] ${message}`);
1983
1758
  }
1984
-
1985
- private startWorkspaceWatcher(): void {
1759
+ startWorkspaceWatcher() {
1986
1760
  if (this.watcherActive) {
1987
1761
  return;
1988
1762
  }
1989
1763
  this.watcherActive = true;
1990
1764
  this.watchDirectory(this.workspaceRoot);
1991
1765
  }
1992
-
1993
- private stopWorkspaceWatcher(): void {
1766
+ stopWorkspaceWatcher() {
1994
1767
  this.watcherActive = false;
1995
1768
  if (this.watcherFlushTimer) {
1996
1769
  clearTimeout(this.watcherFlushTimer);
@@ -2001,8 +1774,7 @@ class InMemoryJsonRpcLspClient {
2001
1774
  }
2002
1775
  this.watchers.clear();
2003
1776
  }
2004
-
2005
- private watchDirectory(dirPath: string): void {
1777
+ watchDirectory(dirPath) {
2006
1778
  const basename = path.basename(dirPath);
2007
1779
  if (basename.startsWith('.') || basename === 'node_modules' || basename === 'out' || basename === 'build') {
2008
1780
  return;
@@ -2019,10 +1791,10 @@ class InMemoryJsonRpcLspClient {
2019
1791
  this.scheduleWorkspaceRefresh();
2020
1792
  });
2021
1793
  this.watchers.add(watcher);
2022
- } catch {
1794
+ }
1795
+ catch {
2023
1796
  return;
2024
1797
  }
2025
-
2026
1798
  try {
2027
1799
  const entries = fsSync.readdirSync(dirPath, { withFileTypes: true });
2028
1800
  for (const entry of entries) {
@@ -2030,22 +1802,21 @@ class InMemoryJsonRpcLspClient {
2030
1802
  this.watchDirectory(path.join(dirPath, entry.name));
2031
1803
  }
2032
1804
  }
2033
- } catch {
1805
+ }
1806
+ catch {
2034
1807
  // Ignore unreadable directories.
2035
1808
  }
2036
1809
  }
2037
-
2038
- private scheduleWorkspaceRefresh(): void {
1810
+ scheduleWorkspaceRefresh() {
2039
1811
  if (this.watcherFlushTimer) {
2040
1812
  return;
2041
1813
  }
2042
1814
  this.watcherFlushTimer = setTimeout(() => {
2043
1815
  this.watcherFlushTimer = undefined;
2044
- void this.refreshWorkspaceOmlDocumentsQueued().catch(() => {});
1816
+ void this.refreshWorkspaceOmlDocumentsQueued().catch(() => { });
2045
1817
  }, 100);
2046
1818
  }
2047
-
2048
- private async refreshWorkspaceOmlDocumentsQueued(): Promise<void> {
1819
+ async refreshWorkspaceOmlDocumentsQueued() {
2049
1820
  if (this.refreshInFlight) {
2050
1821
  this.refreshQueued = true;
2051
1822
  return;
@@ -2053,7 +1824,8 @@ class InMemoryJsonRpcLspClient {
2053
1824
  this.refreshInFlight = true;
2054
1825
  try {
2055
1826
  await this.refreshWorkspaceOmlDocuments();
2056
- } finally {
1827
+ }
1828
+ finally {
2057
1829
  this.refreshInFlight = false;
2058
1830
  }
2059
1831
  if (this.refreshQueued) {
@@ -2061,9 +1833,8 @@ class InMemoryJsonRpcLspClient {
2061
1833
  await this.refreshWorkspaceOmlDocumentsQueued();
2062
1834
  }
2063
1835
  }
2064
-
2065
- async exportAssertedWorkspace(params: Record<string, unknown>): Promise<Record<string, unknown>> {
2066
- const context: RestExportContext = {
1836
+ async exportAssertedWorkspace(params) {
1837
+ const context = {
2067
1838
  workspaceRoot: this.workspaceRoot,
2068
1839
  ensureInitialized: () => this.ensureInitialized(),
2069
1840
  ensureWorkspaceCurrent: () => this.ensureWorkspaceCurrent(),
@@ -2072,8 +1843,7 @@ class InMemoryJsonRpcLspClient {
2072
1843
  };
2073
1844
  return await exportAssertedWorkspace(context, params);
2074
1845
  }
2075
-
2076
- async renderWorkspace(params: Record<string, unknown>): Promise<Record<string, unknown>> {
1846
+ async renderWorkspace(params) {
2077
1847
  await this.ensureInitialized();
2078
1848
  await this.ensureWorkspaceCurrent();
2079
1849
  if (params.only !== true) {
@@ -2095,14 +1865,8 @@ class InMemoryJsonRpcLspClient {
2095
1865
  const outputFromOptions = typeof params.outputDir === 'string' && params.outputDir.trim().length > 0
2096
1866
  ? params.outputDir.trim()
2097
1867
  : (typeof params.web === 'string' && params.web.trim().length > 0 ? params.web.trim() : 'build/web');
2098
- const inputDir = path.resolve(
2099
- workspaceRoot,
2100
- inputFromOptions,
2101
- );
2102
- const outputDir = path.resolve(
2103
- workspaceRoot,
2104
- outputFromOptions,
2105
- );
1868
+ const inputDir = path.resolve(workspaceRoot, inputFromOptions);
1869
+ const outputDir = path.resolve(workspaceRoot, outputFromOptions);
2106
1870
  const contextOption = typeof params.contextOntologyIri === 'string' && params.contextOntologyIri.trim().length > 0
2107
1871
  ? params.contextOntologyIri.trim()
2108
1872
  : (typeof params.context === 'string' && params.context.trim().length > 0 ? params.context.trim() : undefined);
@@ -2111,12 +1875,10 @@ class InMemoryJsonRpcLspClient {
2111
1875
  : undefined;
2112
1876
  const runtime = new MarkdownPreviewRuntime(new MarkdownHandlerRegistry());
2113
1877
  const templateCatalog = await buildTemplateCatalog(workspaceRoot);
2114
- const navigationTemplateCatalog = buildNavigationTemplateCatalog(
2115
- Array.from(templateCatalog.values()).flatMap((entries) => entries.map((entry) => entry.definition))
2116
- );
1878
+ const navigationTemplateCatalog = buildNavigationTemplateCatalog(Array.from(templateCatalog.values()).flatMap((entries) => entries.map((entry) => entry.definition)));
2117
1879
  const staticAssets = await writeStaticAssets(outputDir);
2118
- const reasoningService = this.runtime.Oml.reasoning.ReasoningService as any;
2119
- const ontologyIndex = getOntologyModelIndex(this.runtime.shared as any);
1880
+ const reasoningService = this.runtime.Oml.reasoning.ReasoningService;
1881
+ const ontologyIndex = getOntologyModelIndex(this.runtime.shared);
2120
1882
  const executor = new MarkdownExecutor({
2121
1883
  ensureContext: (modelUri) => reasoningService.ensureQueryContext(modelUri),
2122
1884
  resolveContextIri: (modelUri) => reasoningService.getContextIri(modelUri),
@@ -2126,16 +1888,10 @@ class InMemoryJsonRpcLspClient {
2126
1888
  });
2127
1889
  const markdownFiles = await findFilesByExtension(inputDir, '.md');
2128
1890
  const wikiPageIndex = buildWikiPageIndex(markdownFiles, inputDir, outputDir);
2129
- const workspaceAssetFiles = new Set<string>();
2130
- const aliasesByIri = new Map<string, string>();
2131
- const renderJobs: Array<{
2132
- htmlPath: string;
2133
- renderedHtml: string;
2134
- blockManifest: Array<{ blockId: string; path: string }>;
2135
- blockResults: ReadonlyArray<RenderedBlockResult>;
2136
- wikiLinkHrefByKey: Record<string, string>;
2137
- }> = [];
2138
- const contextModelUris = new Set<string>();
1891
+ const workspaceAssetFiles = new Set();
1892
+ const aliasesByIri = new Map();
1893
+ const renderJobs = [];
1894
+ const contextModelUris = new Set();
2139
1895
  let filesRendered = 0;
2140
1896
  let blockArtifactFiles = 0;
2141
1897
  for (const markdownPath of markdownFiles) {
@@ -2144,11 +1900,9 @@ class InMemoryJsonRpcLspClient {
2144
1900
  continue;
2145
1901
  }
2146
1902
  const frontMatter = extractLeadingFrontMatter(markdown);
2147
- const contextOntologyIri = normalizeContextOntologyIri(
2148
- frontMatter?.contextOntologyIri
2149
- ?? frontMatterString(frontMatter?.data, 'ontologyIri')
2150
- ?? frontMatterString(frontMatter?.data, 'ontology')
2151
- );
1903
+ const contextOntologyIri = normalizeContextOntologyIri(frontMatter?.contextOntologyIri
1904
+ ?? frontMatterString(frontMatter?.data, 'ontologyIri')
1905
+ ?? frontMatterString(frontMatter?.data, 'ontology'));
2152
1906
  const contextMemberIri = frontMatterString(frontMatter?.data, 'memberIri')
2153
1907
  ?? frontMatterString(frontMatter?.data, 'contextMemberIri');
2154
1908
  const referencingUri = pathToFileURL(markdownPath).toString();
@@ -2181,7 +1935,7 @@ class InMemoryJsonRpcLspClient {
2181
1935
  workspaceAssetFiles.add(asset);
2182
1936
  }
2183
1937
  const executableBlocks = toExecutableBlocks(prepared.codeBlocks);
2184
- const optionsByBlockId = new Map(prepared.codeBlocks.map((block) => [block.id, block.options] as const));
1938
+ const optionsByBlockId = new Map(prepared.codeBlocks.map((block) => [block.id, block.options]));
2185
1939
  const blockResults = executableBlocks.length === 0 || !contextModelUri
2186
1940
  ? []
2187
1941
  : (await executor.executeBlocks({
@@ -2191,7 +1945,7 @@ class InMemoryJsonRpcLspClient {
2191
1945
  })).results.map((result) => ({
2192
1946
  ...result,
2193
1947
  options: optionsByBlockId.get(result.blockId),
2194
- } as RenderedBlockResult));
1948
+ }));
2195
1949
  const rewrittenBlockResults = blockResults.map((result) => rewriteBlockResultAssetPaths(result, {
2196
1950
  workspaceRoot,
2197
1951
  inputRoot: inputDir,
@@ -2211,7 +1965,7 @@ class InMemoryJsonRpcLspClient {
2211
1965
  });
2212
1966
  filesRendered += 1;
2213
1967
  }
2214
- const renderedNavigationPages = new Set<string>();
1968
+ const renderedNavigationPages = new Set();
2215
1969
  for (const modelUri of contextModelUris) {
2216
1970
  await reasoningService.ensureQueryContext(modelUri);
2217
1971
  const membersToTypes = await queryMemberTypes(reasoningService, modelUri);
@@ -2231,7 +1985,7 @@ class InMemoryJsonRpcLspClient {
2231
1985
  renderedNavigationPages.add(targetFile);
2232
1986
  const contextOntologyIri = inferOntologyIriFromMemberIri(memberIri);
2233
1987
  const sourceDocumentUri = resolution.template.sourceUri?.trim() || pathToFileURL(path.join(workspaceRoot, 'index.md')).toString();
2234
- const invocation: TemplateInvocation = {
1988
+ const invocation = {
2235
1989
  templateId: resolution.template.id,
2236
1990
  mode: 'navigation',
2237
1991
  context: {
@@ -2279,7 +2033,7 @@ class InMemoryJsonRpcLspClient {
2279
2033
  workspaceAssetFiles.add(asset);
2280
2034
  }
2281
2035
  const executableBlocks = toExecutableBlocks(prepared.codeBlocks);
2282
- const optionsByBlockId = new Map(prepared.codeBlocks.map((block) => [block.id, block.options] as const));
2036
+ const optionsByBlockId = new Map(prepared.codeBlocks.map((block) => [block.id, block.options]));
2283
2037
  const blockResults = executableBlocks.length === 0
2284
2038
  ? []
2285
2039
  : (await executor.executeBlocks({
@@ -2289,7 +2043,7 @@ class InMemoryJsonRpcLspClient {
2289
2043
  })).results.map((result) => ({
2290
2044
  ...result,
2291
2045
  options: optionsByBlockId.get(result.blockId),
2292
- } as RenderedBlockResult));
2046
+ }));
2293
2047
  const rewrittenBlockResults = blockResults.map((result) => rewriteBlockResultAssetPaths(result, {
2294
2048
  workspaceRoot,
2295
2049
  inputRoot: inputDir,
@@ -2312,15 +2066,7 @@ class InMemoryJsonRpcLspClient {
2312
2066
  for (const job of renderJobs) {
2313
2067
  const runtimeScriptRelative = toRelativeWebPath(path.dirname(job.htmlPath), staticAssets.runtimeScriptFile);
2314
2068
  const stylesheetRelative = toRelativeWebPath(path.dirname(job.htmlPath), staticAssets.stylesheetFile);
2315
- const html = wrapHtml(
2316
- job.renderedHtml,
2317
- runtimeScriptRelative,
2318
- stylesheetRelative,
2319
- job.blockManifest,
2320
- job.blockResults,
2321
- job.wikiLinkHrefByKey,
2322
- buildIriAliasMapForPage(aliasesByIri, job.htmlPath),
2323
- );
2069
+ const html = wrapHtml(job.renderedHtml, runtimeScriptRelative, stylesheetRelative, job.blockManifest, job.blockResults, job.wikiLinkHrefByKey, buildIriAliasMapForPage(aliasesByIri, job.htmlPath));
2324
2070
  await fs.mkdir(path.dirname(job.htmlPath), { recursive: true });
2325
2071
  await fs.writeFile(job.htmlPath, html, 'utf-8');
2326
2072
  }
@@ -2340,8 +2086,9 @@ class InMemoryJsonRpcLspClient {
2340
2086
  await fs.mkdir(path.dirname(target), { recursive: true });
2341
2087
  try {
2342
2088
  await fs.copyFile(file, target);
2343
- } catch (error) {
2344
- const code = (error as NodeJS.ErrnoException).code;
2089
+ }
2090
+ catch (error) {
2091
+ const code = error.code;
2345
2092
  if (code !== 'ENOENT') {
2346
2093
  throw error;
2347
2094
  }
@@ -2349,8 +2096,7 @@ class InMemoryJsonRpcLspClient {
2349
2096
  }
2350
2097
  return { success: true, filesRendered, outputDir, blockArtifactFiles };
2351
2098
  }
2352
-
2353
- private async ensureWorkspaceCurrent(): Promise<void> {
2099
+ async ensureWorkspaceCurrent() {
2354
2100
  if (this.useExternalRuntime) {
2355
2101
  return;
2356
2102
  }
@@ -2364,23 +2110,21 @@ class InMemoryJsonRpcLspClient {
2364
2110
  }
2365
2111
  await this.waitForWatcherRefreshIdle();
2366
2112
  }
2367
-
2368
- private async waitForWatcherRefreshIdle(): Promise<void> {
2113
+ async waitForWatcherRefreshIdle() {
2369
2114
  while (this.watcherFlushTimer !== undefined || this.refreshInFlight || this.refreshQueued) {
2370
2115
  await new Promise((resolve) => setTimeout(resolve, 10));
2371
2116
  }
2372
2117
  }
2373
-
2374
- private async refreshWorkspaceOmlDocuments(): Promise<void> {
2118
+ async refreshWorkspaceOmlDocuments() {
2375
2119
  const workspaceRoot = path.resolve(this.workspaceRoot);
2376
2120
  const omlFiles = await findFilesByExtension(workspaceRoot, '.oml');
2377
2121
  const changedUris = omlFiles.map((filePath) => URI.parse(pathToFileURL(filePath).toString()));
2378
2122
  const documents = this.runtime.shared.workspace.LangiumDocuments;
2379
2123
  const allDocs = documents.all ?? [];
2380
- const iterable: any[] = Array.isArray(allDocs)
2124
+ const iterable = Array.isArray(allDocs)
2381
2125
  ? allDocs
2382
- : (typeof allDocs?.toArray === 'function' ? allDocs.toArray() : Array.from(allDocs as Iterable<any>));
2383
- const currentOmlUris = new Set<string>();
2126
+ : (typeof allDocs?.toArray === 'function' ? allDocs.toArray() : Array.from(allDocs));
2127
+ const currentOmlUris = new Set();
2384
2128
  for (const document of iterable) {
2385
2129
  const uri = String(document?.uri ?? '').trim();
2386
2130
  if (!uri.toLowerCase().endsWith('.oml')) {
@@ -2396,23 +2140,22 @@ class InMemoryJsonRpcLspClient {
2396
2140
  const builder = this.runtime.shared.workspace.DocumentBuilder;
2397
2141
  await builder.update(changedUris, deletedUris);
2398
2142
  }
2399
-
2400
- private async ensureInitialized(): Promise<void> {
2143
+ async ensureInitialized() {
2401
2144
  if (this.initPromise) {
2402
2145
  await this.initPromise;
2403
2146
  return;
2404
2147
  }
2405
-
2406
2148
  if (this.useExternalRuntime) {
2407
2149
  this.initPromise = (async () => {
2408
2150
  const workspace = this.runtime.shared.workspace.WorkspaceManager;
2409
2151
  await workspace.ready;
2410
2152
  })();
2411
- } else {
2153
+ }
2154
+ else {
2412
2155
  const rootPath = path.resolve(this.workspaceRoot);
2413
2156
  const rootUri = pathToFileURL(rootPath).toString();
2414
2157
  this.initPromise = (async () => {
2415
- await withTimeout(this.clientConnection!.sendRequest('initialize', {
2158
+ await withTimeout(this.clientConnection.sendRequest('initialize', {
2416
2159
  processId: process.pid,
2417
2160
  rootUri,
2418
2161
  workspaceFolders: [{ uri: rootUri, name: path.basename(rootPath) }],
@@ -2420,25 +2163,23 @@ class InMemoryJsonRpcLspClient {
2420
2163
  clientInfo: { name: 'oml-rest-server', version: '1.0' },
2421
2164
  initializationOptions: {},
2422
2165
  }), this.requestTimeoutMs, 'initialize');
2423
- this.clientConnection!.sendNotification('initialized', {});
2166
+ this.clientConnection.sendNotification('initialized', {});
2424
2167
  })();
2425
2168
  }
2426
2169
  await this.initPromise;
2427
2170
  }
2428
2171
  }
2429
-
2430
- export async function startOmlRestServer(options: OmlRestServerOptions): Promise<{ server: http.Server; updateToken: (token: string) => void }> {
2172
+ export async function startOmlRestServer(options) {
2431
2173
  const workspaceRoot = options.workspaceRoot ? path.resolve(options.workspaceRoot) : process.cwd();
2432
- const client = new InMemoryJsonRpcLspClient(
2433
- workspaceRoot,
2434
- options.requestTimeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS,
2435
- options.watchWorkspace === true,
2436
- options.runtime,
2437
- );
2174
+ const client = new InMemoryJsonRpcLspClient(workspaceRoot, options.requestTimeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS, options.watchWorkspace === true, options.runtime);
2438
2175
  const openApiSpec = createOpenApiSpec(options.host, options.port);
2439
- let currentAccessToken: string | undefined = options.authToken;
2440
- const getAccessToken = (): string | undefined => currentAccessToken;
2441
-
2176
+ let currentAccessToken = options.authToken;
2177
+ const featureGate = options.featureGate;
2178
+ if (featureGate && currentAccessToken) {
2179
+ featureGate.setAccessToken(currentAccessToken);
2180
+ await featureGate.primeEntitlements();
2181
+ }
2182
+ const getAccessToken = () => currentAccessToken;
2442
2183
  const server = http.createServer(async (req, res) => {
2443
2184
  const requestId = randomUUID();
2444
2185
  try {
@@ -2446,7 +2187,6 @@ export async function startOmlRestServer(options: OmlRestServerOptions): Promise
2446
2187
  const rawUrl = req.url ?? '/';
2447
2188
  const parsed = new URL(rawUrl, `http://${req.headers.host ?? 'localhost'}`);
2448
2189
  const pathname = parsed.pathname;
2449
-
2450
2190
  if (method === 'GET' && pathname === '/health') {
2451
2191
  jsonResponse(res, 200, {
2452
2192
  status: 'ok',
@@ -2458,17 +2198,14 @@ export async function startOmlRestServer(options: OmlRestServerOptions): Promise
2458
2198
  });
2459
2199
  return;
2460
2200
  }
2461
-
2462
2201
  if (method === 'GET' && pathname === '/') {
2463
2202
  htmlResponse(res, 200, createSparqlWorkbenchPage(workspaceRoot));
2464
2203
  return;
2465
2204
  }
2466
-
2467
2205
  if (method === 'GET' && pathname === '/openapi.json') {
2468
2206
  jsonResponse(res, 200, openApiSpec);
2469
2207
  return;
2470
2208
  }
2471
-
2472
2209
  if (method === 'GET' && pathname === '/v0/models') {
2473
2210
  const workspaceModelFiles = await listWorkspaceModelFiles(workspaceRoot);
2474
2211
  jsonResponse(res, 200, {
@@ -2477,41 +2214,51 @@ export async function startOmlRestServer(options: OmlRestServerOptions): Promise
2477
2214
  });
2478
2215
  return;
2479
2216
  }
2480
-
2481
2217
  if (method === 'POST') {
2482
2218
  const body = await readJsonBody(req);
2483
- const route = await dispatchRestRoute(method, pathname, body, client);
2484
- if (route) {
2219
+ const operationId = resolveRestOperationId(method, pathname);
2220
+ if (operationId) {
2221
+ const requiredFeature = requiredFeatureForRestOperation(operationId);
2222
+ const result = (featureGate && requiredFeature)
2223
+ ? await featureGate.runWithFeature(requiredFeature, () => dispatchRestOperation(operationId, body, client), { transport: 'rest', operationId })
2224
+ : await dispatchRestOperation(operationId, body, client);
2485
2225
  jsonResponse(res, 200, {
2486
2226
  ok: true,
2487
- method: `oml/rest/${route.operationId}`,
2488
- result: route.result,
2227
+ method: `oml/rest/${operationId}`,
2228
+ result,
2489
2229
  requestId
2490
2230
  });
2491
2231
  return;
2492
2232
  }
2493
2233
  }
2494
-
2495
2234
  jsonResponse(res, 404, { error: `No route for ${method} ${pathname}.`, requestId });
2496
- } catch (error) {
2235
+ }
2236
+ catch (error) {
2497
2237
  const message = error instanceof Error ? error.message : String(error);
2498
- const status = error instanceof SyntaxError || message.includes('Request body exceeds') ? 400 : 500;
2238
+ const status = error instanceof OmlAccessError
2239
+ ? error.statusCode
2240
+ : (error instanceof SyntaxError || message.includes('Request body exceeds') ? 400 : 500);
2499
2241
  jsonResponse(res, status, { error: message, requestId });
2500
2242
  }
2501
2243
  });
2502
-
2503
2244
  server.on('close', () => {
2504
2245
  void client.close();
2246
+ void featureGate?.dispose();
2505
2247
  currentAccessToken = undefined;
2506
2248
  });
2507
-
2508
- await new Promise<void>((resolve, reject) => {
2249
+ await new Promise((resolve, reject) => {
2509
2250
  server.once('error', reject);
2510
2251
  server.listen(options.port, options.host, () => resolve());
2511
2252
  });
2512
-
2513
2253
  return {
2514
2254
  server,
2515
- updateToken: (token: string) => { currentAccessToken = token; },
2255
+ updateToken: async (token) => {
2256
+ currentAccessToken = token;
2257
+ featureGate?.setAccessToken(token);
2258
+ if (featureGate) {
2259
+ await featureGate.primeEntitlements();
2260
+ }
2261
+ },
2516
2262
  };
2517
2263
  }
2264
+ //# sourceMappingURL=server.js.map