feat(surface): Kompositions-Renderer — aus einem Layout werden Inseln - #172
Merged
Merged
Conversation
Baustein 6: der Vertrag, aus dem der Composer später sein Layout liefert, und die Schicht, die daraus Markup macht. Ein Layout ist EIN unveränderliches JSON-Dokument je Version, keine normalisierten Tabellen je Sektion und Block. Rückrollen ist Zeilen-Kopieren, ein Diff ist trivial, und der Renderer liest ein Dokument statt drei Joins. Shopware normalisiert UND versioniert auf jeder Ebene (cmsPageVersionId, cmsSectionVersionId, cmsBlockVersionId); diese Maschinerie kauft eine Abfrage — „welche Layouts benutzen Block X" —, die ein schmaler abgeleiteter Index genauso beantwortet. WAS NICHT HERAUSKOMMT, IST DER SICHERHEITSTEIL data-callora-props steht im ausgelieferten HTML. Wer die Seite abruft, liest es — bei einer Public-Surface ohne Anmeldung. Gefiltert wird deshalb hier, bevor Markup existiert, aus demselben Grund, aus dem SurfaceSlotResolver Views serverseitig filtert statt per CSS: - Als vertraulich deklarierte Controls erscheinen nie im Attribut. - Eine context-Bindung reist als BINDUNG, nie als aufgelöster Wert. Aufgelöst stünde der aktuelle Wert im Quelltext — für jeden Besucher, gleich wer ihn sehen darf. Der Browser löst sie gegen den Kanal auf, wo die Projektion schon entschieden hat. - Jedes Attribut wird kodiert, auch was aus einem konfigurierten Layout kommt: Ein Operator ist vertrauenswürdig, eine gespeicherte Zeichenkette nicht. ZWEI METHODEN, NICHT EINE MIT SCHALTER GetPublishedAsync ruft der öffentliche Pfad, GetDraftAsync nur der Editor. Es gibt kein ?preview=true und keinen Header — bei einer Public-Surface säße so eine Lücke hinter gar keiner Authentifizierung. Ein Schalter machte die Garantie davon abhängig, dass jemand daran denkt, false zu übergeben. Zwei Tests nageln das als Quelltext-Regel fest, nicht über einen Aufruf: Ein Verhaltenstest belegt, dass EIN Pfad den Entwurf nicht holt; die Regel muss für jeden gelten, den jemand morgen hinzufügt. Beide Gegenproben beißen — die erste Fassung des Preview-Tests prüfte nichts, weil sie nach einem zu engen Muster suchte und ihre eigene Begründung im Kommentar gefunden hätte. DIE REIHENFOLGE IST EINE ENTSCHEIDUNG Ein komponiertes Layout gewinnt vor dem SSR-Entry eines Plugins, das vor der eingebauten Schale. Jemand hat das Layout für diese Fläche veröffentlicht; ein Template, das es still überschriebe, machte den Editor unzuverlässig. Die Runtime ändert sich nicht: Das Insel-Format ist das, was mount.ts schon versteht, und ein Block hat genau eine Darstellung — die Vue-Komponente. Ein zweiter, serverseitiger Renderpfad je Block wurde erwogen und verworfen: zwei Implementierungen derselben Darstellung driften, und im Canvas bräuchte jede Konfig-Änderung einen Server-Roundtrip. Unterwegs gelernt: [FromServices] auf einem nicht registrierten INTERFACE lässt die Bindung scheitern und antwortet 400, wo „kein Composer installiert, mach weiter" gemeint ist. Der Dienst kommt jetzt aus dem Container statt aus der Signatur. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
1. Why is this change necessary?
Baustein 6: der Vertrag, aus dem der Composer sein Layout liefert, und die Schicht, die daraus
Markup macht. Damit steht alles unter dem Editor — der Block-Vertrag aus #161, die Kontext-Kanäle
aus #165/#167, und jetzt der Weg vom gespeicherten Layout zur gerenderten Seite.
2. What does this change do, exactly?
Ein Layout ist ein unveränderliches JSON-Dokument je Version, keine normalisierten Tabellen
je Sektion und Block. Rückrollen ist Zeilen-Kopieren, ein Diff ist trivial, und der Renderer liest
ein Dokument statt drei Joins. Shopware normalisiert und versioniert auf jeder Ebene
(
cmsPageVersionId,cmsSectionVersionId,cmsBlockVersionId); diese Maschinerie kauft eineAbfrage — „welche Layouts benutzen Block X" —, die ein schmaler abgeleiteter Index genauso
beantwortet.
Was nicht herauskommt, ist der Sicherheitsteil.
data-callora-propssteht im ausgeliefertenHTML; wer die Seite abruft, liest es — bei einer
Public-Surface ohne Anmeldung. Gefiltert wirddeshalb hier, bevor Markup existiert, aus demselben Grund, aus dem
SurfaceSlotResolverViewsserverseitig filtert statt per CSS:
context-Bindung reist als Bindung, nie als aufgelöster Wert. Aufgelöst stünde deraktuelle Wert im Quelltext — für jeden Besucher, gleich wer ihn sehen darf. Der Browser löst sie
gegen den Kanal auf, wo die Projektion aus feat(surface): geteilter Kontext über Surface-Grenzen, projiziert je Empfänger #167 schon entschieden hat.
vertrauenswürdig, eine gespeicherte Zeichenkette nicht.
Zwei Methoden, nicht eine mit Schalter.
GetPublishedAsyncruft der öffentliche Pfad,GetDraftAsyncnur der Editor. Es gibt kein?preview=trueund keinen Header — bei einerPublic-Surface säße so eine Lücke hinter gar keiner Authentifizierung, und ein Schalter machtedie Garantie davon abhängig, dass jemand daran denkt,
falsezu übergeben.Die Reihenfolge ist eine Entscheidung: ein komponiertes Layout gewinnt vor dem SSR-Entry eines
Plugins, das vor der eingebauten Schale. Jemand hat das Layout für diese Fläche veröffentlicht;
ein Template, das es still überschriebe, machte den Editor unzuverlässig.
Die Runtime ändert sich nicht: Das Insel-Format ist das, was
mount.tsschon versteht.3. Describe each step to reproduce the issue or behaviour.
Ohne Composer-Plugin ändert sich nichts —
ISurfaceLayoutSourceist dann nicht registriert, unddie Surface rendert aus
.njkwie bisher.Mit einer Quelle, die ein Dokument liefert:
10 Renderer-Tests, davon vier zur Filterung: vertrauliches Control, Kontext-Bindung bleibt
Bindung, kein Attribut wenn nichts übrig bleibt, gespeicherte Zeichenkette bricht nicht aus.
4. Please link to the relevant issues (if any).
Setzt §8 Baustein 6 sowie die Regeln 4 und 11 aus §10 um.
Was die Tests gefunden haben
Der erste Preview-Test prüfte nichts: Er suchte nach
"preview"undpreview:, was ein[FromQuery] bool previewnicht trifft. Die gehärtete Fassung sucht das Wort — und hätte dannihre eigene Begründung im Kommentar gefunden, weshalb Kommentarzeilen jetzt ausgenommen sind.
Beide Gegenproben beißen.
Und
[FromServices]auf einem nicht registrierten Interface lässt die Bindung scheitern undantwortet 400, wo „kein Composer installiert, mach weiter" gemeint ist. Das kostete 19 Tests, bis
der Dienst aus dem Container statt aus der Signatur kam.
Zahlen
17 Dateien. .NET 1919 + 29 Analyzer, Surface-Runtime 99 — vier Läufe zur Bestätigung.
Was noch fehlt bis zum Composer
Baustein 7: das Plugin selbst — eigenes Schema über
IPluginDbContextFactory<T>, Canvas mitDrag & Drop, Entwurf/Veröffentlichen, und die Umstellung auf ein Vue-Global statt zwei
(§7.6), damit ein Block-Bundle in beiden Runtimes läuft.