@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.
- package/out/auth/feature-gate.d.ts +23 -0
- package/out/auth/feature-gate.js +172 -0
- package/out/auth/feature-gate.js.map +1 -0
- package/out/auth/feature-policy.d.ts +13 -0
- package/out/auth/feature-policy.js +38 -0
- package/out/auth/feature-policy.js.map +1 -0
- package/out/cli.d.ts +1 -0
- package/{src/cli.ts → out/cli.js} +45 -55
- 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 +15 -0
- package/{src/lsp/language-server.ts → out/lsp/language-server.js} +95 -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 +47 -0
- package/{src/rest/routes.ts → out/rest/routes.js} +40 -35
- package/out/rest/routes.js.map +1 -0
- package/out/rest/server.d.ts +17 -0
- package/{src/rest/server.ts → out/rest/server.js} +232 -485
- 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,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
|
|
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
|
|
47
|
-
import { createOpenApiSpec,
|
|
48
|
-
import {
|
|
49
|
-
|
|
50
|
-
|
|
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 =
|
|
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
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
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
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
.replace(/'/g, ''');
|
|
61
|
+
function escapeHtml(value) {
|
|
62
|
+
return value
|
|
63
|
+
.replace(/&/g, '&')
|
|
64
|
+
.replace(/</g, '<')
|
|
65
|
+
.replace(/>/g, '>')
|
|
66
|
+
.replace(/"/g, '"')
|
|
67
|
+
.replace(/'/g, ''');
|
|
122
68
|
}
|
|
123
|
-
|
|
124
|
-
|
|
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
|
-
|
|
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
|
|
951
|
+
return typeof parsed === 'object' && parsed !== null ? parsed : {};
|
|
1011
952
|
}
|
|
1012
|
-
|
|
1013
|
-
|
|
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
|
-
(
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
(error
|
|
1024
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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) => {
|
|
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
|
|
1166
|
+
let resolved;
|
|
1316
1167
|
if (trimmed.startsWith('workspace:/')) {
|
|
1317
1168
|
resolved = resolveWorkspacePath(context.workspaceRoot, trimmed);
|
|
1318
|
-
}
|
|
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
|
-
}
|
|
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 }
|
|
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
|
|
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
|
-
}
|
|
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
|
-
}
|
|
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, '&')
|
|
1482
1314
|
.replace(/</g, '<')
|
|
@@ -1484,12 +1316,10 @@ function escapeAttribute(value: string): string {
|
|
|
1484
1316
|
.replace(/"/g, '"')
|
|
1485
1317
|
.replace(/'/g, ''');
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
1371
|
+
let parsed;
|
|
1550
1372
|
try {
|
|
1551
1373
|
parsed = new URL(normalized);
|
|
1552
|
-
}
|
|
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
|
|
1407
|
+
return new Map();
|
|
1589
1408
|
}
|
|
1590
|
-
const byMember = new Map
|
|
1591
|
-
for (const row of queryResult.rows
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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) {
|
|
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
|
-
}
|
|
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
|
|
1491
|
+
const iterable = Array.isArray(allDocs)
|
|
1701
1492
|
? allDocs
|
|
1702
|
-
: (typeof allDocs?.toArray === 'function' ? allDocs.toArray() : Array.from(allDocs
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
-
}
|
|
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
|
|
1809
|
-
const 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
|
|
1595
|
+
const iterable = Array.isArray(allDocs)
|
|
1812
1596
|
? allDocs
|
|
1813
|
-
: (typeof allDocs?.toArray === 'function' ? allDocs.toArray() : Array.from(allDocs
|
|
1814
|
-
const modelUris
|
|
1815
|
-
const candidateByIri = new Map
|
|
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
|
-
}
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
}
|
|
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
|
-
}
|
|
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
|
-
}
|
|
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
|
-
}
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
2119
|
-
const ontologyIndex = getOntologyModelIndex(this.runtime.shared
|
|
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
|
|
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>();
|
|
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?.
|
|
2149
|
-
|
|
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]
|
|
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
|
-
}
|
|
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
|
|
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
|
|
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]
|
|
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
|
-
}
|
|
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
|
-
}
|
|
2344
|
-
|
|
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
|
|
2124
|
+
const iterable = Array.isArray(allDocs)
|
|
2381
2125
|
? allDocs
|
|
2382
|
-
: (typeof allDocs?.toArray === 'function' ? allDocs.toArray() : Array.from(allDocs
|
|
2383
|
-
const currentOmlUris = new Set
|
|
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
|
-
}
|
|
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
|
|
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
|
|
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
|
|
2440
|
-
const
|
|
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
|
|
2484
|
-
if (
|
|
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/${
|
|
2488
|
-
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
|
-
}
|
|
2235
|
+
}
|
|
2236
|
+
catch (error) {
|
|
2497
2237
|
const message = error instanceof Error ? error.message : String(error);
|
|
2498
|
-
const status = error instanceof
|
|
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
|
|
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
|