From b1cf0f8fab2334146f30a5e07d404ee8a0e5201b Mon Sep 17 00:00:00 2001 From: Alexander Zielonka Date: Wed, 23 Sep 2026 22:58:35 +0200 Subject: [PATCH] YAML: Listen aus Maps `- titel: X` zerfiel in Zeichenketten wie "titel: X". Jede Kanalpunkt- Belohnung in der Konfiguration war damit ohne jede Meldung unwirksam, auch das Beispiel in config.example.yaml. Ein Listeneintrag der Form `schluessel: wert` beginnt jetzt eine Map; die folgenden, tiefer eingerueckten Zeilen gehoeren dazu. `- http://x` bleibt Text, weil nach dem Doppelpunkt kein Leerzeichen steht. layout.yaml, config.example.yaml und die Hub-Konfiguration werden nachweislich gleich gelesen wie vorher. Co-Authored-By: Claude Opus 5.5 (1M context) --- packages/core/src/yaml.test.ts | 34 ++++++++++++++++++++++++++++++++++ packages/core/src/yaml.ts | 19 ++++++++++++++++--- 2 files changed, 50 insertions(+), 3 deletions(-) diff --git a/packages/core/src/yaml.test.ts b/packages/core/src/yaml.test.ts index d030bdc..4c89a2f 100644 --- a/packages/core/src/yaml.test.ts +++ b/packages/core/src/yaml.test.ts @@ -53,3 +53,37 @@ test('ein Byte Order Mark verstellt nicht den ersten Schluessel', () => { assert.deepEqual(y, { channel: { login: 'abdulive_' } }); assert.ok(!Object.keys(y).some((k) => k.charCodeAt(0) === 0xfeff)); }); + +test('Listen aus Maps, wie in rewards.belohnungen', () => { + // Frueher zerfiel das in Zeichenketten wie "id: abc", und die Belohnung + // war ohne jede Meldung unwirksam. + const y = parseYaml([ + 'rewards:', + ' enabled: true', + ' belohnungen:', + ' - id: abc-123', + ' titel: Nachricht vorlesen', + ' aktion: tts', + ' - titel: Konfetti', + ' aktion: konfetti', + ' tts:', + ' blockliste:', + ' - sieg heil', + ' - http', + ' maxZeichen: 200', + ].join('\n')); + assert.deepEqual(y, { + rewards: { + enabled: true, + belohnungen: [ + { id: 'abc-123', titel: 'Nachricht vorlesen', aktion: 'tts' }, + { titel: 'Konfetti', aktion: 'konfetti' }, + ], + tts: { blockliste: ['sieg heil', 'http'], maxZeichen: 200 }, + }, + }); +}); + +test('Listeneintrag mit Doppelpunkt ohne Leerzeichen bleibt Text', () => { + assert.deepEqual(parseYaml('a:\n - http://x.de\n'), { a: ['http://x.de'] }); +}); diff --git a/packages/core/src/yaml.ts b/packages/core/src/yaml.ts index 699faec..0a61d27 100644 --- a/packages/core/src/yaml.ts +++ b/packages/core/src/yaml.ts @@ -1,8 +1,8 @@ // Minimaler YAML-Leser fuer unsere eigenen Konfigurationsdateien. // // Deckt genau das ab, was config/*.yaml benutzt: verschachtelte Maps ueber -// Einrueckung, Skalare, Inline-Maps `{a: 1, b: 2}`, Listen aus Skalaren und -// Blockskalare (`>` / `|`). Bewusst kein vollstaendiges YAML - das Projekt +// Einrueckung, Skalare, Inline-Maps `{a: 1, b: 2}`, Listen aus Skalaren oder +// Maps (`- titel: X`) und Blockskalare (`>` / `|`). Bewusst kein vollstaendiges YAML - das Projekt // kommt ohne npm-Abhaengigkeiten aus, und der Sprachumfang, den wir selbst // schreiben, ist ueberschaubar. @@ -73,7 +73,20 @@ export function parseYaml(text: string): YamlMap { if (body.startsWith('- ')) { const key = frame.listKey; const list = key ? frame.target[key] : undefined; - if (Array.isArray(list)) list.push(scalar(body.slice(2))); + const item = body.slice(2); + // `- titel: X` beginnt eine Map als Listeneintrag. Die Zeile wird mit + // der Einrueckung ihres Inhalts neu gelesen, die folgenden, tiefer + // eingerueckten Zeilen landen in derselben Map. Ohne das zerfiel + // `rewards.belohnungen` in einzelne Zeichenketten, und jede Belohnung + // war ohne Fehlermeldung unwirksam. + if (Array.isArray(list) && /^[^\s'"{[#-][^:]*:(\s|$)/.test(item)) { + const map: YamlMap = {}; + list.push(map); + stack.push({ indent, target: map }); + lines[i] = ' '.repeat(indent + 2) + item; + continue; + } + if (Array.isArray(list)) list.push(scalar(item)); i++; continue; }