@graphenedata/cli 0.0.15 → 0.0.17
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 +174 -0
- package/dist/cli/bigQuery-OQUNH3VT.js +75 -0
- package/dist/cli/bigQuery-OQUNH3VT.js.map +7 -0
- package/dist/cli/chunk-56K2FF57.js +53 -0
- package/dist/cli/chunk-56K2FF57.js.map +7 -0
- package/dist/cli/chunk-TZTTALAV.js +12868 -0
- package/dist/cli/chunk-TZTTALAV.js.map +7 -0
- package/dist/cli/cli.js +260 -11196
- package/dist/cli/clickhouse-S3BJSKND.js +65 -0
- package/dist/cli/clickhouse-S3BJSKND.js.map +7 -0
- package/dist/cli/duckdb-TKVMONRK.js +87 -0
- package/dist/cli/duckdb-TKVMONRK.js.map +7 -0
- package/dist/cli/serve2-S2LL4D4D.js +448 -0
- package/dist/cli/serve2-S2LL4D4D.js.map +7 -0
- package/dist/cli/snowflake-3VPDEYYP.js +128 -0
- package/dist/cli/snowflake-3VPDEYYP.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 +156 -95
- 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 +70 -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 +793 -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 +84 -19
- 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 +112 -129
- package/dist/ui/internal/runSocket.ts +31 -14
- 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 +30 -11
- package/package.json +40 -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,65 @@
|
|
|
1
|
+
// connections/clickhouse.ts
|
|
2
|
+
import { createClient } from "@clickhouse/client";
|
|
3
|
+
var ClickHouseConnection = class {
|
|
4
|
+
client;
|
|
5
|
+
defaultDatabase;
|
|
6
|
+
constructor(options) {
|
|
7
|
+
this.defaultDatabase = options.database || "default";
|
|
8
|
+
this.client = createClient({
|
|
9
|
+
url: options.url,
|
|
10
|
+
username: options.username,
|
|
11
|
+
password: options.password,
|
|
12
|
+
database: this.defaultDatabase,
|
|
13
|
+
application: "Graphene",
|
|
14
|
+
request_timeout: options.requestTimeout
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
async runQuery(sql, _params) {
|
|
18
|
+
let result = await this.client.query({ query: sql, format: "JSONEachRow" });
|
|
19
|
+
let rows = await result.json();
|
|
20
|
+
return { rows, totalRows: rows.length };
|
|
21
|
+
}
|
|
22
|
+
async listDatasets() {
|
|
23
|
+
let res = await this.runQuery(`
|
|
24
|
+
select name
|
|
25
|
+
from system.databases
|
|
26
|
+
where lower(name) not in ('system', 'information_schema')
|
|
27
|
+
order by name
|
|
28
|
+
`);
|
|
29
|
+
return res.rows.map((row) => String(row["name"]).toLowerCase());
|
|
30
|
+
}
|
|
31
|
+
async listTables(database = this.defaultDatabase) {
|
|
32
|
+
let sql = `
|
|
33
|
+
select database, name
|
|
34
|
+
from system.tables
|
|
35
|
+
where lower(database) = lower('${escapeClickHouseString(database)}')
|
|
36
|
+
order by name
|
|
37
|
+
`.trim();
|
|
38
|
+
let res = await this.runQuery(sql);
|
|
39
|
+
return res.rows.map((row) => `${String(row["database"]).toLowerCase()}.${String(row["name"]).toLowerCase()}`);
|
|
40
|
+
}
|
|
41
|
+
async describeTable(target) {
|
|
42
|
+
let parts = target.split(".").filter(Boolean);
|
|
43
|
+
let table = parts.pop() || "";
|
|
44
|
+
let database = parts.join(".") || this.defaultDatabase;
|
|
45
|
+
let sql = `
|
|
46
|
+
select name, type, position
|
|
47
|
+
from system.columns
|
|
48
|
+
where lower(database) = lower('${escapeClickHouseString(database)}')
|
|
49
|
+
and lower(table) = lower('${escapeClickHouseString(table)}')
|
|
50
|
+
order by position
|
|
51
|
+
`.trim();
|
|
52
|
+
let res = await this.runQuery(sql);
|
|
53
|
+
return res.rows.map((row) => ({ name: String(row["name"]).toLowerCase(), dataType: String(row["type"]) }));
|
|
54
|
+
}
|
|
55
|
+
async close() {
|
|
56
|
+
await this.client.close();
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
function escapeClickHouseString(value) {
|
|
60
|
+
return value.replace(/\\/g, "\\\\").replace(/'/g, "\\'");
|
|
61
|
+
}
|
|
62
|
+
export {
|
|
63
|
+
ClickHouseConnection
|
|
64
|
+
};
|
|
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
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import {
|
|
2
|
+
config
|
|
3
|
+
} from "./chunk-56K2FF57.js";
|
|
4
|
+
|
|
5
|
+
// connections/duckdb.ts
|
|
6
|
+
import { DuckDBTimestampValue, DuckDBInstance, DuckDBDateValue, DuckDBDecimalValue } from "@duckdb/node-api";
|
|
7
|
+
import { promises as fs } from "fs";
|
|
8
|
+
import path from "path";
|
|
9
|
+
var DuckDBConnection = class {
|
|
10
|
+
options;
|
|
11
|
+
ready;
|
|
12
|
+
connection = null;
|
|
13
|
+
constructor(options) {
|
|
14
|
+
this.options = options || {};
|
|
15
|
+
this.ready = this.initialize();
|
|
16
|
+
}
|
|
17
|
+
async initialize() {
|
|
18
|
+
let dbPath = this.options.path || config.duckdb?.path;
|
|
19
|
+
if (!dbPath) {
|
|
20
|
+
let files = await fs.readdir(config.root);
|
|
21
|
+
dbPath = files.find((f) => f.endsWith(".duckdb"));
|
|
22
|
+
if (!dbPath) throw new Error("No .duckdb file found in current directory");
|
|
23
|
+
}
|
|
24
|
+
if (!path.isAbsolute(dbPath)) dbPath = path.resolve(config.root, dbPath);
|
|
25
|
+
let db = await DuckDBInstance.create(":memory:");
|
|
26
|
+
this.connection = await db.connect();
|
|
27
|
+
let escapedPath = dbPath.replace(/'/g, "''");
|
|
28
|
+
await this.connection.run(`attach '${escapedPath}' as graphene_cli (READ_ONLY);`);
|
|
29
|
+
await this.connection.run("use graphene_cli;");
|
|
30
|
+
}
|
|
31
|
+
async runQuery(sql, params) {
|
|
32
|
+
await this.ready;
|
|
33
|
+
let reader = params ? await this.connection.runAndReadAll(sql, params) : await this.connection.runAndReadAll(sql);
|
|
34
|
+
let rows = reader.getRowObjects().map((record) => {
|
|
35
|
+
let out = {};
|
|
36
|
+
for (let [k, v] of Object.entries(record)) {
|
|
37
|
+
if (typeof v === "bigint") out[k] = Number(v);
|
|
38
|
+
else if (v === null) out[k] = null;
|
|
39
|
+
else if (v instanceof DuckDBTimestampValue) out[k] = new Date(Number(v.micros / 1000n)).toUTCString();
|
|
40
|
+
else if (v instanceof DuckDBDateValue) out[k] = v.toString();
|
|
41
|
+
else if (v instanceof DuckDBDecimalValue) out[k] = v.toDouble();
|
|
42
|
+
else if (typeof v === "object") throw new Error(`Unsupported datatype ${v.constructor?.name}`);
|
|
43
|
+
else out[k] = v;
|
|
44
|
+
}
|
|
45
|
+
return out;
|
|
46
|
+
});
|
|
47
|
+
return { rows };
|
|
48
|
+
}
|
|
49
|
+
async listDatasets() {
|
|
50
|
+
return await Promise.resolve([]);
|
|
51
|
+
}
|
|
52
|
+
async listTables() {
|
|
53
|
+
let sql = `
|
|
54
|
+
select table_schema as table_schema, table_name as table_name
|
|
55
|
+
from information_schema.tables
|
|
56
|
+
where table_type in ('BASE TABLE', 'VIEW') and table_schema not in ('information_schema', 'pg_catalog')
|
|
57
|
+
order by table_schema, table_name
|
|
58
|
+
`.trim();
|
|
59
|
+
let res = await this.runQuery(sql);
|
|
60
|
+
return res.rows.map((row) => String(row["table_name"]).toLowerCase());
|
|
61
|
+
}
|
|
62
|
+
async describeTable(target) {
|
|
63
|
+
let parts = target.split(".");
|
|
64
|
+
let table = parts.pop() || "";
|
|
65
|
+
let schema = parts[0];
|
|
66
|
+
let schemaFilter = schema ? "lower(table_schema) = lower($2)" : "table_schema not in ('information_schema', 'pg_catalog')";
|
|
67
|
+
let sql = `
|
|
68
|
+
select column_name as column_name, data_type as data_type, ordinal_position as ordinal_position
|
|
69
|
+
from information_schema.columns
|
|
70
|
+
where lower(table_name) = lower($1) and ${schemaFilter}
|
|
71
|
+
order by ordinal_position
|
|
72
|
+
`.trim();
|
|
73
|
+
let params = schema ? [table, schema] : [table];
|
|
74
|
+
let res = await this.runQuery(sql, params);
|
|
75
|
+
return res.rows.map((row) => {
|
|
76
|
+
return { name: String(row["column_name"]).toLowerCase(), dataType: String(row["data_type"]) };
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
async close() {
|
|
80
|
+
await this.ready;
|
|
81
|
+
this.connection?.closeSync();
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
export {
|
|
85
|
+
DuckDBConnection
|
|
86
|
+
};
|
|
87
|
+
//# sourceMappingURL=duckdb-TKVMONRK.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../connections/duckdb.ts"],
|
|
4
|
+
"sourcesContent": ["import {DuckDBTimestampValue, DuckDBInstance, DuckDBDateValue, DuckDBDecimalValue, type DuckDBConnection as InnerConnection} from '@duckdb/node-api'\nimport {promises as fs} from 'fs'\nimport path from 'path'\n\nimport {config} from '../../lang/config.ts'\nimport {type QueryResult, type QueryConnection, type SchemaColumn, type QueryParams} from './types.ts'\n\ninterface DuckDbOptions {\n path?: string\n}\n\nexport class DuckDBConnection implements QueryConnection {\n options: DuckDbOptions\n ready: Promise<void>\n connection: InnerConnection | null = null\n\n constructor(options?: DuckDbOptions) {\n this.options = options || {}\n this.ready = this.initialize()\n }\n\n private async initialize() {\n let dbPath = this.options.path || config.duckdb?.path\n if (!dbPath) {\n let files = await fs.readdir(config.root)\n dbPath = files.find(f => f.endsWith('.duckdb'))\n if (!dbPath) throw new Error('No .duckdb file found in current directory')\n }\n if (!path.isAbsolute(dbPath)) dbPath = path.resolve(config.root, dbPath)\n\n let db = await DuckDBInstance.create(':memory:')\n this.connection = await db.connect()\n let escapedPath = dbPath.replace(/'/g, \"''\")\n // Attach the project DuckDB file in read-only mode and make it the active schema\n await this.connection.run(`attach '${escapedPath}' as graphene_cli (READ_ONLY);`)\n await this.connection.run('use graphene_cli;')\n }\n\n async runQuery(sql: string, params?: QueryParams): Promise<QueryResult> {\n await this.ready\n let reader = params ? await this.connection!.runAndReadAll(sql, params as any) : await this.connection!.runAndReadAll(sql)\n let rows = reader.getRowObjects().map(record => {\n let out: Record<string, unknown> = {}\n for (let [k, v] of Object.entries(record)) {\n if (typeof v === 'bigint') out[k] = Number(v)\n else if (v === null) out[k] = null\n else if (v instanceof DuckDBTimestampValue) out[k] = new Date(Number(v.micros / 1000n)).toUTCString()\n else if (v instanceof DuckDBDateValue) out[k] = v.toString()\n else if (v instanceof DuckDBDecimalValue) out[k] = v.toDouble()\n else if (typeof v === 'object') throw new Error(`Unsupported datatype ${v.constructor?.name}`)\n else out[k] = v\n }\n return out\n })\n return {rows}\n }\n\n async listDatasets(): Promise<string[]> {\n return await Promise.resolve([])\n }\n\n async listTables(): Promise<string[]> {\n let sql = `\n select table_schema as table_schema, table_name as table_name\n from information_schema.tables\n where table_type in ('BASE TABLE', 'VIEW') and table_schema not in ('information_schema', 'pg_catalog')\n order by table_schema, table_name\n `.trim()\n let res = await this.runQuery(sql)\n return res.rows.map(row => String(row['table_name']).toLowerCase())\n }\n\n async describeTable(target: string): Promise<SchemaColumn[]> {\n let parts = target.split('.')\n let table = parts.pop() || ''\n let schema = parts[0]\n let schemaFilter = schema ? 'lower(table_schema) = lower($2)' : \"table_schema not in ('information_schema', 'pg_catalog')\"\n let sql = `\n select column_name as column_name, data_type as data_type, ordinal_position as ordinal_position\n from information_schema.columns\n where lower(table_name) = lower($1) and ${schemaFilter}\n order by ordinal_position\n `.trim()\n let params = schema ? [table, schema] : [table]\n let res = await this.runQuery(sql, params)\n return res.rows.map(row => {\n return {name: String(row['column_name']).toLowerCase(), dataType: String(row['data_type'])}\n })\n }\n\n async close(): Promise<void> {\n await this.ready\n this.connection?.closeSync()\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,448 @@
|
|
|
1
|
+
import {
|
|
2
|
+
analyzeWorkspace,
|
|
3
|
+
getWorkspaceScanCounts,
|
|
4
|
+
loadWorkspace,
|
|
5
|
+
mockFileMap,
|
|
6
|
+
runQuery,
|
|
7
|
+
runVitePlugin,
|
|
8
|
+
toSql
|
|
9
|
+
} from "./chunk-TZTTALAV.js";
|
|
10
|
+
import {
|
|
11
|
+
config
|
|
12
|
+
} from "./chunk-56K2FF57.js";
|
|
13
|
+
|
|
14
|
+
// serve2.ts
|
|
15
|
+
import { svelte, vitePreprocess } from "@sveltejs/vite-plugin-svelte";
|
|
16
|
+
import crypto from "crypto";
|
|
17
|
+
import fs2 from "fs-extra";
|
|
18
|
+
import { mdsvex } from "mdsvex";
|
|
19
|
+
import { createRequire } from "module";
|
|
20
|
+
import path2 from "path";
|
|
21
|
+
import { fileURLToPath } from "url";
|
|
22
|
+
import { createServer, optimizeDeps, resolveConfig } from "vite";
|
|
23
|
+
|
|
24
|
+
// mdCompile.ts
|
|
25
|
+
import { decodeHTML } from "entities";
|
|
26
|
+
import fs from "fs";
|
|
27
|
+
import yaml from "js-yaml";
|
|
28
|
+
import JSON5 from "json5";
|
|
29
|
+
import path from "path";
|
|
30
|
+
import sanitizeHtml from "sanitize-html";
|
|
31
|
+
import { visit } from "unist-util-visit";
|
|
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}\`}`;
|
|
35
|
+
}
|
|
36
|
+
function liftInlineEChartsConfig(content) {
|
|
37
|
+
return content.replace(/<ECharts\b([^>]*)>([\s\S]*?)<\/ECharts>/g, (match, attrs = "", body = "") => {
|
|
38
|
+
let inline = body.trim();
|
|
39
|
+
if (!inline) return match;
|
|
40
|
+
if (/\sconfig\s*=/.test(attrs)) return match;
|
|
41
|
+
let source = inline.startsWith("{") ? inline : `{${inline}}`;
|
|
42
|
+
let config2 = JSON.stringify(JSON5.parse(source), (_key, value) => typeof value == "string" ? decodeHTML(value) : value);
|
|
43
|
+
return `<ECharts${attrs} config={${config2}}></ECharts>`;
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
function extractQueries() {
|
|
47
|
+
return function transformer(tree) {
|
|
48
|
+
visit(tree, "code", (node, index, parent) => {
|
|
49
|
+
if (index === null) return;
|
|
50
|
+
let name = typeof node.meta === "string" ? node.meta : "";
|
|
51
|
+
let code = typeof node.value === "string" ? node.value.trim() : "";
|
|
52
|
+
parent.children[index] = { type: "html", value: `<GrapheneQuery name="${svelteStringAttr(name)}" code="${svelteStringAttr(code)}" />` };
|
|
53
|
+
});
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
function escapeAngles() {
|
|
57
|
+
return function transformer(tree) {
|
|
58
|
+
visit(tree, "text", (node) => {
|
|
59
|
+
if (!node.value || typeof node.value !== "string") return;
|
|
60
|
+
if (!node.value.includes("<")) return;
|
|
61
|
+
node.value = node.value.replace(/</g, "<");
|
|
62
|
+
});
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
function mergeAdjacentHtml() {
|
|
66
|
+
return function transformer(tree) {
|
|
67
|
+
visit(tree, (parent) => {
|
|
68
|
+
if (!Array.isArray(parent?.children)) return;
|
|
69
|
+
for (let i = 0; i < parent.children.length; i++) {
|
|
70
|
+
if (parent.children[i]?.type !== "html") continue;
|
|
71
|
+
let j = i;
|
|
72
|
+
while (j + 1 < parent.children.length && parent.children[j + 1]?.type === "html") j++;
|
|
73
|
+
if (j == i) continue;
|
|
74
|
+
let value = parent.children.slice(i, j + 1).map((node) => node.value || "").join("\n");
|
|
75
|
+
parent.children.splice(i, j - i + 1, { type: "html", value });
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
function sanitizeMarkdown() {
|
|
81
|
+
return function transformer(tree) {
|
|
82
|
+
visit(tree, "raw", (node) => {
|
|
83
|
+
if (typeof node.value !== "string") return;
|
|
84
|
+
let expanded = node.value.replace(/<(\w+)((?:\s[^<>]*?)?)\s*\/>/gi, (_, name, attrs = "") => {
|
|
85
|
+
let spacing = attrs;
|
|
86
|
+
return `<${name}${spacing}></${name}>`;
|
|
87
|
+
});
|
|
88
|
+
let sanitized = sanitizeHtml(expanded, {
|
|
89
|
+
...sanitizeHtml.defaults,
|
|
90
|
+
allowedTags: [...sanitizeHtml.defaults.allowedTags, ...componentNames()],
|
|
91
|
+
allowedAttributes: {
|
|
92
|
+
...sanitizeHtml.defaults.allowedAttributes,
|
|
93
|
+
...Object.fromEntries(componentNames().map((n) => [n, ["*"]]))
|
|
94
|
+
},
|
|
95
|
+
parser: {
|
|
96
|
+
...sanitizeHtml.defaults.parser || {},
|
|
97
|
+
lowerCaseAttributeNames: false,
|
|
98
|
+
lowerCaseTags: false
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
node.value = sanitized;
|
|
102
|
+
});
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
function injectComponentImports() {
|
|
106
|
+
let imp = `const {${componentNames().join(", ")}} = window.$GRAPHENE.components`;
|
|
107
|
+
return {
|
|
108
|
+
markup: ({ content, filename }) => {
|
|
109
|
+
if (!filename.endsWith(".md")) return;
|
|
110
|
+
content = liftInlineEChartsConfig(content);
|
|
111
|
+
if (content.includes("<script>")) {
|
|
112
|
+
content = content.replace("<script>", `<script>
|
|
113
|
+
${imp}`);
|
|
114
|
+
} else {
|
|
115
|
+
content = `<script>
|
|
116
|
+
${imp}
|
|
117
|
+
</script>
|
|
118
|
+
${content}`;
|
|
119
|
+
}
|
|
120
|
+
return { code: content };
|
|
121
|
+
},
|
|
122
|
+
style: () => {
|
|
123
|
+
},
|
|
124
|
+
script: () => {
|
|
125
|
+
}
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
var cachedComponentNames = null;
|
|
129
|
+
function componentNames() {
|
|
130
|
+
if (cachedComponentNames) return cachedComponentNames;
|
|
131
|
+
let files = fs.readdirSync(path.join(import.meta.dirname, "../ui/components"));
|
|
132
|
+
cachedComponentNames = files.map((f) => path.basename(f, ".svelte")).filter((f) => !f.startsWith("_"));
|
|
133
|
+
return cachedComponentNames || [];
|
|
134
|
+
}
|
|
135
|
+
var frontmatterRe = /^---\s*\n([\s\S]*?)\n---(?:\n|$)/;
|
|
136
|
+
function extractFrontmatter(contents) {
|
|
137
|
+
let match = contents.trimStart().match(frontmatterRe);
|
|
138
|
+
if (!match) return {};
|
|
139
|
+
let raw = yaml.safeLoad(match[1]);
|
|
140
|
+
return { title: raw?.title ? String(raw.title) : void 0 };
|
|
141
|
+
}
|
|
142
|
+
var remarkPlugins = [extractQueries, escapeAngles, mergeAdjacentHtml];
|
|
143
|
+
var rehypePlugins = [sanitizeMarkdown];
|
|
144
|
+
|
|
145
|
+
// serve2.ts
|
|
146
|
+
var svelteWarnings = [];
|
|
147
|
+
function clearSvelteWarnings() {
|
|
148
|
+
svelteWarnings.length = 0;
|
|
149
|
+
}
|
|
150
|
+
var QUERY_VERSION = 1;
|
|
151
|
+
var uiRoot;
|
|
152
|
+
var nodeRequire = createRequire(import.meta.url);
|
|
153
|
+
async function serve2(telemetry) {
|
|
154
|
+
let server = await createServer(await createConfig(telemetry));
|
|
155
|
+
await server.listen();
|
|
156
|
+
console.log(`Server running at http://localhost:${server.config.server.port}`);
|
|
157
|
+
return server;
|
|
158
|
+
}
|
|
159
|
+
async function createConfig(telemetry) {
|
|
160
|
+
uiRoot = path2.join(fileURLToPath(import.meta.url), "../../ui");
|
|
161
|
+
let port = Number(process.env.GRAPHENE_PORT) || 4e3;
|
|
162
|
+
let svelteRoot = path2.dirname(nodeRequire.resolve("svelte/package.json"));
|
|
163
|
+
let sveltePackage = nodeRequire("svelte/package.json");
|
|
164
|
+
let svelteDependencyRoot = path2.dirname(svelteRoot);
|
|
165
|
+
let svelteExport = (name) => path2.join(svelteRoot, sveltePackage.exports[name].browser || sveltePackage.exports[name].default);
|
|
166
|
+
let packaged = path2.basename(path2.dirname(uiRoot)) == "dist";
|
|
167
|
+
await fs2.ensureDir(path2.resolve(config.root, "node_modules/.graphene"));
|
|
168
|
+
let inContainer = fs2.existsSync("/.dockerenv");
|
|
169
|
+
let host = inContainer ? "0.0.0.0" : "127.0.0.1";
|
|
170
|
+
return {
|
|
171
|
+
root: config.root,
|
|
172
|
+
logLevel: process.env.NODE_ENV == "test" ? "silent" : "info",
|
|
173
|
+
plugins: [
|
|
174
|
+
svelte({
|
|
175
|
+
configFile: false,
|
|
176
|
+
extensions: [".svelte", ".md"],
|
|
177
|
+
preprocess: [
|
|
178
|
+
vitePreprocess(),
|
|
179
|
+
mdsvex({
|
|
180
|
+
extensions: [".md"],
|
|
181
|
+
remarkPlugins,
|
|
182
|
+
rehypePlugins
|
|
183
|
+
}),
|
|
184
|
+
injectComponentImports()
|
|
185
|
+
],
|
|
186
|
+
onwarn(warning, defaultHandler) {
|
|
187
|
+
if (process.env.NODE_ENV === "test") {
|
|
188
|
+
svelteWarnings.push({ code: warning.code, message: warning.message, filename: warning.filename });
|
|
189
|
+
}
|
|
190
|
+
defaultHandler?.(warning);
|
|
191
|
+
}
|
|
192
|
+
}),
|
|
193
|
+
fixSvelteDepsInTests(),
|
|
194
|
+
fixHmrForFailedModules(),
|
|
195
|
+
runVitePlugin(),
|
|
196
|
+
handleRequestPlugin,
|
|
197
|
+
updateWorkspacePlugin(telemetry),
|
|
198
|
+
mockFilesForTests()
|
|
199
|
+
],
|
|
200
|
+
publicDir: path2.resolve(uiRoot, "public"),
|
|
201
|
+
// on the fence about this one. This would make it less likely we need to optimize when alternating between dev and tests.
|
|
202
|
+
// cacheDir: process.env.NODE_ENV == 'test' ? 'node_modules/.vite-tests' : 'node_modules/.vite',
|
|
203
|
+
server: {
|
|
204
|
+
port,
|
|
205
|
+
host,
|
|
206
|
+
fs: { strict: false },
|
|
207
|
+
strictPort: true,
|
|
208
|
+
hmr: { overlay: false }
|
|
209
|
+
// we handle compilation errors ourselves (see LocalApp.svelte)
|
|
210
|
+
},
|
|
211
|
+
resolve: {
|
|
212
|
+
alias: [
|
|
213
|
+
{ find: /^graphene$/, replacement: path2.resolve(uiRoot, "web.js") },
|
|
214
|
+
// Vite runs in a user project, but svelte is a direct dependency of the cli, and thus transitive to the user project.
|
|
215
|
+
// So when Vite tries to resolve `svelte` from a compiled md page, it can't find it without these aliases.
|
|
216
|
+
{ find: /^svelte$/, replacement: svelteExport(".") },
|
|
217
|
+
{ find: /^svelte\/animate$/, replacement: svelteExport("./animate") },
|
|
218
|
+
{ find: /^svelte\/attachments$/, replacement: svelteExport("./attachments") },
|
|
219
|
+
{ find: /^svelte\/easing$/, replacement: svelteExport("./easing") },
|
|
220
|
+
{ find: /^svelte\/events$/, replacement: svelteExport("./events") },
|
|
221
|
+
{ find: /^svelte\/internal$/, replacement: svelteExport("./internal") },
|
|
222
|
+
{ find: /^svelte\/internal\/client$/, replacement: svelteExport("./internal/client") },
|
|
223
|
+
{ find: /^svelte\/internal\/disclose-version$/, replacement: svelteExport("./internal/disclose-version") },
|
|
224
|
+
{ find: /^svelte\/internal\/flags\/async$/, replacement: svelteExport("./internal/flags/async") },
|
|
225
|
+
{ find: /^svelte\/internal\/flags\/legacy$/, replacement: svelteExport("./internal/flags/legacy") },
|
|
226
|
+
{ find: /^svelte\/internal\/flags\/tracing$/, replacement: svelteExport("./internal/flags/tracing") },
|
|
227
|
+
{ find: /^svelte\/legacy$/, replacement: svelteExport("./legacy") },
|
|
228
|
+
{ find: /^svelte\/motion$/, replacement: svelteExport("./motion") },
|
|
229
|
+
{ find: /^svelte\/reactivity$/, replacement: svelteExport("./reactivity") },
|
|
230
|
+
{ find: /^svelte\/reactivity\/window$/, replacement: svelteExport("./reactivity/window") },
|
|
231
|
+
{ find: /^svelte\/store$/, replacement: svelteExport("./store") },
|
|
232
|
+
{ find: /^svelte\/transition$/, replacement: svelteExport("./transition") },
|
|
233
|
+
{ find: /^clsx$/, replacement: path2.join(svelteDependencyRoot, "clsx/dist/clsx.mjs") }
|
|
234
|
+
]
|
|
235
|
+
},
|
|
236
|
+
optimizeDeps: {
|
|
237
|
+
noDiscovery: process.env.NODE_ENV == "test",
|
|
238
|
+
// tests manually optimize before starting test workers
|
|
239
|
+
exclude: ["virtual:nav"],
|
|
240
|
+
// provided by a plugin, so don't try and optimize it
|
|
241
|
+
// Vite running in a user project will not naturally discover and optimize these transitive deps.
|
|
242
|
+
// When you launch the server, your first page load will automatically refresh after a second or two as Vite now sees and optimizes these.
|
|
243
|
+
// This line makes it do that up-front, avoiding that reload jank. The packaged CLI also pre-bundles the `graphene` alias itself;
|
|
244
|
+
// doing that from source causes trouble in examples/tests because the alias points outside node_modules.
|
|
245
|
+
// `graphene` here is a special case: when packaged up it is considered a dependency, but in examples/tests, including it would cause errors.
|
|
246
|
+
// oxfmt-ignore
|
|
247
|
+
include: [
|
|
248
|
+
...packaged ? ["graphene"] : [],
|
|
249
|
+
"@graphenedata/cli > svelte",
|
|
250
|
+
"@graphenedata/cli > chroma-js",
|
|
251
|
+
"@graphenedata/cli > echarts",
|
|
252
|
+
"@graphenedata/cli > @graphenedata/html2canvas",
|
|
253
|
+
"@graphenedata/cli > @graphenedata/ui > svelte",
|
|
254
|
+
"@graphenedata/cli > @graphenedata/ui > chroma-js",
|
|
255
|
+
"@graphenedata/cli > @graphenedata/ui > echarts/dist/echarts.esm.js",
|
|
256
|
+
"@graphenedata/cli > @graphenedata/ui > @graphenedata/html2canvas"
|
|
257
|
+
]
|
|
258
|
+
}
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
async function handleQuery(req, res) {
|
|
262
|
+
let chunks = [];
|
|
263
|
+
for await (let chunk of req) chunks.push(chunk);
|
|
264
|
+
let { gsql, params, hashes } = JSON.parse(Buffer.concat(chunks).toString());
|
|
265
|
+
res.setHeader("Content-Type", "application/json");
|
|
266
|
+
await workspaceLoadPromise;
|
|
267
|
+
let gsqlFiles = workspaceFiles.filter((file) => !file.path.endsWith(".md"));
|
|
268
|
+
let result = analyzeWorkspace({ config, files: [...gsqlFiles, { path: "input", contents: gsql }] });
|
|
269
|
+
updateParsedFiles(result);
|
|
270
|
+
let diagnostics = result.diagnostics;
|
|
271
|
+
if (diagnostics.length) {
|
|
272
|
+
res.statusCode = 400;
|
|
273
|
+
res.end(JSON.stringify(diagnostics[0]));
|
|
274
|
+
return;
|
|
275
|
+
}
|
|
276
|
+
let queries = result.files.find((file) => file.path == "input")?.queries || [];
|
|
277
|
+
if (queries.length > 1) throw new Error("Found multiple queries, which could be a parsing error");
|
|
278
|
+
let sql = toSql(queries[0], params);
|
|
279
|
+
let hash = crypto.createHash("SHA1").update(`query-v${QUERY_VERSION}|${sql}`).digest("hex");
|
|
280
|
+
res.setHeader("ETag", hash);
|
|
281
|
+
if (hashes.includes(hash) && req.headers["cache-control"] != "no-cache") {
|
|
282
|
+
res.statusCode = 304;
|
|
283
|
+
return res.end();
|
|
284
|
+
}
|
|
285
|
+
let queryResults = await runQuery(sql);
|
|
286
|
+
let totalRows = queryResults.totalRows ?? queryResults.rows.length;
|
|
287
|
+
if (totalRows > queryResults.rows.length) throw new Error("Query returns too many rows");
|
|
288
|
+
let fields = queries[0].fields.map((field) => ({ name: field.name, type: field.type, metadata: field.metadata || {} }));
|
|
289
|
+
res.end(JSON.stringify({ rows: queryResults.rows, hash, fields, sql }));
|
|
290
|
+
}
|
|
291
|
+
async function handlePage(server, res) {
|
|
292
|
+
res.setHeader("Content-Type", "text/html");
|
|
293
|
+
let html = await server.transformIndexHtml(
|
|
294
|
+
"/index.html",
|
|
295
|
+
`<!doctype html>
|
|
296
|
+
<html lang="en">
|
|
297
|
+
<head>
|
|
298
|
+
<meta charset="UTF-8" />
|
|
299
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
300
|
+
<title>Graphene</title>
|
|
301
|
+
<link rel="icon" href="/favicon.ico" />
|
|
302
|
+
</head>
|
|
303
|
+
<body>
|
|
304
|
+
<script type="module">
|
|
305
|
+
import 'graphene'
|
|
306
|
+
</script>
|
|
307
|
+
</body>
|
|
308
|
+
</html>`
|
|
309
|
+
);
|
|
310
|
+
return res.end(html);
|
|
311
|
+
}
|
|
312
|
+
async function prepareDeps() {
|
|
313
|
+
let cfg = await resolveConfig(await createConfig(), "serve");
|
|
314
|
+
await optimizeDeps(cfg, true);
|
|
315
|
+
}
|
|
316
|
+
function fixSvelteDepsInTests() {
|
|
317
|
+
let viteConfig;
|
|
318
|
+
function configResolved(cfg) {
|
|
319
|
+
viteConfig = cfg;
|
|
320
|
+
}
|
|
321
|
+
function buildStart() {
|
|
322
|
+
if (process.env.NODE_ENV != "test") return;
|
|
323
|
+
viteConfig.optimizeDeps.force = false;
|
|
324
|
+
}
|
|
325
|
+
buildStart.sequential = true;
|
|
326
|
+
return { name: "fix-svelte-deps", enforce: "post", configResolved, buildStart };
|
|
327
|
+
}
|
|
328
|
+
function fixHmrForFailedModules() {
|
|
329
|
+
return {
|
|
330
|
+
name: "fix-hmr-for-failed-modules",
|
|
331
|
+
hotUpdate({ modules }) {
|
|
332
|
+
let hasFailed = modules.some((m) => !m.transformResult);
|
|
333
|
+
if (hasFailed) {
|
|
334
|
+
this.environment.hot.send({ type: "full-reload", path: "*" });
|
|
335
|
+
return [];
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
};
|
|
339
|
+
}
|
|
340
|
+
var workspaceLoadPromise;
|
|
341
|
+
var workspaceFiles = [];
|
|
342
|
+
var mdFiles = [];
|
|
343
|
+
function updateWorkspacePlugin(telemetry) {
|
|
344
|
+
return {
|
|
345
|
+
name: "updateWorkspace",
|
|
346
|
+
resolveId(id) {
|
|
347
|
+
if (id == "virtual:nav") return "\0virtual:nav";
|
|
348
|
+
},
|
|
349
|
+
load(id) {
|
|
350
|
+
if (id != "\0virtual:nav") return;
|
|
351
|
+
let res = [...mdFiles];
|
|
352
|
+
if (process.env.NODE_ENV == "test") {
|
|
353
|
+
for (let [path3, contents] of Object.entries(mockFileMap)) {
|
|
354
|
+
let mockFile = { path: path3, title: extractFrontmatter(contents).title };
|
|
355
|
+
let idx = res.findIndex((file) => file.path == path3);
|
|
356
|
+
if (idx >= 0) res.splice(idx, 1, mockFile);
|
|
357
|
+
else res.push(mockFile);
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
return `export default ${JSON.stringify(res)}`;
|
|
361
|
+
},
|
|
362
|
+
configureServer: (s) => {
|
|
363
|
+
let refresh = async () => {
|
|
364
|
+
workspaceLoadPromise = (async () => {
|
|
365
|
+
let loaded = await loadWorkspace(config.root, true, config.ignoredFiles);
|
|
366
|
+
telemetry?.event("workspace_scanned", { command: "serve", ...getWorkspaceScanCounts(loaded) });
|
|
367
|
+
workspaceFiles = loaded.map((file) => {
|
|
368
|
+
let existing = workspaceFiles.find((existing2) => existing2.path == file.path && existing2.contents == file.contents);
|
|
369
|
+
return existing?.parsed ? { ...file, parsed: existing.parsed } : file;
|
|
370
|
+
});
|
|
371
|
+
})();
|
|
372
|
+
await workspaceLoadPromise;
|
|
373
|
+
mdFiles = workspaceFiles.filter((file) => file.path.endsWith(".md")).map((f) => ({ path: f.path, title: extractFrontmatter(f.contents).title }));
|
|
374
|
+
let mod = s.moduleGraph.getModuleById("\0virtual:nav");
|
|
375
|
+
if (!mod) return;
|
|
376
|
+
s.reloadModule(mod);
|
|
377
|
+
};
|
|
378
|
+
s.watcher.add(["**/*.gsql", "**/*.md"]);
|
|
379
|
+
s.watcher.on("all", refresh);
|
|
380
|
+
refresh();
|
|
381
|
+
}
|
|
382
|
+
};
|
|
383
|
+
}
|
|
384
|
+
function updateParsedFiles(analysis) {
|
|
385
|
+
workspaceFiles = workspaceFiles.map((file) => {
|
|
386
|
+
let analyzed = analysis.files.find((next) => next.path == file.path);
|
|
387
|
+
if (!analyzed) return file;
|
|
388
|
+
return {
|
|
389
|
+
...file,
|
|
390
|
+
parsed: {
|
|
391
|
+
tree: analyzed.tree,
|
|
392
|
+
virtualContents: analyzed.virtualContents,
|
|
393
|
+
virtualToMarkdownOffset: analyzed.virtualToMarkdownOffset
|
|
394
|
+
}
|
|
395
|
+
};
|
|
396
|
+
});
|
|
397
|
+
}
|
|
398
|
+
var handleRequestPlugin = {
|
|
399
|
+
name: "handleRequest",
|
|
400
|
+
configureServer: (s) => {
|
|
401
|
+
s.middlewares.use(async function handleRequest(req, res, next) {
|
|
402
|
+
try {
|
|
403
|
+
let [pathName] = (req.url || "").split("?");
|
|
404
|
+
if (pathName == "/_api/query") return await handleQuery(req, res);
|
|
405
|
+
if (pathName) {
|
|
406
|
+
if (pathName == "/__ct" || pathName == "/_charts" || pathName == "/_styles") return await handlePage(s, res);
|
|
407
|
+
}
|
|
408
|
+
if (!pathName || pathName == "/") pathName = "index";
|
|
409
|
+
let relativeMdPath = pathName.replace(/^\//, "") + ".md";
|
|
410
|
+
let mdPath = path2.join(config.root, relativeMdPath);
|
|
411
|
+
if (mockFileMap[relativeMdPath] || await fs2.exists(mdPath)) {
|
|
412
|
+
await handlePage(s, res);
|
|
413
|
+
} else {
|
|
414
|
+
next();
|
|
415
|
+
}
|
|
416
|
+
} catch (err) {
|
|
417
|
+
if (process.env.NODE_ENV != "test") console.error(err);
|
|
418
|
+
res.statusCode = 500;
|
|
419
|
+
res.end(JSON.stringify({ message: err.message, stack: err.stack }));
|
|
420
|
+
}
|
|
421
|
+
});
|
|
422
|
+
}
|
|
423
|
+
};
|
|
424
|
+
function mockFilesForTests() {
|
|
425
|
+
if (process.env.NODE_ENV !== "test") return null;
|
|
426
|
+
function toMockKey(id) {
|
|
427
|
+
return id.replace(config.root + "/", "").replace(/^\//, "");
|
|
428
|
+
}
|
|
429
|
+
return {
|
|
430
|
+
name: "mock-files-for-tests",
|
|
431
|
+
enforce: "pre",
|
|
432
|
+
resolveId(id) {
|
|
433
|
+
if (!mockFileMap[toMockKey(id)]) return;
|
|
434
|
+
return path2.join(config.root, toMockKey(id)) + "?mock";
|
|
435
|
+
},
|
|
436
|
+
load(id) {
|
|
437
|
+
if (!id.endsWith("?mock")) return null;
|
|
438
|
+
return mockFileMap[toMockKey(id.replace(/\?mock$/, ""))];
|
|
439
|
+
}
|
|
440
|
+
};
|
|
441
|
+
}
|
|
442
|
+
export {
|
|
443
|
+
clearSvelteWarnings,
|
|
444
|
+
prepareDeps,
|
|
445
|
+
serve2,
|
|
446
|
+
svelteWarnings
|
|
447
|
+
};
|
|
448
|
+
//# sourceMappingURL=serve2-S2LL4D4D.js.map
|