@graphenedata/cli 0.0.15 → 0.0.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +138 -0
- package/dist/cli/bigQuery-I3F46SC6.js +75 -0
- package/dist/cli/bigQuery-I3F46SC6.js.map +7 -0
- package/dist/cli/chunk-OVWODUTJ.js +12849 -0
- package/dist/cli/chunk-OVWODUTJ.js.map +7 -0
- package/dist/cli/chunk-QAXEOZ43.js +53 -0
- package/dist/cli/chunk-QAXEOZ43.js.map +7 -0
- package/dist/cli/cli.js +234 -11197
- package/dist/cli/clickhouse-ZN5AN2UL.js +64 -0
- package/dist/cli/clickhouse-ZN5AN2UL.js.map +7 -0
- package/dist/cli/duckdb-IYBIO5KJ.js +87 -0
- package/dist/cli/duckdb-IYBIO5KJ.js.map +7 -0
- package/dist/cli/serve2-TNN5EROW.js +447 -0
- package/dist/cli/serve2-TNN5EROW.js.map +7 -0
- package/dist/cli/snowflake-MOQB5GA4.js +128 -0
- package/dist/cli/snowflake-MOQB5GA4.js.map +7 -0
- package/dist/index.d.ts +63 -0
- package/dist/lang/index.d.ts +63 -0
- package/dist/skills/graphene/SKILL.md +150 -96
- package/dist/skills/graphene/references/big-value.md +6 -41
- package/dist/skills/graphene/references/date-range.md +64 -0
- package/dist/skills/graphene/references/dropdown.md +3 -4
- package/dist/skills/graphene/references/echarts.md +162 -0
- package/dist/skills/graphene/references/gsql.md +55 -25
- package/dist/skills/graphene/references/model-gsql.md +72 -0
- package/dist/skills/graphene/references/table.md +13 -14
- package/dist/skills/graphene/references/text-input.md +2 -1
- package/dist/ui/app.css +239 -340
- package/dist/ui/component-utilities/dataShaping.ts +484 -0
- package/dist/ui/component-utilities/dataSummary.ts +57 -0
- package/dist/ui/component-utilities/enrich.ts +763 -0
- package/dist/ui/component-utilities/format.ts +177 -0
- package/dist/ui/component-utilities/inputUtils.ts +44 -8
- package/dist/ui/component-utilities/theme.ts +200 -0
- package/dist/ui/component-utilities/themeStores.ts +21 -8
- package/dist/ui/component-utilities/types.ts +70 -0
- package/dist/ui/components/AreaChart.svelte +57 -105
- package/dist/ui/components/BarChart.svelte +71 -129
- package/dist/ui/components/BigValue.svelte +24 -40
- package/dist/ui/components/Column.svelte +10 -18
- package/dist/ui/components/DateRange.svelte +54 -21
- package/dist/ui/components/Dropdown.svelte +47 -26
- package/dist/ui/components/DropdownOption.svelte +1 -2
- package/dist/ui/components/ECharts.svelte +181 -67
- package/dist/ui/components/InlineDelta.svelte +50 -31
- package/dist/ui/components/LineChart.svelte +54 -125
- package/dist/ui/components/PieChart.svelte +27 -37
- package/dist/ui/components/QueryLoad.svelte +77 -45
- package/dist/ui/components/Row.svelte +2 -1
- package/dist/ui/components/ScatterPlot.svelte +52 -0
- package/dist/ui/components/Skeleton.svelte +32 -0
- package/dist/ui/components/Table.svelte +3 -2
- package/dist/ui/components/TableGroupRow.svelte +28 -36
- package/dist/ui/components/TableHarness.svelte +32 -0
- package/dist/ui/components/TableHeader.svelte +34 -59
- package/dist/ui/components/TableRow.svelte +14 -38
- package/dist/ui/components/TableSubtotalRow.svelte +18 -21
- package/dist/ui/components/TableTotalRow.svelte +27 -37
- package/dist/ui/components/TextInput.svelte +13 -12
- package/dist/ui/components/Value.svelte +25 -0
- package/dist/ui/components/_Table.svelte +72 -70
- package/dist/ui/internal/ChartGallery.svelte +527 -0
- package/dist/ui/internal/ErrorDisplay.svelte +22 -97
- package/dist/ui/internal/LocalApp.svelte +80 -17
- package/dist/ui/internal/PageNavGroup.svelte +269 -0
- package/dist/ui/internal/Sidebar.svelte +178 -0
- package/dist/ui/internal/SidebarToggle.svelte +47 -0
- package/dist/ui/internal/StyleGallery.svelte +244 -0
- package/dist/ui/internal/clientCache.ts +2 -2
- package/dist/ui/internal/pageInputs.svelte.js +292 -0
- package/dist/ui/internal/queryEngine.ts +102 -117
- package/dist/ui/internal/runSocket.ts +32 -12
- package/dist/ui/internal/sidebar.svelte.js +18 -0
- package/dist/ui/internal/telemetry.ts +51 -16
- package/dist/ui/internal/types.d.ts +7 -0
- package/dist/ui/web.js +28 -11
- package/package.json +36 -38
- package/dist/skills/graphene/references/area-chart.md +0 -95
- package/dist/skills/graphene/references/bar-chart.md +0 -112
- package/dist/skills/graphene/references/line-chart.md +0 -108
- package/dist/skills/graphene/references/pie-chart.md +0 -29
- package/dist/skills/graphene/references/value-formats.md +0 -104
- package/dist/ui/component-utilities/autoFormatting.js +0 -280
- package/dist/ui/component-utilities/builtInFormats.js +0 -481
- package/dist/ui/component-utilities/chartContext.js +0 -12
- package/dist/ui/component-utilities/chartWindowDebug.js +0 -21
- package/dist/ui/component-utilities/checkInputs.js +0 -84
- package/dist/ui/component-utilities/convert.js +0 -15
- package/dist/ui/component-utilities/dateParsing.js +0 -56
- package/dist/ui/component-utilities/dropdownContext.ts +0 -1
- package/dist/ui/component-utilities/echarts.js +0 -252
- package/dist/ui/component-utilities/echartsThemes.js +0 -443
- package/dist/ui/component-utilities/formatTitle.js +0 -24
- package/dist/ui/component-utilities/formatting.js +0 -241
- package/dist/ui/component-utilities/getColumnExtents.js +0 -79
- package/dist/ui/component-utilities/getColumnSummary.js +0 -62
- package/dist/ui/component-utilities/getCompletedData.js +0 -122
- package/dist/ui/component-utilities/getDistinctCount.js +0 -7
- package/dist/ui/component-utilities/getDistinctValues.js +0 -15
- package/dist/ui/component-utilities/getSeriesConfig.js +0 -231
- package/dist/ui/component-utilities/getSortedData.js +0 -9
- package/dist/ui/component-utilities/getStackPercentages.js +0 -45
- package/dist/ui/component-utilities/getStackedData.js +0 -19
- package/dist/ui/component-utilities/getYAxisIndex.js +0 -15
- package/dist/ui/component-utilities/globalContexts.js +0 -1
- package/dist/ui/component-utilities/helpers/getCompletedData.helpers.js +0 -119
- package/dist/ui/component-utilities/replaceNulls.js +0 -16
- package/dist/ui/component-utilities/tableUtils.ts +0 -107
- package/dist/ui/component-utilities/tidyWithTypes.js +0 -9
- package/dist/ui/components/Area.svelte +0 -214
- package/dist/ui/components/Bar.svelte +0 -347
- package/dist/ui/components/Chart.svelte +0 -995
- package/dist/ui/components/Line.svelte +0 -227
- package/dist/ui/internal/NavSidebar.svelte +0 -396
- package/dist/ui/internal/theme.ts +0 -60
- package/dist/ui/public/inter-latin-ext.woff2 +0 -0
- package/dist/ui/public/inter-latin.woff2 +0 -0
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../serve2.ts", "../../mdCompile.ts"],
|
|
4
|
+
"sourcesContent": ["import {svelte, vitePreprocess} from '@sveltejs/vite-plugin-svelte'\nimport crypto from 'crypto'\nimport fs from 'fs-extra'\n// import sveltePreprocess from 'svelte-preprocess' // this would be nice, but it breaks sourcemaps by default\nimport {type IncomingMessage, type ServerResponse} from 'http'\nimport {mdsvex} from 'mdsvex'\nimport {createRequire} from 'module'\nimport path from 'path'\nimport {fileURLToPath} from 'url'\nimport {createServer, type InlineConfig, optimizeDeps, resolveConfig, type ViteDevServer} from 'vite'\n\nimport type {AnalysisResult, WorkspaceFileInput} from '../lang/types.ts'\n\nimport {config} from '../lang/config.ts'\nimport {analyzeWorkspace, loadWorkspace, toSql} from '../lang/core.ts'\nimport {runQuery} from './connections/index.ts'\nimport {extractFrontmatter, injectComponentImports, remarkPlugins, rehypePlugins} from './mdCompile.ts'\nimport {mockFileMap} from './mockFiles.ts'\nimport {runVitePlugin} from './run.ts'\nimport {getWorkspaceScanCounts, type CliTelemetry} from './telemetry/index.ts'\n\n// Collect Svelte compiler warnings for test assertions\nexport type SvelteWarning = {code: string; message: string; filename?: string}\nexport const svelteWarnings: SvelteWarning[] = []\nexport function clearSvelteWarnings() {\n svelteWarnings.length = 0\n}\n\n// Bump this whenever the query response shape changes so client caches invalidate.\nconst QUERY_VERSION = 1\n\nlet uiRoot: string\nlet nodeRequire = createRequire(import.meta.url)\n\nexport async function serve2(telemetry?: CliTelemetry): Promise<ViteDevServer> {\n let server = await createServer(await createConfig(telemetry))\n // I originally added this to avoid the page refreshing immediately on load.\n // We def don't want to run it in tests, because its not safe to do in parallel.\n // I'm not sure it's still needed, now that we explicitly list out `optimizeDeps.includes`, refreshes should be rare\n // await optimizeDeps(server.config, true)\n await server.listen()\n console.log(`Server running at http://localhost:${server.config.server.port}`)\n\n return server\n}\n\nasync function createConfig(telemetry?: CliTelemetry): Promise<InlineConfig> {\n uiRoot = path.join(fileURLToPath(import.meta.url), '../../ui')\n let port = Number(process.env.GRAPHENE_PORT) || 4000\n let svelteRoot = path.dirname(nodeRequire.resolve('svelte/package.json'))\n let sveltePackage = nodeRequire('svelte/package.json')\n let svelteDependencyRoot = path.dirname(svelteRoot)\n let svelteExport = (name: string) => path.join(svelteRoot, sveltePackage.exports[name].browser || sveltePackage.exports[name].default)\n let packaged = path.basename(path.dirname(uiRoot)) == 'dist'\n await fs.ensureDir(path.resolve(config.root, 'node_modules/.graphene'))\n\n // Bind to 0.0.0.0 when running in a container so port forwarding works from the host\n let inContainer = fs.existsSync('/.dockerenv')\n let host = inContainer ? '0.0.0.0' : '127.0.0.1'\n\n return {\n root: config.root,\n logLevel: process.env.NODE_ENV == 'test' ? 'silent' : 'info',\n plugins: [\n svelte({\n configFile: false,\n extensions: ['.svelte', '.md'],\n preprocess: [\n vitePreprocess(),\n mdsvex({\n extensions: ['.md'],\n remarkPlugins,\n rehypePlugins,\n }) as any,\n injectComponentImports(),\n ],\n onwarn(warning, defaultHandler) {\n if (process.env.NODE_ENV === 'test') {\n svelteWarnings.push({code: warning.code, message: warning.message, filename: warning.filename})\n }\n defaultHandler?.(warning) // Still call the default handler to print warnings\n },\n }),\n fixSvelteDepsInTests(),\n fixHmrForFailedModules(),\n runVitePlugin(),\n handleRequestPlugin,\n updateWorkspacePlugin(telemetry),\n mockFilesForTests(),\n ],\n publicDir: path.resolve(uiRoot, 'public'),\n // on the fence about this one. This would make it less likely we need to optimize when alternating between dev and tests.\n // cacheDir: process.env.NODE_ENV == 'test' ? 'node_modules/.vite-tests' : 'node_modules/.vite',\n server: {\n port,\n host,\n fs: {strict: false},\n strictPort: true,\n hmr: {overlay: false}, // we handle compilation errors ourselves (see LocalApp.svelte)\n },\n resolve: {\n alias: [\n {find: /^graphene$/, replacement: path.resolve(uiRoot, 'web.js')},\n // Vite runs in a user project, but svelte is a direct dependency of the cli, and thus transitive to the user project.\n // So when Vite tries to resolve `svelte` from a compiled md page, it can't find it without these aliases.\n {find: /^svelte$/, replacement: svelteExport('.')},\n {find: /^svelte\\/animate$/, replacement: svelteExport('./animate')},\n {find: /^svelte\\/attachments$/, replacement: svelteExport('./attachments')},\n {find: /^svelte\\/easing$/, replacement: svelteExport('./easing')},\n {find: /^svelte\\/events$/, replacement: svelteExport('./events')},\n {find: /^svelte\\/internal$/, replacement: svelteExport('./internal')},\n {find: /^svelte\\/internal\\/client$/, replacement: svelteExport('./internal/client')},\n {find: /^svelte\\/internal\\/disclose-version$/, replacement: svelteExport('./internal/disclose-version')},\n {find: /^svelte\\/internal\\/flags\\/async$/, replacement: svelteExport('./internal/flags/async')},\n {find: /^svelte\\/internal\\/flags\\/legacy$/, replacement: svelteExport('./internal/flags/legacy')},\n {find: /^svelte\\/internal\\/flags\\/tracing$/, replacement: svelteExport('./internal/flags/tracing')},\n {find: /^svelte\\/legacy$/, replacement: svelteExport('./legacy')},\n {find: /^svelte\\/motion$/, replacement: svelteExport('./motion')},\n {find: /^svelte\\/reactivity$/, replacement: svelteExport('./reactivity')},\n {find: /^svelte\\/reactivity\\/window$/, replacement: svelteExport('./reactivity/window')},\n {find: /^svelte\\/store$/, replacement: svelteExport('./store')},\n {find: /^svelte\\/transition$/, replacement: svelteExport('./transition')},\n {find: /^clsx$/, replacement: path.join(svelteDependencyRoot, 'clsx/dist/clsx.mjs')},\n ],\n },\n\n optimizeDeps: {\n noDiscovery: process.env.NODE_ENV == 'test', // tests manually optimize before starting test workers\n exclude: ['virtual:nav'], // provided by a plugin, so don't try and optimize it\n // Vite running in a user project will not naturally discover and optimize these transitive deps.\n // When you launch the server, your first page load will automatically refresh after a second or two as Vite now sees and optimizes these.\n // This line makes it do that up-front, avoiding that reload jank. The packaged CLI also pre-bundles the `graphene` alias itself;\n // doing that from source causes trouble in examples/tests because the alias points outside node_modules.\n // `graphene` here is a special case: when packaged up it is considered a dependency, but in examples/tests, including it would cause errors.\n // oxfmt-ignore\n include: [\n ...(packaged ? ['graphene'] : []),\n '@graphenedata/cli > svelte',\n '@graphenedata/cli > chroma-js',\n '@graphenedata/cli > echarts',\n '@graphenedata/cli > @graphenedata/html2canvas',\n '@graphenedata/cli > @graphenedata/ui > svelte',\n '@graphenedata/cli > @graphenedata/ui > chroma-js',\n '@graphenedata/cli > @graphenedata/ui > echarts/dist/echarts.esm.js',\n '@graphenedata/cli > @graphenedata/ui > @graphenedata/html2canvas',\n ],\n },\n }\n}\n\nasync function handleQuery(req: IncomingMessage, res: ServerResponse<IncomingMessage>) {\n let chunks = [] as any[]\n for await (let chunk of req) chunks.push(chunk)\n let {gsql, params, hashes} = JSON.parse(Buffer.concat(chunks).toString())\n res.setHeader('Content-Type', 'application/json')\n\n await workspaceLoadPromise\n\n // queries should not analyze md files\n let gsqlFiles = workspaceFiles.filter(file => !file.path.endsWith('.md'))\n let result = analyzeWorkspace({config, files: [...gsqlFiles, {path: 'input', contents: gsql}]})\n updateParsedFiles(result)\n\n let diagnostics = result.diagnostics\n if (diagnostics.length) {\n res.statusCode = 400\n res.end(JSON.stringify(diagnostics[0]))\n return\n }\n\n let queries = result.files.find(file => file.path == 'input')?.queries || []\n if (queries.length > 1) throw new Error('Found multiple queries, which could be a parsing error')\n let sql = toSql(queries[0], params)\n\n // If the client already has this data, dont run the query\n let hash = crypto.createHash('SHA1').update(`query-v${QUERY_VERSION}|${sql}`).digest('hex')\n res.setHeader('ETag', hash)\n if (hashes.includes(hash) && req.headers['cache-control'] != 'no-cache') {\n res.statusCode = 304\n return res.end()\n }\n\n let queryResults = await runQuery(sql)\n let totalRows = queryResults.totalRows ?? queryResults.rows.length\n if (totalRows > queryResults.rows.length) throw new Error('Query returns too many rows')\n let fields = queries[0].fields.map(field => ({name: field.name, type: field.type, metadata: field.metadata || {}}))\n res.end(JSON.stringify({rows: queryResults.rows, hash, fields, sql}))\n}\n\nasync function handlePage(server: ViteDevServer, res: ServerResponse<IncomingMessage>) {\n res.setHeader('Content-Type', 'text/html')\n\n // Use a .html URL for transformIndexHtml so Vite doesn't run the svelte plugin on our HTML template.\n let html = await server.transformIndexHtml(\n '/index.html',\n `<!doctype html>\n <html lang=\"en\">\n <head>\n <meta charset=\"UTF-8\" />\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n <title>Graphene</title>\n <link rel=\"icon\" href=\"/favicon.ico\" />\n </head>\n <body>\n <script type=\"module\">\n import 'graphene'\n </script>\n </body>\n </html>`,\n )\n return res.end(html)\n}\n\n// Runs vite's pre-bundling of dependencies. Used by tests to do this once, instead of for each worker.\nexport async function prepareDeps() {\n let cfg = await resolveConfig(await createConfig(), 'serve')\n await optimizeDeps(cfg, true)\n}\n\n// Svelte forces optimizeDeps whenever its own metadata has changed.\n// For tests, we already optimizeDeps before any tests start up, so we don't need this, and it causes problems\n// if multiple workers are all trying to optimizeDeps at the same time (vite isn't exactly concurrency-safe).\nfunction fixSvelteDepsInTests() {\n let viteConfig: any\n\n function configResolved(cfg: any) {\n viteConfig = cfg\n }\n\n // This must run AFTER Svelte's buildStart which sets force=true based on metadata changes.\n // Using enforce:'post' and sequential:true ensures we run last and can override.\n function buildStart() {\n if (process.env.NODE_ENV != 'test') return\n viteConfig.optimizeDeps.force = false\n }\n buildStart.sequential = true // force running after other sequential hooks (like svelte's)\n return {name: 'fix-svelte-deps', enforce: 'post' as const, configResolved, buildStart}\n}\n\n// When a module's transform fails (e.g. Svelte compilation error in an md file), Vite's import analysis\n// never runs on it, leaving `isSelfAccepting` as undefined. Vite's `propagateUpdate` silently skips\n// unanalyzed modules, so fixing the file produces no HMR update and the page stays broken.\n// We detect this and send a full-reload instead, since the module was never successfully loaded\n// and can't be hot-swapped.\nfunction fixHmrForFailedModules() {\n return {\n name: 'fix-hmr-for-failed-modules',\n hotUpdate(this: any, {modules}: {modules: any[]}) {\n // When a module's last transform failed, its transformResult is null. Vite's normal HMR can't\n // hot-swap a module that has no valid transform \u2014 either because it was never analyzed\n // (isSelfAccepting === undefined) or because it was previously working but is now broken.\n // In both cases, force a full page reload so the browser re-requests everything fresh.\n let hasFailed = modules.some(m => !m.transformResult)\n if (hasFailed) {\n this.environment.hot.send({type: 'full-reload', path: '*'})\n return []\n }\n },\n }\n}\n\n// Watch for changes to gsql files and reload the workspace.\n// This reload blocks all requests, so we shouldn't ever analyze without a workspace.\n// Also tracks all the md files in the workspace to populate the nav sidebar\nlet workspaceLoadPromise: Promise<void> | undefined\nlet workspaceFiles: WorkspaceFileInput[] = []\nlet mdFiles: {path: string; title?: string}[] = []\nfunction updateWorkspacePlugin(telemetry?: CliTelemetry) {\n return {\n name: 'updateWorkspace',\n resolveId(id: string) {\n if (id == 'virtual:nav') return '\\0virtual:nav'\n },\n load(id: string) {\n if (id != '\\0virtual:nav') return\n\n // in tests, inject mock files into the nav.\n // we do this on `load` as each test doesn't always refresh the workspace\n // TODO, we should prob inject these into `loadWorkspace`, then we wouldn't need this block at all\n let res = [...mdFiles]\n if (process.env.NODE_ENV == 'test') {\n for (let [path, contents] of Object.entries(mockFileMap)) {\n let mockFile = {path, title: extractFrontmatter(contents).title}\n let idx = res.findIndex(file => file.path == path)\n if (idx >= 0) res.splice(idx, 1, mockFile)\n else res.push(mockFile)\n }\n }\n\n return `export default ${JSON.stringify(res)}`\n },\n configureServer: (s: ViteDevServer) => {\n let refresh = async () => {\n workspaceLoadPromise = (async () => {\n let loaded = await loadWorkspace(config.root, true, config.ignoredFiles)\n telemetry?.event('workspace_scanned', {command: 'serve', ...getWorkspaceScanCounts(loaded)})\n workspaceFiles = loaded.map(file => {\n let existing = workspaceFiles.find(existing => existing.path == file.path && existing.contents == file.contents)\n return existing?.parsed ? {...file, parsed: existing.parsed} : file\n })\n })()\n await workspaceLoadPromise\n\n // store md file path/title so we can serve it as virtual:nav for the sidebar\n mdFiles = workspaceFiles.filter(file => file.path.endsWith('.md')).map(f => ({path: f.path, title: extractFrontmatter(f.contents).title}))\n\n let mod = s.moduleGraph.getModuleById('\\0virtual:nav')\n if (!mod) return\n s.reloadModule(mod) // triggers HMR of any `virtual:nav` imports\n }\n\n s.watcher.add(['**/*.gsql', '**/*.md'])\n s.watcher.on('all', refresh)\n refresh()\n },\n }\n}\n\nfunction updateParsedFiles(analysis: AnalysisResult) {\n workspaceFiles = workspaceFiles.map(file => {\n let analyzed = analysis.files.find(next => next.path == file.path)\n if (!analyzed) return file\n return {\n ...file,\n parsed: {\n tree: analyzed.tree!,\n virtualContents: analyzed.virtualContents,\n virtualToMarkdownOffset: analyzed.virtualToMarkdownOffset,\n },\n }\n })\n}\n\nconst handleRequestPlugin = {\n name: 'handleRequest',\n configureServer: (s: ViteDevServer) => {\n s.middlewares.use(async function handleRequest(req, res, next) {\n try {\n let [pathName] = (req.url || '').split('?')\n if (pathName == '/_api/query') return await handleQuery(req, res)\n if (pathName) if (pathName == '/__ct' || pathName == '/_charts' || pathName == '/_styles') return await handlePage(s, res)\n\n if (!pathName || pathName == '/') pathName = 'index'\n let relativeMdPath = pathName.replace(/^\\//, '') + '.md'\n let mdPath = path.join(config.root, relativeMdPath)\n\n if (mockFileMap[relativeMdPath] || (await fs.exists(mdPath))) {\n await handlePage(s, res)\n } else {\n next()\n }\n } catch (err: any) {\n if (process.env.NODE_ENV != 'test') console.error(err) // ignore in tests because they're noisy, and any unexpected errors should be captured by browserConsole.\n res.statusCode = 500\n res.end(JSON.stringify({message: err.message, stack: err.stack}))\n }\n })\n },\n}\n\nfunction mockFilesForTests() {\n if (process.env.NODE_ENV !== 'test') return null\n\n function toMockKey(id: string) {\n // Handle both absolute paths (/wt/.../index.md) and root-relative paths (/index.md)\n return id.replace(config.root + '/', '').replace(/^\\//, '')\n }\n\n return {\n name: 'mock-files-for-tests',\n enforce: 'pre' as const,\n resolveId(id: any) {\n if (!mockFileMap[toMockKey(id)]) return\n // Always resolve to the absolute path so the module graph key matches\n // what updateMockFile emits via server.watcher (needed for HMR to work).\n return path.join(config.root, toMockKey(id)) + '?mock'\n },\n load(id: any) {\n if (!id.endsWith('?mock')) return null\n return mockFileMap[toMockKey(id.replace(/\\?mock$/, ''))]\n },\n }\n}\n", "import type {Plugin} from 'unified'\n\nimport {decodeHTML} from 'entities'\nimport fs from 'fs'\nimport yaml from 'js-yaml'\nimport JSON5 from 'json5'\nimport path from 'path'\nimport sanitizeHtml from 'sanitize-html'\nimport {visit} from 'unist-util-visit'\n\nfunction escapeHtml(str: string) {\n return str.replace(/&/g, '&').replace(/\"/g, '"').replace(/</g, '<').replace(/>/g, '>')\n}\n\n// Takes the contents of a <ECharts> tag, and json5 parses it\nexport function liftInlineEChartsConfig(content: string) {\n return content.replace(/<ECharts\\b([^>]*)>([\\s\\S]*?)<\\/ECharts>/g, (match: string, attrs = '', body = '') => {\n let inline = body.trim()\n if (!inline) return match\n if (/\\sconfig\\s*=/.test(attrs)) return match\n let source = inline.startsWith('{') ? inline : `{${inline}}`\n let config = JSON.stringify(JSON5.parse(source), (_key, value) => (typeof value == 'string' ? decodeHTML(value) : value))\n return `<ECharts${attrs} config={${config}}></ECharts>`\n })\n}\n\n// Turn code fences into <GrapheneQuery> tags, which register those queries\nexport function extractQueries() {\n return function transformer(tree: any) {\n visit(tree, 'code', (node, index, parent) => {\n if (index === null) return\n let name = typeof node.meta === 'string' ? node.meta : ''\n let code = typeof node.value === 'string' ? node.value.trim() : ''\n parent.children[index] = {type: 'html', value: `<GrapheneQuery name=\"${escapeHtml(name)}\" code=\"${escapeHtml(code)}\" />`}\n })\n }\n}\n\n// remark will leave less-than and greater-than unescaped, which breaks svelte and prevents the page from loading.\nexport function escapeAngles() {\n return function transformer(tree: any) {\n visit(tree, 'text', (node: any) => {\n if (!node.value || typeof node.value !== 'string') return\n if (!node.value.includes('<')) return\n node.value = node.value.replace(/</g, '<')\n })\n }\n}\n\n// remark can split one html block into adjacent html nodes when self-closing tags are involved.\n// Merge those sibling html nodes so downstream rehype/sanitize work on the full block.\nexport function mergeAdjacentHtml() {\n return function transformer(tree: any) {\n visit(tree, (parent: any) => {\n if (!Array.isArray(parent?.children)) return\n\n for (let i = 0; i < parent.children.length; i++) {\n if (parent.children[i]?.type !== 'html') continue\n\n let j = i\n while (j + 1 < parent.children.length && parent.children[j + 1]?.type === 'html') j++\n if (j == i) continue\n\n let value = parent.children\n .slice(i, j + 1)\n .map((node: any) => node.value || '')\n .join('\\n')\n parent.children.splice(i, j - i + 1, {type: 'html', value})\n }\n })\n }\n}\n\n// Restrict allowed components in markdown files to avoid xss issues.\n// This uses sanitize-html rather than rehype-sanitize because the latter had lots of issues with preserving tag casing,\n// as well as allowing all attributes on our allowlisted components.\nexport function sanitizeMarkdown() {\n return function transformer(tree: any) {\n visit(tree, 'raw', (node: any) => {\n if (typeof node.value !== 'string') return\n\n // sanitize-html doesn't like non-standard self-closing tags, so we need to rewrite them into open+close tags\n let expanded = node.value.replace(/<(\\w+)((?:\\s[^<>]*?)?)\\s*\\/>/gi, (_: string, name: string, attrs = '') => {\n let spacing = attrs\n return `<${name}${spacing}></${name}>`\n })\n\n let sanitized = sanitizeHtml(expanded, {\n ...sanitizeHtml.defaults,\n allowedTags: [...sanitizeHtml.defaults.allowedTags, ...componentNames()],\n allowedAttributes: {\n ...sanitizeHtml.defaults.allowedAttributes,\n ...Object.fromEntries(componentNames().map(n => [n, ['*']])),\n },\n parser: {\n ...((sanitizeHtml.defaults as any).parser || {}),\n lowerCaseAttributeNames: false,\n lowerCaseTags: false,\n },\n })\n node.value = sanitized\n })\n }\n}\n\n// We don't want users to have to manually import components in their md files, so we auto-import them.\nexport function injectComponentImports() {\n let imp = `const {${componentNames().join(', ')}} = window.$GRAPHENE.components`\n\n return {\n markup: ({content, filename}: {content: string; filename: string}) => {\n if (!filename.endsWith('.md')) return // only auto-import components for md files\n content = liftInlineEChartsConfig(content)\n if (content.includes('<script>')) {\n content = content.replace('<script>', `<script>\\n${imp}`)\n } else {\n content = `<script>\\n${imp}\\n</script>\\n${content}`\n }\n return {code: content}\n },\n style: () => {},\n script: () => {},\n }\n}\n\n// List out the component names from ui/components\nlet cachedComponentNames: string[] | null = null\nexport function componentNames() {\n if (cachedComponentNames) return cachedComponentNames\n\n let files = fs.readdirSync(path.join(import.meta.dirname, '../ui/components'))\n cachedComponentNames = files.map(f => path.basename(f, '.svelte')).filter(f => !f.startsWith('_'))\n return cachedComponentNames || []\n}\n\nexport type PageFrontmatter = {title?: string}\n\n// Parse YAML frontmatter from the --- delimited block at the top of a markdown file.\nconst frontmatterRe = /^---\\s*\\n([\\s\\S]*?)\\n---(?:\\n|$)/\nexport function extractFrontmatter(contents: string): PageFrontmatter {\n let match = contents.trimStart().match(frontmatterRe)\n if (!match) return {}\n let raw = yaml.safeLoad(match[1]) as Record<string, any> | undefined\n return {title: raw?.title ? String(raw.title) : undefined}\n}\n\nexport const remarkPlugins: Array<Plugin> = [extractQueries, escapeAngles, mergeAdjacentHtml]\nexport const rehypePlugins: Array<Plugin> = [sanitizeMarkdown]\n"],
|
|
5
|
+
"mappings": ";;;;;;;;;;;;;;AAAA,SAAQ,QAAQ,sBAAqB;AACrC,OAAO,YAAY;AACnB,OAAOA,SAAQ;AAGf,SAAQ,cAAa;AACrB,SAAQ,qBAAoB;AAC5B,OAAOC,WAAU;AACjB,SAAQ,qBAAoB;AAC5B,SAAQ,cAAiC,cAAc,qBAAwC;;;ACP/F,SAAQ,kBAAiB;AACzB,OAAO,QAAQ;AACf,OAAO,UAAU;AACjB,OAAO,WAAW;AAClB,OAAO,UAAU;AACjB,OAAO,kBAAkB;AACzB,SAAQ,aAAY;AAEpB,SAAS,WAAW,KAAa;AAC/B,SAAO,IAAI,QAAQ,MAAM,OAAO,EAAE,QAAQ,MAAM,QAAQ,EAAE,QAAQ,MAAM,MAAM,EAAE,QAAQ,MAAM,MAAM;AACtG;AAGO,SAAS,wBAAwB,SAAiB;AACvD,SAAO,QAAQ,QAAQ,4CAA4C,CAAC,OAAe,QAAQ,IAAI,OAAO,OAAO;AAC3G,QAAI,SAAS,KAAK,KAAK;AACvB,QAAI,CAAC,OAAQ,QAAO;AACpB,QAAI,eAAe,KAAK,KAAK,EAAG,QAAO;AACvC,QAAI,SAAS,OAAO,WAAW,GAAG,IAAI,SAAS,IAAI,MAAM;AACzD,QAAIC,UAAS,KAAK,UAAU,MAAM,MAAM,MAAM,GAAG,CAAC,MAAM,UAAW,OAAO,SAAS,WAAW,WAAW,KAAK,IAAI,KAAM;AACxH,WAAO,WAAW,KAAK,YAAYA,OAAM;AAAA,EAC3C,CAAC;AACH;AAGO,SAAS,iBAAiB;AAC/B,SAAO,SAAS,YAAY,MAAW;AACrC,UAAM,MAAM,QAAQ,CAAC,MAAM,OAAO,WAAW;AAC3C,UAAI,UAAU,KAAM;AACpB,UAAI,OAAO,OAAO,KAAK,SAAS,WAAW,KAAK,OAAO;AACvD,UAAI,OAAO,OAAO,KAAK,UAAU,WAAW,KAAK,MAAM,KAAK,IAAI;AAChE,aAAO,SAAS,KAAK,IAAI,EAAC,MAAM,QAAQ,OAAO,wBAAwB,WAAW,IAAI,CAAC,WAAW,WAAW,IAAI,CAAC,OAAM;AAAA,IAC1H,CAAC;AAAA,EACH;AACF;AAGO,SAAS,eAAe;AAC7B,SAAO,SAAS,YAAY,MAAW;AACrC,UAAM,MAAM,QAAQ,CAAC,SAAc;AACjC,UAAI,CAAC,KAAK,SAAS,OAAO,KAAK,UAAU,SAAU;AACnD,UAAI,CAAC,KAAK,MAAM,SAAS,GAAG,EAAG;AAC/B,WAAK,QAAQ,KAAK,MAAM,QAAQ,MAAM,MAAM;AAAA,IAC9C,CAAC;AAAA,EACH;AACF;AAIO,SAAS,oBAAoB;AAClC,SAAO,SAAS,YAAY,MAAW;AACrC,UAAM,MAAM,CAAC,WAAgB;AAC3B,UAAI,CAAC,MAAM,QAAQ,QAAQ,QAAQ,EAAG;AAEtC,eAAS,IAAI,GAAG,IAAI,OAAO,SAAS,QAAQ,KAAK;AAC/C,YAAI,OAAO,SAAS,CAAC,GAAG,SAAS,OAAQ;AAEzC,YAAI,IAAI;AACR,eAAO,IAAI,IAAI,OAAO,SAAS,UAAU,OAAO,SAAS,IAAI,CAAC,GAAG,SAAS,OAAQ;AAClF,YAAI,KAAK,EAAG;AAEZ,YAAI,QAAQ,OAAO,SAChB,MAAM,GAAG,IAAI,CAAC,EACd,IAAI,CAAC,SAAc,KAAK,SAAS,EAAE,EACnC,KAAK,IAAI;AACZ,eAAO,SAAS,OAAO,GAAG,IAAI,IAAI,GAAG,EAAC,MAAM,QAAQ,MAAK,CAAC;AAAA,MAC5D;AAAA,IACF,CAAC;AAAA,EACH;AACF;AAKO,SAAS,mBAAmB;AACjC,SAAO,SAAS,YAAY,MAAW;AACrC,UAAM,MAAM,OAAO,CAAC,SAAc;AAChC,UAAI,OAAO,KAAK,UAAU,SAAU;AAGpC,UAAI,WAAW,KAAK,MAAM,QAAQ,kCAAkC,CAAC,GAAW,MAAc,QAAQ,OAAO;AAC3G,YAAI,UAAU;AACd,eAAO,IAAI,IAAI,GAAG,OAAO,MAAM,IAAI;AAAA,MACrC,CAAC;AAED,UAAI,YAAY,aAAa,UAAU;AAAA,QACrC,GAAG,aAAa;AAAA,QAChB,aAAa,CAAC,GAAG,aAAa,SAAS,aAAa,GAAG,eAAe,CAAC;AAAA,QACvE,mBAAmB;AAAA,UACjB,GAAG,aAAa,SAAS;AAAA,UACzB,GAAG,OAAO,YAAY,eAAe,EAAE,IAAI,OAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC;AAAA,QAC7D;AAAA,QACA,QAAQ;AAAA,UACN,GAAK,aAAa,SAAiB,UAAU,CAAC;AAAA,UAC9C,yBAAyB;AAAA,UACzB,eAAe;AAAA,QACjB;AAAA,MACF,CAAC;AACD,WAAK,QAAQ;AAAA,IACf,CAAC;AAAA,EACH;AACF;AAGO,SAAS,yBAAyB;AACvC,MAAI,MAAM,UAAU,eAAe,EAAE,KAAK,IAAI,CAAC;AAE/C,SAAO;AAAA,IACL,QAAQ,CAAC,EAAC,SAAS,SAAQ,MAA2C;AACpE,UAAI,CAAC,SAAS,SAAS,KAAK,EAAG;AAC/B,gBAAU,wBAAwB,OAAO;AACzC,UAAI,QAAQ,SAAS,UAAU,GAAG;AAChC,kBAAU,QAAQ,QAAQ,YAAY;AAAA,EAAa,GAAG,EAAE;AAAA,MAC1D,OAAO;AACL,kBAAU;AAAA,EAAa,GAAG;AAAA;AAAA,EAAgB,OAAO;AAAA,MACnD;AACA,aAAO,EAAC,MAAM,QAAO;AAAA,IACvB;AAAA,IACA,OAAO,MAAM;AAAA,IAAC;AAAA,IACd,QAAQ,MAAM;AAAA,IAAC;AAAA,EACjB;AACF;AAGA,IAAI,uBAAwC;AACrC,SAAS,iBAAiB;AAC/B,MAAI,qBAAsB,QAAO;AAEjC,MAAI,QAAQ,GAAG,YAAY,KAAK,KAAK,YAAY,SAAS,kBAAkB,CAAC;AAC7E,yBAAuB,MAAM,IAAI,OAAK,KAAK,SAAS,GAAG,SAAS,CAAC,EAAE,OAAO,OAAK,CAAC,EAAE,WAAW,GAAG,CAAC;AACjG,SAAO,wBAAwB,CAAC;AAClC;AAKA,IAAM,gBAAgB;AACf,SAAS,mBAAmB,UAAmC;AACpE,MAAI,QAAQ,SAAS,UAAU,EAAE,MAAM,aAAa;AACpD,MAAI,CAAC,MAAO,QAAO,CAAC;AACpB,MAAI,MAAM,KAAK,SAAS,MAAM,CAAC,CAAC;AAChC,SAAO,EAAC,OAAO,KAAK,QAAQ,OAAO,IAAI,KAAK,IAAI,OAAS;AAC3D;AAEO,IAAM,gBAA+B,CAAC,gBAAgB,cAAc,iBAAiB;AACrF,IAAM,gBAA+B,CAAC,gBAAgB;;;AD5HtD,IAAM,iBAAkC,CAAC;AACzC,SAAS,sBAAsB;AACpC,iBAAe,SAAS;AAC1B;AAGA,IAAM,gBAAgB;AAEtB,IAAI;AACJ,IAAI,cAAc,cAAc,YAAY,GAAG;AAE/C,eAAsB,OAAO,WAAkD;AAC7E,MAAI,SAAS,MAAM,aAAa,MAAM,aAAa,SAAS,CAAC;AAK7D,QAAM,OAAO,OAAO;AACpB,UAAQ,IAAI,sCAAsC,OAAO,OAAO,OAAO,IAAI,EAAE;AAE7E,SAAO;AACT;AAEA,eAAe,aAAa,WAAiD;AAC3E,WAASC,MAAK,KAAK,cAAc,YAAY,GAAG,GAAG,UAAU;AAC7D,MAAI,OAAO,OAAO,QAAQ,IAAI,aAAa,KAAK;AAChD,MAAI,aAAaA,MAAK,QAAQ,YAAY,QAAQ,qBAAqB,CAAC;AACxE,MAAI,gBAAgB,YAAY,qBAAqB;AACrD,MAAI,uBAAuBA,MAAK,QAAQ,UAAU;AAClD,MAAI,eAAe,CAAC,SAAiBA,MAAK,KAAK,YAAY,cAAc,QAAQ,IAAI,EAAE,WAAW,cAAc,QAAQ,IAAI,EAAE,OAAO;AACrI,MAAI,WAAWA,MAAK,SAASA,MAAK,QAAQ,MAAM,CAAC,KAAK;AACtD,QAAMC,IAAG,UAAUD,MAAK,QAAQ,OAAO,MAAM,wBAAwB,CAAC;AAGtE,MAAI,cAAcC,IAAG,WAAW,aAAa;AAC7C,MAAI,OAAO,cAAc,YAAY;AAErC,SAAO;AAAA,IACL,MAAM,OAAO;AAAA,IACb,UAAU,QAAQ,IAAI,YAAY,SAAS,WAAW;AAAA,IACtD,SAAS;AAAA,MACP,OAAO;AAAA,QACL,YAAY;AAAA,QACZ,YAAY,CAAC,WAAW,KAAK;AAAA,QAC7B,YAAY;AAAA,UACV,eAAe;AAAA,UACf,OAAO;AAAA,YACL,YAAY,CAAC,KAAK;AAAA,YAClB;AAAA,YACA;AAAA,UACF,CAAC;AAAA,UACD,uBAAuB;AAAA,QACzB;AAAA,QACA,OAAO,SAAS,gBAAgB;AAC9B,cAAI,QAAQ,IAAI,aAAa,QAAQ;AACnC,2BAAe,KAAK,EAAC,MAAM,QAAQ,MAAM,SAAS,QAAQ,SAAS,UAAU,QAAQ,SAAQ,CAAC;AAAA,UAChG;AACA,2BAAiB,OAAO;AAAA,QAC1B;AAAA,MACF,CAAC;AAAA,MACD,qBAAqB;AAAA,MACrB,uBAAuB;AAAA,MACvB,cAAc;AAAA,MACd;AAAA,MACA,sBAAsB,SAAS;AAAA,MAC/B,kBAAkB;AAAA,IACpB;AAAA,IACA,WAAWD,MAAK,QAAQ,QAAQ,QAAQ;AAAA;AAAA;AAAA,IAGxC,QAAQ;AAAA,MACN;AAAA,MACA;AAAA,MACA,IAAI,EAAC,QAAQ,MAAK;AAAA,MAClB,YAAY;AAAA,MACZ,KAAK,EAAC,SAAS,MAAK;AAAA;AAAA,IACtB;AAAA,IACA,SAAS;AAAA,MACP,OAAO;AAAA,QACL,EAAC,MAAM,cAAc,aAAaA,MAAK,QAAQ,QAAQ,QAAQ,EAAC;AAAA;AAAA;AAAA,QAGhE,EAAC,MAAM,YAAY,aAAa,aAAa,GAAG,EAAC;AAAA,QACjD,EAAC,MAAM,qBAAqB,aAAa,aAAa,WAAW,EAAC;AAAA,QAClE,EAAC,MAAM,yBAAyB,aAAa,aAAa,eAAe,EAAC;AAAA,QAC1E,EAAC,MAAM,oBAAoB,aAAa,aAAa,UAAU,EAAC;AAAA,QAChE,EAAC,MAAM,oBAAoB,aAAa,aAAa,UAAU,EAAC;AAAA,QAChE,EAAC,MAAM,sBAAsB,aAAa,aAAa,YAAY,EAAC;AAAA,QACpE,EAAC,MAAM,8BAA8B,aAAa,aAAa,mBAAmB,EAAC;AAAA,QACnF,EAAC,MAAM,wCAAwC,aAAa,aAAa,6BAA6B,EAAC;AAAA,QACvG,EAAC,MAAM,oCAAoC,aAAa,aAAa,wBAAwB,EAAC;AAAA,QAC9F,EAAC,MAAM,qCAAqC,aAAa,aAAa,yBAAyB,EAAC;AAAA,QAChG,EAAC,MAAM,sCAAsC,aAAa,aAAa,0BAA0B,EAAC;AAAA,QAClG,EAAC,MAAM,oBAAoB,aAAa,aAAa,UAAU,EAAC;AAAA,QAChE,EAAC,MAAM,oBAAoB,aAAa,aAAa,UAAU,EAAC;AAAA,QAChE,EAAC,MAAM,wBAAwB,aAAa,aAAa,cAAc,EAAC;AAAA,QACxE,EAAC,MAAM,gCAAgC,aAAa,aAAa,qBAAqB,EAAC;AAAA,QACvF,EAAC,MAAM,mBAAmB,aAAa,aAAa,SAAS,EAAC;AAAA,QAC9D,EAAC,MAAM,wBAAwB,aAAa,aAAa,cAAc,EAAC;AAAA,QACxE,EAAC,MAAM,UAAU,aAAaA,MAAK,KAAK,sBAAsB,oBAAoB,EAAC;AAAA,MACrF;AAAA,IACF;AAAA,IAEA,cAAc;AAAA,MACZ,aAAa,QAAQ,IAAI,YAAY;AAAA;AAAA,MACrC,SAAS,CAAC,aAAa;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAOvB,SAAS;AAAA,QACP,GAAI,WAAW,CAAC,UAAU,IAAI,CAAC;AAAA,QAC/B;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;AAEA,eAAe,YAAY,KAAsB,KAAsC;AACrF,MAAI,SAAS,CAAC;AACd,iBAAe,SAAS,IAAK,QAAO,KAAK,KAAK;AAC9C,MAAI,EAAC,MAAM,QAAQ,OAAM,IAAI,KAAK,MAAM,OAAO,OAAO,MAAM,EAAE,SAAS,CAAC;AACxE,MAAI,UAAU,gBAAgB,kBAAkB;AAEhD,QAAM;AAGN,MAAI,YAAY,eAAe,OAAO,UAAQ,CAAC,KAAK,KAAK,SAAS,KAAK,CAAC;AACxE,MAAI,SAAS,iBAAiB,EAAC,QAAQ,OAAO,CAAC,GAAG,WAAW,EAAC,MAAM,SAAS,UAAU,KAAI,CAAC,EAAC,CAAC;AAC9F,oBAAkB,MAAM;AAExB,MAAI,cAAc,OAAO;AACzB,MAAI,YAAY,QAAQ;AACtB,QAAI,aAAa;AACjB,QAAI,IAAI,KAAK,UAAU,YAAY,CAAC,CAAC,CAAC;AACtC;AAAA,EACF;AAEA,MAAI,UAAU,OAAO,MAAM,KAAK,UAAQ,KAAK,QAAQ,OAAO,GAAG,WAAW,CAAC;AAC3E,MAAI,QAAQ,SAAS,EAAG,OAAM,IAAI,MAAM,wDAAwD;AAChG,MAAI,MAAM,MAAM,QAAQ,CAAC,GAAG,MAAM;AAGlC,MAAI,OAAO,OAAO,WAAW,MAAM,EAAE,OAAO,UAAU,aAAa,IAAI,GAAG,EAAE,EAAE,OAAO,KAAK;AAC1F,MAAI,UAAU,QAAQ,IAAI;AAC1B,MAAI,OAAO,SAAS,IAAI,KAAK,IAAI,QAAQ,eAAe,KAAK,YAAY;AACvE,QAAI,aAAa;AACjB,WAAO,IAAI,IAAI;AAAA,EACjB;AAEA,MAAI,eAAe,MAAM,SAAS,GAAG;AACrC,MAAI,YAAY,aAAa,aAAa,aAAa,KAAK;AAC5D,MAAI,YAAY,aAAa,KAAK,OAAQ,OAAM,IAAI,MAAM,6BAA6B;AACvF,MAAI,SAAS,QAAQ,CAAC,EAAE,OAAO,IAAI,YAAU,EAAC,MAAM,MAAM,MAAM,MAAM,MAAM,MAAM,UAAU,MAAM,YAAY,CAAC,EAAC,EAAE;AAClH,MAAI,IAAI,KAAK,UAAU,EAAC,MAAM,aAAa,MAAM,MAAM,QAAQ,IAAG,CAAC,CAAC;AACtE;AAEA,eAAe,WAAW,QAAuB,KAAsC;AACrF,MAAI,UAAU,gBAAgB,WAAW;AAGzC,MAAI,OAAO,MAAM,OAAO;AAAA,IACtB;AAAA,IACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAcF;AACA,SAAO,IAAI,IAAI,IAAI;AACrB;AAGA,eAAsB,cAAc;AAClC,MAAI,MAAM,MAAM,cAAc,MAAM,aAAa,GAAG,OAAO;AAC3D,QAAM,aAAa,KAAK,IAAI;AAC9B;AAKA,SAAS,uBAAuB;AAC9B,MAAI;AAEJ,WAAS,eAAe,KAAU;AAChC,iBAAa;AAAA,EACf;AAIA,WAAS,aAAa;AACpB,QAAI,QAAQ,IAAI,YAAY,OAAQ;AACpC,eAAW,aAAa,QAAQ;AAAA,EAClC;AACA,aAAW,aAAa;AACxB,SAAO,EAAC,MAAM,mBAAmB,SAAS,QAAiB,gBAAgB,WAAU;AACvF;AAOA,SAAS,yBAAyB;AAChC,SAAO;AAAA,IACL,MAAM;AAAA,IACN,UAAqB,EAAC,QAAO,GAAqB;AAKhD,UAAI,YAAY,QAAQ,KAAK,OAAK,CAAC,EAAE,eAAe;AACpD,UAAI,WAAW;AACb,aAAK,YAAY,IAAI,KAAK,EAAC,MAAM,eAAe,MAAM,IAAG,CAAC;AAC1D,eAAO,CAAC;AAAA,MACV;AAAA,IACF;AAAA,EACF;AACF;AAKA,IAAI;AACJ,IAAI,iBAAuC,CAAC;AAC5C,IAAI,UAA4C,CAAC;AACjD,SAAS,sBAAsB,WAA0B;AACvD,SAAO;AAAA,IACL,MAAM;AAAA,IACN,UAAU,IAAY;AACpB,UAAI,MAAM,cAAe,QAAO;AAAA,IAClC;AAAA,IACA,KAAK,IAAY;AACf,UAAI,MAAM,gBAAiB;AAK3B,UAAI,MAAM,CAAC,GAAG,OAAO;AACrB,UAAI,QAAQ,IAAI,YAAY,QAAQ;AAClC,iBAAS,CAACA,OAAM,QAAQ,KAAK,OAAO,QAAQ,WAAW,GAAG;AACxD,cAAI,WAAW,EAAC,MAAAA,OAAM,OAAO,mBAAmB,QAAQ,EAAE,MAAK;AAC/D,cAAI,MAAM,IAAI,UAAU,UAAQ,KAAK,QAAQA,KAAI;AACjD,cAAI,OAAO,EAAG,KAAI,OAAO,KAAK,GAAG,QAAQ;AAAA,cACpC,KAAI,KAAK,QAAQ;AAAA,QACxB;AAAA,MACF;AAEA,aAAO,kBAAkB,KAAK,UAAU,GAAG,CAAC;AAAA,IAC9C;AAAA,IACA,iBAAiB,CAAC,MAAqB;AACrC,UAAI,UAAU,YAAY;AACxB,gCAAwB,YAAY;AAClC,cAAI,SAAS,MAAM,cAAc,OAAO,MAAM,MAAM,OAAO,YAAY;AACvE,qBAAW,MAAM,qBAAqB,EAAC,SAAS,SAAS,GAAG,uBAAuB,MAAM,EAAC,CAAC;AAC3F,2BAAiB,OAAO,IAAI,UAAQ;AAClC,gBAAI,WAAW,eAAe,KAAK,CAAAE,cAAYA,UAAS,QAAQ,KAAK,QAAQA,UAAS,YAAY,KAAK,QAAQ;AAC/G,mBAAO,UAAU,SAAS,EAAC,GAAG,MAAM,QAAQ,SAAS,OAAM,IAAI;AAAA,UACjE,CAAC;AAAA,QACH,GAAG;AACH,cAAM;AAGN,kBAAU,eAAe,OAAO,UAAQ,KAAK,KAAK,SAAS,KAAK,CAAC,EAAE,IAAI,QAAM,EAAC,MAAM,EAAE,MAAM,OAAO,mBAAmB,EAAE,QAAQ,EAAE,MAAK,EAAE;AAEzI,YAAI,MAAM,EAAE,YAAY,cAAc,eAAe;AACrD,YAAI,CAAC,IAAK;AACV,UAAE,aAAa,GAAG;AAAA,MACpB;AAEA,QAAE,QAAQ,IAAI,CAAC,aAAa,SAAS,CAAC;AACtC,QAAE,QAAQ,GAAG,OAAO,OAAO;AAC3B,cAAQ;AAAA,IACV;AAAA,EACF;AACF;AAEA,SAAS,kBAAkB,UAA0B;AACnD,mBAAiB,eAAe,IAAI,UAAQ;AAC1C,QAAI,WAAW,SAAS,MAAM,KAAK,UAAQ,KAAK,QAAQ,KAAK,IAAI;AACjE,QAAI,CAAC,SAAU,QAAO;AACtB,WAAO;AAAA,MACL,GAAG;AAAA,MACH,QAAQ;AAAA,QACN,MAAM,SAAS;AAAA,QACf,iBAAiB,SAAS;AAAA,QAC1B,yBAAyB,SAAS;AAAA,MACpC;AAAA,IACF;AAAA,EACF,CAAC;AACH;AAEA,IAAM,sBAAsB;AAAA,EAC1B,MAAM;AAAA,EACN,iBAAiB,CAAC,MAAqB;AACrC,MAAE,YAAY,IAAI,eAAe,cAAc,KAAK,KAAK,MAAM;AAC7D,UAAI;AACF,YAAI,CAAC,QAAQ,KAAK,IAAI,OAAO,IAAI,MAAM,GAAG;AAC1C,YAAI,YAAY,cAAe,QAAO,MAAM,YAAY,KAAK,GAAG;AAChE,YAAI;AAAU,cAAI,YAAY,WAAW,YAAY,cAAc,YAAY,WAAY,QAAO,MAAM,WAAW,GAAG,GAAG;AAAA;AAEzH,YAAI,CAAC,YAAY,YAAY,IAAK,YAAW;AAC7C,YAAI,iBAAiB,SAAS,QAAQ,OAAO,EAAE,IAAI;AACnD,YAAI,SAASF,MAAK,KAAK,OAAO,MAAM,cAAc;AAElD,YAAI,YAAY,cAAc,KAAM,MAAMC,IAAG,OAAO,MAAM,GAAI;AAC5D,gBAAM,WAAW,GAAG,GAAG;AAAA,QACzB,OAAO;AACL,eAAK;AAAA,QACP;AAAA,MACF,SAAS,KAAU;AACjB,YAAI,QAAQ,IAAI,YAAY,OAAQ,SAAQ,MAAM,GAAG;AACrD,YAAI,aAAa;AACjB,YAAI,IAAI,KAAK,UAAU,EAAC,SAAS,IAAI,SAAS,OAAO,IAAI,MAAK,CAAC,CAAC;AAAA,MAClE;AAAA,IACF,CAAC;AAAA,EACH;AACF;AAEA,SAAS,oBAAoB;AAC3B,MAAI,QAAQ,IAAI,aAAa,OAAQ,QAAO;AAE5C,WAAS,UAAU,IAAY;AAE7B,WAAO,GAAG,QAAQ,OAAO,OAAO,KAAK,EAAE,EAAE,QAAQ,OAAO,EAAE;AAAA,EAC5D;AAEA,SAAO;AAAA,IACL,MAAM;AAAA,IACN,SAAS;AAAA,IACT,UAAU,IAAS;AACjB,UAAI,CAAC,YAAY,UAAU,EAAE,CAAC,EAAG;AAGjC,aAAOD,MAAK,KAAK,OAAO,MAAM,UAAU,EAAE,CAAC,IAAI;AAAA,IACjD;AAAA,IACA,KAAK,IAAS;AACZ,UAAI,CAAC,GAAG,SAAS,OAAO,EAAG,QAAO;AAClC,aAAO,YAAY,UAAU,GAAG,QAAQ,WAAW,EAAE,CAAC,CAAC;AAAA,IACzD;AAAA,EACF;AACF;",
|
|
6
|
+
"names": ["fs", "path", "config", "path", "fs", "existing"]
|
|
7
|
+
}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import {
|
|
2
|
+
config
|
|
3
|
+
} from "./chunk-QAXEOZ43.js";
|
|
4
|
+
|
|
5
|
+
// connections/snowflake.ts
|
|
6
|
+
import { createPrivateKey } from "node:crypto";
|
|
7
|
+
import snowflake from "snowflake-sdk";
|
|
8
|
+
var SnowflakeConnection = class {
|
|
9
|
+
ready;
|
|
10
|
+
connection;
|
|
11
|
+
constructor(opts) {
|
|
12
|
+
this.ready = this.initialize(opts || {});
|
|
13
|
+
}
|
|
14
|
+
async initialize(opts) {
|
|
15
|
+
let privateKeyPath = opts.privateKeyPath || config.snowflake?.privateKeyPath;
|
|
16
|
+
let authOptions = {};
|
|
17
|
+
if (privateKeyPath) {
|
|
18
|
+
authOptions = { privateKeyPath, privateKeyPass: opts.privateKeyPass };
|
|
19
|
+
} else if (opts.privateKey) {
|
|
20
|
+
let privateKey = createPrivateKey({ key: opts.privateKey, format: "pem", passphrase: opts.privateKeyPass });
|
|
21
|
+
authOptions = { privateKey: privateKey.export({ format: "pem", type: "pkcs8" }) };
|
|
22
|
+
}
|
|
23
|
+
snowflake.configure({ logLevel: opts.logLevel || "WARN", logFilePath: "/dev/null" });
|
|
24
|
+
this.connection = snowflake.createConnection({
|
|
25
|
+
...opts,
|
|
26
|
+
...config.snowflake || {},
|
|
27
|
+
...authOptions,
|
|
28
|
+
authenticator: "SNOWFLAKE_JWT",
|
|
29
|
+
application: "Graphene"
|
|
30
|
+
});
|
|
31
|
+
await new Promise((resolve, reject) => {
|
|
32
|
+
this.connection.connect((err, conn) => err ? reject(err) : resolve(conn));
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
async runQuery(sql, params) {
|
|
36
|
+
await this.ready;
|
|
37
|
+
return await new Promise((resolve, reject) => {
|
|
38
|
+
let rows = [];
|
|
39
|
+
this.connection.execute({
|
|
40
|
+
sqlText: sql,
|
|
41
|
+
binds: params,
|
|
42
|
+
streamResult: true,
|
|
43
|
+
complete: (error, statement) => {
|
|
44
|
+
if (error) {
|
|
45
|
+
reject(new Error(`Snowflake query failed: ${error.message || error}`));
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
let stream = statement.streamRows();
|
|
49
|
+
stream.on("error", (err) => reject(err));
|
|
50
|
+
stream.on("readable", function(row) {
|
|
51
|
+
while ((row = this.read()) !== null) {
|
|
52
|
+
rows.push(row);
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
stream.on("end", () => {
|
|
56
|
+
let totalRows = Number(statement.getNumRows());
|
|
57
|
+
resolve({ rows, totalRows });
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
async listDatasets() {
|
|
64
|
+
let res = await this.runQuery("show databases");
|
|
65
|
+
return res.rows.map((row) => String(row["name"] || ""));
|
|
66
|
+
}
|
|
67
|
+
async listSchemas(database) {
|
|
68
|
+
let resolvedDatabase = await this.resolveDatabaseName(database);
|
|
69
|
+
let res = await this.runQuery(`
|
|
70
|
+
select schema_name as "schema_name"
|
|
71
|
+
from ${snowflakeIdent(resolvedDatabase)}.INFORMATION_SCHEMA.SCHEMATA
|
|
72
|
+
where schema_name != 'INFORMATION_SCHEMA'
|
|
73
|
+
order by schema_name
|
|
74
|
+
`);
|
|
75
|
+
return res.rows.map((row) => String(row["schema_name"]).toLowerCase());
|
|
76
|
+
}
|
|
77
|
+
async listTables(dataset) {
|
|
78
|
+
let parts = dataset.split(".");
|
|
79
|
+
let database = await this.resolveDatabaseName(parts.shift() || "");
|
|
80
|
+
let schema = parts.join(".");
|
|
81
|
+
let res = await this.runQuery(
|
|
82
|
+
`
|
|
83
|
+
select table_schema as "table_schema", table_name as "table_name"
|
|
84
|
+
from ${snowflakeIdent(database)}.INFORMATION_SCHEMA.TABLES
|
|
85
|
+
where table_type in ('BASE TABLE', 'VIEW') and upper(table_schema) = upper(?)
|
|
86
|
+
order by table_name
|
|
87
|
+
`,
|
|
88
|
+
[schema]
|
|
89
|
+
);
|
|
90
|
+
return res.rows.map((row) => `${String(row["table_schema"]).toLowerCase()}.${String(row["table_name"]).toLowerCase()}`);
|
|
91
|
+
}
|
|
92
|
+
async describeTable(target) {
|
|
93
|
+
let parts = target.split(".");
|
|
94
|
+
let database = await this.resolveDatabaseName(parts.shift() || "");
|
|
95
|
+
let table = parts.pop() || "";
|
|
96
|
+
let schema = parts.join(".");
|
|
97
|
+
let res = await this.runQuery(
|
|
98
|
+
`
|
|
99
|
+
select column_name as "column_name", data_type as "data_type", ordinal_position as ordinal_position
|
|
100
|
+
from ${snowflakeIdent(database)}.INFORMATION_SCHEMA.COLUMNS
|
|
101
|
+
where upper(table_schema) = upper(?) and upper(table_name) = upper(?)
|
|
102
|
+
order by ordinal_position
|
|
103
|
+
`,
|
|
104
|
+
[schema, table]
|
|
105
|
+
);
|
|
106
|
+
return res.rows.map((row) => {
|
|
107
|
+
return { name: String(row["column_name"]).toLowerCase(), dataType: String(row["data_type"]) };
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
async resolveDatabaseName(name) {
|
|
111
|
+
let databases = await this.listDatasets();
|
|
112
|
+
return databases.find((db) => db.toLowerCase() == name.toLowerCase()) || name;
|
|
113
|
+
}
|
|
114
|
+
async close() {
|
|
115
|
+
await this.ready;
|
|
116
|
+
await new Promise((resolve, reject) => {
|
|
117
|
+
this.connection.destroy((err) => err ? reject(err) : resolve());
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
};
|
|
121
|
+
function snowflakeIdent(value) {
|
|
122
|
+
if (!value) throw new Error("Snowflake identifiers cannot be empty");
|
|
123
|
+
return `"${value.replace(/"/g, '""')}"`;
|
|
124
|
+
}
|
|
125
|
+
export {
|
|
126
|
+
SnowflakeConnection
|
|
127
|
+
};
|
|
128
|
+
//# sourceMappingURL=snowflake-MOQB5GA4.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../connections/snowflake.ts"],
|
|
4
|
+
"sourcesContent": ["import {createPrivateKey} from 'node:crypto'\nimport snowflake from 'snowflake-sdk'\n\nimport {config} from '../../lang/config.ts'\nimport {type QueryConnection, type QueryResult, type SchemaColumn, type QueryParams} from './types.ts'\n\ninterface SnowflakeOptions {\n username?: string\n account?: string\n privateKey?: string\n privateKeyPath?: string\n privateKeyPass?: string\n logLevel?: string\n}\n\n// Raw notes on setting up a new user:\n// * create a `demouser` with a new `demorole`. It should have\n// That role needs `operate` and `usage` on a warehouse, and `usage` on the relevant db or schema\n// `ALTER USER DEMOUSER SET DEFAULT_WAREHOUSE = COMPUTE_WH;`\n// `ALTER USER DEMOUSER SET DEFAULT_ROLE = DEMOREAD;`\n// You can get the `account` by looking at the bit before \"snowflakecomputing\" in the account url (which is found in the snowflake ui)\n// Instructions for generating private/public keys: https://docs.snowflake.com/en/user-guide/key-pair-auth#generate-the-private-keys\n\nexport class SnowflakeConnection implements QueryConnection {\n private ready: Promise<void>\n private connection!: snowflake.Connection\n\n constructor(opts: SnowflakeOptions) {\n this.ready = this.initialize(opts || {})\n }\n\n async initialize(opts: SnowflakeOptions) {\n let privateKeyPath = opts.privateKeyPath || config.snowflake?.privateKeyPath\n\n let authOptions: any = {}\n if (privateKeyPath) {\n authOptions = {privateKeyPath, privateKeyPass: opts.privateKeyPass}\n } else if (opts.privateKey) {\n let privateKey = createPrivateKey({key: opts.privateKey, format: 'pem', passphrase: opts.privateKeyPass})\n authOptions = {privateKey: privateKey.export({format: 'pem', type: 'pkcs8'})}\n }\n\n // default is info, which is kinda chatty on success. TRACE is super useful for debugging though\n snowflake.configure({logLevel: (opts.logLevel as any) || 'WARN', logFilePath: '/dev/null'})\n\n this.connection = snowflake.createConnection({\n ...opts,\n ...(config.snowflake || {}),\n ...authOptions,\n authenticator: 'SNOWFLAKE_JWT',\n application: 'Graphene',\n })\n\n await new Promise((resolve, reject) => {\n this.connection.connect((err, conn) => (err ? reject(err) : resolve(conn)))\n })\n }\n\n async runQuery(sql: string, params?: QueryParams): Promise<QueryResult> {\n await this.ready\n return await new Promise<QueryResult>((resolve, reject) => {\n let rows: any[] = []\n this.connection.execute({\n sqlText: sql,\n binds: params as any,\n streamResult: true,\n complete: (error, statement) => {\n if (error) {\n reject(new Error(`Snowflake query failed: ${error.message || error}`))\n return\n }\n\n let stream = statement.streamRows()\n stream.on('error', err => reject(err))\n stream.on('readable', function (this: any, row) {\n while ((row = this.read()) !== null) {\n rows.push(row)\n }\n })\n stream.on('end', () => {\n let totalRows = Number(statement.getNumRows())\n resolve({rows, totalRows})\n })\n },\n })\n })\n }\n\n async listDatasets(): Promise<string[]> {\n let res = await this.runQuery('show databases')\n return res.rows.map(row => String(row['name'] || ''))\n }\n\n async listSchemas(database: string): Promise<string[]> {\n let resolvedDatabase = await this.resolveDatabaseName(database)\n let res = await this.runQuery(`\n select schema_name as \"schema_name\"\n from ${snowflakeIdent(resolvedDatabase)}.INFORMATION_SCHEMA.SCHEMATA\n where schema_name != 'INFORMATION_SCHEMA'\n order by schema_name\n `)\n return res.rows.map(row => String(row['schema_name']).toLowerCase())\n }\n\n async listTables(dataset: string): Promise<string[]> {\n let parts = dataset.split('.')\n let database = await this.resolveDatabaseName(parts.shift() || '')\n let schema = parts.join('.')\n\n let res = await this.runQuery(\n `\n select table_schema as \"table_schema\", table_name as \"table_name\"\n from ${snowflakeIdent(database)}.INFORMATION_SCHEMA.TABLES\n where table_type in ('BASE TABLE', 'VIEW') and upper(table_schema) = upper(?)\n order by table_name\n `,\n [schema],\n )\n return res.rows.map(row => `${String(row['table_schema']).toLowerCase()}.${String(row['table_name']).toLowerCase()}`)\n }\n\n async describeTable(target: string): Promise<SchemaColumn[]> {\n let parts = target.split('.')\n let database = await this.resolveDatabaseName(parts.shift() || '')\n let table = parts.pop() || ''\n let schema = parts.join('.')\n\n let res = await this.runQuery(\n `\n select column_name as \"column_name\", data_type as \"data_type\", ordinal_position as ordinal_position\n from ${snowflakeIdent(database)}.INFORMATION_SCHEMA.COLUMNS\n where upper(table_schema) = upper(?) and upper(table_name) = upper(?)\n order by ordinal_position\n `,\n [schema, table],\n )\n return res.rows.map(row => {\n return {name: String(row['column_name']).toLowerCase(), dataType: String(row['data_type'])}\n })\n }\n\n async resolveDatabaseName(name: string): Promise<string> {\n let databases = await this.listDatasets()\n return databases.find(db => db.toLowerCase() == name.toLowerCase()) || name\n }\n\n async close(): Promise<void> {\n await this.ready\n await new Promise<void>((resolve, reject) => {\n this.connection.destroy(err => (err ? reject(err) : resolve()))\n })\n }\n}\n\nfunction snowflakeIdent(value: string) {\n if (!value) throw new Error('Snowflake identifiers cannot be empty')\n return `\"${value.replace(/\"/g, '\"\"')}\"`\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
// Standard Graphene error shape used by query responses and diagnostics.
|
|
2
|
+
// Result payload returned by Graphene query execution endpoints.
|
|
3
|
+
export interface QueryResult {
|
|
4
|
+
rows: any[]
|
|
5
|
+
fields: Field[]
|
|
6
|
+
error?: GrapheneError
|
|
7
|
+
hash?: string // hash of the compiled sql for caching
|
|
8
|
+
sql?: string
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
// A single output column in a query result.
|
|
12
|
+
export type Field = {
|
|
13
|
+
name: string
|
|
14
|
+
type: FieldType
|
|
15
|
+
metadata?: FieldMeta
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// Metadata attached to fields.
|
|
19
|
+
// There are a few built-in ones that Graphene already uses, but you can always attach your own metadata:
|
|
20
|
+
// `price: cogs * 1.15 #ratio #format="US Dollar"` -> {ratio: true, format: 'US Dollar'}
|
|
21
|
+
export type FieldMeta = {
|
|
22
|
+
ratio?: true // 0 to 1 value
|
|
23
|
+
pct?: true // 0 to 100 value
|
|
24
|
+
units?: string
|
|
25
|
+
timeGrain?: TimeGrain // resolution when the field is a date or timestamp
|
|
26
|
+
timePart?: string // extracted temporal part, normalized across backend spellings
|
|
27
|
+
timeOrdinal?: TimeOrdinal // if the value represents something special like day_of_week, week_of_year, etc
|
|
28
|
+
defaultName?: string // preferred output column name when an expression is selected without an alias
|
|
29
|
+
[key: string]: string | true | undefined
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export type FieldType = ScalarField | ArrayField
|
|
33
|
+
export type ScalarField = 'string' | 'number' | 'boolean' | 'date' | 'timestamp' | 'json' | 'sql native' | 'error' | 'null' | 'interval' | 'record'
|
|
34
|
+
export type ArrayField = {type: 'array'; elementType: FieldType}
|
|
35
|
+
|
|
36
|
+
export type TimeGrain = 'year' | 'quarter' | 'month' | 'week' | 'day' | 'hour' | 'minute' | 'second'
|
|
37
|
+
export type TimeOrdinal = 'hour_of_day' | DayOfWeekOrdinal | 'day_of_month' | 'day_of_year' | 'week_of_year' | 'month_of_year' | 'quarter_of_year'
|
|
38
|
+
|
|
39
|
+
export type DayOfWeekOrdinal =
|
|
40
|
+
| 'dow_1s' // 1-7, starting sunday
|
|
41
|
+
| 'dow_0s' // 0-6, starting sunday
|
|
42
|
+
| 'dow_1m' // 1-7, starting monday
|
|
43
|
+
|
|
44
|
+
export interface GrapheneError {
|
|
45
|
+
message: string
|
|
46
|
+
name?: string
|
|
47
|
+
stack?: string
|
|
48
|
+
cause?: unknown
|
|
49
|
+
severity?: 'error' | 'warn'
|
|
50
|
+
componentId?: string
|
|
51
|
+
file?: string
|
|
52
|
+
from?: Position
|
|
53
|
+
to?: Position
|
|
54
|
+
frame?: string
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export interface Position {
|
|
58
|
+
offset: number
|
|
59
|
+
line: number
|
|
60
|
+
col: number
|
|
61
|
+
lineStart?: number
|
|
62
|
+
lineText?: string
|
|
63
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
// Standard Graphene error shape used by query responses and diagnostics.
|
|
2
|
+
// Result payload returned by Graphene query execution endpoints.
|
|
3
|
+
export interface QueryResult {
|
|
4
|
+
rows: any[]
|
|
5
|
+
fields: Field[]
|
|
6
|
+
error?: GrapheneError
|
|
7
|
+
hash?: string // hash of the compiled sql for caching
|
|
8
|
+
sql?: string
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
// A single output column in a query result.
|
|
12
|
+
export type Field = {
|
|
13
|
+
name: string
|
|
14
|
+
type: FieldType
|
|
15
|
+
metadata?: FieldMeta
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// Metadata attached to fields.
|
|
19
|
+
// There are a few built-in ones that Graphene already uses, but you can always attach your own metadata:
|
|
20
|
+
// `price: cogs * 1.15 #ratio #format="US Dollar"` -> {ratio: true, format: 'US Dollar'}
|
|
21
|
+
export type FieldMeta = {
|
|
22
|
+
ratio?: true // 0 to 1 value
|
|
23
|
+
pct?: true // 0 to 100 value
|
|
24
|
+
units?: string
|
|
25
|
+
timeGrain?: TimeGrain // resolution when the field is a date or timestamp
|
|
26
|
+
timePart?: string // extracted temporal part, normalized across backend spellings
|
|
27
|
+
timeOrdinal?: TimeOrdinal // if the value represents something special like day_of_week, week_of_year, etc
|
|
28
|
+
defaultName?: string // preferred output column name when an expression is selected without an alias
|
|
29
|
+
[key: string]: string | true | undefined
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export type FieldType = ScalarField | ArrayField
|
|
33
|
+
export type ScalarField = 'string' | 'number' | 'boolean' | 'date' | 'timestamp' | 'json' | 'sql native' | 'error' | 'null' | 'interval' | 'record'
|
|
34
|
+
export type ArrayField = {type: 'array'; elementType: FieldType}
|
|
35
|
+
|
|
36
|
+
export type TimeGrain = 'year' | 'quarter' | 'month' | 'week' | 'day' | 'hour' | 'minute' | 'second'
|
|
37
|
+
export type TimeOrdinal = 'hour_of_day' | DayOfWeekOrdinal | 'day_of_month' | 'day_of_year' | 'week_of_year' | 'month_of_year' | 'quarter_of_year'
|
|
38
|
+
|
|
39
|
+
export type DayOfWeekOrdinal =
|
|
40
|
+
| 'dow_1s' // 1-7, starting sunday
|
|
41
|
+
| 'dow_0s' // 0-6, starting sunday
|
|
42
|
+
| 'dow_1m' // 1-7, starting monday
|
|
43
|
+
|
|
44
|
+
export interface GrapheneError {
|
|
45
|
+
message: string
|
|
46
|
+
name?: string
|
|
47
|
+
stack?: string
|
|
48
|
+
cause?: unknown
|
|
49
|
+
severity?: 'error' | 'warn'
|
|
50
|
+
componentId?: string
|
|
51
|
+
file?: string
|
|
52
|
+
from?: Position
|
|
53
|
+
to?: Position
|
|
54
|
+
frame?: string
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export interface Position {
|
|
58
|
+
offset: number
|
|
59
|
+
line: number
|
|
60
|
+
col: number
|
|
61
|
+
lineStart?: number
|
|
62
|
+
lineText?: string
|
|
63
|
+
}
|