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