@diff-review-system/drs 4.0.1 → 5.0.0-rc.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.pi/agents/review/unified-reviewer.md +2 -6
- package/.pi/agents/task/okf-wiki-maintainer.md +72 -0
- package/.pi/agents/task/review-assistant.md +22 -0
- package/.pi/workflows/github-pr-fix-review-issues-stacked.yaml +12 -4
- package/.pi/workflows/github-pr-review-post.yaml +29 -20
- package/.pi/workflows/github-pr-review.yaml +37 -8
- package/.pi/workflows/github-pr-update-agents-md-stacked.yaml +2 -0
- package/.pi/workflows/github-pr-visual-explain.yaml +46 -1
- package/.pi/workflows/gitlab-mr-fix-review-issues-stacked.yaml +12 -4
- package/.pi/workflows/gitlab-mr-review.yaml +20 -8
- package/.pi/workflows/gitlab-mr-update-agents-md-stacked.yaml +2 -0
- package/.pi/workflows/gitlab-mr-visual-explain.yaml +46 -1
- package/.pi/workflows/local-fix-review-issues.yaml +13 -6
- package/.pi/workflows/local-review.yaml +7 -1
- package/.pi/workflows/local-visual-explain.yaml +46 -1
- package/.pi/workflows/release-changelog-finalize.yaml +10 -0
- package/.pi/workflows/repository-wiki-check.yaml +40 -0
- package/.pi/workflows/repository-wiki-sync.yaml +187 -0
- package/.pi/workflows/temporal-control-smoke.yaml +63 -0
- package/.wiki-site/.vitepress/config.mts +452 -0
- package/.wiki-site/.vitepress/theme/PageLead.vue +122 -0
- package/.wiki-site/.vitepress/theme/index.ts +12 -0
- package/.wiki-site/.vitepress/theme/style.css +270 -0
- package/README.md +77 -4
- package/dist/cli/index.js +87 -4
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/init.d.ts +6 -1
- package/dist/cli/init.d.ts.map +1 -1
- package/dist/cli/init.js +26 -10
- package/dist/cli/init.js.map +1 -1
- package/dist/cli/run-agent.d.ts +5 -7
- package/dist/cli/run-agent.d.ts.map +1 -1
- package/dist/cli/run-agent.js +56 -5
- package/dist/cli/run-agent.js.map +1 -1
- package/dist/cli/wiki.d.ts +3 -0
- package/dist/cli/wiki.d.ts.map +1 -0
- package/dist/cli/wiki.js +174 -0
- package/dist/cli/wiki.js.map +1 -0
- package/dist/cli/workflow-executor-selection.d.ts +3 -0
- package/dist/cli/workflow-executor-selection.d.ts.map +1 -0
- package/dist/cli/workflow-executor-selection.js +15 -0
- package/dist/cli/workflow-executor-selection.js.map +1 -0
- package/dist/cli/workflow.d.ts +40 -42
- package/dist/cli/workflow.d.ts.map +1 -1
- package/dist/cli/workflow.js +533 -868
- package/dist/cli/workflow.js.map +1 -1
- package/dist/github/client.d.ts.map +1 -1
- package/dist/github/client.js +2 -2
- package/dist/github/client.js.map +1 -1
- package/dist/github/platform-adapter.d.ts.map +1 -1
- package/dist/github/platform-adapter.js +15 -1
- package/dist/github/platform-adapter.js.map +1 -1
- package/dist/gitlab/client.d.ts +4 -0
- package/dist/gitlab/client.d.ts.map +1 -1
- package/dist/gitlab/client.js +17 -1
- package/dist/gitlab/client.js.map +1 -1
- package/dist/gitlab/platform-adapter.d.ts.map +1 -1
- package/dist/gitlab/platform-adapter.js +17 -1
- package/dist/gitlab/platform-adapter.js.map +1 -1
- package/dist/lib/agent-permissions.d.ts +44 -0
- package/dist/lib/agent-permissions.d.ts.map +1 -0
- package/dist/lib/agent-permissions.js +358 -0
- package/dist/lib/agent-permissions.js.map +1 -0
- package/dist/lib/agent-result.d.ts +10 -0
- package/dist/lib/agent-result.d.ts.map +1 -0
- package/dist/lib/agent-result.js +2 -0
- package/dist/lib/agent-result.js.map +1 -0
- package/dist/lib/comment-poster.d.ts +1 -1
- package/dist/lib/comment-poster.d.ts.map +1 -1
- package/dist/lib/comment-poster.js +38 -16
- package/dist/lib/comment-poster.js.map +1 -1
- package/dist/lib/config.d.ts +27 -1
- package/dist/lib/config.d.ts.map +1 -1
- package/dist/lib/config.js +17 -0
- package/dist/lib/config.js.map +1 -1
- package/dist/lib/error-comment-poster.d.ts +1 -1
- package/dist/lib/error-comment-poster.d.ts.map +1 -1
- package/dist/lib/error-comment-poster.js +15 -7
- package/dist/lib/error-comment-poster.js.map +1 -1
- package/dist/lib/okf-wiki.d.ts +65 -0
- package/dist/lib/okf-wiki.d.ts.map +1 -0
- package/dist/lib/okf-wiki.js +647 -0
- package/dist/lib/okf-wiki.js.map +1 -0
- package/dist/lib/output-paths.d.ts +0 -1
- package/dist/lib/output-paths.d.ts.map +1 -1
- package/dist/lib/output-paths.js +0 -1
- package/dist/lib/output-paths.js.map +1 -1
- package/dist/lib/platform-client.d.ts +2 -0
- package/dist/lib/platform-client.d.ts.map +1 -1
- package/dist/lib/project-setup.d.ts +7 -0
- package/dist/lib/project-setup.d.ts.map +1 -0
- package/dist/lib/project-setup.js +11 -0
- package/dist/lib/project-setup.js.map +1 -0
- package/dist/lib/review-artifact-store.d.ts +16 -0
- package/dist/lib/review-artifact-store.d.ts.map +1 -0
- package/dist/lib/review-artifact-store.js +84 -0
- package/dist/lib/review-artifact-store.js.map +1 -0
- package/dist/lib/review-artifact.d.ts +11 -1
- package/dist/lib/review-artifact.d.ts.map +1 -1
- package/dist/lib/review-artifact.js +203 -0
- package/dist/lib/review-artifact.js.map +1 -1
- package/dist/lib/review-core.d.ts +1 -0
- package/dist/lib/review-core.d.ts.map +1 -1
- package/dist/lib/review-core.js +11 -14
- package/dist/lib/review-core.js.map +1 -1
- package/dist/lib/review-orchestrator.d.ts +6 -1
- package/dist/lib/review-orchestrator.d.ts.map +1 -1
- package/dist/lib/review-orchestrator.js +3 -1
- package/dist/lib/review-orchestrator.js.map +1 -1
- package/dist/lib/review-parser.d.ts.map +1 -1
- package/dist/lib/review-parser.js +15 -12
- package/dist/lib/review-parser.js.map +1 -1
- package/dist/lib/wiki-delta.d.ts +71 -0
- package/dist/lib/wiki-delta.d.ts.map +1 -0
- package/dist/lib/wiki-delta.js +524 -0
- package/dist/lib/wiki-delta.js.map +1 -0
- package/dist/lib/wiki-run-summary.d.ts +45 -0
- package/dist/lib/wiki-run-summary.d.ts.map +1 -0
- package/dist/lib/wiki-run-summary.js +168 -0
- package/dist/lib/wiki-run-summary.js.map +1 -0
- package/dist/lib/wiki-search.d.ts +24 -0
- package/dist/lib/wiki-search.d.ts.map +1 -0
- package/dist/lib/wiki-search.js +200 -0
- package/dist/lib/wiki-search.js.map +1 -0
- package/dist/lib/wiki-site-graph.d.ts +33 -0
- package/dist/lib/wiki-site-graph.d.ts.map +1 -0
- package/dist/lib/wiki-site-graph.js +446 -0
- package/dist/lib/wiki-site-graph.js.map +1 -0
- package/dist/lib/wiki-site-safety.d.ts +6 -0
- package/dist/lib/wiki-site-safety.d.ts.map +1 -0
- package/dist/lib/wiki-site-safety.js +83 -0
- package/dist/lib/wiki-site-safety.js.map +1 -0
- package/dist/lib/wiki-site-smoke.d.ts +16 -0
- package/dist/lib/wiki-site-smoke.d.ts.map +1 -0
- package/dist/lib/wiki-site-smoke.js +270 -0
- package/dist/lib/wiki-site-smoke.js.map +1 -0
- package/dist/lib/wiki-site.d.ts +26 -0
- package/dist/lib/wiki-site.d.ts.map +1 -0
- package/dist/lib/wiki-site.js +221 -0
- package/dist/lib/wiki-site.js.map +1 -0
- package/dist/lib/workflow/artifact-store.d.ts +71 -0
- package/dist/lib/workflow/artifact-store.d.ts.map +1 -0
- package/dist/lib/workflow/artifact-store.js +121 -0
- package/dist/lib/workflow/artifact-store.js.map +1 -0
- package/dist/lib/workflow/compiled-plan.d.ts +87 -0
- package/dist/lib/workflow/compiled-plan.d.ts.map +1 -0
- package/dist/lib/workflow/compiled-plan.js +62 -0
- package/dist/lib/workflow/compiled-plan.js.map +1 -0
- package/dist/lib/workflow/executor.d.ts +18 -0
- package/dist/lib/workflow/executor.d.ts.map +1 -0
- package/dist/lib/workflow/executor.js +2 -0
- package/dist/lib/workflow/executor.js.map +1 -0
- package/dist/lib/workflow/graph.d.ts +30 -0
- package/dist/lib/workflow/graph.d.ts.map +1 -0
- package/dist/lib/workflow/graph.js +139 -0
- package/dist/lib/workflow/graph.js.map +1 -0
- package/dist/lib/workflow/input.d.ts +4 -0
- package/dist/lib/workflow/input.d.ts.map +1 -0
- package/dist/lib/workflow/input.js +4 -0
- package/dist/lib/workflow/input.js.map +1 -0
- package/dist/lib/workflow/node-executor.d.ts +19 -0
- package/dist/lib/workflow/node-executor.d.ts.map +1 -0
- package/dist/lib/workflow/node-executor.js +2 -0
- package/dist/lib/workflow/node-executor.js.map +1 -0
- package/dist/lib/workflow/planning.d.ts +52 -0
- package/dist/lib/workflow/planning.d.ts.map +1 -0
- package/dist/lib/workflow/planning.js +865 -0
- package/dist/lib/workflow/planning.js.map +1 -0
- package/dist/lib/workflow/types.d.ts +71 -0
- package/dist/lib/workflow/types.d.ts.map +1 -0
- package/dist/lib/workflow/types.js +2 -0
- package/dist/lib/workflow/types.js.map +1 -0
- package/dist/lib/workflow-artifacts.d.ts.map +1 -1
- package/dist/lib/workflow-artifacts.js +23 -1
- package/dist/lib/workflow-artifacts.js.map +1 -1
- package/dist/lib/write-json-output.d.ts.map +1 -1
- package/dist/lib/write-json-output.js +3 -8
- package/dist/lib/write-json-output.js.map +1 -1
- package/dist/pi/sdk.d.ts.map +1 -1
- package/dist/pi/sdk.js +276 -19
- package/dist/pi/sdk.js.map +1 -1
- package/dist/runtime/client.d.ts +3 -0
- package/dist/runtime/client.d.ts.map +1 -1
- package/dist/runtime/client.js +4 -0
- package/dist/runtime/client.js.map +1 -1
- package/dist/temporal/activities.d.ts +26 -0
- package/dist/temporal/activities.d.ts.map +1 -0
- package/dist/temporal/activities.js +219 -0
- package/dist/temporal/activities.js.map +1 -0
- package/dist/temporal/config.d.ts +4 -0
- package/dist/temporal/config.d.ts.map +1 -0
- package/dist/temporal/config.js +36 -0
- package/dist/temporal/config.js.map +1 -0
- package/dist/temporal/executor.d.ts +7 -0
- package/dist/temporal/executor.d.ts.map +1 -0
- package/dist/temporal/executor.js +240 -0
- package/dist/temporal/executor.js.map +1 -0
- package/dist/temporal/retry-policy.d.ts +28 -0
- package/dist/temporal/retry-policy.d.ts.map +1 -0
- package/dist/temporal/retry-policy.js +61 -0
- package/dist/temporal/retry-policy.js.map +1 -0
- package/dist/temporal/types.d.ts +86 -0
- package/dist/temporal/types.d.ts.map +1 -0
- package/dist/temporal/types.js +2 -0
- package/dist/temporal/types.js.map +1 -0
- package/dist/temporal/worker.d.ts +3 -0
- package/dist/temporal/worker.d.ts.map +1 -0
- package/dist/temporal/worker.js +18 -0
- package/dist/temporal/worker.js.map +1 -0
- package/dist/temporal/workflow-id.d.ts +27 -0
- package/dist/temporal/workflow-id.d.ts.map +1 -0
- package/dist/temporal/workflow-id.js +53 -0
- package/dist/temporal/workflow-id.js.map +1 -0
- package/dist/temporal/workflows.d.ts +6 -0
- package/dist/temporal/workflows.d.ts.map +1 -0
- package/dist/temporal/workflows.js +355 -0
- package/dist/temporal/workflows.js.map +1 -0
- package/package.json +24 -9
|
@@ -0,0 +1,446 @@
|
|
|
1
|
+
import MarkdownIt from 'markdown-it';
|
|
2
|
+
import { posix } from 'path';
|
|
3
|
+
const TYPE_COLORS = [
|
|
4
|
+
'#8ea2ff',
|
|
5
|
+
'#f28b6d',
|
|
6
|
+
'#63c7b2',
|
|
7
|
+
'#d4a85b',
|
|
8
|
+
'#b68cff',
|
|
9
|
+
'#70b6e8',
|
|
10
|
+
'#df7da8',
|
|
11
|
+
'#93bd68',
|
|
12
|
+
];
|
|
13
|
+
const markdownParser = new MarkdownIt({ html: false });
|
|
14
|
+
export function extractWikiSiteConceptLinks(content, currentId, conceptIds) {
|
|
15
|
+
const body = content.replace(/^\uFEFF?---\r?\n[\s\S]*?\r?\n---(?:\r?\n|$)/u, '');
|
|
16
|
+
const links = new Set();
|
|
17
|
+
for (const token of markdownParser.parse(body, {})) {
|
|
18
|
+
for (const child of token.children ?? []) {
|
|
19
|
+
if (child.type !== 'link_open')
|
|
20
|
+
continue;
|
|
21
|
+
const rawTarget = child.attrGet('href');
|
|
22
|
+
if (!rawTarget || rawTarget.startsWith('#') || /^[a-z][a-z\d+.-]*:/i.test(rawTarget)) {
|
|
23
|
+
continue;
|
|
24
|
+
}
|
|
25
|
+
let target = rawTarget.split(/[?#]/, 1)[0] ?? '';
|
|
26
|
+
try {
|
|
27
|
+
target = decodeURIComponent(target);
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
continue;
|
|
31
|
+
}
|
|
32
|
+
if (!target.endsWith('.md'))
|
|
33
|
+
continue;
|
|
34
|
+
const sourceDirectory = posix.dirname(`${currentId}.md`);
|
|
35
|
+
const normalized = posix.normalize(target.startsWith('/') ? target.slice(1) : posix.join(sourceDirectory, target));
|
|
36
|
+
if (normalized.startsWith('../'))
|
|
37
|
+
continue;
|
|
38
|
+
const targetId = normalized.replace(/\.md$/, '');
|
|
39
|
+
if (targetId !== currentId && conceptIds.has(targetId))
|
|
40
|
+
links.add(targetId);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
return [...links].sort(compareStrings);
|
|
44
|
+
}
|
|
45
|
+
/** Analyze semantic concept links as directed edges and deterministic quality metrics. */
|
|
46
|
+
export function analyzeWikiConceptGraph(concepts) {
|
|
47
|
+
const nodeIds = new Set(concepts.map((concept) => concept.id));
|
|
48
|
+
const edgeKeys = new Set();
|
|
49
|
+
const edges = [];
|
|
50
|
+
for (const concept of concepts) {
|
|
51
|
+
for (const target of concept.links) {
|
|
52
|
+
if (target === concept.id || !nodeIds.has(target))
|
|
53
|
+
continue;
|
|
54
|
+
const key = `${concept.id}\0${target}`;
|
|
55
|
+
if (edgeKeys.has(key))
|
|
56
|
+
continue;
|
|
57
|
+
edgeKeys.add(key);
|
|
58
|
+
edges.push({ source: concept.id, target });
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
edges.sort((left, right) => compareStrings(left.source, right.source) || compareStrings(left.target, right.target));
|
|
62
|
+
const neighbors = new Map([...nodeIds].map((id) => [id, new Set()]));
|
|
63
|
+
for (const edge of edges) {
|
|
64
|
+
neighbors.get(edge.source)?.add(edge.target);
|
|
65
|
+
neighbors.get(edge.target)?.add(edge.source);
|
|
66
|
+
}
|
|
67
|
+
const orphanIds = [...nodeIds].filter((id) => neighbors.get(id)?.size === 0).sort(compareStrings);
|
|
68
|
+
const weaklyConnectedIds = [...nodeIds]
|
|
69
|
+
.filter((id) => neighbors.get(id)?.size === 1)
|
|
70
|
+
.sort(compareStrings);
|
|
71
|
+
return {
|
|
72
|
+
edges,
|
|
73
|
+
metrics: {
|
|
74
|
+
directedEdgeCount: edges.length,
|
|
75
|
+
nodeCount: nodeIds.size,
|
|
76
|
+
orphanConceptCount: orphanIds.length,
|
|
77
|
+
weaklyConnectedConceptCount: weaklyConnectedIds.length,
|
|
78
|
+
},
|
|
79
|
+
orphanIds,
|
|
80
|
+
weaklyConnectedIds,
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
export function createWikiSiteGraphHtml(concepts, options) {
|
|
84
|
+
const types = [...new Set(concepts.map((concept) => concept.type))].sort(compareStrings);
|
|
85
|
+
const colors = new Map(types.map((type, index) => [type, TYPE_COLORS[index % TYPE_COLORS.length]]));
|
|
86
|
+
const nodes = concepts.map((concept) => ({
|
|
87
|
+
...concept,
|
|
88
|
+
color: colors.get(concept.type) ?? TYPE_COLORS[0],
|
|
89
|
+
href: `${options.base}${encodeWikiSiteConceptId(concept.id)}.html`,
|
|
90
|
+
}));
|
|
91
|
+
const analysis = analyzeWikiConceptGraph(nodes);
|
|
92
|
+
const edges = analysis.edges;
|
|
93
|
+
const graphJson = JSON.stringify({ nodes, edges, types, metrics: analysis.metrics })
|
|
94
|
+
.replaceAll('<', '\\u003c')
|
|
95
|
+
.replaceAll('\u2028', '\\u2028')
|
|
96
|
+
.replaceAll('\u2029', '\\u2029');
|
|
97
|
+
const fallbackLinks = nodes
|
|
98
|
+
.map((node) => `<li><a href="${escapeHtml(node.href)}">${escapeHtml(node.title)}</a> <span>${escapeHtml(node.type)}</span></li>`)
|
|
99
|
+
.join('');
|
|
100
|
+
return `<!doctype html>
|
|
101
|
+
<html lang="en">
|
|
102
|
+
<head>
|
|
103
|
+
<meta charset="utf-8">
|
|
104
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
105
|
+
<meta name="color-scheme" content="dark light">
|
|
106
|
+
<meta http-equiv="Content-Security-Policy" content="default-src 'none'; script-src 'unsafe-inline'; style-src 'unsafe-inline'; img-src 'none'; connect-src 'none'; base-uri 'none'; form-action 'none'">
|
|
107
|
+
<title>Concept graph | ${escapeHtml(options.siteTitle)}</title>
|
|
108
|
+
<style>
|
|
109
|
+
:root { color-scheme: dark; --ink: #edf0ff; --muted: #aeb8d2; --panel: #171e30; --line: #35405c; --brand: #91a3ff; --accent: #f28b6d; }
|
|
110
|
+
* { box-sizing: border-box; }
|
|
111
|
+
body { margin: 0; color: var(--ink); background: #0d1321; font: 15px/1.5 Inter, ui-sans-serif, system-ui, sans-serif; }
|
|
112
|
+
a { color: inherit; }
|
|
113
|
+
.topbar { display: flex; gap: 1rem; align-items: center; min-height: 64px; padding: .75rem 1.25rem; border-bottom: 1px solid var(--line); background: rgba(13, 19, 33, .94); }
|
|
114
|
+
.brand { margin-right: auto; font-weight: 800; text-decoration: none; letter-spacing: -.02em; }
|
|
115
|
+
.topbar a:not(.brand) { color: var(--muted); font-size: .86rem; font-weight: 700; text-decoration: none; }
|
|
116
|
+
.layout { display: grid; grid-template-columns: 290px minmax(0, 1fr); min-height: calc(100vh - 64px); }
|
|
117
|
+
.panel { z-index: 2; padding: 1.25rem; border-right: 1px solid var(--line); background: var(--panel); }
|
|
118
|
+
.eyebrow { margin: 0 0 .35rem; color: var(--accent); font: 700 .7rem/1.2 ui-monospace, monospace; letter-spacing: .13em; }
|
|
119
|
+
h1 { margin: 0; font-size: 1.85rem; line-height: 1.05; letter-spacing: -.04em; }
|
|
120
|
+
.intro { margin: .8rem 0 1.1rem; color: var(--muted); font-size: .88rem; }
|
|
121
|
+
label { display: block; margin: 1rem 0 .35rem; color: var(--muted); font-size: .72rem; font-weight: 800; letter-spacing: .08em; text-transform: uppercase; }
|
|
122
|
+
input { width: 100%; padding: .65rem .7rem; border: 1px solid var(--line); border-radius: 3px; color: var(--ink); background: #0e1525; font: inherit; }
|
|
123
|
+
input:focus { outline: 2px solid var(--brand); outline-offset: 1px; }
|
|
124
|
+
.filters { display: flex; flex-wrap: wrap; gap: .4rem; }
|
|
125
|
+
.filter { padding: .3rem .5rem; border: 1px solid var(--line); border-radius: 999px; color: var(--muted); background: transparent; cursor: pointer; font: 700 .72rem/1.2 ui-monospace, monospace; }
|
|
126
|
+
.filter[aria-pressed="true"] { border-color: var(--filter-color); color: #0d1321; background: var(--filter-color); }
|
|
127
|
+
.stats { margin: 1rem 0; padding: .7rem 0; border-block: 1px solid var(--line); color: var(--muted); font: .75rem/1.5 ui-monospace, monospace; }
|
|
128
|
+
.detail { min-height: 10rem; }
|
|
129
|
+
.detail h2 { margin: 0 0 .35rem; font-size: 1.05rem; }
|
|
130
|
+
.detail p { margin: .35rem 0; color: var(--muted); font-size: .82rem; }
|
|
131
|
+
.detail .type { display: inline-block; padding: .15rem .4rem; border: 1px solid currentcolor; border-radius: 999px; font: 700 .68rem ui-monospace, monospace; }
|
|
132
|
+
.detail a { display: inline-block; margin-top: .6rem; color: var(--brand); font-weight: 800; text-decoration: none; }
|
|
133
|
+
.neighbor-group h3 { margin: .8rem 0 0; color: var(--muted); font: 800 .68rem/1.2 ui-monospace, monospace; letter-spacing: .08em; text-transform: uppercase; }
|
|
134
|
+
.neighbors { display: flex; flex-wrap: wrap; gap: .3rem; margin-top: .55rem; }
|
|
135
|
+
.neighbors button { padding: .2rem .4rem; border: 1px solid var(--line); color: var(--muted); background: transparent; cursor: pointer; font-size: .7rem; }
|
|
136
|
+
.stage { position: relative; min-width: 0; overflow: hidden; background: radial-gradient(circle at 50% 45%, #182441 0, #0d1321 58%); }
|
|
137
|
+
svg { display: block; width: 100%; height: calc(100vh - 64px); min-height: 620px; touch-action: none; }
|
|
138
|
+
.edge { stroke: #44506e; stroke-width: 1.1; opacity: .6; }
|
|
139
|
+
.edge.active { stroke: var(--brand); stroke-width: 2; opacity: 1; }
|
|
140
|
+
.node { cursor: grab; }
|
|
141
|
+
.node:active { cursor: grabbing; }
|
|
142
|
+
.node circle { stroke: #0d1321; stroke-width: 3; transition: r .15s, opacity .15s; }
|
|
143
|
+
.node text { fill: var(--ink); font-size: 11px; font-weight: 750; paint-order: stroke; stroke: #0d1321; stroke-width: 4px; stroke-linejoin: round; }
|
|
144
|
+
.node.dim { opacity: .18; }
|
|
145
|
+
.node.selected circle { r: 13; stroke: #fff; }
|
|
146
|
+
.hint { position: absolute; right: 1rem; bottom: 1rem; color: var(--muted); font: .7rem ui-monospace, monospace; }
|
|
147
|
+
noscript { display: block; padding: 2rem; }
|
|
148
|
+
noscript span { color: var(--muted); }
|
|
149
|
+
@media (max-width: 760px) {
|
|
150
|
+
.topbar { min-height: 56px; padding: .65rem 1rem; }
|
|
151
|
+
.topbar a:not(.brand) { display: none; }
|
|
152
|
+
.layout { grid-template-columns: 1fr; }
|
|
153
|
+
.panel { border-right: 0; border-bottom: 1px solid var(--line); }
|
|
154
|
+
.intro { max-width: 42rem; }
|
|
155
|
+
.detail { min-height: 0; }
|
|
156
|
+
.stage { overflow-x: auto; }
|
|
157
|
+
svg { width: 760px; max-width: none; height: 532px; min-height: 532px; touch-action: pan-x pan-y; }
|
|
158
|
+
.hint { display: none; }
|
|
159
|
+
}
|
|
160
|
+
</style>
|
|
161
|
+
</head>
|
|
162
|
+
<body>
|
|
163
|
+
<header class="topbar">
|
|
164
|
+
<a class="brand" href="${escapeHtml(options.base)}">${escapeHtml(options.siteTitle)}</a>
|
|
165
|
+
<a href="${escapeHtml(options.base)}">Concepts</a>
|
|
166
|
+
<a href="${escapeHtml(`${options.base}okf/index.md`)}">Raw OKF</a>
|
|
167
|
+
</header>
|
|
168
|
+
<main class="layout">
|
|
169
|
+
<aside class="panel">
|
|
170
|
+
<p class="eyebrow">OKF RELATIONSHIP VIEW</p>
|
|
171
|
+
<h1>Concept graph</h1>
|
|
172
|
+
<p class="intro">Follow the internal links that connect architecture, workflows, integrations, and operations.</p>
|
|
173
|
+
<label for="search">Find a concept</label>
|
|
174
|
+
<input id="search" type="search" placeholder="Search titles and descriptions" autocomplete="off">
|
|
175
|
+
<label>Concept types</label>
|
|
176
|
+
<div id="filters" class="filters"></div>
|
|
177
|
+
<div id="stats" class="stats" aria-live="polite"></div>
|
|
178
|
+
<section id="detail" class="detail" aria-live="polite"><p>Select a node to inspect its direct relationships.</p></section>
|
|
179
|
+
</aside>
|
|
180
|
+
<section class="stage" aria-label="Interactive concept relationship graph">
|
|
181
|
+
<svg id="graph" viewBox="0 0 1000 700" role="img" aria-labelledby="graph-title graph-description">
|
|
182
|
+
<title id="graph-title">${escapeHtml(options.siteTitle)} concept relationship graph</title>
|
|
183
|
+
<desc id="graph-description">Concept nodes connected by directed internal Markdown links. Arrowheads point from the linking concept to its target. Select a node for incoming and outgoing details.</desc>
|
|
184
|
+
<defs><marker id="arrowhead" markerWidth="8" markerHeight="8" refX="7" refY="4" orient="auto" markerUnits="strokeWidth"><path d="M0,0 L8,4 L0,8 Z" fill="context-stroke"></path></marker></defs>
|
|
185
|
+
<g id="edges"></g>
|
|
186
|
+
<g id="nodes"></g>
|
|
187
|
+
</svg>
|
|
188
|
+
<p class="hint">Drag to rearrange / Space to inspect / Enter to open</p>
|
|
189
|
+
</section>
|
|
190
|
+
</main>
|
|
191
|
+
<noscript><h1>Concepts</h1><ul>${fallbackLinks}</ul></noscript>
|
|
192
|
+
<script id="graph-data" type="application/json">${graphJson}</script>
|
|
193
|
+
<script>
|
|
194
|
+
(() => {
|
|
195
|
+
const data = JSON.parse(document.getElementById('graph-data').textContent);
|
|
196
|
+
const svg = document.getElementById('graph');
|
|
197
|
+
const stage = svg.parentElement;
|
|
198
|
+
const edgesLayer = document.getElementById('edges');
|
|
199
|
+
const nodesLayer = document.getElementById('nodes');
|
|
200
|
+
const search = document.getElementById('search');
|
|
201
|
+
const filters = document.getElementById('filters');
|
|
202
|
+
const stats = document.getElementById('stats');
|
|
203
|
+
const detail = document.getElementById('detail');
|
|
204
|
+
const nodeById = new Map(data.nodes.map((node, index) => {
|
|
205
|
+
const angle = (index / Math.max(data.nodes.length, 1)) * Math.PI * 2;
|
|
206
|
+
return [node.id, Object.assign(node, { x: 500 + Math.cos(angle) * 230, y: 350 + Math.sin(angle) * 230, vx: 0, vy: 0 })];
|
|
207
|
+
}));
|
|
208
|
+
const activeTypes = new Set(data.types);
|
|
209
|
+
const edgeElements = [];
|
|
210
|
+
const nodeElements = new Map();
|
|
211
|
+
const outgoingById = new Map(data.nodes.map((node) => [node.id, new Set()]));
|
|
212
|
+
const incomingById = new Map(data.nodes.map((node) => [node.id, new Set()]));
|
|
213
|
+
for (const edge of data.edges) {
|
|
214
|
+
outgoingById.get(edge.source).add(edge.target);
|
|
215
|
+
incomingById.get(edge.target).add(edge.source);
|
|
216
|
+
}
|
|
217
|
+
let selectedId = '';
|
|
218
|
+
let dragged = null;
|
|
219
|
+
let ticks = 0;
|
|
220
|
+
const simulationTickLimit = Math.max(40, Math.min(320, Math.ceil(32000 / Math.max(data.nodes.length, 1))));
|
|
221
|
+
const repulsionStride = Math.max(1, Math.ceil(data.nodes.length / 180));
|
|
222
|
+
|
|
223
|
+
function svgElement(name, attributes) {
|
|
224
|
+
const element = document.createElementNS('http://www.w3.org/2000/svg', name);
|
|
225
|
+
for (const [key, value] of Object.entries(attributes || {})) element.setAttribute(key, String(value));
|
|
226
|
+
return element;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
for (const edge of data.edges) {
|
|
230
|
+
const line = svgElement('line', { class: 'edge', 'marker-end': 'url(#arrowhead)' });
|
|
231
|
+
edgesLayer.append(line);
|
|
232
|
+
edgeElements.push({ edge, line });
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
for (const [index, node] of data.nodes.entries()) {
|
|
236
|
+
const outgoingCount = outgoingById.get(node.id).size;
|
|
237
|
+
const incomingCount = incomingById.get(node.id).size;
|
|
238
|
+
const group = svgElement('g', { class: 'node', tabindex: '0', role: 'link', 'aria-label': node.title + ', ' + node.type + ', ' + outgoingCount + ' outgoing links, ' + incomingCount + ' incoming links' });
|
|
239
|
+
const circle = svgElement('circle', { r: 10, fill: node.color });
|
|
240
|
+
const text = svgElement('text', { x: 15, y: index % 2 ? 17 : -10 });
|
|
241
|
+
text.textContent = node.title;
|
|
242
|
+
group.append(circle, text);
|
|
243
|
+
group.addEventListener('click', () => selectNode(node.id));
|
|
244
|
+
group.addEventListener('dblclick', () => { location.href = node.href; });
|
|
245
|
+
group.addEventListener('keydown', (event) => {
|
|
246
|
+
if (event.key === 'Enter') location.href = node.href;
|
|
247
|
+
if (event.key === ' ') { event.preventDefault(); selectNode(node.id); }
|
|
248
|
+
});
|
|
249
|
+
group.addEventListener('pointerdown', (event) => {
|
|
250
|
+
dragged = node;
|
|
251
|
+
group.setPointerCapture(event.pointerId);
|
|
252
|
+
});
|
|
253
|
+
group.addEventListener('pointermove', (event) => {
|
|
254
|
+
if (dragged !== node) return;
|
|
255
|
+
const point = svg.createSVGPoint();
|
|
256
|
+
point.x = event.clientX;
|
|
257
|
+
point.y = event.clientY;
|
|
258
|
+
const transformed = point.matrixTransform(svg.getScreenCTM().inverse());
|
|
259
|
+
node.x = transformed.x;
|
|
260
|
+
node.y = transformed.y;
|
|
261
|
+
node.vx = 0;
|
|
262
|
+
node.vy = 0;
|
|
263
|
+
render();
|
|
264
|
+
});
|
|
265
|
+
group.addEventListener('pointerup', () => { dragged = null; });
|
|
266
|
+
nodesLayer.append(group);
|
|
267
|
+
nodeElements.set(node.id, group);
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
for (const type of data.types) {
|
|
271
|
+
const sample = data.nodes.find((node) => node.type === type);
|
|
272
|
+
const button = document.createElement('button');
|
|
273
|
+
button.className = 'filter';
|
|
274
|
+
button.type = 'button';
|
|
275
|
+
button.textContent = type;
|
|
276
|
+
button.setAttribute('aria-pressed', 'true');
|
|
277
|
+
button.style.setProperty('--filter-color', sample ? sample.color : '#91a3ff');
|
|
278
|
+
button.addEventListener('click', () => {
|
|
279
|
+
if (activeTypes.has(type)) activeTypes.delete(type); else activeTypes.add(type);
|
|
280
|
+
button.setAttribute('aria-pressed', String(activeTypes.has(type)));
|
|
281
|
+
applyFilters();
|
|
282
|
+
});
|
|
283
|
+
filters.append(button);
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
function visible(node) {
|
|
287
|
+
const query = search.value.trim().toLowerCase();
|
|
288
|
+
return activeTypes.has(node.type) && (!query || (node.title + ' ' + node.description + ' ' + node.type).toLowerCase().includes(query));
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
function applyFilters() {
|
|
292
|
+
let visibleNodes = 0;
|
|
293
|
+
for (const node of data.nodes) {
|
|
294
|
+
const show = visible(node);
|
|
295
|
+
nodeElements.get(node.id).classList.toggle('dim', !show);
|
|
296
|
+
if (show) visibleNodes += 1;
|
|
297
|
+
}
|
|
298
|
+
const visibleEdges = data.edges.filter((edge) => visible(nodeById.get(edge.source)) && visible(nodeById.get(edge.target))).length;
|
|
299
|
+
stats.textContent = visibleNodes + ' matching concepts / ' + visibleEdges + ' matching directed links / ' + data.metrics.orphanConceptCount + ' true orphans / ' + data.metrics.weaklyConnectedConceptCount + ' weak overall';
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
function selectNode(id) {
|
|
303
|
+
selectedId = id;
|
|
304
|
+
const node = nodeById.get(id);
|
|
305
|
+
for (const [nodeId, element] of nodeElements) element.classList.toggle('selected', nodeId === id);
|
|
306
|
+
for (const item of edgeElements) item.line.classList.toggle('active', item.edge.source === id || item.edge.target === id);
|
|
307
|
+
detail.replaceChildren();
|
|
308
|
+
const title = document.createElement('h2');
|
|
309
|
+
title.textContent = node.title;
|
|
310
|
+
const type = document.createElement('span');
|
|
311
|
+
type.className = 'type';
|
|
312
|
+
type.style.color = node.color;
|
|
313
|
+
type.textContent = node.type;
|
|
314
|
+
const description = document.createElement('p');
|
|
315
|
+
description.textContent = node.description;
|
|
316
|
+
const open = document.createElement('a');
|
|
317
|
+
open.href = node.href;
|
|
318
|
+
open.textContent = 'Open concept ->';
|
|
319
|
+
detail.append(title, type, description, open);
|
|
320
|
+
appendNeighborGroup('Links to', outgoingById.get(id));
|
|
321
|
+
appendNeighborGroup('Linked from', incomingById.get(id));
|
|
322
|
+
|
|
323
|
+
function appendNeighborGroup(label, neighborIds) {
|
|
324
|
+
if (!neighborIds.size) return;
|
|
325
|
+
const group = document.createElement('section');
|
|
326
|
+
group.className = 'neighbor-group';
|
|
327
|
+
const heading = document.createElement('h3');
|
|
328
|
+
heading.textContent = label;
|
|
329
|
+
const related = document.createElement('div');
|
|
330
|
+
related.className = 'neighbors';
|
|
331
|
+
const sortedIds = [...neighborIds].sort((left, right) => nodeById.get(left).title.localeCompare(nodeById.get(right).title));
|
|
332
|
+
for (const relatedId of sortedIds) {
|
|
333
|
+
const neighbor = nodeById.get(relatedId);
|
|
334
|
+
const button = document.createElement('button');
|
|
335
|
+
button.type = 'button';
|
|
336
|
+
button.textContent = neighbor.title;
|
|
337
|
+
button.addEventListener('click', () => selectNode(relatedId));
|
|
338
|
+
related.append(button);
|
|
339
|
+
}
|
|
340
|
+
group.append(heading, related);
|
|
341
|
+
detail.append(group);
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
function simulate() {
|
|
346
|
+
if (ticks < simulationTickLimit && !dragged) {
|
|
347
|
+
for (let i = 0; i < data.nodes.length; i += 1) {
|
|
348
|
+
const left = data.nodes[i];
|
|
349
|
+
for (let j = i + 1; j < data.nodes.length; j += repulsionStride) {
|
|
350
|
+
const right = data.nodes[j];
|
|
351
|
+
const dx = right.x - left.x || .1;
|
|
352
|
+
const dy = right.y - left.y || .1;
|
|
353
|
+
const distance = Math.max(Math.hypot(dx, dy), 20);
|
|
354
|
+
const distanceSquared = Math.max(dx * dx + dy * dy, 400);
|
|
355
|
+
const force = 2200 / distanceSquared;
|
|
356
|
+
const forceX = (dx / distance) * force;
|
|
357
|
+
const forceY = (dy / distance) * force;
|
|
358
|
+
left.vx -= forceX;
|
|
359
|
+
left.vy -= forceY;
|
|
360
|
+
right.vx += forceX;
|
|
361
|
+
right.vy += forceY;
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
for (const edge of data.edges) {
|
|
365
|
+
const source = nodeById.get(edge.source);
|
|
366
|
+
const target = nodeById.get(edge.target);
|
|
367
|
+
const dx = target.x - source.x;
|
|
368
|
+
const dy = target.y - source.y;
|
|
369
|
+
const distance = Math.max(Math.hypot(dx, dy), 1);
|
|
370
|
+
const force = (distance - 210) * .0015;
|
|
371
|
+
const forceX = (dx / distance) * force;
|
|
372
|
+
const forceY = (dy / distance) * force;
|
|
373
|
+
source.vx += forceX;
|
|
374
|
+
source.vy += forceY;
|
|
375
|
+
target.vx -= forceX;
|
|
376
|
+
target.vy -= forceY;
|
|
377
|
+
}
|
|
378
|
+
for (const node of data.nodes) {
|
|
379
|
+
node.vx += (500 - node.x) * .0008;
|
|
380
|
+
node.vy += (350 - node.y) * .0008;
|
|
381
|
+
node.vx *= .89;
|
|
382
|
+
node.vy *= .89;
|
|
383
|
+
node.x = Math.max(45, Math.min(940, node.x + node.vx));
|
|
384
|
+
node.y = Math.max(35, Math.min(665, node.y + node.vy));
|
|
385
|
+
}
|
|
386
|
+
ticks += 1;
|
|
387
|
+
}
|
|
388
|
+
render();
|
|
389
|
+
requestAnimationFrame(simulate);
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
function render() {
|
|
393
|
+
for (const item of edgeElements) {
|
|
394
|
+
const source = nodeById.get(item.edge.source);
|
|
395
|
+
const target = nodeById.get(item.edge.target);
|
|
396
|
+
const dx = target.x - source.x;
|
|
397
|
+
const dy = target.y - source.y;
|
|
398
|
+
const distance = Math.max(Math.hypot(dx, dy), 1);
|
|
399
|
+
const unitX = dx / distance;
|
|
400
|
+
const unitY = dy / distance;
|
|
401
|
+
item.line.setAttribute('x1', source.x + unitX * 12);
|
|
402
|
+
item.line.setAttribute('y1', source.y + unitY * 12);
|
|
403
|
+
item.line.setAttribute('x2', target.x - unitX * 16);
|
|
404
|
+
item.line.setAttribute('y2', target.y - unitY * 16);
|
|
405
|
+
}
|
|
406
|
+
for (const node of data.nodes) {
|
|
407
|
+
const element = nodeElements.get(node.id);
|
|
408
|
+
const label = element.querySelector('text');
|
|
409
|
+
const alignLeft = node.x > 800;
|
|
410
|
+
label.setAttribute('x', alignLeft ? '-15' : '15');
|
|
411
|
+
label.setAttribute('text-anchor', alignLeft ? 'end' : 'start');
|
|
412
|
+
element.setAttribute('transform', 'translate(' + node.x + ' ' + node.y + ')');
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
search.addEventListener('input', applyFilters);
|
|
417
|
+
applyFilters();
|
|
418
|
+
if (data.nodes[0]) selectNode(data.nodes[0].id);
|
|
419
|
+
if (matchMedia('(max-width: 760px)').matches) {
|
|
420
|
+
requestAnimationFrame(() => { stage.scrollLeft = (stage.scrollWidth - stage.clientWidth) / 2; });
|
|
421
|
+
}
|
|
422
|
+
simulate();
|
|
423
|
+
})();
|
|
424
|
+
</script>
|
|
425
|
+
</body>
|
|
426
|
+
</html>
|
|
427
|
+
`;
|
|
428
|
+
}
|
|
429
|
+
export function encodeWikiSiteConceptId(id) {
|
|
430
|
+
return id
|
|
431
|
+
.split('/')
|
|
432
|
+
.map((part) => encodeURIComponent(part))
|
|
433
|
+
.join('/');
|
|
434
|
+
}
|
|
435
|
+
function escapeHtml(value) {
|
|
436
|
+
return value
|
|
437
|
+
.replaceAll('&', '&')
|
|
438
|
+
.replaceAll('<', '<')
|
|
439
|
+
.replaceAll('>', '>')
|
|
440
|
+
.replaceAll('"', '"')
|
|
441
|
+
.replaceAll("'", ''');
|
|
442
|
+
}
|
|
443
|
+
function compareStrings(left, right) {
|
|
444
|
+
return left < right ? -1 : left > right ? 1 : 0;
|
|
445
|
+
}
|
|
446
|
+
//# sourceMappingURL=wiki-site-graph.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"wiki-site-graph.js","sourceRoot":"","sources":["../../src/lib/wiki-site-graph.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export declare function normalizeWikiSiteBase(value: string): string;
|
|
2
|
+
export declare function neutralizeWikiSiteMarkdown(source: string): string;
|
|
3
|
+
export declare function sanitizeWikiSiteFrontmatter(frontmatter: Record<string, unknown>): Record<string, unknown>;
|
|
4
|
+
export declare function isSafeWikiSiteRemoteUrl(value: string): boolean;
|
|
5
|
+
export declare function readWikiSiteOkfVersion(source: string): string | undefined;
|
|
6
|
+
//# sourceMappingURL=wiki-site-safety.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"wiki-site-safety.d.ts","sourceRoot":"","sources":["../../src/lib/wiki-site-safety.ts"],"names":[],"mappings":"AAEA,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAK3D;AAED,wBAAgB,0BAA0B,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAKjE;AAED,wBAAgB,2BAA2B,CACzC,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GACnC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CA6BzB;AAED,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAO9D;AAED,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAazE"}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { parse as parseYaml } from 'yaml';
|
|
2
|
+
export function normalizeWikiSiteBase(value) {
|
|
3
|
+
if (!value.startsWith('/') || !value.endsWith('/')) {
|
|
4
|
+
throw new Error(`WIKI_SITE_BASE must start and end with a slash, received ${value}`);
|
|
5
|
+
}
|
|
6
|
+
return value;
|
|
7
|
+
}
|
|
8
|
+
export function neutralizeWikiSiteMarkdown(source) {
|
|
9
|
+
return source
|
|
10
|
+
.replace(/<!--\s*@include:/g, (directive) => directive.replace('<', '<'))
|
|
11
|
+
.replace(/^(\s{0,3})<<</gm, '$1<<<')
|
|
12
|
+
.replace(/<(\/?)(script|style|template)(?=[\s>])/gi, '<$1$2');
|
|
13
|
+
}
|
|
14
|
+
export function sanitizeWikiSiteFrontmatter(frontmatter) {
|
|
15
|
+
const sanitized = {};
|
|
16
|
+
for (const field of ['okf_version', 'type', 'title', 'description', 'resource', 'timestamp']) {
|
|
17
|
+
const value = safeScalarString(frontmatter[field]);
|
|
18
|
+
if (value !== undefined)
|
|
19
|
+
sanitized[field] = value;
|
|
20
|
+
}
|
|
21
|
+
if (Array.isArray(frontmatter.tags)) {
|
|
22
|
+
sanitized.tags = frontmatter.tags.flatMap((tag) => {
|
|
23
|
+
const value = safeScalarString(tag);
|
|
24
|
+
return value === undefined ? [] : [value];
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
if (Array.isArray(frontmatter.drs_sources)) {
|
|
28
|
+
const sources = frontmatter.drs_sources.flatMap((entry) => {
|
|
29
|
+
if (entry === null || typeof entry !== 'object' || Array.isArray(entry))
|
|
30
|
+
return [];
|
|
31
|
+
const record = entry;
|
|
32
|
+
const sourcePath = safeScalarString(record.path);
|
|
33
|
+
if (!sourcePath)
|
|
34
|
+
return [];
|
|
35
|
+
const symbols = Array.isArray(record.symbols)
|
|
36
|
+
? record.symbols.flatMap((symbol) => {
|
|
37
|
+
const value = safeScalarString(symbol);
|
|
38
|
+
return value === undefined ? [] : [value];
|
|
39
|
+
})
|
|
40
|
+
: undefined;
|
|
41
|
+
return [{ path: sourcePath, ...(symbols?.length ? { symbols } : {}) }];
|
|
42
|
+
});
|
|
43
|
+
if (sources.length > 0)
|
|
44
|
+
sanitized.drs_sources = sources;
|
|
45
|
+
}
|
|
46
|
+
return sanitized;
|
|
47
|
+
}
|
|
48
|
+
export function isSafeWikiSiteRemoteUrl(value) {
|
|
49
|
+
try {
|
|
50
|
+
const url = new URL(value);
|
|
51
|
+
return url.protocol === 'https:' || url.protocol === 'http:';
|
|
52
|
+
}
|
|
53
|
+
catch {
|
|
54
|
+
return false;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
export function readWikiSiteOkfVersion(source) {
|
|
58
|
+
const match = /^---\r?\n([\s\S]*?)\r?\n---(?:\r?\n|$)/.exec(source);
|
|
59
|
+
if (!match?.[1])
|
|
60
|
+
return undefined;
|
|
61
|
+
try {
|
|
62
|
+
const frontmatter = parseYaml(match[1]);
|
|
63
|
+
if (frontmatter === null || typeof frontmatter !== 'object' || Array.isArray(frontmatter)) {
|
|
64
|
+
return undefined;
|
|
65
|
+
}
|
|
66
|
+
const version = frontmatter.okf_version;
|
|
67
|
+
return typeof version === 'string' ? version : undefined;
|
|
68
|
+
}
|
|
69
|
+
catch {
|
|
70
|
+
return undefined;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
function safeScalarString(value) {
|
|
74
|
+
if (typeof value === 'string')
|
|
75
|
+
return value;
|
|
76
|
+
if (typeof value === 'number' || typeof value === 'boolean' || typeof value === 'bigint') {
|
|
77
|
+
return `${value}`;
|
|
78
|
+
}
|
|
79
|
+
if (value instanceof Date)
|
|
80
|
+
return value.toISOString();
|
|
81
|
+
return undefined;
|
|
82
|
+
}
|
|
83
|
+
//# sourceMappingURL=wiki-site-safety.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"wiki-site-safety.js","sourceRoot":"","sources":["../../src/lib/wiki-site-safety.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export interface WikiSiteSmokeResult {
|
|
2
|
+
assetsChecked: number;
|
|
3
|
+
baseUrl: string;
|
|
4
|
+
pagesChecked: number;
|
|
5
|
+
}
|
|
6
|
+
export interface WikiSiteSmokeOptions {
|
|
7
|
+
fetch?: typeof fetch;
|
|
8
|
+
timeoutMs?: number;
|
|
9
|
+
}
|
|
10
|
+
export interface WaitForWikiSiteOptions extends WikiSiteSmokeOptions {
|
|
11
|
+
attempts?: number;
|
|
12
|
+
delayMs?: number;
|
|
13
|
+
}
|
|
14
|
+
export declare function checkWikiSite(value: string, options?: WikiSiteSmokeOptions): Promise<WikiSiteSmokeResult>;
|
|
15
|
+
export declare function waitForWikiSite(value: string, options?: WaitForWikiSiteOptions): Promise<WikiSiteSmokeResult>;
|
|
16
|
+
//# sourceMappingURL=wiki-site-smoke.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"wiki-site-smoke.d.ts","sourceRoot":"","sources":["../../src/lib/wiki-site-smoke.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,mBAAmB;IAClC,aAAa,EAAE,MAAM,CAAC;IACtB,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,oBAAoB;IACnC,KAAK,CAAC,EAAE,OAAO,KAAK,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,sBAAuB,SAAQ,oBAAoB;IAClE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAQD,wBAAsB,aAAa,CACjC,KAAK,EAAE,MAAM,EACb,OAAO,GAAE,oBAAyB,GACjC,OAAO,CAAC,mBAAmB,CAAC,CAqE9B;AA6GD,wBAAsB,eAAe,CACnC,KAAK,EAAE,MAAM,EACb,OAAO,GAAE,sBAA2B,GACnC,OAAO,CAAC,mBAAmB,CAAC,CAa9B"}
|