>, submissions: list>} $tree * @return array{v: int, steps: list>, submissions: list>} */ public static function stamped(array $tree): array { return [self::VERSION_KEY => self::VERSION] + $tree; } /** * One payload's tree, with every node passed through `$rewrite`. * * The dig into `template.tree.steps` is here rather than at each caller * for the reason the child keys are: a payload whose snapshot is missing * or malformed comes back UNTOUCHED rather than throwing, and one reader * remembering that and another not is exactly the asymmetry that ends in a * fatal on a page WConvert was asked to leave alone (ADR 0004). * * `$rewrite` sees every node, layout and leaf alike, and returns the node * it wants. **Its own children are walked afterwards**, so a rewrite may * change a node's content without having to think about recursion — which * is the whole of what a link resolver does. * * An entry nothing was done to comes back byte-identical, which matters * because the payload is inlined into every matching page against a 2KB * budget. * * @param array $payload One entry, as it travels to the browser. * @param callable(array): array $rewrite * @return array */ public static function rewrittenIn(array $payload, callable $rewrite): array { $steps = $payload['template']['tree']['steps'] ?? null; if (!is_array($steps)) { return $payload; } $payload['template']['tree']['steps'] = array_map( static fn ($step): mixed => is_array($step) ? self::rewrite($step, $rewrite) : $step, array_values($steps) ); return $payload; } /** * @param array $node * @param callable(array): array $rewrite * @return array */ private static function rewrite(array $node, callable $rewrite): array { if (is_array($node['content'] ?? null)) { $node['content'] = self::rewrite($node['content'], $rewrite); return $node; } $node = $rewrite($node); foreach (self::CHILD_KEYS as $key) { if (is_array($node[$key] ?? null)) { $node[$key] = array_map( static fn ($child): mixed => is_array($child) ? self::rewrite($child, $rewrite) : $child, array_values($node[$key]) ); } } return $node; } /** * Every child of one node, in tree order, whichever key holds it. * * @param array $node * @return list */ public static function childrenOf(array $node): array { $children = is_array($node['content'] ?? null) ? [$node['content']] : []; foreach (self::CHILD_KEYS as $key) { if (is_array($node[$key] ?? null)) { $children = array_merge($children, array_values($node[$key])); } } return $children; } }