*/ public static function contract(): array { return once(fn (): array => json_decode(file_get_contents(resource_path('js/lib/ticket-description-format.json')), true, flags: JSON_THROW_ON_ERROR)); } public static function instructions(): string { $contract = self::contract(); return implode(' ', array_map(fn (string $key): string => $contract[$key], ['format', 'supported', 'unsupported', 'links', 'criteria'])) ." Valid description example (Markdown string):\n".$contract['example']; } public static function criteriaInstructions(): string { return self::contract()['criteria']; } public static function safeLink(string $value): bool { $contract = self::contract(); $decoded = rawurldecode($value); if (! mb_check_encoding($decoded, 'UTF-8') || preg_match('~'.$contract['unsafe_link_characters'].'~u', $decoded) || ! preg_match('~'.$contract['link_prefix'].'~i', $value)) { return false; } if (str_starts_with($value, '/') || str_starts_with($value, '#')) { return true; } $url = Uri::tryNew($decoded); return $url !== null && ($url->getScheme() === 'mailto' || (filled($url->getHost()) && ($url->getPort() ?? 0) <= 65535)); } /** @return list */ public static function unsupportedContent(string $value): array { $environment = new Environment; $environment->addExtension(new CommonMarkCoreExtension); $environment->addExtension(new GithubFlavoredMarkdownExtension); $document = (new MarkdownParser($environment))->parse($value); $unsupported = []; foreach ($document->iterator() as $node) { $type = match (true) { $node instanceof HtmlBlock, $node instanceof HtmlInline => 'html', $node instanceof Table => 'table', $node instanceof Image => 'image', $node instanceof TaskListItemMarker => 'task', $node instanceof Link && ! self::safeLink($node->getUrl()) => 'unsafe_link', default => null, }; if ($type !== null) { $unsupported[] = self::contract()['unsupported_tokens'][$type]; } } return array_values(array_unique($unsupported)); } }