noteconnection 1.6.8 → 1.8.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/LICENSE +674 -21
- package/README.md +258 -64
- package/dist/src/agent_workspace.contract.parity.test.js +475 -0
- package/dist/src/agent_workspace.frontend.test.js +9989 -0
- package/dist/src/agent_workspace.locale.contract.test.js +95 -0
- package/dist/src/agent_workspace.runtime.behavior.test.js +5072 -0
- package/dist/src/copy.assets.contract.test.js +130 -0
- package/dist/src/core/PathBridge.js +28 -3
- package/dist/src/export/WorkspaceExportBundle.js +1130 -0
- package/dist/src/export/WorkspaceExportBundle.test.js +1480 -0
- package/dist/src/export/index.js +18 -0
- package/dist/src/export/types.js +2 -0
- package/dist/src/fixrisk.issue.verifier.contract.test.js +3 -1
- package/dist/src/foundation.ann.runtime.contract.test.js +81 -0
- package/dist/src/foundation.release.evidence.contract.test.js +406 -0
- package/dist/src/foundation.sqlite.runtime.contract.test.js +82 -0
- package/dist/src/frontend/README.md +258 -64
- package/dist/src/frontend/agent_workspace.js +5353 -0
- package/dist/src/frontend/agent_workspace_runtime.js +4434 -0
- package/dist/src/frontend/analysis.js +31 -4
- package/dist/src/frontend/app.js +3597 -141
- package/dist/src/frontend/focus_mode_interactions.js +33 -0
- package/dist/src/frontend/godot_future_path_renderer.js +540 -0
- package/dist/src/frontend/godot_tree_interactions.js +178 -0
- package/dist/src/frontend/graph_state.mjs +105 -0
- package/dist/src/frontend/hosted_future_path_runtime.js +157 -0
- package/dist/src/frontend/i18n.mjs +186 -0
- package/dist/src/frontend/index.html +641 -266
- package/dist/src/frontend/layout_gpu.js +12 -4
- package/dist/src/frontend/locales/en.json +721 -5
- package/dist/src/frontend/locales/zh.json +721 -5
- package/dist/src/frontend/main.mjs +60 -0
- package/dist/src/frontend/markdown_runtime.js +827 -0
- package/dist/src/frontend/notemd.css +49 -0
- package/dist/src/frontend/notemd.html +7 -1
- package/dist/src/frontend/notemd.js +64 -0
- package/dist/src/frontend/path.html +107 -0
- package/dist/src/frontend/path_app.js +2189 -150
- package/dist/src/frontend/path_layout.mjs +143 -0
- package/dist/src/frontend/path_mermaid_utils.mjs +108 -0
- package/dist/src/frontend/path_modules_bridge.js +486 -0
- package/dist/src/frontend/path_state.mjs +118 -0
- package/dist/src/frontend/path_styles.css +146 -0
- package/dist/src/frontend/path_worker_bridge.mjs +85 -0
- package/dist/src/frontend/reader.js +522 -27
- package/dist/src/frontend/runtime_bridge.js +67 -54
- package/dist/src/frontend/runtime_bridge.mjs +279 -0
- package/dist/src/frontend/settings.js +130 -12
- package/dist/src/frontend/simulationWorker.js +241 -6
- package/dist/src/frontend/source_manager.js +190 -21
- package/dist/src/frontend/styles.css +2853 -72
- package/dist/src/frontend/workbench_state.mjs +101 -0
- package/dist/src/frontend/workspace_panes.js +10168 -0
- package/dist/src/frontend.locale.contract.test.js +62 -0
- package/dist/src/godot.sidecar.bootstrap.contract.test.js +244 -0
- package/dist/src/indexing/IndexLifecycle.js +195 -0
- package/dist/src/indexing/IndexLifecycle.test.js +49 -0
- package/dist/src/indexing/SegmentBuilder.js +64 -0
- package/dist/src/indexing/UnitBuilder.js +48 -0
- package/dist/src/indexing/types.js +2 -0
- package/dist/src/knowledge.api.contract.test.js +170 -0
- package/dist/src/learning/KnowledgeLearningPlatform.js +10386 -0
- package/dist/src/learning/KnowledgeLearningPlatform.persistence.test.js +327 -0
- package/dist/src/learning/KnowledgeLearningPlatform.program-f.test.js +99 -0
- package/dist/src/learning/KnowledgeLearningPlatform.test.js +2971 -0
- package/dist/src/learning/KnowledgeWorkspaceConversationRegression.js +2974 -0
- package/dist/src/learning/KnowledgeWorkspaceConversationRegression.test.js +3928 -0
- package/dist/src/learning/answerReleaseReview.js +4319 -0
- package/dist/src/learning/answerReleaseReview.test.js +2888 -0
- package/dist/src/learning/api.js +2 -0
- package/dist/src/learning/conversationComposer.js +1480 -0
- package/dist/src/learning/conversationComposer.test.js +1817 -0
- package/dist/src/learning/domains/ConversationManager.js +53 -0
- package/dist/src/learning/domains/KnowledgeIngestor.js +238 -0
- package/dist/src/learning/domains/KnowledgeQuerier.js +187 -0
- package/dist/src/learning/domains/MasteryEngine.js +387 -0
- package/dist/src/learning/domains/MemoryPolicyManager.js +408 -0
- package/dist/src/learning/domains/QualityEvaluator.js +307 -0
- package/dist/src/learning/domains/TutorRouter.js +313 -0
- package/dist/src/learning/domains/index.js +33 -0
- package/dist/src/learning/domains/types.js +7 -0
- package/dist/src/learning/errors.js +29 -0
- package/dist/src/learning/evidenceContextAssembler.js +1176 -0
- package/dist/src/learning/evidenceContextAssembler.test.js +6332 -0
- package/dist/src/learning/graphContextAssembler.js +870 -0
- package/dist/src/learning/graphContextAssembler.test.js +1033 -0
- package/dist/src/learning/index.js +28 -0
- package/dist/src/learning/queryBackend.js +1898 -0
- package/dist/src/learning/queryBackend.test.js +955 -0
- package/dist/src/learning/ragContextPack.js +257 -0
- package/dist/src/learning/ragContextPack.test.js +160 -0
- package/dist/src/learning/ragPublicText.js +38 -0
- package/dist/src/learning/ragSufficiencyJudge.js +161 -0
- package/dist/src/learning/ragSufficiencyJudge.test.js +177 -0
- package/dist/src/learning/ragSufficiencyProviderJudge.js +227 -0
- package/dist/src/learning/ragSufficiencyProviderJudge.test.js +156 -0
- package/dist/src/learning/requestNormalization.js +198 -0
- package/dist/src/learning/runtimeCapability.js +4677 -0
- package/dist/src/learning/runtimeCapability.test.js +3635 -0
- package/dist/src/learning/store.js +1240 -0
- package/dist/src/learning/store.test.js +1126 -0
- package/dist/src/learning/tutorAdapter.js +2 -0
- package/dist/src/learning/types.js +2 -0
- package/dist/src/learning/vectorAccelerationAdapter.js +942 -0
- package/dist/src/learning/vectorAccelerationAdapter.test.js +382 -0
- package/dist/src/lfs.asset.policy.contract.test.js +153 -0
- package/dist/src/license.policy.contract.test.js +66 -0
- package/dist/src/memory/MemoryGovernance.js +74 -0
- package/dist/src/memory/MemoryGovernance.test.js +46 -0
- package/dist/src/memory/types.js +2 -0
- package/dist/src/mermaid.frontend.guard.contract.test.js +77 -0
- package/dist/src/middleware/auth.js +17 -0
- package/dist/src/middleware/body-parser.js +45 -0
- package/dist/src/middleware/cors.js +44 -0
- package/dist/src/middleware/index.js +21 -0
- package/dist/src/middleware/request-trace.js +96 -0
- package/dist/src/notemd/AppConfigToml.js +6 -4
- package/dist/src/notemd/MermaidProcessor.js +400 -50
- package/dist/src/notemd/NotemdService.js +498 -13
- package/dist/src/notemd/PromptManager.js +15 -0
- package/dist/src/notemd/cli/commands.js +357 -0
- package/dist/src/notemd/cli/dispatcher.js +225 -0
- package/dist/src/notemd/cli/index.js +169 -0
- package/dist/src/notemd/cli/parser.js +68 -0
- package/dist/src/notemd/cli/types.js +2 -0
- package/dist/src/notemd/constants.js +43 -0
- package/dist/src/notemd/diagram/diagramGenerationService.js +78 -0
- package/dist/src/notemd/diagram/diagramSpec.js +79 -0
- package/dist/src/notemd/diagram/diagramSpecResponseParser.js +131 -0
- package/dist/src/notemd/diagram/intent.js +95 -0
- package/dist/src/notemd/diagram/planner.js +71 -0
- package/dist/src/notemd/diagram/prompts/diagramSpecPrompt.js +42 -0
- package/dist/src/notemd/diagram/types.js +18 -0
- package/dist/src/notemd/index.js +26 -0
- package/dist/src/notemd/operations/capabilityManifest.js +23 -0
- package/dist/src/notemd/operations/cliContracts.js +17 -0
- package/dist/src/notemd/operations/configProfileCommands.js +85 -0
- package/dist/src/notemd/operations/registry.contract.test.js +95 -0
- package/dist/src/notemd/operations/registry.js +991 -0
- package/dist/src/notemd/operations/types.js +2 -0
- package/dist/src/notemd/providerDiagnostics.js +220 -0
- package/dist/src/notemd/providerProfiles.js +42 -0
- package/dist/src/notemd/providerTemplates.js +231 -0
- package/dist/src/notemd/search/DuckDuckGoProvider.js +39 -0
- package/dist/src/notemd/search/SearchManager.js +13 -0
- package/dist/src/notemd/search/SearchProvider.js +2 -0
- package/dist/src/notemd/search/TavilyProvider.js +44 -0
- package/dist/src/notemd.agent.manifest.test.js +85 -0
- package/dist/src/notemd.api.contract.test.js +14 -1
- package/dist/src/notemd.app_config_toml.test.js +2 -1
- package/dist/src/notemd.batch.workflow.test.js +117 -0
- package/dist/src/notemd.cli.e2e.test.js +136 -0
- package/dist/src/notemd.core.test.js +51 -0
- package/dist/src/notemd.diagram.pipeline.test.js +233 -0
- package/dist/src/notemd.providerTemplates.test.js +34 -0
- package/dist/src/notemd.server.integration.test.js +143 -35
- package/dist/src/notemd.workflow.pipeline.test.js +162 -0
- package/dist/src/pathbridge.handshake.contract.test.js +16 -2
- package/dist/src/pathmode.background.contract.test.js +69 -0
- package/dist/src/pathmode.settings.api.contract.test.js +9 -0
- package/dist/src/pkg.sidecar.contract.test.js +9 -3
- package/dist/src/platform/ExportProfile.js +58 -0
- package/dist/src/platform/PlatformCapabilities.js +45 -0
- package/dist/src/platform/PlatformCapabilities.test.js +30 -0
- package/dist/src/platform/RenderMaterializer.js +33 -0
- package/dist/src/platform/RenderMaterializer.test.js +32 -0
- package/dist/src/query_backend.external_http.integration.test.js +410 -0
- package/dist/src/reader_renderer.js +404 -3
- package/dist/src/reader_renderer.test.js +87 -0
- package/dist/src/release.godot.mirror.contract.test.js +73 -0
- package/dist/src/resources/ResourceRegistry.js +223 -0
- package/dist/src/resources/ResourceRegistry.test.js +61 -0
- package/dist/src/resources/types.js +2 -0
- package/dist/src/routes/agentWorkspaceDiagnostics.js +173 -0
- package/dist/src/routes/data.js +267 -0
- package/dist/src/routes/diagnostics.js +51 -0
- package/dist/src/routes/index.js +23 -0
- package/dist/src/routes/knowledge.js +968 -0
- package/dist/src/routes/markdown.js +287 -0
- package/dist/src/routes/notemd.js +565 -0
- package/dist/src/routes/registry.contract.test.js +130 -0
- package/dist/src/routes/render.js +285 -0
- package/dist/src/routes/runtimeRunbookRouteOps.js +149 -0
- package/dist/src/routes/runtimeRunbookRouteOps.test.js +194 -0
- package/dist/src/routes/settings.js +6 -0
- package/dist/src/routes/staticFiles.js +94 -0
- package/dist/src/routes/types.js +2 -0
- package/dist/src/runtime.transport.adapter.contract.test.js +81 -0
- package/dist/src/server.js +11090 -1549
- package/dist/src/server.migration.test.js +193 -21
- package/dist/src/server.port.fallback.contract.test.js +63 -0
- package/dist/src/session/SessionStateStore.js +81 -0
- package/dist/src/session/SessionStateStore.test.js +58 -0
- package/dist/src/session/types.js +2 -0
- package/dist/src/settings.runtime.contract.test.js +50 -0
- package/dist/src/shared/types.contract.test.js +107 -0
- package/dist/src/shared/types.js +22 -0
- package/dist/src/sidecar.replacement.boundary.contract.test.js +128 -0
- package/dist/src/sidecar.supply.readiness.contract.test.js +144 -0
- package/dist/src/source_manager.loadflow.test.js +46 -0
- package/dist/src/startup.layout.snapshot.contract.test.js +57 -0
- package/dist/src/tauri.frontend.build.contract.test.js +60 -0
- package/dist/src/tauri.sidecar.cleanup.contract.test.js +21 -0
- package/dist/src/utils/RuntimePaths.js +4 -13
- package/dist/src/utils/platform.js +153 -0
- package/dist/src/workflows/WorkflowArtifactStore.js +96 -0
- package/dist/src/workflows/WorkflowArtifactStore.test.js +80 -0
- package/dist/src/workflows/types.js +2 -0
- package/dist/src/workspace/WorkspaceRegistry.js +122 -0
- package/dist/src/workspace/WorkspaceRegistry.test.js +29 -0
- package/dist/src/workspace/types.js +2 -0
- package/package.json +61 -10
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
var _a;
|
|
3
3
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.normalizeMermaidDefinition = normalizeMermaidDefinition;
|
|
4
5
|
exports.renderMathSvg = renderMathSvg;
|
|
5
6
|
exports.renderMermaidSvg = renderMermaidSvg;
|
|
6
7
|
exports.collectMermaidRenderStageSnapshots = collectMermaidRenderStageSnapshots;
|
|
@@ -90,6 +91,403 @@ const MERMAID_GLOBAL_BINDING_KEYS = [
|
|
|
90
91
|
'Node',
|
|
91
92
|
'getComputedStyle',
|
|
92
93
|
];
|
|
94
|
+
function normalizeMermaidLineBreaks(source) {
|
|
95
|
+
return String(source || '')
|
|
96
|
+
.replace(/\r\n?/g, '\n')
|
|
97
|
+
.replace(/<br\s*>/gi, '<br/>');
|
|
98
|
+
}
|
|
99
|
+
function stripWrappingDoubleQuotes(text) {
|
|
100
|
+
const trimmed = String(text || '').trim();
|
|
101
|
+
if (trimmed.length >= 2 && trimmed.startsWith('"') && trimmed.endsWith('"')) {
|
|
102
|
+
return trimmed.slice(1, -1);
|
|
103
|
+
}
|
|
104
|
+
return trimmed;
|
|
105
|
+
}
|
|
106
|
+
function normalizeMermaidQuotedNodeLabels(source) {
|
|
107
|
+
return String(source || "")
|
|
108
|
+
.split("\n")
|
|
109
|
+
.map((line) => {
|
|
110
|
+
const text = String(line || "");
|
|
111
|
+
const startPattern = /([A-Za-z_][A-Za-z0-9_]*)\["/g;
|
|
112
|
+
let result = "";
|
|
113
|
+
let cursor = 0;
|
|
114
|
+
let match;
|
|
115
|
+
while ((match = startPattern.exec(text)) !== null) {
|
|
116
|
+
const labelStart = match.index + match[0].length;
|
|
117
|
+
let labelEnd = -1;
|
|
118
|
+
for (let index = labelStart; index < text.length - 1; index += 1) {
|
|
119
|
+
if (text.charAt(index) === "\"" && text.charAt(index - 1) !== "\\" && text.charAt(index + 1) === "]") {
|
|
120
|
+
labelEnd = index;
|
|
121
|
+
break;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
if (labelEnd < 0) {
|
|
125
|
+
continue;
|
|
126
|
+
}
|
|
127
|
+
result += text.slice(cursor, labelStart);
|
|
128
|
+
result += escapeMermaidQuotedNodeLabel(text.slice(labelStart, labelEnd));
|
|
129
|
+
result += "\"]";
|
|
130
|
+
cursor = labelEnd + 2;
|
|
131
|
+
startPattern.lastIndex = cursor;
|
|
132
|
+
}
|
|
133
|
+
return result + text.slice(cursor);
|
|
134
|
+
})
|
|
135
|
+
.join("\n");
|
|
136
|
+
}
|
|
137
|
+
function splitMermaidTopLevelStatements(source) {
|
|
138
|
+
const text = normalizeMermaidQuotedNodeLabels(normalizeMermaidLineBreaks(source))
|
|
139
|
+
.split('\n')
|
|
140
|
+
.map((line) => rewriteQuotedLabelAfterSemicolonLine(line) || line)
|
|
141
|
+
.join('\n');
|
|
142
|
+
let result = '';
|
|
143
|
+
let quote = false;
|
|
144
|
+
let squareDepth = 0;
|
|
145
|
+
let roundDepth = 0;
|
|
146
|
+
let curlyDepth = 0;
|
|
147
|
+
for (let index = 0; index < text.length; index += 1) {
|
|
148
|
+
const char = text.charAt(index);
|
|
149
|
+
if (char === '"' && text.charAt(index - 1) !== '\\') {
|
|
150
|
+
quote = !quote;
|
|
151
|
+
result += char;
|
|
152
|
+
continue;
|
|
153
|
+
}
|
|
154
|
+
if (!quote) {
|
|
155
|
+
if (char === '[')
|
|
156
|
+
squareDepth += 1;
|
|
157
|
+
else if (char === ']')
|
|
158
|
+
squareDepth = Math.max(0, squareDepth - 1);
|
|
159
|
+
else if (char === '(')
|
|
160
|
+
roundDepth += 1;
|
|
161
|
+
else if (char === ')')
|
|
162
|
+
roundDepth = Math.max(0, roundDepth - 1);
|
|
163
|
+
else if (char === '{')
|
|
164
|
+
curlyDepth += 1;
|
|
165
|
+
else if (char === '}')
|
|
166
|
+
curlyDepth = Math.max(0, curlyDepth - 1);
|
|
167
|
+
if (char === ';' && squareDepth === 0 && roundDepth === 0 && curlyDepth === 0) {
|
|
168
|
+
result += '\n';
|
|
169
|
+
continue;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
result += char;
|
|
173
|
+
}
|
|
174
|
+
return result;
|
|
175
|
+
}
|
|
176
|
+
function rewriteQuotedLabelAfterSemicolonLine(line) {
|
|
177
|
+
if (!line.includes('-->')) {
|
|
178
|
+
return null;
|
|
179
|
+
}
|
|
180
|
+
const match = line.match(/^(.*?)\s*(-->)\s*(.*?);\s*"([^"\n]+)"\s*$/);
|
|
181
|
+
if (!match) {
|
|
182
|
+
return null;
|
|
183
|
+
}
|
|
184
|
+
const source = String(match[1] || '').trim();
|
|
185
|
+
const arrow = String(match[2] || '').trim();
|
|
186
|
+
const target = String(match[3] || '').trim();
|
|
187
|
+
const label = String(match[4] || '').trim();
|
|
188
|
+
if (!source || !target || !label) {
|
|
189
|
+
return null;
|
|
190
|
+
}
|
|
191
|
+
return `${source} -- "${label}" ${arrow} ${target};`;
|
|
192
|
+
}
|
|
193
|
+
function rewriteSingleDoubleDashLabelLine(line) {
|
|
194
|
+
const match = line.match(/^(.*?)\s*(?<!-)--(?!>|-)\s*"([^"\n]+)"\s*(?<!-)--(?!>|-)\s*(.+?)\s*;?\s*$/);
|
|
195
|
+
if (!match) {
|
|
196
|
+
return null;
|
|
197
|
+
}
|
|
198
|
+
const start = String(match[1] || '').trim();
|
|
199
|
+
const label = String(match[2] || '').trim();
|
|
200
|
+
const end = String(match[3] || '').trim();
|
|
201
|
+
if (!start || !label || !end || /^"/.test(end)) {
|
|
202
|
+
return null;
|
|
203
|
+
}
|
|
204
|
+
return `${start} -- "${label}" --> ${end};`;
|
|
205
|
+
}
|
|
206
|
+
function mergeDoubleArrowLabelLine(line) {
|
|
207
|
+
const match = line.match(/^(.*?)\s*--\s*"([^"\n]+)"\s*--\s*"([^"\n]+)"\s*(-->|---)\s*(.*)$/);
|
|
208
|
+
if (!match) {
|
|
209
|
+
return null;
|
|
210
|
+
}
|
|
211
|
+
const start = String(match[1] || '').trim();
|
|
212
|
+
const labelLeft = String(match[2] || '').trim();
|
|
213
|
+
const labelRight = String(match[3] || '').trim();
|
|
214
|
+
const arrow = String(match[4] || '').trim();
|
|
215
|
+
const end = String(match[5] || '').trim();
|
|
216
|
+
if (!start || !labelLeft || !labelRight || !end) {
|
|
217
|
+
return null;
|
|
218
|
+
}
|
|
219
|
+
return `${start} -- "${labelLeft}<br/>${labelRight}" ${arrow} ${end}`;
|
|
220
|
+
}
|
|
221
|
+
function quoteUnquotedEdgeLabelLine(line) {
|
|
222
|
+
const match = line.match(/^(.*?)\s*--\s*([^"\n][^>\n]*?)\s*-->\s*(.*)$/);
|
|
223
|
+
if (!match) {
|
|
224
|
+
return null;
|
|
225
|
+
}
|
|
226
|
+
const start = String(match[1] || '').trim();
|
|
227
|
+
const label = String(match[2] || '').trim();
|
|
228
|
+
const end = String(match[3] || '').trim();
|
|
229
|
+
if (!start || !label || !end) {
|
|
230
|
+
return null;
|
|
231
|
+
}
|
|
232
|
+
if (/^[A-Za-z_][A-Za-z0-9_]*$/.test(label)
|
|
233
|
+
|| /^[A-Za-z_][A-Za-z0-9_]*\s*[\[\(\{].*$/.test(label)
|
|
234
|
+
|| /^["[{(]/.test(label)) {
|
|
235
|
+
return null;
|
|
236
|
+
}
|
|
237
|
+
return `${start} -- "${label}" --> ${end}`;
|
|
238
|
+
}
|
|
239
|
+
function normalizeSubgraphLine(line) {
|
|
240
|
+
const match = /^(\s*subgraph\s+)(.+)$/i.exec(line);
|
|
241
|
+
if (!match) {
|
|
242
|
+
return { next: line, changed: false };
|
|
243
|
+
}
|
|
244
|
+
const prefix = match[1];
|
|
245
|
+
const rawTitle = match[2].trim();
|
|
246
|
+
if (!rawTitle || rawTitle.startsWith('"') || rawTitle.startsWith("'")) {
|
|
247
|
+
return { next: line, changed: false };
|
|
248
|
+
}
|
|
249
|
+
if (/[\s()[\]{}:;,-]/.test(rawTitle)) {
|
|
250
|
+
return {
|
|
251
|
+
next: `${prefix}"${rawTitle.replace(/"/g, '\\"')}"`,
|
|
252
|
+
changed: true,
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
return { next: line, changed: false };
|
|
256
|
+
}
|
|
257
|
+
function splitIntermediateNodeLine(line) {
|
|
258
|
+
const match = line.match(/^(.*?)\s*(-->|---)\s*([A-Za-z_][A-Za-z0-9_]*)\s*(\[[^\]]+\]|\([^)]+\)|\{[^}]+\})\s*(-->|---)\s*(.*)$/);
|
|
259
|
+
if (!match) {
|
|
260
|
+
return null;
|
|
261
|
+
}
|
|
262
|
+
const start = String(match[1] || '').trim();
|
|
263
|
+
const leftArrow = String(match[2] || '').trim();
|
|
264
|
+
const nodeId = String(match[3] || '').trim();
|
|
265
|
+
const nodeShape = String(match[4] || '').trim();
|
|
266
|
+
const rightArrow = String(match[5] || '').trim();
|
|
267
|
+
const end = String(match[6] || '').trim();
|
|
268
|
+
if (!start || !nodeId || !nodeShape || !end) {
|
|
269
|
+
return null;
|
|
270
|
+
}
|
|
271
|
+
return [
|
|
272
|
+
`${start} ${leftArrow} ${nodeId}${nodeShape}`,
|
|
273
|
+
`${nodeId} ${rightArrow} ${end}`,
|
|
274
|
+
];
|
|
275
|
+
}
|
|
276
|
+
function normalizeMermaidPipes(content) {
|
|
277
|
+
return String(content || '')
|
|
278
|
+
.replace(/\|""\|"/g, '')
|
|
279
|
+
.replace(/"\[\]"/g, '');
|
|
280
|
+
}
|
|
281
|
+
function fixExcessiveBrackets(content) {
|
|
282
|
+
return String(content || '')
|
|
283
|
+
.replace(/\]{3,}/g, ']')
|
|
284
|
+
.replace(/\[""\]/g, '')
|
|
285
|
+
.replace(/\["\]/g, ']')
|
|
286
|
+
.replace(/\[\/\["/g, '["')
|
|
287
|
+
.replace(/\["\/\]/g, '"]');
|
|
288
|
+
}
|
|
289
|
+
function normalizeMermaidComments(content) {
|
|
290
|
+
return String(content || '')
|
|
291
|
+
.split('\n')
|
|
292
|
+
.map((line) => {
|
|
293
|
+
const hashCommentMatch = line.match(/^(\s*[A-Za-z0-9_()[\]{}"'\s.-]+?)\s*-->\s*([A-Za-z0-9_()[\]{}"'\s.-]+?)\s*;\s*#(.*)$/);
|
|
294
|
+
if (hashCommentMatch) {
|
|
295
|
+
return `${hashCommentMatch[1].trim()} -- "${hashCommentMatch[3].trim()}" --> ${hashCommentMatch[2].trim()};`;
|
|
296
|
+
}
|
|
297
|
+
const percentCommentMatch = line.match(/^(\s*[A-Za-z0-9_()[\]{}"'\s.-]+?)\s*-->\s*([A-Za-z0-9_()[\]{}"'\s.-]+?)\s*;\s*%(.*)$/);
|
|
298
|
+
if (percentCommentMatch) {
|
|
299
|
+
return `${percentCommentMatch[1].trim()} -- "${percentCommentMatch[3].trim()}" --> ${percentCommentMatch[2].trim()};`;
|
|
300
|
+
}
|
|
301
|
+
const slashCommentMatch = line.match(/^(\s*[A-Za-z0-9_()[\]{}"'\s.-]+?)\s*-->\s*([A-Za-z0-9_()[\]{}"'\s.-]+?)\s*;\s*\/\/(.*)$/);
|
|
302
|
+
if (slashCommentMatch) {
|
|
303
|
+
return `${slashCommentMatch[1].trim()} -- "${slashCommentMatch[3].trim()}" --> ${slashCommentMatch[2].trim()};`;
|
|
304
|
+
}
|
|
305
|
+
return line;
|
|
306
|
+
})
|
|
307
|
+
.join('\n');
|
|
308
|
+
}
|
|
309
|
+
function fixDanglingBracketedNodeLabelLine(line) {
|
|
310
|
+
const match = line.match(/^(\s*[A-Za-z_][A-Za-z0-9_]*)\["([^"\n]*)\["\s*;?\s*$/);
|
|
311
|
+
if (!match) {
|
|
312
|
+
return null;
|
|
313
|
+
}
|
|
314
|
+
const nodeId = String(match[1] || '').trim();
|
|
315
|
+
const label = String(match[2] || '').trim();
|
|
316
|
+
if (!nodeId || !label) {
|
|
317
|
+
return null;
|
|
318
|
+
}
|
|
319
|
+
return `${nodeId}["${escapeMermaidLabel(label)}"]`;
|
|
320
|
+
}
|
|
321
|
+
function sanitizeMermaidIdentifier(rawValue, fallbackPrefix, counter) {
|
|
322
|
+
const raw = String(rawValue || '').trim();
|
|
323
|
+
const prefixedMatch = raw.match(/^([A-Za-z][A-Za-z0-9_]*)/);
|
|
324
|
+
if (prefixedMatch && prefixedMatch[1]) {
|
|
325
|
+
return prefixedMatch[1];
|
|
326
|
+
}
|
|
327
|
+
return `${fallbackPrefix}${counter}`;
|
|
328
|
+
}
|
|
329
|
+
function escapeMermaidLabel(rawValue) {
|
|
330
|
+
return String(rawValue || '')
|
|
331
|
+
.replace(/\\/g, '\\\\')
|
|
332
|
+
.replace(/"/g, '\\"')
|
|
333
|
+
.trim();
|
|
334
|
+
}
|
|
335
|
+
function escapeMermaidQuotedNodeLabel(rawValue) {
|
|
336
|
+
return String(rawValue || "")
|
|
337
|
+
.replace(/\\/g, "\\\\")
|
|
338
|
+
.replace(/"/g, "#quot;")
|
|
339
|
+
.trim();
|
|
340
|
+
}
|
|
341
|
+
function isMermaidControlLine(line) {
|
|
342
|
+
return /^\s*(?:graph|flowchart|subgraph|end|classDef|class|style|linkStyle|click|%%)\b/i.test(line);
|
|
343
|
+
}
|
|
344
|
+
function convertNoteLine(line, state) {
|
|
345
|
+
const directionalMatch = line.match(/^\s*note\s+(?:right|left|top|bottom)\s+of\s+([A-Za-z0-9_]+)\s*:\s*(.+)$/i);
|
|
346
|
+
if (directionalMatch) {
|
|
347
|
+
state.syntheticCounter += 1;
|
|
348
|
+
const noteId = `AutoNote${state.syntheticCounter}`;
|
|
349
|
+
const noteText = escapeMermaidLabel(stripWrappingDoubleQuotes(directionalMatch[2]));
|
|
350
|
+
return [
|
|
351
|
+
`${noteId}["${noteText}"]`,
|
|
352
|
+
`${directionalMatch[1]} -.- ${noteId}`,
|
|
353
|
+
];
|
|
354
|
+
}
|
|
355
|
+
const targetedMatch = line.match(/^\s*note\s+([A-Za-z0-9_]+)\s+"(.+)"\s*$/i);
|
|
356
|
+
if (targetedMatch) {
|
|
357
|
+
state.syntheticCounter += 1;
|
|
358
|
+
const noteId = `AutoNote${state.syntheticCounter}`;
|
|
359
|
+
const noteText = escapeMermaidLabel(stripWrappingDoubleQuotes(targetedMatch[2]));
|
|
360
|
+
return [
|
|
361
|
+
`${noteId}["${noteText}"]`,
|
|
362
|
+
`${targetedMatch[1]} -.- ${noteId}`,
|
|
363
|
+
];
|
|
364
|
+
}
|
|
365
|
+
return null;
|
|
366
|
+
}
|
|
367
|
+
function normalizeMermaidEdgeLabels(line) {
|
|
368
|
+
return String(line || '')
|
|
369
|
+
.replace(/--\s*"([^"\n]+)"\s*-->/g, '-->|$1|')
|
|
370
|
+
.replace(/--\s*"([^"\n]+)"\s*--\s*/g, ' -->|$1| ');
|
|
371
|
+
}
|
|
372
|
+
function normalizeMermaidHtmlLineBreakLabels(line) {
|
|
373
|
+
const text = String(line || '');
|
|
374
|
+
const arrowMatch = text.match(/^(.*?(?:-->|---|-\.->|\-.-))\s*([A-Za-z_][A-Za-z0-9_]*)\s+(.+?)\s*;?\s*$/);
|
|
375
|
+
if (!arrowMatch) {
|
|
376
|
+
return text;
|
|
377
|
+
}
|
|
378
|
+
const prefix = String(arrowMatch[1] || '');
|
|
379
|
+
const nodeId = String(arrowMatch[2] || '').trim();
|
|
380
|
+
const label = String(arrowMatch[3] || '').trim();
|
|
381
|
+
if (!nodeId || !label || !label.includes('<br/>')) {
|
|
382
|
+
return text;
|
|
383
|
+
}
|
|
384
|
+
if (/^[\[\(\{"]/.test(label)) {
|
|
385
|
+
return text;
|
|
386
|
+
}
|
|
387
|
+
if (/(?:-->|---|-\.->|\-.-)/.test(label)) {
|
|
388
|
+
return text;
|
|
389
|
+
}
|
|
390
|
+
return `${prefix} ${nodeId}["${escapeMermaidLabel(stripWrappingDoubleQuotes(label))}"]`;
|
|
391
|
+
}
|
|
392
|
+
function fixMalformedMermaidArrows(line) {
|
|
393
|
+
return String(line || '')
|
|
394
|
+
.replace(/--\|>/g, '-->')
|
|
395
|
+
.replace(/<--/g, '-->')
|
|
396
|
+
.replace(/-- >/g, '-->')
|
|
397
|
+
.replace(/--\s*"([^"\n]+)"\s*--\s*([A-Za-z_][A-Za-z0-9_]*)\s*;/g, '-- "$1" --> $2;');
|
|
398
|
+
}
|
|
399
|
+
function isMermaidErrorSvgMarkup(markup) {
|
|
400
|
+
const text = String(markup || '').toLowerCase();
|
|
401
|
+
if (!text) {
|
|
402
|
+
return false;
|
|
403
|
+
}
|
|
404
|
+
return (text.includes('syntax error in text') ||
|
|
405
|
+
text.includes('lexical error on line') ||
|
|
406
|
+
text.includes('parse error on line') ||
|
|
407
|
+
text.includes('mermaid version') ||
|
|
408
|
+
text.includes('class="error-icon"') ||
|
|
409
|
+
text.includes('id="error-icon"'));
|
|
410
|
+
}
|
|
411
|
+
function normalizeMermaidEndpoint(rawValue, state) {
|
|
412
|
+
const raw = String(rawValue || '').trim();
|
|
413
|
+
if (!raw)
|
|
414
|
+
return raw;
|
|
415
|
+
if (/^(?:\[|\{|\(|")/.test(raw))
|
|
416
|
+
return raw;
|
|
417
|
+
if (/^[A-Za-z_][A-Za-z0-9_]*$/.test(raw))
|
|
418
|
+
return raw;
|
|
419
|
+
if (/^[A-Za-z_][A-Za-z0-9_]*\s*[\[\(\{].*$/.test(raw))
|
|
420
|
+
return raw;
|
|
421
|
+
let nodeId = state.labelToId.get(raw);
|
|
422
|
+
if (!nodeId) {
|
|
423
|
+
state.syntheticCounter += 1;
|
|
424
|
+
nodeId = sanitizeMermaidIdentifier(raw, 'AutoNode', state.syntheticCounter);
|
|
425
|
+
state.labelToId.set(raw, nodeId);
|
|
426
|
+
}
|
|
427
|
+
return `${nodeId}["${escapeMermaidLabel(raw)}"]`;
|
|
428
|
+
}
|
|
429
|
+
function normalizeMermaidEdgeLine(line, state) {
|
|
430
|
+
const next = normalizeMermaidEdgeLabels(line);
|
|
431
|
+
const match = /(.*?)(--(?:>|o|x)(?:\|[^|]*\|)?\s*)(.+)$/.exec(next);
|
|
432
|
+
if (!match) {
|
|
433
|
+
return next;
|
|
434
|
+
}
|
|
435
|
+
const left = String(match[1] || '');
|
|
436
|
+
const connector = String(match[2] || '');
|
|
437
|
+
const right = String(match[3] || '').trim();
|
|
438
|
+
if (!right) {
|
|
439
|
+
return next;
|
|
440
|
+
}
|
|
441
|
+
return `${left}${connector}${normalizeMermaidEndpoint(right, state)}`;
|
|
442
|
+
}
|
|
443
|
+
function applyLegacyMermaidRepairs(line, state) {
|
|
444
|
+
const subgraphNormalized = normalizeSubgraphLine(line);
|
|
445
|
+
let next = String(subgraphNormalized.next || '');
|
|
446
|
+
next = normalizeMermaidComments(next);
|
|
447
|
+
next = fixExcessiveBrackets(normalizeMermaidPipes(next));
|
|
448
|
+
const noteLines = convertNoteLine(next, state);
|
|
449
|
+
if (noteLines) {
|
|
450
|
+
return noteLines;
|
|
451
|
+
}
|
|
452
|
+
next = fixMalformedMermaidArrows(next);
|
|
453
|
+
next = rewriteQuotedLabelAfterSemicolonLine(next) || next;
|
|
454
|
+
next = mergeDoubleArrowLabelLine(next) || next;
|
|
455
|
+
next = rewriteSingleDoubleDashLabelLine(next) || next;
|
|
456
|
+
next = quoteUnquotedEdgeLabelLine(next) || next;
|
|
457
|
+
next = normalizeMermaidHtmlLineBreakLabels(next);
|
|
458
|
+
next = fixDanglingBracketedNodeLabelLine(next) || next;
|
|
459
|
+
const splitIntermediate = splitIntermediateNodeLine(next);
|
|
460
|
+
if (splitIntermediate) {
|
|
461
|
+
return splitIntermediate;
|
|
462
|
+
}
|
|
463
|
+
return [next];
|
|
464
|
+
}
|
|
465
|
+
function normalizeMermaidDefinition(source) {
|
|
466
|
+
const split = splitMermaidTopLevelStatements(source);
|
|
467
|
+
const state = {
|
|
468
|
+
labelToId: new Map(),
|
|
469
|
+
syntheticCounter: 0,
|
|
470
|
+
};
|
|
471
|
+
return split
|
|
472
|
+
.split('\n')
|
|
473
|
+
.reduce((lines, rawLine) => {
|
|
474
|
+
const repairedLines = applyLegacyMermaidRepairs(rawLine, state);
|
|
475
|
+
repairedLines.forEach((line) => {
|
|
476
|
+
if (!String(line || '').trim()) {
|
|
477
|
+
return;
|
|
478
|
+
}
|
|
479
|
+
if (isMermaidControlLine(line)) {
|
|
480
|
+
lines.push(line.trimEnd());
|
|
481
|
+
return;
|
|
482
|
+
}
|
|
483
|
+
lines.push(normalizeMermaidEdgeLine(line, state).trimEnd());
|
|
484
|
+
});
|
|
485
|
+
return lines;
|
|
486
|
+
}, [])
|
|
487
|
+
.join('\n')
|
|
488
|
+
.replace(/\n{3,}/g, '\n\n')
|
|
489
|
+
.trim();
|
|
490
|
+
}
|
|
93
491
|
async function renderMathSvg(source, options = {}) {
|
|
94
492
|
const trimmedSource = source.trim();
|
|
95
493
|
if (!trimmedSource) {
|
|
@@ -143,6 +541,9 @@ async function buildMermaidRenderArtifacts(trimmedSource, options) {
|
|
|
143
541
|
const host = environment.host;
|
|
144
542
|
host.innerHTML = '';
|
|
145
543
|
const result = await withMermaidDomGlobals(environment.window, async () => environment.mermaid.render(renderId, trimmedSource, host));
|
|
544
|
+
if (result && typeof result.svg === 'string' && isMermaidErrorSvgMarkup(result.svg)) {
|
|
545
|
+
throw new Error('Mermaid renderer produced an error SVG instead of a diagram.');
|
|
546
|
+
}
|
|
146
547
|
const svgMarkup = sanitizeSvgMarkup(result.svg);
|
|
147
548
|
const svgDom = new jsdom_1.JSDOM(svgMarkup, { contentType: 'image/svg+xml' });
|
|
148
549
|
installSvgMeasurementPolyfills(svgDom.window);
|
|
@@ -180,7 +581,7 @@ async function buildMermaidRenderArtifacts(trimmedSource, options) {
|
|
|
180
581
|
};
|
|
181
582
|
}
|
|
182
583
|
async function renderMermaidSvg(source, options = {}) {
|
|
183
|
-
const trimmedSource = source
|
|
584
|
+
const trimmedSource = normalizeMermaidDefinition(source);
|
|
184
585
|
if (!trimmedSource) {
|
|
185
586
|
throw new Error('Cannot render an empty Mermaid definition.');
|
|
186
587
|
}
|
|
@@ -190,7 +591,7 @@ async function renderMermaidSvg(source, options = {}) {
|
|
|
190
591
|
});
|
|
191
592
|
}
|
|
192
593
|
async function collectMermaidRenderStageSnapshots(source, options = {}) {
|
|
193
|
-
const trimmedSource = source
|
|
594
|
+
const trimmedSource = normalizeMermaidDefinition(source);
|
|
194
595
|
if (!trimmedSource) {
|
|
195
596
|
throw new Error('Cannot render an empty Mermaid definition.');
|
|
196
597
|
}
|
|
@@ -374,7 +775,7 @@ function getMermaidConfig(theme) {
|
|
|
374
775
|
securityLevel: 'loose',
|
|
375
776
|
theme,
|
|
376
777
|
fontFamily: MERMAID_FONT_FAMILY,
|
|
377
|
-
// Keep Mermaid output
|
|
778
|
+
// Keep Mermaid output free of HTML label nodes so rasterization stays deterministic.
|
|
378
779
|
htmlLabels: false,
|
|
379
780
|
markdownAutoWrap: true,
|
|
380
781
|
maxTextSize: 200000,
|
|
@@ -129,6 +129,93 @@ describe('reader_renderer', () => {
|
|
|
129
129
|
expect(svg).toContain('Start');
|
|
130
130
|
expect(svg).toContain('Segoe UI');
|
|
131
131
|
});
|
|
132
|
+
it('normalizes semicolon-chained Mermaid statements and quoted edge labels before rendering', () => {
|
|
133
|
+
const svg = runRenderer('renderMermaidSvg', [
|
|
134
|
+
'graph TD;',
|
|
135
|
+
'P["入射波"] -- "入射角 θi" -- N; P -- "反射角 θr" -- R;',
|
|
136
|
+
'N --> T["透射波"]',
|
|
137
|
+
].join('\n'), { theme: 'dark' });
|
|
138
|
+
expect(svg.startsWith('<svg')).toBe(true);
|
|
139
|
+
expect(svg).toContain('入射波');
|
|
140
|
+
expect(svg).toContain('透射波');
|
|
141
|
+
});
|
|
142
|
+
it('wraps bare Mermaid node labels that contain html line breaks or mixed CJK text', () => {
|
|
143
|
+
const svg = runRenderer('renderMermaidSvg', [
|
|
144
|
+
'graph TD',
|
|
145
|
+
'A[Incident Wave] --> I界面<br>Interface;',
|
|
146
|
+
'I --> R反射波<br>Reflected Wave;',
|
|
147
|
+
].join('\n'), { theme: 'dark' });
|
|
148
|
+
expect(svg.startsWith('<svg')).toBe(true);
|
|
149
|
+
expect(svg).toContain('Incident Wave');
|
|
150
|
+
expect(svg).toContain('Interface');
|
|
151
|
+
});
|
|
152
|
+
it('rewrites quoted labels that were displaced after a semicolon into valid Mermaid edge labels', () => {
|
|
153
|
+
const svg = runRenderer('renderMermaidSvg', [
|
|
154
|
+
'flowchart TD',
|
|
155
|
+
'A[Incident Wave] --> I[Interface]; "界面法线 Normal"',
|
|
156
|
+
'I --> R[Reflected Wave]',
|
|
157
|
+
].join('\n'), { theme: 'dark' });
|
|
158
|
+
expect(svg.startsWith('<svg')).toBe(true);
|
|
159
|
+
expect(svg).toContain('界面法线');
|
|
160
|
+
expect(svg).toContain('Reflected Wave');
|
|
161
|
+
});
|
|
162
|
+
it('converts directional note directives into note nodes instead of leaving parser-breaking legacy syntax', () => {
|
|
163
|
+
const svg = runRenderer('renderMermaidSvg', [
|
|
164
|
+
'flowchart TD',
|
|
165
|
+
'A[Incident Wave] --> B[Interface]',
|
|
166
|
+
'note right of A : Important constraint',
|
|
167
|
+
].join('\n'), { theme: 'dark' });
|
|
168
|
+
expect(svg.startsWith('<svg')).toBe(true);
|
|
169
|
+
expect(svg).toContain('Important constraint');
|
|
170
|
+
expect(svg).toContain('Incident Wave');
|
|
171
|
+
});
|
|
172
|
+
it('repairs legacy double-dash labelled edges before rendering', () => {
|
|
173
|
+
const svg = runRenderer('renderMermaidSvg', [
|
|
174
|
+
'graph TD;',
|
|
175
|
+
'P["Incident Wave"] -- "theta_i" -- N;',
|
|
176
|
+
'P -- "theta_r" -- R;',
|
|
177
|
+
'N --> T["Transmitted Wave"]',
|
|
178
|
+
].join('\n'), { theme: 'dark' });
|
|
179
|
+
expect(svg.startsWith('<svg')).toBe(true);
|
|
180
|
+
expect(svg).toContain('Incident Wave');
|
|
181
|
+
expect(svg).toContain('Transmitted Wave');
|
|
182
|
+
expect(svg).toContain('theta_i');
|
|
183
|
+
expect(svg).toContain('theta_r');
|
|
184
|
+
});
|
|
185
|
+
it('wraps spaced mixed-language html-break labels into Mermaid node syntax', () => {
|
|
186
|
+
const svg = runRenderer('renderMermaidSvg', [
|
|
187
|
+
'flowchart TD',
|
|
188
|
+
'A[Incident Wave] --> I Interface<br/>Boundary;',
|
|
189
|
+
'I --> R Reflected<br/>Wave;',
|
|
190
|
+
].join('\n'), { theme: 'dark' });
|
|
191
|
+
expect(svg.startsWith('<svg')).toBe(true);
|
|
192
|
+
expect(svg).toContain('Incident Wave');
|
|
193
|
+
expect(svg).toContain('Interface');
|
|
194
|
+
expect(svg).toContain('Reflected');
|
|
195
|
+
});
|
|
196
|
+
it('repairs Mermaid nodes whose quoted labels end with a stray opening bracket', () => {
|
|
197
|
+
const svg = runRenderer('renderMermaidSvg', [
|
|
198
|
+
'graph LR',
|
|
199
|
+
'IncidentWave["入射波 Incident Wave"]',
|
|
200
|
+
'InteractionPoint["作用点 Interaction Point["',
|
|
201
|
+
'IncidentWave --> InteractionPoint',
|
|
202
|
+
'InteractionPoint --> Absorption["吸收 Absorption"]',
|
|
203
|
+
].join('\n'), { theme: 'dark' });
|
|
204
|
+
expect(svg.startsWith('<svg')).toBe(true);
|
|
205
|
+
expect(svg).toContain('作用点');
|
|
206
|
+
expect(svg).toContain('吸收');
|
|
207
|
+
});
|
|
208
|
+
it("escapes stray quotes inside Mermaid bracketed node labels before rendering", () => {
|
|
209
|
+
const svg = runRenderer("renderMermaidSvg", [
|
|
210
|
+
"flowchart TD",
|
|
211
|
+
"HQWG[\"High Quality Water Glass\"] -->|\">Superior Overall\"| FS[\"Final Score \">Superior Overall\"| FS\"]",
|
|
212
|
+
].join("\n"), { theme: "dark" });
|
|
213
|
+
expect(svg.startsWith("<svg")).toBe(true);
|
|
214
|
+
expect(svg).toContain("High Quality Water Glass");
|
|
215
|
+
expect(svg).toContain("Superior");
|
|
216
|
+
expect(svg).toContain("Overall");
|
|
217
|
+
expect(svg).toContain("Final Score");
|
|
218
|
+
});
|
|
132
219
|
it('does not leak JSDOM globals onto Node global scope after Mermaid rendering', () => {
|
|
133
220
|
const scopeProbe = runRendererScopeProbe(['flowchart TD', 'A[Start] --> B{Check}', 'B -->|Yes| C[Done]', 'B -->|No| D[Retry]'].join('\n'), { theme: 'dark' });
|
|
134
221
|
expect(scopeProbe.afterWindow).toBe(scopeProbe.beforeWindow);
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
const fs = __importStar(require("fs"));
|
|
37
|
+
const path = __importStar(require("path"));
|
|
38
|
+
describe('release workflow godot mirror contract', () => {
|
|
39
|
+
const repoRoot = path.resolve(__dirname, '..');
|
|
40
|
+
const workflowPath = path.join(repoRoot, '.github', 'workflows', 'release-desktop-multi-os.yml');
|
|
41
|
+
test('release workflow seeds a project-controlled godot mirror and builds from that mirror first', () => {
|
|
42
|
+
const workflow = fs.readFileSync(workflowPath, 'utf8');
|
|
43
|
+
expect(workflow).toContain('allow_godot_upstream_fallback:');
|
|
44
|
+
expect(workflow).toContain('description: "Allow desktop release jobs to fall back to the upstream Godot release when the project mirror is unavailable."');
|
|
45
|
+
expect(workflow).toContain('default: true');
|
|
46
|
+
expect(workflow).toContain('type: boolean');
|
|
47
|
+
expect(workflow).toContain('GODOT_MIRROR_TAG: "godot-mirror-v4.3-stable"');
|
|
48
|
+
expect(workflow).toContain('GODOT_WINDOWS_ARCHIVE_SHA256: "8f2c75b734bd956027ae3ca92c41f78b5d5a255dacc0f20e4e3c523c545ad410"');
|
|
49
|
+
expect(workflow).toContain('GODOT_LINUX_ARCHIVE_SHA256: "7de56444b130b10af84d19c7e0cf63cf9e9937ee4ba94364c3b7dd114253ca21"');
|
|
50
|
+
expect(workflow).toContain('GODOT_MACOS_ARCHIVE_SHA256: "d17940b913b3f3bf54c941eeb09042099d93865c6e2638e09e20f7c649aa474a"');
|
|
51
|
+
expect(workflow).toContain("GODOT_ALLOW_UPSTREAM_FALLBACK: ${{ github.event_name != 'workflow_dispatch' || github.event.inputs.allow_godot_upstream_fallback != 'false' }}");
|
|
52
|
+
expect(workflow).toContain('ensure-godot-mirror-assets:');
|
|
53
|
+
expect(workflow).toContain('gh release view "$GODOT_MIRROR_TAG" --repo "$GITHUB_REPOSITORY"');
|
|
54
|
+
expect(workflow).toContain('gh release create "$GODOT_MIRROR_TAG" \\');
|
|
55
|
+
expect(workflow).toContain('--repo "$GITHUB_REPOSITORY" \\');
|
|
56
|
+
expect(workflow).toContain('--target "$GITHUB_SHA" \\');
|
|
57
|
+
expect(workflow).toContain('EXPECTED_SHA256="$(expected_sha256_for_asset "$ASSET_NAME")"');
|
|
58
|
+
expect(workflow).toContain('sha256sum "$ARCHIVE_PATH"');
|
|
59
|
+
expect(workflow).toContain('Archive digest mismatch for $ASSET_NAME');
|
|
60
|
+
expect(workflow).toContain('gh release upload "$GODOT_MIRROR_TAG" "$ARCHIVE_PATH" --repo "$GITHUB_REPOSITORY" --clobber');
|
|
61
|
+
expect(workflow).toContain('https://github.com/Jacobinwwey/NoteConnection/releases/download/${GODOT_MIRROR_TAG}');
|
|
62
|
+
expect(workflow).toContain('$allowUpstreamFallback = "$env:GODOT_ALLOW_UPSTREAM_FALLBACK".ToLower() -eq "true"');
|
|
63
|
+
expect(workflow).toContain('if (-not $allowUpstreamFallback) {');
|
|
64
|
+
expect(workflow).toContain('throw "Project mirror download failed and upstream fallback is disabled');
|
|
65
|
+
expect(workflow).toContain('if [ "${GODOT_ALLOW_UPSTREAM_FALLBACK}" != "true" ]; then');
|
|
66
|
+
expect(workflow).toContain('echo "::error::Project mirror download failed and upstream fallback is disabled."');
|
|
67
|
+
expect(workflow).toContain('Get-FileHash -Path $archive -Algorithm SHA256');
|
|
68
|
+
expect(workflow).toContain('Downloaded Godot archive digest mismatch');
|
|
69
|
+
expect(workflow).toContain('ACTUAL_SHA256="$(sha256sum build/godot/godot-linux.zip | awk \'{print $1}\')"');
|
|
70
|
+
expect(workflow).toContain('ACTUAL_SHA256="$(sha256sum build/godot/godot-macos.zip | awk \'{print $1}\')"');
|
|
71
|
+
expect(workflow).toContain('needs: [ensure-release, ensure-godot-mirror-assets]');
|
|
72
|
+
});
|
|
73
|
+
});
|