LCOV - code coverage report
Current view: top level - dashql-app/src/session/snippets - snippet_registry.ts Coverage Total Hit
Test: DashQL 915828b Lines: 98.4 % 62 61
Test Date: 2026-11-06 19:30:32

            Line data    Source code
       1              : import type * as dashql from '@ankoh/dashql-core';
       2              : 
       3              : import { ConnectorType } from '../../connections/connector_info.js';
       4              : import { SNIPPET_DATASETS_FOLDER, type Snippet } from './snippet.js';
       5              : import { parseSnippetFile, type SnippetFile } from './snippet_header.js';
       6              : 
       7              : /// The snippets of the snippet browser
       8              : export interface SnippetRegistry {
       9              :     /// The snippets, the datasets first, then ordered by path
      10              :     snippets: Snippet[];
      11              :     /// The snippets by id
      12              :     byId: ReadonlyMap<string, Snippet>;
      13              :     /// The problems of the snippet files, the affected snippets are left out
      14              :     errors: string[];
      15              : }
      16              : 
      17              : /// The snippet files of examples/snippets, bundled as text. The Vite config aliases the folder.
      18            1 : const SNIPPET_FILES = import.meta.glob<string>('@dashql/snippets/*/*/*.sql', {
      19              :     query: '?raw',
      20              :     import: 'default',
      21              :     eager: true,
      22            1 : });
      23              : 
      24              : /// The text of a snippet for a connector
      25            1 : export function snippetText(snippet: Snippet, connector: ConnectorType): string {
      26            1 :     const body = snippet.bodies.get(connector);
      27            1 :     if (body == null) {
      28            0 :         throw new Error(`snippet ${snippet.id} does not support connector ${ConnectorType[connector]}`);
      29              :     }
      30            1 :     return `${body}\n`;
      31            1 : }
      32              : 
      33              : /// Merge the files of one snippet, the engine variants of a snippet share its id
      34            1 : function mergeSnippetFiles(files: SnippetFile[]): Snippet {
      35            1 :     const bodies = new Map<ConnectorType, string>();
      36            1 :     const paths = new Map<ConnectorType, string>();
      37            1 :     for (const file of files) {
      38            1 :         for (const connector of file.connectors) {
      39            1 :             bodies.set(connector, file.body);
      40            1 :             paths.set(connector, file.path);
      41            1 :         }
      42              :     }
      43            1 :     return {
      44            1 :         id: files[0].id,
      45            1 :         category: files[0].category,
      46            1 :         connectors: [...bodies.keys()],
      47            1 :         bodies,
      48            1 :         paths,
      49            1 :     };
      50            1 : }
      51              : 
      52              : /// Build the registry from the raw snippet files by path below the snippet folder, the folders below the engine
      53              : /// folders are the categories
      54            1 : export function buildSnippetRegistry(core: dashql.DashQL, rawFiles: Record<string, string>): SnippetRegistry {
      55            1 :     const errors: string[] = [];
      56              : 
      57              :     // Parse the files and group the engine variants of a snippet
      58            1 :     const filesById = new Map<string, SnippetFile[]>();
      59            1 :     for (const [path, raw] of Object.entries(rawFiles).sort(([l], [r]) => l.localeCompare(r))) {
      60            1 :         try {
      61            1 :             const file = parseSnippetFile(core, path, raw);
      62            1 :             const files = filesById.get(file.id) ?? [];
      63            1 :             files.push(file);
      64            1 :             filesById.set(file.id, files);
      65            1 :         } catch (error) {
      66            1 :             errors.push(String(error instanceof Error ? error.message : error));
      67            1 :         }
      68              :     }
      69            1 :     const byId = new Map([...filesById].map(([id, files]) => [id, mergeSnippetFiles(files)]));
      70            1 :     const datasetsFirst = (snippet: Snippet) => snippet.category === SNIPPET_DATASETS_FOLDER ? 0 : 1;
      71            1 :     const snippets = [...byId.values()].sort((l, r) => (datasetsFirst(l) - datasetsFirst(r)) || l.id.localeCompare(r.id));
      72            1 :     return { snippets, byId, errors };
      73            1 : }
      74              : 
      75              : /// The snippet files by path below the snippet folder
      76            1 : export function bundledSnippetFiles(): Record<string, string> {
      77            1 :     const files: Record<string, string> = {};
      78            1 :     for (const [path, raw] of Object.entries(SNIPPET_FILES)) {
      79              :         // The files are `<engine>/<category>/<name>.sql`
      80            1 :         files[path.split('/').slice(-3).join('/')] = raw;
      81              :     }
      82            1 :     return files;
      83            1 : }
      84              : 
      85              : /// The registries of the bundled snippets by core instance
      86            1 : const BUNDLED_REGISTRIES = new WeakMap<dashql.DashQL, SnippetRegistry>();
      87              : 
      88              : /// The bundled snippets, parsed once per core instance
      89            1 : export function getBundledSnippets(core: dashql.DashQL): SnippetRegistry {
      90            1 :     let registry = BUNDLED_REGISTRIES.get(core);
      91            1 :     if (registry == null) {
      92            1 :         registry = buildSnippetRegistry(core, bundledSnippetFiles());
      93            1 :         BUNDLED_REGISTRIES.set(core, registry);
      94            1 :     }
      95            1 :     return registry;
      96            1 : }
      97              : 
      98              : /// Does a snippet run on a connector?
      99            1 : export function snippetSupports(snippet: Snippet, connector: ConnectorType): boolean {
     100            1 :     return snippet.bodies.has(connector);
     101            1 : }
     102              : 
     103              : /// The snippets that run on a connector, in the order of the registry
     104            1 : export function snippetsForConnector(registry: SnippetRegistry, connector: ConnectorType): Snippet[] {
     105            1 :     return registry.snippets.filter(snippet => snippetSupports(snippet, connector));
     106            1 : }
        

Generated by: LCOV version 2.0-1