@graphenedata/cli 0.0.16 → 0.0.18
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 +65 -29
- package/dist/cli/{bigQuery-I3F46SC6.js → bigQuery-YIWXZPY6.js} +2 -2
- package/dist/cli/{chunk-QAXEOZ43.js → chunk-SQVXTHE5.js} +2 -2
- package/dist/cli/chunk-SQVXTHE5.js.map +7 -0
- package/dist/cli/{chunk-OVWODUTJ.js → chunk-UTV3ERGI.js} +279 -150
- package/dist/cli/chunk-UTV3ERGI.js.map +7 -0
- package/dist/cli/cli.js +33 -6
- package/dist/cli/{clickhouse-ZN5AN2UL.js → clickhouse-S3BJSKND.js} +3 -2
- package/dist/cli/clickhouse-S3BJSKND.js.map +7 -0
- package/dist/cli/{duckdb-IYBIO5KJ.js → duckdb-V6PJEA7H.js} +2 -2
- package/dist/cli/{serve2-TNN5EROW.js → serve2-CGQSM7TD.js} +7 -6
- package/dist/cli/{serve2-TNN5EROW.js.map → serve2-CGQSM7TD.js.map} +2 -2
- package/dist/cli/{snowflake-MOQB5GA4.js → snowflake-HVSTYBLB.js} +2 -2
- package/dist/index.d.ts +4 -4
- package/dist/lang/index.d.ts +4 -4
- package/dist/skills/graphene/SKILL.md +10 -3
- package/dist/skills/graphene/references/gsql.md +26 -23
- package/dist/skills/graphene/references/model-gsql.md +19 -21
- package/dist/ui/component-utilities/enrich.ts +88 -23
- package/dist/ui/component-utilities/format.ts +36 -21
- package/dist/ui/component-utilities/theme.ts +0 -1
- package/dist/ui/components/AreaChart.svelte +1 -1
- package/dist/ui/components/BarChart.svelte +1 -1
- package/dist/ui/components/LineChart.svelte +1 -1
- package/dist/ui/internal/LocalApp.svelte +29 -27
- package/dist/ui/internal/PageNavGroup.svelte +2 -2
- package/dist/ui/internal/Sidebar.svelte +7 -7
- package/dist/ui/internal/queryEngine.ts +13 -15
- package/dist/ui/internal/runSocket.ts +2 -5
- package/dist/ui/internal/sidebar.svelte.js +11 -1
- package/dist/ui/web.js +4 -2
- package/package.json +5 -1
- package/dist/cli/chunk-OVWODUTJ.js.map +0 -7
- package/dist/cli/chunk-QAXEOZ43.js.map +0 -7
- package/dist/cli/clickhouse-ZN5AN2UL.js.map +0 -7
- /package/dist/cli/{bigQuery-I3F46SC6.js.map → bigQuery-YIWXZPY6.js.map} +0 -0
- /package/dist/cli/{duckdb-IYBIO5KJ.js.map → duckdb-V6PJEA7H.js.map} +0 -0
- /package/dist/cli/{snowflake-MOQB5GA4.js.map → snowflake-HVSTYBLB.js.map} +0 -0
package/dist/cli/cli.js
CHANGED
|
@@ -20,12 +20,12 @@ import {
|
|
|
20
20
|
runServeInBackground,
|
|
21
21
|
stopGrapheneIfRunning,
|
|
22
22
|
toSql
|
|
23
|
-
} from "./chunk-
|
|
23
|
+
} from "./chunk-UTV3ERGI.js";
|
|
24
24
|
import {
|
|
25
25
|
config,
|
|
26
26
|
loadConfig,
|
|
27
27
|
setGlobalConfig
|
|
28
|
-
} from "./chunk-
|
|
28
|
+
} from "./chunk-SQVXTHE5.js";
|
|
29
29
|
|
|
30
30
|
// cli.ts
|
|
31
31
|
import { Command } from "commander";
|
|
@@ -97,15 +97,16 @@ program.command("compile").description("Translate a query to SQL and print it").
|
|
|
97
97
|
console.log(toSql(query));
|
|
98
98
|
})
|
|
99
99
|
);
|
|
100
|
-
program.command("run").description("Run a query or screenshot a Graphene page").argument("[input]", 'Path to file, a raw string, or "-" for stdin').option("-c, --chart <chartTitleOrComponentId>", "Title or component ID of a specific chart to capture").option("-q, --query <queryName>", "Query or table name to run from a markdown page").action(
|
|
100
|
+
program.command("run").description("Run a query or screenshot a Graphene page").argument("[input]", 'Path to file, a raw string, or "-" for stdin').option("-c, --chart <chartTitleOrComponentId>", "Title or component ID of a specific chart to capture").option("-q, --query <queryName>", "Query or table name to run from a markdown page").option("--input <key=value>", "Input value to use for parameters; repeat for multiple values", (value, previous) => previous.concat(value), []).action(
|
|
101
101
|
withTelemetry("run", async (exit, input, options) => {
|
|
102
102
|
if (options.chart && options.query) {
|
|
103
103
|
console.error("Cannot use --chart and --query together");
|
|
104
104
|
return exit(1);
|
|
105
105
|
}
|
|
106
|
+
let inputs = parseRunInputs(options.input || [], exit);
|
|
106
107
|
let inputPath = getExistingPath(input);
|
|
107
108
|
if (inputPath && inputPath.endsWith(".md")) {
|
|
108
|
-
let res2 = options.query ? await runNamedQueryFromMd(inputPath, options.query, telemetry) : await runMdFile({ mdArg: inputPath, chart: options.chart, telemetry });
|
|
109
|
+
let res2 = options.query ? await runNamedQueryFromMd(inputPath, options.query, { inputs, telemetry }) : await runMdFile({ mdArg: inputPath, chart: options.chart, inputs, telemetry });
|
|
109
110
|
return exit(res2 ? 0 : 1);
|
|
110
111
|
}
|
|
111
112
|
if (options.chart || options.query) {
|
|
@@ -121,7 +122,7 @@ program.command("run").description("Run a query or screenshot a Graphene page").
|
|
|
121
122
|
let gsql = await readInput(input);
|
|
122
123
|
let analysis = analyzeWorkspace({ config, files: files.filter((file) => file.path != "input").concat({ path: "input", contents: gsql }) }, "input");
|
|
123
124
|
let [query] = validateInputQuery(analysis, exit);
|
|
124
|
-
let sql =
|
|
125
|
+
let sql = renderSql(query, inputs, exit);
|
|
125
126
|
let res = await runQuery(sql);
|
|
126
127
|
printTable(res.rows);
|
|
127
128
|
})
|
|
@@ -192,7 +193,7 @@ program.command("serve").description("Run the local server").option("--bg", "Run
|
|
|
192
193
|
await runServeInBackground();
|
|
193
194
|
return exit(0);
|
|
194
195
|
} else {
|
|
195
|
-
let mod = await import("./serve2-
|
|
196
|
+
let mod = await import("./serve2-CGQSM7TD.js");
|
|
196
197
|
await mod.serve2(telemetry);
|
|
197
198
|
}
|
|
198
199
|
})
|
|
@@ -249,6 +250,32 @@ function validateInputQuery(analysis, exit) {
|
|
|
249
250
|
}
|
|
250
251
|
return queries;
|
|
251
252
|
}
|
|
253
|
+
function parseRunInputs(values, exit) {
|
|
254
|
+
let inputs = {};
|
|
255
|
+
for (let value of values) {
|
|
256
|
+
let index = value.indexOf("=");
|
|
257
|
+
let key = index >= 0 ? value.slice(0, index) : "";
|
|
258
|
+
if (index < 0 || !key) {
|
|
259
|
+
console.error(`Invalid --input "${value}". Expected key=value.`);
|
|
260
|
+
return exit(1);
|
|
261
|
+
}
|
|
262
|
+
let next = value.slice(index + 1);
|
|
263
|
+
let existing = inputs[key];
|
|
264
|
+
if (existing === void 0) inputs[key] = next;
|
|
265
|
+
else if (Array.isArray(existing)) existing.push(next);
|
|
266
|
+
else inputs[key] = [existing, next];
|
|
267
|
+
}
|
|
268
|
+
return inputs;
|
|
269
|
+
}
|
|
270
|
+
function renderSql(query, inputs, exit) {
|
|
271
|
+
try {
|
|
272
|
+
return toSql(query, inputs);
|
|
273
|
+
} catch (err) {
|
|
274
|
+
if (err instanceof Error) console.error(err.message);
|
|
275
|
+
else console.error(String(err));
|
|
276
|
+
return exit(1);
|
|
277
|
+
}
|
|
278
|
+
}
|
|
252
279
|
function findCaseInsensitive(values, needle) {
|
|
253
280
|
return values.find((value) => value.toLowerCase() == needle.toLowerCase()) || null;
|
|
254
281
|
}
|
|
@@ -10,7 +10,8 @@ var ClickHouseConnection = class {
|
|
|
10
10
|
username: options.username,
|
|
11
11
|
password: options.password,
|
|
12
12
|
database: this.defaultDatabase,
|
|
13
|
-
application: "Graphene"
|
|
13
|
+
application: "Graphene",
|
|
14
|
+
request_timeout: options.requestTimeout
|
|
14
15
|
});
|
|
15
16
|
}
|
|
16
17
|
async runQuery(sql, _params) {
|
|
@@ -61,4 +62,4 @@ function escapeClickHouseString(value) {
|
|
|
61
62
|
export {
|
|
62
63
|
ClickHouseConnection
|
|
63
64
|
};
|
|
64
|
-
//# sourceMappingURL=clickhouse-
|
|
65
|
+
//# sourceMappingURL=clickhouse-S3BJSKND.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../connections/clickhouse.ts"],
|
|
4
|
+
"sourcesContent": ["import {createClient, type ClickHouseClient} from '@clickhouse/client'\n\nimport {type QueryConnection, type QueryResult, type QueryParams, type SchemaColumn} from './types.ts'\n\nexport interface ClickHouseOptions {\n url: string\n username: string\n password: string\n database?: string\n requestTimeout?: number\n}\n\nexport class ClickHouseConnection implements QueryConnection {\n private client: ClickHouseClient\n private defaultDatabase: string\n\n constructor(options: ClickHouseOptions) {\n this.defaultDatabase = options.database || 'default'\n this.client = createClient({\n url: options.url,\n username: options.username,\n password: options.password,\n database: this.defaultDatabase,\n application: 'Graphene',\n request_timeout: options.requestTimeout,\n })\n }\n\n async runQuery(sql: string, _params?: QueryParams): Promise<QueryResult> {\n let result = await this.client.query({query: sql, format: 'JSONEachRow'})\n let rows = (await result.json()) as Array<Record<string, unknown>>\n return {rows, totalRows: rows.length}\n }\n\n async listDatasets(): Promise<string[]> {\n let res = await this.runQuery(`\n select name\n from system.databases\n where lower(name) not in ('system', 'information_schema')\n order by name\n `)\n return res.rows.map(row => String(row['name']).toLowerCase())\n }\n\n async listTables(database = this.defaultDatabase): Promise<string[]> {\n let sql = `\n select database, name\n from system.tables\n where lower(database) = lower('${escapeClickHouseString(database)}')\n order by name\n `.trim()\n let res = await this.runQuery(sql)\n return res.rows.map(row => `${String(row['database']).toLowerCase()}.${String(row['name']).toLowerCase()}`)\n }\n\n async describeTable(target: string): Promise<SchemaColumn[]> {\n let parts = target.split('.').filter(Boolean)\n let table = parts.pop() || ''\n let database = parts.join('.') || this.defaultDatabase\n let sql = `\n select name, type, position\n from system.columns\n where lower(database) = lower('${escapeClickHouseString(database)}')\n and lower(table) = lower('${escapeClickHouseString(table)}')\n order by position\n `.trim()\n let res = await this.runQuery(sql)\n return res.rows.map(row => ({name: String(row['name']).toLowerCase(), dataType: String(row['type'])}))\n }\n\n async close(): Promise<void> {\n await this.client.close()\n }\n}\n\nfunction escapeClickHouseString(value: string) {\n return value.replace(/\\\\/g, '\\\\\\\\').replace(/'/g, \"\\\\'\")\n}\n"],
|
|
5
|
+
"mappings": ";AAAA,SAAQ,oBAA0C;AAY3C,IAAM,uBAAN,MAAsD;AAAA,EACnD;AAAA,EACA;AAAA,EAER,YAAY,SAA4B;AACtC,SAAK,kBAAkB,QAAQ,YAAY;AAC3C,SAAK,SAAS,aAAa;AAAA,MACzB,KAAK,QAAQ;AAAA,MACb,UAAU,QAAQ;AAAA,MAClB,UAAU,QAAQ;AAAA,MAClB,UAAU,KAAK;AAAA,MACf,aAAa;AAAA,MACb,iBAAiB,QAAQ;AAAA,IAC3B,CAAC;AAAA,EACH;AAAA,EAEA,MAAM,SAAS,KAAa,SAA6C;AACvE,QAAI,SAAS,MAAM,KAAK,OAAO,MAAM,EAAC,OAAO,KAAK,QAAQ,cAAa,CAAC;AACxE,QAAI,OAAQ,MAAM,OAAO,KAAK;AAC9B,WAAO,EAAC,MAAM,WAAW,KAAK,OAAM;AAAA,EACtC;AAAA,EAEA,MAAM,eAAkC;AACtC,QAAI,MAAM,MAAM,KAAK,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA,KAK7B;AACD,WAAO,IAAI,KAAK,IAAI,SAAO,OAAO,IAAI,MAAM,CAAC,EAAE,YAAY,CAAC;AAAA,EAC9D;AAAA,EAEA,MAAM,WAAW,WAAW,KAAK,iBAAoC;AACnE,QAAI,MAAM;AAAA;AAAA;AAAA,uCAGyB,uBAAuB,QAAQ,CAAC;AAAA;AAAA,MAEjE,KAAK;AACP,QAAI,MAAM,MAAM,KAAK,SAAS,GAAG;AACjC,WAAO,IAAI,KAAK,IAAI,SAAO,GAAG,OAAO,IAAI,UAAU,CAAC,EAAE,YAAY,CAAC,IAAI,OAAO,IAAI,MAAM,CAAC,EAAE,YAAY,CAAC,EAAE;AAAA,EAC5G;AAAA,EAEA,MAAM,cAAc,QAAyC;AAC3D,QAAI,QAAQ,OAAO,MAAM,GAAG,EAAE,OAAO,OAAO;AAC5C,QAAI,QAAQ,MAAM,IAAI,KAAK;AAC3B,QAAI,WAAW,MAAM,KAAK,GAAG,KAAK,KAAK;AACvC,QAAI,MAAM;AAAA;AAAA;AAAA,uCAGyB,uBAAuB,QAAQ,CAAC;AAAA,oCACnC,uBAAuB,KAAK,CAAC;AAAA;AAAA,MAE3D,KAAK;AACP,QAAI,MAAM,MAAM,KAAK,SAAS,GAAG;AACjC,WAAO,IAAI,KAAK,IAAI,UAAQ,EAAC,MAAM,OAAO,IAAI,MAAM,CAAC,EAAE,YAAY,GAAG,UAAU,OAAO,IAAI,MAAM,CAAC,EAAC,EAAE;AAAA,EACvG;AAAA,EAEA,MAAM,QAAuB;AAC3B,UAAM,KAAK,OAAO,MAAM;AAAA,EAC1B;AACF;AAEA,SAAS,uBAAuB,OAAe;AAC7C,SAAO,MAAM,QAAQ,OAAO,MAAM,EAAE,QAAQ,MAAM,KAAK;AACzD;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
config
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-SQVXTHE5.js";
|
|
4
4
|
|
|
5
5
|
// connections/duckdb.ts
|
|
6
6
|
import { DuckDBTimestampValue, DuckDBInstance, DuckDBDateValue, DuckDBDecimalValue } from "@duckdb/node-api";
|
|
@@ -84,4 +84,4 @@ var DuckDBConnection = class {
|
|
|
84
84
|
export {
|
|
85
85
|
DuckDBConnection
|
|
86
86
|
};
|
|
87
|
-
//# sourceMappingURL=duckdb-
|
|
87
|
+
//# sourceMappingURL=duckdb-V6PJEA7H.js.map
|
|
@@ -6,10 +6,10 @@ import {
|
|
|
6
6
|
runQuery,
|
|
7
7
|
runVitePlugin,
|
|
8
8
|
toSql
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-UTV3ERGI.js";
|
|
10
10
|
import {
|
|
11
11
|
config
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-SQVXTHE5.js";
|
|
13
13
|
|
|
14
14
|
// serve2.ts
|
|
15
15
|
import { svelte, vitePreprocess } from "@sveltejs/vite-plugin-svelte";
|
|
@@ -29,8 +29,9 @@ import JSON5 from "json5";
|
|
|
29
29
|
import path from "path";
|
|
30
30
|
import sanitizeHtml from "sanitize-html";
|
|
31
31
|
import { visit } from "unist-util-visit";
|
|
32
|
-
function
|
|
33
|
-
|
|
32
|
+
function svelteStringAttr(str) {
|
|
33
|
+
let literal = str.replace(/\\/g, "\\\\").replace(/`/g, "\\`").replace(/\$\{/g, "\\${").replace(/\r/g, "\\r").replace(/\n/g, "\\n").replace(/&/g, "\\u0026").replace(/"/g, "\\u0022").replace(/</g, "\\u003c").replace(/>/g, "\\u003e");
|
|
34
|
+
return `{\`${literal}\`}`;
|
|
34
35
|
}
|
|
35
36
|
function liftInlineEChartsConfig(content) {
|
|
36
37
|
return content.replace(/<ECharts\b([^>]*)>([\s\S]*?)<\/ECharts>/g, (match, attrs = "", body = "") => {
|
|
@@ -48,7 +49,7 @@ function extractQueries() {
|
|
|
48
49
|
if (index === null) return;
|
|
49
50
|
let name = typeof node.meta === "string" ? node.meta : "";
|
|
50
51
|
let code = typeof node.value === "string" ? node.value.trim() : "";
|
|
51
|
-
parent.children[index] = { type: "html", value: `<GrapheneQuery name="${
|
|
52
|
+
parent.children[index] = { type: "html", value: `<GrapheneQuery name="${svelteStringAttr(name)}" code="${svelteStringAttr(code)}" />` };
|
|
52
53
|
});
|
|
53
54
|
};
|
|
54
55
|
}
|
|
@@ -444,4 +445,4 @@ export {
|
|
|
444
445
|
serve2,
|
|
445
446
|
svelteWarnings
|
|
446
447
|
};
|
|
447
|
-
//# sourceMappingURL=serve2-
|
|
448
|
+
//# sourceMappingURL=serve2-CGQSM7TD.js.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
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;",
|
|
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\n// Use JS escapes for HTML-sensitive chars so Svelte restores them before query registration.\nfunction svelteStringAttr(str: string) {\n let literal = str\n .replace(/\\\\/g, '\\\\\\\\')\n .replace(/`/g, '\\\\`')\n .replace(/\\$\\{/g, '\\\\${')\n .replace(/\\r/g, '\\\\r')\n .replace(/\\n/g, '\\\\n')\n .replace(/&/g, '\\\\u0026')\n .replace(/\"/g, '\\\\u0022')\n .replace(/</g, '\\\\u003c')\n .replace(/>/g, '\\\\u003e')\n return `{\\`${literal}\\`}`\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=\"${svelteStringAttr(name)}\" code=\"${svelteStringAttr(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": "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",
|
|
6
6
|
"names": ["fs", "path", "config", "path", "fs", "existing"]
|
|
7
7
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
config
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-SQVXTHE5.js";
|
|
4
4
|
|
|
5
5
|
// connections/snowflake.ts
|
|
6
6
|
import { createPrivateKey } from "node:crypto";
|
|
@@ -125,4 +125,4 @@ function snowflakeIdent(value) {
|
|
|
125
125
|
export {
|
|
126
126
|
SnowflakeConnection
|
|
127
127
|
};
|
|
128
|
-
//# sourceMappingURL=snowflake-
|
|
128
|
+
//# sourceMappingURL=snowflake-HVSTYBLB.js.map
|
package/dist/index.d.ts
CHANGED
|
@@ -16,14 +16,14 @@ export type Field = {
|
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
// Metadata attached to fields.
|
|
19
|
-
//
|
|
20
|
-
// `price: cogs * 1.15 #ratio #
|
|
19
|
+
// Graphene validates user-authored metadata annotations, while inferred metadata may add internal keys.
|
|
20
|
+
// `price: cogs * 1.15 #ratio #currency=USD` -> {ratio: true, currency: 'USD'}
|
|
21
21
|
export type FieldMeta = {
|
|
22
22
|
ratio?: true // 0 to 1 value
|
|
23
23
|
pct?: true // 0 to 100 value
|
|
24
|
-
|
|
24
|
+
currency?: string // ISO 4217 currency code
|
|
25
|
+
unit?: string // physical unit label
|
|
25
26
|
timeGrain?: TimeGrain // resolution when the field is a date or timestamp
|
|
26
|
-
timePart?: string // extracted temporal part, normalized across backend spellings
|
|
27
27
|
timeOrdinal?: TimeOrdinal // if the value represents something special like day_of_week, week_of_year, etc
|
|
28
28
|
defaultName?: string // preferred output column name when an expression is selected without an alias
|
|
29
29
|
[key: string]: string | true | undefined
|
package/dist/lang/index.d.ts
CHANGED
|
@@ -16,14 +16,14 @@ export type Field = {
|
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
// Metadata attached to fields.
|
|
19
|
-
//
|
|
20
|
-
// `price: cogs * 1.15 #ratio #
|
|
19
|
+
// Graphene validates user-authored metadata annotations, while inferred metadata may add internal keys.
|
|
20
|
+
// `price: cogs * 1.15 #ratio #currency=USD` -> {ratio: true, currency: 'USD'}
|
|
21
21
|
export type FieldMeta = {
|
|
22
22
|
ratio?: true // 0 to 1 value
|
|
23
23
|
pct?: true // 0 to 100 value
|
|
24
|
-
|
|
24
|
+
currency?: string // ISO 4217 currency code
|
|
25
|
+
unit?: string // physical unit label
|
|
25
26
|
timeGrain?: TimeGrain // resolution when the field is a date or timestamp
|
|
26
|
-
timePart?: string // extracted temporal part, normalized across backend spellings
|
|
27
27
|
timeOrdinal?: TimeOrdinal // if the value represents something special like day_of_week, week_of_year, etc
|
|
28
28
|
defaultName?: string // preferred output column name when an expression is selected without an alias
|
|
29
29
|
[key: string]: string | true | undefined
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: graphene
|
|
3
3
|
description: How to use Graphene, our framework for data modeling, analysis, and visualization.
|
|
4
|
+
allowed-tools: Bash(npx graphene:*) Bash(pnpm graphene:*) Bash(yarn graphene:*) Bash(bun run graphene:*)
|
|
4
5
|
---
|
|
5
6
|
|
|
6
7
|
Graphene is a framework for doing data analysis and BI as code. Schema definitions and semantic models are in `.gsql` files, dashboards/notebooks (called pages) in `.md`.
|
|
@@ -13,12 +14,12 @@ table orders (
|
|
|
13
14
|
id bigint
|
|
14
15
|
created_at datetime
|
|
15
16
|
user_id bigint
|
|
16
|
-
amount float #
|
|
17
|
+
amount float #currency=USD
|
|
17
18
|
status string
|
|
18
19
|
join one users on user_id = users.id -- many orders per user
|
|
19
20
|
is_complete: status = 'Complete' -- dimension (scalar expression)
|
|
20
|
-
revenue: sum(amount) -- measure (agg expression) #
|
|
21
|
-
avg_order: revenue / count(*) -- measures can compose #
|
|
21
|
+
revenue: sum(amount) -- measure (agg expression) #currency=USD
|
|
22
|
+
avg_order: revenue / count(*) -- measures can compose #currency=USD
|
|
22
23
|
)
|
|
23
24
|
table users (
|
|
24
25
|
id bigint
|
|
@@ -170,9 +171,11 @@ graphene check path/to/file.gsql # Check diagnostics for one specific gsql file
|
|
|
170
171
|
graphene check path/to/page.md # Check diagnostics for one specific markdown file
|
|
171
172
|
|
|
172
173
|
graphene run "from flights select count() as total" # Run inline GSQL and print results
|
|
174
|
+
graphene run 'from flights where carrier = $carrier select count() as total' --input carrier=AA # Provide parameter input values
|
|
173
175
|
graphene run - # Read GSQL from stdin and print results
|
|
174
176
|
|
|
175
177
|
graphene run path/to/page.md # Run the page and save a full-page screenshot
|
|
178
|
+
graphene run path/to/page.md --input carrier=AA # Run the page with input values, overriding page defaults
|
|
176
179
|
|
|
177
180
|
# `-c/--chart` can target either a chart title or the chart's `queryId`. For charts without titles use `graphene list` to see the exact IDs for charts on a page.
|
|
178
181
|
graphene run path/to/page.md -c "Chart Title" # Run the page and screenshot one chart by title
|
|
@@ -180,6 +183,10 @@ graphene run path/to/page.md -c 'Query (data="query_name" x="category" y="total"
|
|
|
180
183
|
|
|
181
184
|
# `-q/--query` can target anything usable in a chart `data` prop (for example, a gsql table or a named code-fenced query in the markdown file).
|
|
182
185
|
graphene run path/to/page.md -q query_name # Run a named query/table from the markdown context and print results
|
|
186
|
+
graphene run path/to/page.md -q query_name --input carrier=AA # Run a parameterized named query/table
|
|
187
|
+
|
|
188
|
+
# `--input` values are strings. Repeat the flag to pass multiple values for one input.
|
|
189
|
+
graphene run path/to/page.md --input carrier=AA --input carrier=DL
|
|
183
190
|
|
|
184
191
|
graphene compile "[GSQL]" # Show the compiled, dialect-specific SQL
|
|
185
192
|
|
|
@@ -17,8 +17,8 @@ table orders (
|
|
|
17
17
|
user_id BIGINT
|
|
18
18
|
created_at DATETIME
|
|
19
19
|
status STRING -- One of 'Processing', 'Shipped', 'Complete', 'Cancelled', 'Returned'
|
|
20
|
-
amount FLOAT -- Amount paid by customer #
|
|
21
|
-
cost FLOAT -- Cost of materials #
|
|
20
|
+
amount FLOAT -- Amount paid by customer #currency=USD
|
|
21
|
+
cost FLOAT -- Cost of materials #currency=USD
|
|
22
22
|
|
|
23
23
|
-- Join relationships
|
|
24
24
|
|
|
@@ -30,9 +30,9 @@ table orders (
|
|
|
30
30
|
|
|
31
31
|
-- Measures
|
|
32
32
|
|
|
33
|
-
revenue: sum(case when revenue_recognized then amount else 0 end) #
|
|
34
|
-
cogs: sum(case when revenue_recognized then cost else 0 end) #
|
|
35
|
-
profit: revenue - cogs #
|
|
33
|
+
revenue: sum(case when revenue_recognized then amount else 0 end) #currency=USD
|
|
34
|
+
cogs: sum(case when revenue_recognized then cost else 0 end) #currency=USD
|
|
35
|
+
profit: revenue - cogs #currency=USD
|
|
36
36
|
profit_margin: profit / revenue #ratio
|
|
37
37
|
)
|
|
38
38
|
|
|
@@ -123,9 +123,9 @@ table orders (
|
|
|
123
123
|
revenue_recognized: status in ('Processing', 'Shipped', 'Complete')
|
|
124
124
|
|
|
125
125
|
/* Agg expressions */
|
|
126
|
-
revenue: sum(case when revenue_recognized then amount else 0 end) #
|
|
127
|
-
cogs: sum(case when revenue_recognized then cost else 0 end) #
|
|
128
|
-
profit: revenue - cogs #
|
|
126
|
+
revenue: sum(case when revenue_recognized then amount else 0 end) #currency=USD
|
|
127
|
+
cogs: sum(case when revenue_recognized then cost else 0 end) #currency=USD
|
|
128
|
+
profit: revenue - cogs #currency=USD -- even though there are no agg functions here, this is still aggregative as it references other aggregative expressions
|
|
129
129
|
profit_margin: profit / revenue #ratio
|
|
130
130
|
)
|
|
131
131
|
```
|
|
@@ -138,7 +138,7 @@ There isn't always a SQL expression that can tip Graphene to the semantic meanin
|
|
|
138
138
|
- The field could be a base column that has no source expression
|
|
139
139
|
- There might not be enough information in the expression (eg. what currency a float is tied to)
|
|
140
140
|
|
|
141
|
-
For this reason, some metadata should be set explicitly in the GSQL model, using annotations. Metadata annotations resemble hashtags (eg. `#ratio`, `#
|
|
141
|
+
For this reason, some metadata should be set explicitly in the GSQL model, using annotations. Metadata annotations resemble hashtags (eg. `#ratio`, `#currency=USD`) that can be inlined or written above the object they decorate.
|
|
142
142
|
|
|
143
143
|
#### Recognized metadata
|
|
144
144
|
|
|
@@ -146,9 +146,12 @@ For this reason, some metadata should be set explicitly in the GSQL model, using
|
|
|
146
146
|
|---|---|---|
|
|
147
147
|
| `#ratio` | no | Value is 0–1; rendered as `value × 100%` (e.g. `0.42` → `42%`) |
|
|
148
148
|
| `#pct` | no | Value is already 0–100; rendered as `value%` (e.g. `42` → `42%`) |
|
|
149
|
-
| `#
|
|
149
|
+
| `#currency=<code>` | no | Adds currency symbol and compacts to K/M/B. Accepts ISO 4217 currency codes like `USD`, `EUR`, and `JPY` |
|
|
150
|
+
| `#unit=<unit>` | no | Appends the provided value to the end of labels in visualizations (e.g. `unit=minutes` appends "minutes", or "(minutes)" on axes). Any non-empty value is accepted |
|
|
150
151
|
| `#timeGrain=<grain>` | yes (from `date_trunc`, `date_bin`, casts) | Controls time axis label format. Values: `year`, `quarter`, `month`, `week`, `day`, `hour`, `minute`, `second` |
|
|
151
|
-
| `#timeOrdinal=<ordinal>` | yes (from `extract`) | Treats values as
|
|
152
|
+
| `#timeOrdinal=<ordinal>` | yes (from `extract`) | Treats extracted time values as ordered positions. Values: `hour_of_day`, `day_of_month`, `day_of_year`, `week_of_year`, `month_of_year`, `quarter_of_year`, `dow_0s` (0=Sun), `dow_1s` (1=Sun), `dow_1m` (1=Mon) |
|
|
153
|
+
| `#description=<text>` | no | Description text for a table or field. `--` comments are also collected as descriptions |
|
|
154
|
+
| `#pii` | no | Marks a field as containing personally identifiable information. |
|
|
152
155
|
|
|
153
156
|
## `select` statements
|
|
154
157
|
|
|
@@ -241,15 +244,15 @@ table orders (
|
|
|
241
244
|
user_id BIGINT
|
|
242
245
|
created_at DATETIME
|
|
243
246
|
status STRING -- One of 'Processing', 'Shipped', 'Complete', 'Cancelled', 'Returned'
|
|
244
|
-
amount FLOAT -- Amount paid by customer #
|
|
245
|
-
cost FLOAT -- Cost of materials #
|
|
247
|
+
amount FLOAT -- Amount paid by customer #currency=USD
|
|
248
|
+
cost FLOAT -- Cost of materials #currency=USD
|
|
246
249
|
|
|
247
250
|
join one users on user_id = users.id
|
|
248
251
|
|
|
249
252
|
revenue_recognized: status in ('Processing', 'Shipped', 'Complete')
|
|
250
|
-
revenue: sum(case when revenue_recognized then amount else 0 end) #
|
|
251
|
-
cogs: sum(case when revenue_recognized then cost else 0 end) #
|
|
252
|
-
profit: revenue - cogs #
|
|
253
|
+
revenue: sum(case when revenue_recognized then amount else 0 end) #currency=USD
|
|
254
|
+
cogs: sum(case when revenue_recognized then cost else 0 end) #currency=USD
|
|
255
|
+
profit: revenue - cogs #currency=USD
|
|
253
256
|
profit_margin: profit / revenue #ratio
|
|
254
257
|
)
|
|
255
258
|
```
|
|
@@ -330,15 +333,15 @@ table orders (
|
|
|
330
333
|
user_id BIGINT
|
|
331
334
|
created_at DATETIME
|
|
332
335
|
status STRING -- One of 'Processing', 'Shipped', 'Complete', 'Cancelled', 'Returned'
|
|
333
|
-
amount FLOAT -- Amount paid by customer #
|
|
334
|
-
cost FLOAT -- Cost of materials #
|
|
336
|
+
amount FLOAT -- Amount paid by customer #currency=USD
|
|
337
|
+
cost FLOAT -- Cost of materials #currency=USD
|
|
335
338
|
|
|
336
339
|
join one users on user_id = users.id
|
|
337
340
|
|
|
338
341
|
revenue_recognized: status in ('Processing', 'Shipped', 'Complete')
|
|
339
|
-
revenue: sum(case when revenue_recognized then amount else 0 end) #
|
|
340
|
-
cogs: sum(case when revenue_recognized then cost else 0 end) #
|
|
341
|
-
profit: revenue - cogs #
|
|
342
|
+
revenue: sum(case when revenue_recognized then amount else 0 end) #currency=USD
|
|
343
|
+
cogs: sum(case when revenue_recognized then cost else 0 end) #currency=USD
|
|
344
|
+
profit: revenue - cogs #currency=USD
|
|
342
345
|
profit_margin: profit / revenue #ratio
|
|
343
346
|
)
|
|
344
347
|
|
|
@@ -351,7 +354,7 @@ table users (
|
|
|
351
354
|
join many orders on id = orders.user_id
|
|
352
355
|
join one user_facts on id = user_facts.id
|
|
353
356
|
|
|
354
|
-
ltv: user_facts.ltv #
|
|
357
|
+
ltv: user_facts.ltv #currency=USD
|
|
355
358
|
lifetime_orders: user_facts.lifetime_orders
|
|
356
359
|
)
|
|
357
360
|
|
|
@@ -388,6 +391,6 @@ We can extend this table to add measures or joins:
|
|
|
388
391
|
extend regional_orders (
|
|
389
392
|
join one regions on region = regions.name
|
|
390
393
|
|
|
391
|
-
avg_order_value: total_revenue / num_orders #
|
|
394
|
+
avg_order_value: total_revenue / num_orders #currency=USD
|
|
392
395
|
)
|
|
393
396
|
```
|