@oml/markdown 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +39 -0
- package/out/index.d.ts +2 -0
- package/out/index.js +4 -0
- package/out/index.js.map +1 -0
- package/out/md/index.d.ts +6 -0
- package/out/md/index.js +8 -0
- package/out/md/index.js.map +1 -0
- package/out/md/md-execution.d.ts +33 -0
- package/out/md/md-execution.js +3 -0
- package/out/md/md-execution.js.map +1 -0
- package/out/md/md-executor.d.ts +21 -0
- package/out/md/md-executor.js +498 -0
- package/out/md/md-executor.js.map +1 -0
- package/out/md/md-frontmatter.d.ts +4 -0
- package/out/md/md-frontmatter.js +48 -0
- package/out/md/md-frontmatter.js.map +1 -0
- package/out/md/md-registry.d.ts +7 -0
- package/out/md/md-registry.js +19 -0
- package/out/md/md-registry.js.map +1 -0
- package/out/md/md-runtime.d.ts +10 -0
- package/out/md/md-runtime.js +166 -0
- package/out/md/md-runtime.js.map +1 -0
- package/out/md/md-types.d.ts +40 -0
- package/out/md/md-types.js +3 -0
- package/out/md/md-types.js.map +1 -0
- package/out/md/md-yaml.d.ts +1 -0
- package/out/md/md-yaml.js +15 -0
- package/out/md/md-yaml.js.map +1 -0
- package/out/renderers/chart-renderer.d.ts +6 -0
- package/out/renderers/chart-renderer.js +392 -0
- package/out/renderers/chart-renderer.js.map +1 -0
- package/out/renderers/diagram-renderer.d.ts +7 -0
- package/out/renderers/diagram-renderer.js +2354 -0
- package/out/renderers/diagram-renderer.js.map +1 -0
- package/out/renderers/graph-renderer.d.ts +6 -0
- package/out/renderers/graph-renderer.js +1384 -0
- package/out/renderers/graph-renderer.js.map +1 -0
- package/out/renderers/index.d.ts +14 -0
- package/out/renderers/index.js +16 -0
- package/out/renderers/index.js.map +1 -0
- package/out/renderers/list-renderer.d.ts +6 -0
- package/out/renderers/list-renderer.js +252 -0
- package/out/renderers/list-renderer.js.map +1 -0
- package/out/renderers/matrix-renderer.d.ts +14 -0
- package/out/renderers/matrix-renderer.js +498 -0
- package/out/renderers/matrix-renderer.js.map +1 -0
- package/out/renderers/message-renderer.d.ts +6 -0
- package/out/renderers/message-renderer.js +14 -0
- package/out/renderers/message-renderer.js.map +1 -0
- package/out/renderers/registry.d.ts +9 -0
- package/out/renderers/registry.js +41 -0
- package/out/renderers/registry.js.map +1 -0
- package/out/renderers/renderer.d.ts +28 -0
- package/out/renderers/renderer.js +61 -0
- package/out/renderers/renderer.js.map +1 -0
- package/out/renderers/table-editor-renderer.d.ts +4 -0
- package/out/renderers/table-editor-renderer.js +9 -0
- package/out/renderers/table-editor-renderer.js.map +1 -0
- package/out/renderers/table-renderer.d.ts +95 -0
- package/out/renderers/table-renderer.js +1571 -0
- package/out/renderers/table-renderer.js.map +1 -0
- package/out/renderers/text-renderer.d.ts +7 -0
- package/out/renderers/text-renderer.js +219 -0
- package/out/renderers/text-renderer.js.map +1 -0
- package/out/renderers/tree-renderer.d.ts +4 -0
- package/out/renderers/tree-renderer.js +9 -0
- package/out/renderers/tree-renderer.js.map +1 -0
- package/out/renderers/types.d.ts +18 -0
- package/out/renderers/types.js +3 -0
- package/out/renderers/types.js.map +1 -0
- package/out/renderers/wikilink-utils.d.ts +6 -0
- package/out/renderers/wikilink-utils.js +100 -0
- package/out/renderers/wikilink-utils.js.map +1 -0
- package/out/static/browser-runtime.bundle.js +74155 -0
- package/out/static/browser-runtime.bundle.js.map +7 -0
- package/out/static/browser-runtime.d.ts +1 -0
- package/out/static/browser-runtime.js +218 -0
- package/out/static/browser-runtime.js.map +1 -0
- package/out/static/index.d.ts +1 -0
- package/out/static/index.js +3 -0
- package/out/static/index.js.map +1 -0
- package/out/static/runtime-assets.d.ts +2 -0
- package/out/static/runtime-assets.js +174 -0
- package/out/static/runtime-assets.js.map +1 -0
- package/package.json +74 -0
- package/src/index.ts +4 -0
- package/src/md/index.ts +8 -0
- package/src/md/md-execution.ts +51 -0
- package/src/md/md-executor.ts +598 -0
- package/src/md/md-frontmatter.ts +53 -0
- package/src/md/md-registry.ts +22 -0
- package/src/md/md-runtime.ts +191 -0
- package/src/md/md-types.ts +48 -0
- package/src/md/md-yaml.ts +17 -0
- package/src/renderers/chart-renderer.ts +473 -0
- package/src/renderers/diagram-renderer.ts +2520 -0
- package/src/renderers/graph-renderer.ts +1653 -0
- package/src/renderers/index.ts +16 -0
- package/src/renderers/list-renderer.ts +289 -0
- package/src/renderers/matrix-renderer.ts +616 -0
- package/src/renderers/message-renderer.ts +18 -0
- package/src/renderers/registry.ts +45 -0
- package/src/renderers/renderer.ts +84 -0
- package/src/renderers/table-editor-renderer.ts +8 -0
- package/src/renderers/table-renderer.ts +1868 -0
- package/src/renderers/text-renderer.ts +252 -0
- package/src/renderers/tree-renderer.ts +7 -0
- package/src/renderers/types.ts +22 -0
- package/src/renderers/wikilink-utils.ts +108 -0
- package/src/static/browser-runtime.ts +249 -0
- package/src/static/index.ts +3 -0
- package/src/static/runtime-assets.ts +175 -0
package/README.md
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# OML Markdown
|
|
2
|
+
|
|
3
|
+
`@oml/markdown` contains the markdown layer for OML.
|
|
4
|
+
|
|
5
|
+
It is responsible for:
|
|
6
|
+
|
|
7
|
+
- markdown parsing and front matter handling
|
|
8
|
+
- markdown execution request/result types
|
|
9
|
+
- markdown block execution through `MarkdownExecutor`
|
|
10
|
+
- browser runtime assets
|
|
11
|
+
- shared markdown renderers
|
|
12
|
+
|
|
13
|
+
## Package Boundary
|
|
14
|
+
|
|
15
|
+
- `@oml/language` owns the language layer
|
|
16
|
+
- `@oml/owl` owns the semantic web layer
|
|
17
|
+
- `@oml/markdown` depends on `@oml/owl`
|
|
18
|
+
|
|
19
|
+
This package does not implement the core OML language services directly.
|
|
20
|
+
|
|
21
|
+
## Main Exports
|
|
22
|
+
|
|
23
|
+
- `MarkdownPreviewRuntime`
|
|
24
|
+
- `MarkdownExecutionRequest`
|
|
25
|
+
- `MarkdownExecutor`
|
|
26
|
+
- markdown execution/result types
|
|
27
|
+
- `STATIC_MARKDOWN_RUNTIME_BUNDLE_FILE`
|
|
28
|
+
- `STATIC_MARKDOWN_RUNTIME_CSS`
|
|
29
|
+
- shared renderers via `@oml/markdown/renderers`
|
|
30
|
+
|
|
31
|
+
## Instructions
|
|
32
|
+
|
|
33
|
+
Use this package when you need markdown preparation, markdown execution contracts, or OML markdown block execution on top of `@oml/owl`.
|
|
34
|
+
|
|
35
|
+
Typical usage:
|
|
36
|
+
|
|
37
|
+
```ts
|
|
38
|
+
import { MarkdownExecutor } from '@oml/markdown';
|
|
39
|
+
```
|
package/out/index.d.ts
ADDED
package/out/index.js
ADDED
package/out/index.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,qDAAqD;AAErD,cAAc,eAAe,CAAC;AAC9B,cAAc,mBAAmB,CAAC"}
|
package/out/md/index.js
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
// Copyright (c) 2026 Modelware. All rights reserved.
|
|
2
|
+
export * from './md-types.js';
|
|
3
|
+
export * from './md-execution.js';
|
|
4
|
+
export * from './md-frontmatter.js';
|
|
5
|
+
export * from './md-registry.js';
|
|
6
|
+
export * from './md-runtime.js';
|
|
7
|
+
export * from './md-executor.js';
|
|
8
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/md/index.ts"],"names":[],"mappings":"AAAA,qDAAqD;AAErD,cAAc,eAAe,CAAC;AAC9B,cAAc,mBAAmB,CAAC;AAClC,cAAc,qBAAqB,CAAC;AACpC,cAAc,kBAAkB,CAAC;AACjC,cAAc,iBAAiB,CAAC;AAChC,cAAc,kBAAkB,CAAC"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
export declare const MarkdownExecutionRequest = "oml/markdown/execute";
|
|
2
|
+
export type MdBlockKind = 'table' | 'tree' | 'graph' | 'chart' | 'diagram' | 'list' | 'text' | 'matrix' | 'table-editor';
|
|
3
|
+
export interface MdExecutableBlock {
|
|
4
|
+
id: string;
|
|
5
|
+
kind: MdBlockKind;
|
|
6
|
+
source: string;
|
|
7
|
+
meta?: string;
|
|
8
|
+
options?: Record<string, unknown>;
|
|
9
|
+
lineStart: number;
|
|
10
|
+
lineEnd: number;
|
|
11
|
+
}
|
|
12
|
+
export interface MdExecuteBlocksParams {
|
|
13
|
+
documentUri: string;
|
|
14
|
+
modelUri?: string;
|
|
15
|
+
blocks: MdExecutableBlock[];
|
|
16
|
+
}
|
|
17
|
+
export type MdExecutionStatus = 'ok' | 'error' | 'unimplemented';
|
|
18
|
+
export interface MdTablePayload {
|
|
19
|
+
columns: string[];
|
|
20
|
+
rows: string[][];
|
|
21
|
+
}
|
|
22
|
+
export interface MdBlockExecutionResult {
|
|
23
|
+
blockId: string;
|
|
24
|
+
kind: MdBlockKind;
|
|
25
|
+
status: MdExecutionStatus;
|
|
26
|
+
format: 'table' | 'message';
|
|
27
|
+
payload?: MdTablePayload;
|
|
28
|
+
message?: string;
|
|
29
|
+
}
|
|
30
|
+
export interface MdExecuteBlocksResult {
|
|
31
|
+
results: MdBlockExecutionResult[];
|
|
32
|
+
contextQuadCount?: number;
|
|
33
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"md-execution.js","sourceRoot":"","sources":["../../src/md/md-execution.ts"],"names":[],"mappings":"AAAA,qDAAqD;AAErD,MAAM,CAAC,MAAM,wBAAwB,GAAG,sBAAsB,CAAC"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { type OwlConstructResult, type OwlQueryResult } from '@oml/owl';
|
|
2
|
+
import type { MdExecuteBlocksParams, MdExecuteBlocksResult } from './md-execution.js';
|
|
3
|
+
export interface MarkdownExecutionBackend {
|
|
4
|
+
ensureContext(modelUri: string): Promise<void>;
|
|
5
|
+
resolveContextIri(modelUri: string): string;
|
|
6
|
+
countContextQuads?: (modelUri: string) => Promise<number>;
|
|
7
|
+
query(modelUri: string, sparql: string): Promise<OwlQueryResult>;
|
|
8
|
+
construct(modelUri: string, sparql: string): Promise<OwlConstructResult>;
|
|
9
|
+
}
|
|
10
|
+
export declare class MarkdownExecutor {
|
|
11
|
+
private readonly backend;
|
|
12
|
+
constructor(backend: MarkdownExecutionBackend);
|
|
13
|
+
executeBlocks(params: MdExecuteBlocksParams): Promise<MdExecuteBlocksResult>;
|
|
14
|
+
private executeMatrixBlock;
|
|
15
|
+
private executeTableBlock;
|
|
16
|
+
private executeGraphBlock;
|
|
17
|
+
private executeDiagramBlock;
|
|
18
|
+
private executeTreeBlock;
|
|
19
|
+
private executeTextBlock;
|
|
20
|
+
private executeListBlock;
|
|
21
|
+
}
|
|
@@ -0,0 +1,498 @@
|
|
|
1
|
+
// Copyright (c) 2026 Modelware. All rights reserved.
|
|
2
|
+
import { deriveSelectQueryFromShape } from '@oml/owl';
|
|
3
|
+
export class MarkdownExecutor {
|
|
4
|
+
constructor(backend) {
|
|
5
|
+
this.backend = backend;
|
|
6
|
+
}
|
|
7
|
+
async executeBlocks(params) {
|
|
8
|
+
const resolvedModelUri = params.modelUri?.trim() || undefined;
|
|
9
|
+
const contextQuadCount = resolvedModelUri && this.backend.countContextQuads
|
|
10
|
+
? await this.backend.countContextQuads(resolvedModelUri)
|
|
11
|
+
: undefined;
|
|
12
|
+
if (resolvedModelUri) {
|
|
13
|
+
await this.backend.ensureContext(resolvedModelUri);
|
|
14
|
+
}
|
|
15
|
+
const results = [];
|
|
16
|
+
for (const block of params.blocks) {
|
|
17
|
+
try {
|
|
18
|
+
if (block.kind === 'table' || block.kind === 'table-editor' || block.kind === 'chart') {
|
|
19
|
+
results.push(await this.executeTableBlock(block.kind, resolvedModelUri, block.id, block.source, block.options));
|
|
20
|
+
continue;
|
|
21
|
+
}
|
|
22
|
+
if (block.kind === 'matrix') {
|
|
23
|
+
results.push(await this.executeMatrixBlock(resolvedModelUri, block.id, block.source));
|
|
24
|
+
continue;
|
|
25
|
+
}
|
|
26
|
+
if (block.kind === 'tree') {
|
|
27
|
+
results.push(await this.executeTreeBlock(resolvedModelUri, block.id, block.source, block.options));
|
|
28
|
+
continue;
|
|
29
|
+
}
|
|
30
|
+
if (block.kind === 'graph') {
|
|
31
|
+
results.push(await this.executeGraphBlock(resolvedModelUri, block.id, block.source));
|
|
32
|
+
continue;
|
|
33
|
+
}
|
|
34
|
+
if (block.kind === 'diagram') {
|
|
35
|
+
results.push(await this.executeDiagramBlock(resolvedModelUri, block.id, block.source));
|
|
36
|
+
continue;
|
|
37
|
+
}
|
|
38
|
+
if (block.kind === 'text') {
|
|
39
|
+
results.push(await this.executeTextBlock(resolvedModelUri, block.id, block.source));
|
|
40
|
+
continue;
|
|
41
|
+
}
|
|
42
|
+
if (block.kind === 'list') {
|
|
43
|
+
results.push(await this.executeListBlock(resolvedModelUri, block.id, block.source));
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
46
|
+
results.push({
|
|
47
|
+
blockId: block.id,
|
|
48
|
+
kind: block.kind,
|
|
49
|
+
status: 'unimplemented',
|
|
50
|
+
format: 'message',
|
|
51
|
+
message: `${block.kind} blocks are not implemented yet.`
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
catch (error) {
|
|
55
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
56
|
+
results.push({
|
|
57
|
+
blockId: block.id,
|
|
58
|
+
kind: block.kind,
|
|
59
|
+
status: 'error',
|
|
60
|
+
format: 'message',
|
|
61
|
+
message: message || `${block.kind} block execution failed.`
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return { results, contextQuadCount };
|
|
66
|
+
}
|
|
67
|
+
async executeMatrixBlock(modelUri, blockId, source) {
|
|
68
|
+
if (!modelUri) {
|
|
69
|
+
return missingContext(blockId, 'matrix');
|
|
70
|
+
}
|
|
71
|
+
const sparql = preprocessQuerySource(this.backend, modelUri, source);
|
|
72
|
+
const queryResult = await this.backend.query(modelUri, sparql);
|
|
73
|
+
if (!queryResult.success) {
|
|
74
|
+
return queryFailed(blockId, 'matrix', queryResult.error ?? 'SPARQL SELECT failed.');
|
|
75
|
+
}
|
|
76
|
+
const columns = extractSelectVariables(sparql);
|
|
77
|
+
const columnSet = new Set(columns);
|
|
78
|
+
for (const row of queryResult.rows) {
|
|
79
|
+
for (const key of row.keys()) {
|
|
80
|
+
if (columnSet.has(key)) {
|
|
81
|
+
continue;
|
|
82
|
+
}
|
|
83
|
+
columnSet.add(key);
|
|
84
|
+
columns.push(key);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
const rows = queryResult.rows.map((row) => columns.map((column) => row.get(column)?.value ?? ''));
|
|
88
|
+
return okTable(blockId, 'matrix', columns, rows);
|
|
89
|
+
}
|
|
90
|
+
async executeTableBlock(kind, modelUri, blockId, source, options) {
|
|
91
|
+
if (!modelUri) {
|
|
92
|
+
return missingContext(blockId, kind);
|
|
93
|
+
}
|
|
94
|
+
const preparedSource = preprocessQuerySource(this.backend, modelUri, source);
|
|
95
|
+
const ownGraphIri = this.backend.resolveContextIri(modelUri);
|
|
96
|
+
const derivedQuery = kind === 'table-editor'
|
|
97
|
+
? deriveSelectQueryFromShape(preparedSource, ownGraphIri)
|
|
98
|
+
: undefined;
|
|
99
|
+
const sparql = kind === 'table-editor'
|
|
100
|
+
? ensureOrderedByFirstVariable(derivedQuery?.sparql ?? preparedSource, derivedQuery?.columnVariables?.[0])
|
|
101
|
+
: (derivedQuery?.sparql ?? preparedSource);
|
|
102
|
+
if (kind === 'table-editor' && !derivedQuery) {
|
|
103
|
+
return queryFailed(blockId, kind, 'Unable to derive SPARQL SELECT query from table-editor block shape.');
|
|
104
|
+
}
|
|
105
|
+
const queryResult = await this.backend.query(modelUri, sparql);
|
|
106
|
+
if (!queryResult.success) {
|
|
107
|
+
return queryFailed(blockId, kind, queryResult.error ?? 'SPARQL SELECT failed.');
|
|
108
|
+
}
|
|
109
|
+
const variableColumns = kind === 'table-editor'
|
|
110
|
+
? derivedQuery?.columnVariables.slice() ?? []
|
|
111
|
+
: extractSelectVariables(sparql);
|
|
112
|
+
const labelByVariable = kind === 'table-editor'
|
|
113
|
+
? (derivedQuery?.columnLabels ?? new Map())
|
|
114
|
+
: new Map();
|
|
115
|
+
const optionLabelByVariable = extractColumnLabelOverrides(options);
|
|
116
|
+
for (const [variable, label] of optionLabelByVariable.entries()) {
|
|
117
|
+
labelByVariable.set(variable, label);
|
|
118
|
+
}
|
|
119
|
+
const columns = variableColumns;
|
|
120
|
+
const columnSet = new Set(columns);
|
|
121
|
+
for (const row of queryResult.rows) {
|
|
122
|
+
for (const key of row.keys()) {
|
|
123
|
+
if (columnSet.has(key)) {
|
|
124
|
+
continue;
|
|
125
|
+
}
|
|
126
|
+
columnSet.add(key);
|
|
127
|
+
columns.push(key);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
const rows = queryResult.rows.map((row) => columns.map((column) => row.get(column)?.value ?? ''));
|
|
131
|
+
const displayColumns = columns.map((column) => labelByVariable.get(column) ?? column);
|
|
132
|
+
return okTable(blockId, kind, displayColumns, rows);
|
|
133
|
+
}
|
|
134
|
+
async executeGraphBlock(modelUri, blockId, source) {
|
|
135
|
+
if (!modelUri) {
|
|
136
|
+
return missingContext(blockId, 'graph');
|
|
137
|
+
}
|
|
138
|
+
const sparql = preprocessQuerySource(this.backend, modelUri, source);
|
|
139
|
+
const constructResult = await this.backend.construct(modelUri, sparql);
|
|
140
|
+
if (!constructResult.success) {
|
|
141
|
+
return queryFailed(blockId, 'graph', constructResult.error ?? 'SPARQL CONSTRUCT failed.');
|
|
142
|
+
}
|
|
143
|
+
return constructRowsResult(blockId, 'graph', constructResult);
|
|
144
|
+
}
|
|
145
|
+
async executeDiagramBlock(modelUri, blockId, source) {
|
|
146
|
+
if (!modelUri) {
|
|
147
|
+
return missingContext(blockId, 'diagram');
|
|
148
|
+
}
|
|
149
|
+
const sparql = preprocessQuerySource(this.backend, modelUri, source);
|
|
150
|
+
const constructResult = await this.backend.construct(modelUri, sparql);
|
|
151
|
+
if (!constructResult.success) {
|
|
152
|
+
return queryFailed(blockId, 'diagram', constructResult.error ?? 'SPARQL CONSTRUCT failed.');
|
|
153
|
+
}
|
|
154
|
+
return constructRowsResult(blockId, 'diagram', constructResult);
|
|
155
|
+
}
|
|
156
|
+
async executeTreeBlock(modelUri, blockId, source, options) {
|
|
157
|
+
if (!modelUri) {
|
|
158
|
+
return missingContext(blockId, 'tree');
|
|
159
|
+
}
|
|
160
|
+
const sparql = preprocessQuerySource(this.backend, modelUri, source);
|
|
161
|
+
const constructResult = await this.backend.construct(modelUri, sparql);
|
|
162
|
+
if (!constructResult.success) {
|
|
163
|
+
return queryFailed(blockId, 'tree', constructResult.error ?? 'SPARQL CONSTRUCT failed.');
|
|
164
|
+
}
|
|
165
|
+
const containmentNames = extractContainmentPropertyNames(options);
|
|
166
|
+
const focusOrder = [];
|
|
167
|
+
const focusSet = new Set();
|
|
168
|
+
const valuesByFocus = new Map();
|
|
169
|
+
const predicateToColumn = new Map();
|
|
170
|
+
const usedColumns = new Set(['focus']);
|
|
171
|
+
const columnOrder = [];
|
|
172
|
+
const columnLabelByVariable = new Map([['focus', 'focus']]);
|
|
173
|
+
const addFocus = (focus) => {
|
|
174
|
+
if (focusSet.has(focus)) {
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
focusSet.add(focus);
|
|
178
|
+
focusOrder.push(focus);
|
|
179
|
+
valuesByFocus.set(focus, new Map());
|
|
180
|
+
};
|
|
181
|
+
const addCellValue = (focus, column, value) => {
|
|
182
|
+
const byColumn = valuesByFocus.get(focus);
|
|
183
|
+
if (!byColumn) {
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
const values = byColumn.get(column) ?? [];
|
|
187
|
+
if (!values.includes(value)) {
|
|
188
|
+
values.push(value);
|
|
189
|
+
byColumn.set(column, values);
|
|
190
|
+
}
|
|
191
|
+
};
|
|
192
|
+
for (const quad of constructResult.quads) {
|
|
193
|
+
const focus = termToCellValue(quad.subject);
|
|
194
|
+
addFocus(focus);
|
|
195
|
+
const predicateIri = quad.predicate.value;
|
|
196
|
+
let column = predicateToColumn.get(predicateIri);
|
|
197
|
+
if (!column) {
|
|
198
|
+
const base = normalizeSparqlVariableName(localName(predicateIri) || 'value');
|
|
199
|
+
column = uniqueVariableName(base, usedColumns);
|
|
200
|
+
usedColumns.add(column);
|
|
201
|
+
predicateToColumn.set(predicateIri, column);
|
|
202
|
+
columnOrder.push(column);
|
|
203
|
+
columnLabelByVariable.set(column, localName(predicateIri) || column);
|
|
204
|
+
}
|
|
205
|
+
const objectValue = termToCellValue(quad.object);
|
|
206
|
+
addCellValue(focus, column, objectValue);
|
|
207
|
+
if (containmentNames.size > 0
|
|
208
|
+
&& (quad.object.termType === 'NamedNode' || quad.object.termType === 'BlankNode')
|
|
209
|
+
&& containmentNames.has(normalizePropertyIdentifier(predicateIri))) {
|
|
210
|
+
addFocus(objectValue);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
const optionLabelByVariable = extractColumnLabelOverrides(options);
|
|
214
|
+
for (const [variable, label] of optionLabelByVariable.entries()) {
|
|
215
|
+
columnLabelByVariable.set(variable, label);
|
|
216
|
+
}
|
|
217
|
+
const columns = ['focus', ...columnOrder];
|
|
218
|
+
const displayColumns = columns.map((column) => columnLabelByVariable.get(column) ?? column);
|
|
219
|
+
const rows = focusOrder.map((focus) => {
|
|
220
|
+
const byColumn = valuesByFocus.get(focus) ?? new Map();
|
|
221
|
+
return columns.map((column) => {
|
|
222
|
+
if (column === 'focus') {
|
|
223
|
+
return focus;
|
|
224
|
+
}
|
|
225
|
+
return (byColumn.get(column) ?? []).join(', ');
|
|
226
|
+
});
|
|
227
|
+
});
|
|
228
|
+
return okTable(blockId, 'tree', displayColumns, rows);
|
|
229
|
+
}
|
|
230
|
+
async executeTextBlock(modelUri, blockId, source) {
|
|
231
|
+
if (!modelUri) {
|
|
232
|
+
return missingContext(blockId, 'text');
|
|
233
|
+
}
|
|
234
|
+
const sparql = preprocessQuerySource(this.backend, modelUri, source);
|
|
235
|
+
const queryResult = await this.backend.query(modelUri, sparql);
|
|
236
|
+
if (!queryResult.success) {
|
|
237
|
+
return queryFailed(blockId, 'text', queryResult.error ?? 'SPARQL SELECT failed.');
|
|
238
|
+
}
|
|
239
|
+
const selected = extractSelectVariables(sparql);
|
|
240
|
+
const firstVariable = selected[0] ?? queryResult.rows[0]?.keys().next().value;
|
|
241
|
+
if (!firstVariable) {
|
|
242
|
+
return queryFailed(blockId, 'text', 'Text block requires at least one SELECT variable.');
|
|
243
|
+
}
|
|
244
|
+
const columns = [firstVariable];
|
|
245
|
+
const columnSet = new Set(columns);
|
|
246
|
+
for (const row of queryResult.rows) {
|
|
247
|
+
for (const key of row.keys()) {
|
|
248
|
+
if (columnSet.has(key)) {
|
|
249
|
+
continue;
|
|
250
|
+
}
|
|
251
|
+
columnSet.add(key);
|
|
252
|
+
columns.push(key);
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
const rows = queryResult.rows.map((row) => columns.map((column) => row.get(column)?.value ?? ''));
|
|
256
|
+
return okTable(blockId, 'text', columns, rows);
|
|
257
|
+
}
|
|
258
|
+
async executeListBlock(modelUri, blockId, source) {
|
|
259
|
+
if (!modelUri) {
|
|
260
|
+
return missingContext(blockId, 'list');
|
|
261
|
+
}
|
|
262
|
+
const sparql = preprocessQuerySource(this.backend, modelUri, source);
|
|
263
|
+
const queryResult = await this.backend.query(modelUri, sparql);
|
|
264
|
+
if (!queryResult.success) {
|
|
265
|
+
return queryFailed(blockId, 'list', queryResult.error ?? 'SPARQL SELECT failed.');
|
|
266
|
+
}
|
|
267
|
+
const selected = extractSelectVariables(sparql);
|
|
268
|
+
const firstVariable = selected[0] ?? queryResult.rows[0]?.keys().next().value;
|
|
269
|
+
if (!firstVariable) {
|
|
270
|
+
return queryFailed(blockId, 'list', 'List block requires at least one SELECT variable.');
|
|
271
|
+
}
|
|
272
|
+
const columns = [firstVariable];
|
|
273
|
+
const columnSet = new Set(columns);
|
|
274
|
+
for (const row of queryResult.rows) {
|
|
275
|
+
for (const key of row.keys()) {
|
|
276
|
+
if (columnSet.has(key)) {
|
|
277
|
+
continue;
|
|
278
|
+
}
|
|
279
|
+
columnSet.add(key);
|
|
280
|
+
columns.push(key);
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
const rows = queryResult.rows.map((row) => columns.map((column) => row.get(column)?.value ?? ''));
|
|
284
|
+
return okTable(blockId, 'list', columns, rows);
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
function preprocessQuerySource(backend, modelUri, source) {
|
|
288
|
+
if (!source.includes('${contextIri}')) {
|
|
289
|
+
return source;
|
|
290
|
+
}
|
|
291
|
+
return source.split('${contextIri}').join(backend.resolveContextIri(modelUri));
|
|
292
|
+
}
|
|
293
|
+
function missingContext(blockId, kind) {
|
|
294
|
+
return queryFailed(blockId, kind, 'No contextUri model is available for this markdown document.');
|
|
295
|
+
}
|
|
296
|
+
function queryFailed(blockId, kind, message) {
|
|
297
|
+
return {
|
|
298
|
+
blockId,
|
|
299
|
+
kind,
|
|
300
|
+
status: 'error',
|
|
301
|
+
format: 'message',
|
|
302
|
+
message,
|
|
303
|
+
};
|
|
304
|
+
}
|
|
305
|
+
function okTable(blockId, kind, columns, rows) {
|
|
306
|
+
return {
|
|
307
|
+
blockId,
|
|
308
|
+
kind,
|
|
309
|
+
status: 'ok',
|
|
310
|
+
format: 'table',
|
|
311
|
+
payload: { columns, rows },
|
|
312
|
+
};
|
|
313
|
+
}
|
|
314
|
+
function constructRowsResult(blockId, kind, constructResult) {
|
|
315
|
+
return okTable(blockId, kind, ['subject', 'predicate', 'object'], constructResult.quads.map((quad) => [
|
|
316
|
+
termToCellValue(quad.subject),
|
|
317
|
+
termToCellValue(quad.predicate),
|
|
318
|
+
termToCellValue(quad.object),
|
|
319
|
+
]));
|
|
320
|
+
}
|
|
321
|
+
function extractSelectVariables(sparql) {
|
|
322
|
+
const match = /select\s+(?:distinct\s+|reduced\s+)?([\s\S]*?)\bwhere\b/i.exec(sparql);
|
|
323
|
+
if (!match) {
|
|
324
|
+
return [];
|
|
325
|
+
}
|
|
326
|
+
const variables = [];
|
|
327
|
+
for (const term of splitTopLevelProjectionTerms(match[1])) {
|
|
328
|
+
const trimmed = term.trim();
|
|
329
|
+
if (!trimmed) {
|
|
330
|
+
continue;
|
|
331
|
+
}
|
|
332
|
+
const directVariable = /^\?([A-Za-z_][A-Za-z0-9_-]*)$/i.exec(trimmed);
|
|
333
|
+
if (directVariable) {
|
|
334
|
+
variables.push(`?${directVariable[1]}`);
|
|
335
|
+
continue;
|
|
336
|
+
}
|
|
337
|
+
const alias = /\bas\s+\?([A-Za-z_][A-Za-z0-9_-]*)\b/i.exec(trimmed);
|
|
338
|
+
if (alias) {
|
|
339
|
+
variables.push(`?${alias[1]}`);
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
const seen = new Set();
|
|
343
|
+
const result = [];
|
|
344
|
+
for (const variable of variables) {
|
|
345
|
+
const name = variable.slice(1);
|
|
346
|
+
if (seen.has(name)) {
|
|
347
|
+
continue;
|
|
348
|
+
}
|
|
349
|
+
seen.add(name);
|
|
350
|
+
result.push(name);
|
|
351
|
+
}
|
|
352
|
+
return result;
|
|
353
|
+
}
|
|
354
|
+
function splitTopLevelProjectionTerms(rawProjection) {
|
|
355
|
+
const terms = [];
|
|
356
|
+
let current = '';
|
|
357
|
+
let depth = 0;
|
|
358
|
+
let quote;
|
|
359
|
+
for (let index = 0; index < rawProjection.length; index += 1) {
|
|
360
|
+
const char = rawProjection[index];
|
|
361
|
+
if (quote) {
|
|
362
|
+
current += char;
|
|
363
|
+
if (char === quote && rawProjection[index - 1] !== '\\') {
|
|
364
|
+
quote = undefined;
|
|
365
|
+
}
|
|
366
|
+
continue;
|
|
367
|
+
}
|
|
368
|
+
if (char === '"' || char === '\'') {
|
|
369
|
+
quote = char;
|
|
370
|
+
current += char;
|
|
371
|
+
continue;
|
|
372
|
+
}
|
|
373
|
+
if (char === '(') {
|
|
374
|
+
depth += 1;
|
|
375
|
+
current += char;
|
|
376
|
+
continue;
|
|
377
|
+
}
|
|
378
|
+
if (char === ')') {
|
|
379
|
+
depth = Math.max(0, depth - 1);
|
|
380
|
+
current += char;
|
|
381
|
+
continue;
|
|
382
|
+
}
|
|
383
|
+
if (/\s/.test(char) && depth === 0) {
|
|
384
|
+
if (current.trim()) {
|
|
385
|
+
terms.push(current.trim());
|
|
386
|
+
current = '';
|
|
387
|
+
}
|
|
388
|
+
continue;
|
|
389
|
+
}
|
|
390
|
+
current += char;
|
|
391
|
+
}
|
|
392
|
+
if (current.trim()) {
|
|
393
|
+
terms.push(current.trim());
|
|
394
|
+
}
|
|
395
|
+
return terms;
|
|
396
|
+
}
|
|
397
|
+
function ensureOrderedByFirstVariable(sparql, firstVariable) {
|
|
398
|
+
const variable = (firstVariable ?? '').trim().replace(/^\?/, '');
|
|
399
|
+
if (!variable || /\border\s+by\b/i.test(sparql)) {
|
|
400
|
+
return sparql;
|
|
401
|
+
}
|
|
402
|
+
return `${sparql.trimEnd()}\nORDER BY ?${variable}`;
|
|
403
|
+
}
|
|
404
|
+
function extractColumnLabelOverrides(options) {
|
|
405
|
+
const labels = new Map();
|
|
406
|
+
const columns = options?.columns;
|
|
407
|
+
if (!isPlainObject(columns)) {
|
|
408
|
+
return labels;
|
|
409
|
+
}
|
|
410
|
+
for (const [variable, rawConfig] of Object.entries(columns)) {
|
|
411
|
+
if (typeof rawConfig === 'string') {
|
|
412
|
+
labels.set(variable, rawConfig);
|
|
413
|
+
continue;
|
|
414
|
+
}
|
|
415
|
+
if (!isPlainObject(rawConfig)) {
|
|
416
|
+
continue;
|
|
417
|
+
}
|
|
418
|
+
const label = rawConfig.label;
|
|
419
|
+
if (typeof label === 'string' && label.trim().length > 0) {
|
|
420
|
+
labels.set(variable, label);
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
return labels;
|
|
424
|
+
}
|
|
425
|
+
function extractContainmentPropertyNames(options) {
|
|
426
|
+
const names = new Set();
|
|
427
|
+
const containment = options?.containment;
|
|
428
|
+
if (!Array.isArray(containment)) {
|
|
429
|
+
return names;
|
|
430
|
+
}
|
|
431
|
+
for (const entry of containment) {
|
|
432
|
+
if (typeof entry !== 'string') {
|
|
433
|
+
continue;
|
|
434
|
+
}
|
|
435
|
+
const normalized = normalizePropertyIdentifier(entry);
|
|
436
|
+
if (normalized) {
|
|
437
|
+
names.add(normalized);
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
return names;
|
|
441
|
+
}
|
|
442
|
+
function normalizePropertyIdentifier(raw) {
|
|
443
|
+
const trimmed = raw.trim();
|
|
444
|
+
if (!trimmed) {
|
|
445
|
+
return '';
|
|
446
|
+
}
|
|
447
|
+
if (trimmed.includes(':')) {
|
|
448
|
+
return trimmed.slice(trimmed.lastIndexOf(':') + 1).trim().toLowerCase();
|
|
449
|
+
}
|
|
450
|
+
return localName(trimmed).trim().toLowerCase();
|
|
451
|
+
}
|
|
452
|
+
function termToCellValue(term) {
|
|
453
|
+
if (term.termType === 'NamedNode') {
|
|
454
|
+
return term.value;
|
|
455
|
+
}
|
|
456
|
+
if (term.termType === 'BlankNode') {
|
|
457
|
+
return `_:${term.value}`;
|
|
458
|
+
}
|
|
459
|
+
if (term.termType === 'Literal') {
|
|
460
|
+
return term.value;
|
|
461
|
+
}
|
|
462
|
+
return String(term.value ?? '');
|
|
463
|
+
}
|
|
464
|
+
function normalizeSparqlVariableName(raw) {
|
|
465
|
+
const normalized = raw.replace(/[^A-Za-z0-9_]/g, '_');
|
|
466
|
+
if (!normalized) {
|
|
467
|
+
return 'value';
|
|
468
|
+
}
|
|
469
|
+
if (!/^[A-Za-z_]/.test(normalized)) {
|
|
470
|
+
return `v_${normalized}`;
|
|
471
|
+
}
|
|
472
|
+
return normalized;
|
|
473
|
+
}
|
|
474
|
+
function uniqueVariableName(base, used) {
|
|
475
|
+
if (!used.has(base)) {
|
|
476
|
+
return base;
|
|
477
|
+
}
|
|
478
|
+
let index = 2;
|
|
479
|
+
while (used.has(`${base}_${index}`)) {
|
|
480
|
+
index += 1;
|
|
481
|
+
}
|
|
482
|
+
return `${base}_${index}`;
|
|
483
|
+
}
|
|
484
|
+
function localName(iri) {
|
|
485
|
+
const hash = iri.lastIndexOf('#');
|
|
486
|
+
if (hash >= 0 && hash < iri.length - 1) {
|
|
487
|
+
return iri.slice(hash + 1);
|
|
488
|
+
}
|
|
489
|
+
const slash = iri.lastIndexOf('/');
|
|
490
|
+
if (slash >= 0 && slash < iri.length - 1) {
|
|
491
|
+
return iri.slice(slash + 1);
|
|
492
|
+
}
|
|
493
|
+
return iri;
|
|
494
|
+
}
|
|
495
|
+
function isPlainObject(value) {
|
|
496
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
497
|
+
}
|
|
498
|
+
//# sourceMappingURL=md-executor.js.map
|