@graphenedata/cli 0.0.1
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/LICENSE.md +100 -0
- package/THIRD_PARTY_NOTICES.md +12 -0
- package/cli.ts +157 -0
- package/dist/cli/cli.js +43 -0
- package/dist/docs/data_apps/components/charts/annotations.md +673 -0
- package/dist/docs/data_apps/components/charts/area-chart.md +202 -0
- package/dist/docs/data_apps/components/charts/bar-chart.md +317 -0
- package/dist/docs/data_apps/components/charts/box-plot.md +190 -0
- package/dist/docs/data_apps/components/charts/bubble-chart.md +151 -0
- package/dist/docs/data_apps/components/charts/calendar-heatmap.md +112 -0
- package/dist/docs/data_apps/components/charts/custom-echarts.md +308 -0
- package/dist/docs/data_apps/components/charts/echarts-options.md +217 -0
- package/dist/docs/data_apps/components/charts/funnel-chart.md +106 -0
- package/dist/docs/data_apps/components/charts/heatmap.md +180 -0
- package/dist/docs/data_apps/components/charts/histogram.md +107 -0
- package/dist/docs/data_apps/components/charts/line-chart.md +265 -0
- package/dist/docs/data_apps/components/charts/mixed-type-charts.md +240 -0
- package/dist/docs/data_apps/components/charts/sankey-diagram.md +301 -0
- package/dist/docs/data_apps/components/charts/scatter-plot.md +134 -0
- package/dist/docs/data_apps/components/charts/sparkline.md +68 -0
- package/dist/docs/data_apps/components/data/big-value.md +153 -0
- package/dist/docs/data_apps/components/data/delta.md +89 -0
- package/dist/docs/data_apps/components/data/table.md +470 -0
- package/dist/docs/data_apps/components/data/value.md +97 -0
- package/dist/docs/data_apps/components/inputs/button-group.md +154 -0
- package/dist/docs/data_apps/components/inputs/checkbox.md +52 -0
- package/dist/docs/data_apps/components/inputs/date-input.md +131 -0
- package/dist/docs/data_apps/components/inputs/date-range.md +124 -0
- package/dist/docs/data_apps/components/inputs/dimension-grid.md +67 -0
- package/dist/docs/data_apps/components/inputs/dropdown.md +199 -0
- package/dist/docs/data_apps/components/inputs/index.md +3 -0
- package/dist/docs/data_apps/components/inputs/slider.md +126 -0
- package/dist/docs/data_apps/components/inputs/text-input.md +86 -0
- package/dist/docs/data_apps/components/maps/area-map.md +397 -0
- package/dist/docs/data_apps/components/maps/base-map.md +269 -0
- package/dist/docs/data_apps/components/maps/bubble-map.md +361 -0
- package/dist/docs/data_apps/components/maps/point-map.md +326 -0
- package/dist/docs/data_apps/components/maps/us-map.md +167 -0
- package/dist/docs/data_apps/components/ui/accordion.md +116 -0
- package/dist/docs/data_apps/components/ui/alert.md +37 -0
- package/dist/docs/data_apps/components/ui/big-link.md +19 -0
- package/dist/docs/data_apps/components/ui/details.md +58 -0
- package/dist/docs/data_apps/components/ui/download-data.md +41 -0
- package/dist/docs/data_apps/components/ui/embed.md +47 -0
- package/dist/docs/data_apps/components/ui/grid.md +45 -0
- package/dist/docs/data_apps/components/ui/image.md +61 -0
- package/dist/docs/data_apps/components/ui/info.md +47 -0
- package/dist/docs/data_apps/components/ui/last-refreshed.md +28 -0
- package/dist/docs/data_apps/components/ui/link-button.md +20 -0
- package/dist/docs/data_apps/components/ui/link.md +40 -0
- package/dist/docs/data_apps/components/ui/modal.md +57 -0
- package/dist/docs/data_apps/components/ui/note.md +32 -0
- package/dist/docs/data_apps/components/ui/print-format-components.md +85 -0
- package/dist/docs/data_apps/components/ui/tabs.md +122 -0
- package/dist/docs/graphene.md +129 -0
- package/dist/ui/app.css +332 -0
- package/dist/ui/assets/favicon.ico +0 -0
- package/dist/ui/component-utilities/autoFormatting.js +301 -0
- package/dist/ui/component-utilities/builtInFormats.js +482 -0
- package/dist/ui/component-utilities/chartContext.js +12 -0
- package/dist/ui/component-utilities/chartWindowDebug.js +21 -0
- package/dist/ui/component-utilities/checkInputs.js +95 -0
- package/dist/ui/component-utilities/convert.js +15 -0
- package/dist/ui/component-utilities/dateParsing.js +57 -0
- package/dist/ui/component-utilities/dropdownContext.ts +1 -0
- package/dist/ui/component-utilities/echarts.js +262 -0
- package/dist/ui/component-utilities/echartsThemes.js +453 -0
- package/dist/ui/component-utilities/formatTitle.js +24 -0
- package/dist/ui/component-utilities/formatting.js +258 -0
- package/dist/ui/component-utilities/getColumnExtents.js +79 -0
- package/dist/ui/component-utilities/getColumnSummary.js +67 -0
- package/dist/ui/component-utilities/getCompletedData.js +114 -0
- package/dist/ui/component-utilities/getDistinctCount.js +7 -0
- package/dist/ui/component-utilities/getDistinctValues.js +15 -0
- package/dist/ui/component-utilities/getSeriesConfig.js +236 -0
- package/dist/ui/component-utilities/getSortedData.js +7 -0
- package/dist/ui/component-utilities/getStackPercentages.js +43 -0
- package/dist/ui/component-utilities/getStackedData.js +17 -0
- package/dist/ui/component-utilities/getYAxisIndex.js +15 -0
- package/dist/ui/component-utilities/globalContexts.js +1 -0
- package/dist/ui/component-utilities/helpers/getCompletedData.helpers.js +119 -0
- package/dist/ui/component-utilities/inputUtils.ts +25 -0
- package/dist/ui/component-utilities/replaceNulls.js +14 -0
- package/dist/ui/component-utilities/tableUtils.ts +120 -0
- package/dist/ui/component-utilities/themeStores.ts +116 -0
- package/dist/ui/components/Area.svelte +174 -0
- package/dist/ui/components/AreaChart.svelte +150 -0
- package/dist/ui/components/Bar.svelte +326 -0
- package/dist/ui/components/BarChart.svelte +194 -0
- package/dist/ui/components/BigValue.svelte +69 -0
- package/dist/ui/components/Chart.svelte +1070 -0
- package/dist/ui/components/Column.svelte +172 -0
- package/dist/ui/components/DateRange.svelte +324 -0
- package/dist/ui/components/Dropdown.svelte +738 -0
- package/dist/ui/components/DropdownOption.svelte +21 -0
- package/dist/ui/components/ECharts.svelte +77 -0
- package/dist/ui/components/ErrorChart.svelte +54 -0
- package/dist/ui/components/GrapheneQuery.svelte +12 -0
- package/dist/ui/components/InlineDelta.svelte +150 -0
- package/dist/ui/components/Line.svelte +210 -0
- package/dist/ui/components/LineChart.svelte +178 -0
- package/dist/ui/components/PieChart.svelte +36 -0
- package/dist/ui/components/QueryLoad.svelte +82 -0
- package/dist/ui/components/Row.svelte +14 -0
- package/dist/ui/components/SortIcon.svelte +32 -0
- package/dist/ui/components/Table.svelte +19 -0
- package/dist/ui/components/TableCell.svelte +75 -0
- package/dist/ui/components/TableGroupRow.svelte +136 -0
- package/dist/ui/components/TableGroupToggle.svelte +42 -0
- package/dist/ui/components/TableHeader.svelte +242 -0
- package/dist/ui/components/TableRow.svelte +283 -0
- package/dist/ui/components/TableSubtotalRow.svelte +62 -0
- package/dist/ui/components/TableTotalRow.svelte +88 -0
- package/dist/ui/components/TextInput.svelte +92 -0
- package/dist/ui/components/_Table.svelte +516 -0
- package/dist/ui/internal/clientCache.ts +43 -0
- package/dist/ui/internal/queryEngine.ts +169 -0
- package/dist/ui/internal/telemetry.ts +28 -0
- package/dist/ui/internal/theme.ts +88 -0
- package/dist/ui/layout.svelte +3 -0
- package/dist/ui/playwright.config.ts +30 -0
- package/dist/ui/web.js +106 -0
- package/package.json +71 -0
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
// The query engine gathers query requests and inputs from components, and issues requests to the server.
|
|
2
|
+
// When inputs change, it takes care of notifying affected components and requesting new data.
|
|
3
|
+
|
|
4
|
+
import {cacheRead, cacheWrite, getHashes} from './clientCache'
|
|
5
|
+
import {errorProvider} from './telemetry.ts'
|
|
6
|
+
|
|
7
|
+
interface QueryError {
|
|
8
|
+
file: string
|
|
9
|
+
message: string
|
|
10
|
+
from: {lineText: string}
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
interface QueryResult {
|
|
14
|
+
rows?: any[]
|
|
15
|
+
errors?: Error[]
|
|
16
|
+
fields?: Field[]
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
interface Field {
|
|
20
|
+
name: string
|
|
21
|
+
type?: string
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
type ResultHandler = (res: QueryResult) => void
|
|
25
|
+
|
|
26
|
+
interface QueryNode {
|
|
27
|
+
name?: string
|
|
28
|
+
contents: string
|
|
29
|
+
callback?: ResultHandler
|
|
30
|
+
loading: boolean
|
|
31
|
+
fields: string[]
|
|
32
|
+
errors: Error[]
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
let runPending: Promise<void> | null = null
|
|
36
|
+
let params = {} as Record<string, any>
|
|
37
|
+
let queries = [] as QueryNode[]
|
|
38
|
+
|
|
39
|
+
function registerQuery (name: string, contents: string) {
|
|
40
|
+
queries = queries.filter(q => q.name !== name)
|
|
41
|
+
queries.push({name, contents, loading: false, fields: [], errors: []})
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function updateParam (name: string, value: any) {
|
|
45
|
+
params[name] = value
|
|
46
|
+
runAll() // for now, do the easy thing and reload it all
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function query (source: string, fields: string[], callback: ResultHandler) {
|
|
50
|
+
let contents = `from ${source} select ${fields.join(', ')}`
|
|
51
|
+
queries.push({contents, callback, loading: false, fields, errors: []})
|
|
52
|
+
runAll()
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function unsubscribe (callback: ResultHandler) {
|
|
56
|
+
queries = queries.filter(q => q.callback !== callback)
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
async function runNode (n: QueryNode) {
|
|
60
|
+
if (!n.callback) throw new Error('running node nobody is listening to')
|
|
61
|
+
n.callback({}) // notify that the query is loading
|
|
62
|
+
n.loading = true
|
|
63
|
+
n.errors = []
|
|
64
|
+
|
|
65
|
+
let hashes = await getHashes()
|
|
66
|
+
let tables = queries.filter(q => q.name)
|
|
67
|
+
let gsql = [
|
|
68
|
+
...tables.map(q => `table ${q.name} as (${q.contents})`),
|
|
69
|
+
n.contents,
|
|
70
|
+
].join('\n')
|
|
71
|
+
|
|
72
|
+
try {
|
|
73
|
+
let response = await fetch('/_api/query', {
|
|
74
|
+
method: 'POST',
|
|
75
|
+
headers: {'Content-Type': 'application/json'},
|
|
76
|
+
body: JSON.stringify({params, gsql, hashes}),
|
|
77
|
+
})
|
|
78
|
+
let hash = response.headers.get('ETag') || ''
|
|
79
|
+
|
|
80
|
+
if (response.status == 304) { // cache hit. Read it out and use that
|
|
81
|
+
let body = await cacheRead(hash)
|
|
82
|
+
n.callback(translateData(body, n))
|
|
83
|
+
} else if (response.ok) { // cache miss. write it to the cache, and return the data
|
|
84
|
+
cacheWrite(hash, response.clone()) // clone allows us to write the raw response into the cache
|
|
85
|
+
let body = await response.json()
|
|
86
|
+
n.callback(translateData(body, n)) // nb that translateData modifies in place for performance
|
|
87
|
+
} else { // request failed. Record it
|
|
88
|
+
let isJson = response.headers.get('Content-Type') === 'application/json'
|
|
89
|
+
let body = isJson ? await response.json() : await response.text()
|
|
90
|
+
n.errors = Array.isArray(body) ? body : [body]
|
|
91
|
+
n.callback({errors: n.errors})
|
|
92
|
+
}
|
|
93
|
+
} catch (e) {
|
|
94
|
+
n.errors = [e]
|
|
95
|
+
} finally {
|
|
96
|
+
n.loading = false
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function runAll () {
|
|
101
|
+
if (runPending) return runPending
|
|
102
|
+
runPending = Promise.resolve().then(_runAll).finally(() => runPending = null)
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
async function _runAll () {
|
|
106
|
+
await Promise.all(queries.map(async n => {
|
|
107
|
+
if (!n.callback) return
|
|
108
|
+
await runNode(n)
|
|
109
|
+
}))
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function translateData (data: any, node: QueryNode) {
|
|
113
|
+
let rows = data.rows || []
|
|
114
|
+
rows.dataLoaded = true // evidence components need this to be set
|
|
115
|
+
rows._evidenceColumnTypes = []
|
|
116
|
+
|
|
117
|
+
data.fields.forEach((field, index) => {
|
|
118
|
+
let name = field.name
|
|
119
|
+
|
|
120
|
+
// server gives names like `col_1` to unnamed expressions but we translate it back into the original expression like `avg(price)`
|
|
121
|
+
if (field.name.match(/col_\d+/)) {
|
|
122
|
+
name = node.fields[index]
|
|
123
|
+
rows.forEach(r => {
|
|
124
|
+
r[name] = r[field.name]
|
|
125
|
+
delete r[field.name]
|
|
126
|
+
})
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// map graphene types down to the ones evidence expects
|
|
130
|
+
rows._evidenceColumnTypes.push({name, evidenceType: evidenceType(field.type)})
|
|
131
|
+
})
|
|
132
|
+
|
|
133
|
+
return {rows}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
export const isLoading = () => !!queries.find(q => q.loading)
|
|
137
|
+
|
|
138
|
+
errorProvider('queryEngine', () => {
|
|
139
|
+
let unique = {}
|
|
140
|
+
queries.flatMap(q => q.errors).filter(q => !!q).forEach(e => {
|
|
141
|
+
unique[e.message + String((e as any).from?.lineText)] = e
|
|
142
|
+
})
|
|
143
|
+
return Object.values(unique)
|
|
144
|
+
})
|
|
145
|
+
|
|
146
|
+
async function waitForQueries (timeout = 20_000) {
|
|
147
|
+
let end = performance.now() + timeout
|
|
148
|
+
while (isLoading() && performance.now() < end) {
|
|
149
|
+
await new Promise(resolve => setTimeout(resolve, 25))
|
|
150
|
+
}
|
|
151
|
+
return !isLoading()
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function evidenceType (type: string | undefined) {
|
|
155
|
+
if (type === 'string') return 'string'
|
|
156
|
+
if (type === 'number') return 'number'
|
|
157
|
+
if (type === 'boolean') return 'boolean'
|
|
158
|
+
if (type === 'date' || type === 'timestamp') return 'date'
|
|
159
|
+
console.warn('Unsupported evidence type ' + type)
|
|
160
|
+
return 'string'
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
Object.assign(window.$GRAPHENE, {
|
|
164
|
+
registerQuery,
|
|
165
|
+
updateParam,
|
|
166
|
+
query,
|
|
167
|
+
unsubscribe,
|
|
168
|
+
waitForQueries,
|
|
169
|
+
})
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
type ErrorProvider = () => Error[]
|
|
2
|
+
|
|
3
|
+
window.$GRAPHENE = {getErrors}
|
|
4
|
+
|
|
5
|
+
let staticErrors: Error[] = []
|
|
6
|
+
let errorProviders: Record<string, ErrorProvider> = {}
|
|
7
|
+
|
|
8
|
+
window.addEventListener('error', (event) => {
|
|
9
|
+
console.log('recordedError')
|
|
10
|
+
staticErrors.push(event.error)
|
|
11
|
+
})
|
|
12
|
+
window.addEventListener('unhandledrejection', (event) => {
|
|
13
|
+
console.log('record unhandled')
|
|
14
|
+
staticErrors.push(event.reason)
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
export function error (e: Error, ctx?: any) {
|
|
18
|
+
staticErrors.push(e)
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function errorProvider (key:string, fn: ErrorProvider) {
|
|
22
|
+
errorProviders[key] = fn
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function getErrors (): Error[] {
|
|
26
|
+
return staticErrors.concat(Object.values(errorProviders)
|
|
27
|
+
.flatMap(p => p()))
|
|
28
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
// Evidence has a complex theme loading system that we don't really want to keep.
|
|
2
|
+
// This was generated by copying the runtime export of $evidence/themes from the evidence sample app.
|
|
3
|
+
export const themes = {
|
|
4
|
+
'light': {
|
|
5
|
+
'colors': {
|
|
6
|
+
'primary': '#2563eb',
|
|
7
|
+
'accent': '#c2410c',
|
|
8
|
+
'base-100': '#ffffff',
|
|
9
|
+
'info': '#0284c7',
|
|
10
|
+
'positive': '#16a34a',
|
|
11
|
+
'warning': '#f8c900',
|
|
12
|
+
'negative': '#dc2626',
|
|
13
|
+
'primary-content': '#f6f8fb',
|
|
14
|
+
'accent-content': '#fbf7f6',
|
|
15
|
+
'base-200': '#f7f7f7',
|
|
16
|
+
'base-300': '#d6d6d6',
|
|
17
|
+
'base-heading': '#060606',
|
|
18
|
+
'base-content': '#2c2c2c',
|
|
19
|
+
'base-content-muted': '#717171',
|
|
20
|
+
'info-content': '#030709',
|
|
21
|
+
'positive-content': '#040906',
|
|
22
|
+
'negative-content': '#fbf6f6',
|
|
23
|
+
'warning-content': '#0a0803',
|
|
24
|
+
},
|
|
25
|
+
'colorPalettes': {
|
|
26
|
+
'default': [
|
|
27
|
+
'#236aa4',
|
|
28
|
+
'#45a1bf',
|
|
29
|
+
'#a5cdee',
|
|
30
|
+
'#8dacbf',
|
|
31
|
+
'#85c7c6',
|
|
32
|
+
'#d2c6ac',
|
|
33
|
+
'#f4b548',
|
|
34
|
+
'#8f3d56',
|
|
35
|
+
'#71b9f4',
|
|
36
|
+
'#46a485',
|
|
37
|
+
],
|
|
38
|
+
},
|
|
39
|
+
'colorScales': {
|
|
40
|
+
'default': [
|
|
41
|
+
'#ADD8E6',
|
|
42
|
+
'#00008B',
|
|
43
|
+
],
|
|
44
|
+
},
|
|
45
|
+
},
|
|
46
|
+
'dark': {
|
|
47
|
+
'colors': {
|
|
48
|
+
'primary': '#3b82f6',
|
|
49
|
+
'accent': '#fdba74',
|
|
50
|
+
'base-100': '#09090b',
|
|
51
|
+
'info': '#38bdf8',
|
|
52
|
+
'positive': '#4ade80',
|
|
53
|
+
'warning': '#fbbf24',
|
|
54
|
+
'negative': '#f87171',
|
|
55
|
+
'primary-content': '#030609',
|
|
56
|
+
'accent-content': '#090603',
|
|
57
|
+
'base-200': '#111113',
|
|
58
|
+
'base-300': '#29292b',
|
|
59
|
+
'base-heading': '#f8f8f9',
|
|
60
|
+
'base-content': '#cacacb',
|
|
61
|
+
'base-content-muted': '#7b7b7c',
|
|
62
|
+
'info-content': '#030809',
|
|
63
|
+
'positive-content': '#040906',
|
|
64
|
+
'negative-content': '#090303',
|
|
65
|
+
'warning-content': '#090803',
|
|
66
|
+
},
|
|
67
|
+
'colorPalettes': {
|
|
68
|
+
'default': [
|
|
69
|
+
'#236aa4',
|
|
70
|
+
'#45a1bf',
|
|
71
|
+
'#a5cdee',
|
|
72
|
+
'#8dacbf',
|
|
73
|
+
'#85c7c6',
|
|
74
|
+
'#d2c6ac',
|
|
75
|
+
'#f4b548',
|
|
76
|
+
'#8f3d56',
|
|
77
|
+
'#71b9f4',
|
|
78
|
+
'#46a485',
|
|
79
|
+
],
|
|
80
|
+
},
|
|
81
|
+
'colorScales': {
|
|
82
|
+
'default': [
|
|
83
|
+
'#ADD8E6',
|
|
84
|
+
'#00008B',
|
|
85
|
+
],
|
|
86
|
+
},
|
|
87
|
+
},
|
|
88
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import {defineConfig, devices} from '@playwright/test'
|
|
2
|
+
|
|
3
|
+
export default defineConfig({
|
|
4
|
+
testDir: './tests',
|
|
5
|
+
outputDir: './tests/results',
|
|
6
|
+
timeout: 10_000,
|
|
7
|
+
expect: {
|
|
8
|
+
timeout: process.env.DEBUG ? 0 : 2_000,
|
|
9
|
+
toHaveScreenshot: {
|
|
10
|
+
pathTemplate: '{testDir}/snapshots/{testFilePath}/{arg}{ext}',
|
|
11
|
+
},
|
|
12
|
+
},
|
|
13
|
+
fullyParallel: false,
|
|
14
|
+
forbidOnly: !!process.env.CI,
|
|
15
|
+
retries: 0, // process.env.CI ? 1 : 0,
|
|
16
|
+
reporter: process.env.CI ? [['list'], ['github']] : 'list',
|
|
17
|
+
use: {
|
|
18
|
+
headless: true,
|
|
19
|
+
actionTimeout: 0,
|
|
20
|
+
trace: 'retain-on-failure',
|
|
21
|
+
video: 'off',
|
|
22
|
+
launchOptions: {devtools: !!process.env.DEBUG},
|
|
23
|
+
},
|
|
24
|
+
projects: [
|
|
25
|
+
{
|
|
26
|
+
name: 'chromium',
|
|
27
|
+
use: {...devices['Desktop Chrome'], browserName: 'chromium'},
|
|
28
|
+
},
|
|
29
|
+
],
|
|
30
|
+
})
|
package/dist/ui/web.js
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import {getErrors} from './internal/telemetry.ts'
|
|
2
|
+
import './app.css'
|
|
3
|
+
import {isLoading} from './internal/queryEngine.ts'
|
|
4
|
+
|
|
5
|
+
import Area from './components/Area.svelte'
|
|
6
|
+
import AreaChart from './components/AreaChart.svelte'
|
|
7
|
+
import Bar from './components/Bar.svelte'
|
|
8
|
+
import BarChart from './components/BarChart.svelte'
|
|
9
|
+
import BigValue from './components/BigValue.svelte'
|
|
10
|
+
import Chart from './components/Chart.svelte'
|
|
11
|
+
import Column from './components/Column.svelte'
|
|
12
|
+
import DateRange from './components/DateRange.svelte'
|
|
13
|
+
import Dropdown from './components/Dropdown.svelte'
|
|
14
|
+
import DropdownOption from './components/DropdownOption.svelte'
|
|
15
|
+
import ECharts from './components/ECharts.svelte'
|
|
16
|
+
import ErrorChart from './components/ErrorChart.svelte'
|
|
17
|
+
import GrapheneQuery from './components/GrapheneQuery.svelte'
|
|
18
|
+
import InlineDelta from './components/InlineDelta.svelte'
|
|
19
|
+
import Line from './components/Line.svelte'
|
|
20
|
+
import LineChart from './components/LineChart.svelte'
|
|
21
|
+
import PieChart from './components/PieChart.svelte'
|
|
22
|
+
import QueryLoad from './components/QueryLoad.svelte'
|
|
23
|
+
import Row from './components/Row.svelte'
|
|
24
|
+
import SortIcon from './components/SortIcon.svelte'
|
|
25
|
+
import Table from './components/Table.svelte'
|
|
26
|
+
import TableCell from './components/TableCell.svelte'
|
|
27
|
+
import TableGroupRow from './components/TableGroupRow.svelte'
|
|
28
|
+
import TableGroupToggle from './components/TableGroupToggle.svelte'
|
|
29
|
+
import TableHeader from './components/TableHeader.svelte'
|
|
30
|
+
import TableRow from './components/TableRow.svelte'
|
|
31
|
+
import TableSubtotalRow from './components/TableSubtotalRow.svelte'
|
|
32
|
+
import TableTotalRow from './components/TableTotalRow.svelte'
|
|
33
|
+
import TextInput from './components/TextInput.svelte'
|
|
34
|
+
|
|
35
|
+
window.$GRAPHENE.components = {
|
|
36
|
+
Area,
|
|
37
|
+
AreaChart,
|
|
38
|
+
Bar,
|
|
39
|
+
BarChart,
|
|
40
|
+
BigValue,
|
|
41
|
+
Chart,
|
|
42
|
+
Column,
|
|
43
|
+
DateRange,
|
|
44
|
+
Dropdown,
|
|
45
|
+
DropdownOption,
|
|
46
|
+
ECharts,
|
|
47
|
+
ErrorChart,
|
|
48
|
+
GrapheneQuery,
|
|
49
|
+
InlineDelta,
|
|
50
|
+
Line,
|
|
51
|
+
LineChart,
|
|
52
|
+
PieChart,
|
|
53
|
+
QueryLoad,
|
|
54
|
+
Row,
|
|
55
|
+
SortIcon,
|
|
56
|
+
Table,
|
|
57
|
+
TableCell,
|
|
58
|
+
TableGroupRow,
|
|
59
|
+
TableGroupToggle,
|
|
60
|
+
TableHeader,
|
|
61
|
+
TableRow,
|
|
62
|
+
TableSubtotalRow,
|
|
63
|
+
TableTotalRow,
|
|
64
|
+
TextInput,
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
let socket = null
|
|
69
|
+
|
|
70
|
+
connectWebSocket()
|
|
71
|
+
|
|
72
|
+
async function takeScreenshot (chartName = null) {
|
|
73
|
+
if (!window.html2canvas) {
|
|
74
|
+
let html2canvas = await import('html2canvas')
|
|
75
|
+
window.html2canvas = html2canvas.default
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// wait some time for queries to finish loading
|
|
79
|
+
let startTime = Date.now()
|
|
80
|
+
while (isLoading() && Date.now() - startTime < 20_000) {
|
|
81
|
+
await new Promise(resolve => setTimeout(resolve, 100))
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
let targetElement = chartName ? document.querySelector(`[name="${chartName}"]`) : document.body
|
|
85
|
+
let canvas = targetElement && await window.html2canvas(targetElement, {useCORS: true, allowTaint: true, scale: 1})
|
|
86
|
+
return {stillLoading: isLoading(), screenshot: canvas?.toDataURL('image/png')}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function connectWebSocket () {
|
|
90
|
+
socket = new WebSocket(`ws://${window.location.host}/graphene-ws`)
|
|
91
|
+
socket.onclose = () => setTimeout(connectWebSocket, 2000)
|
|
92
|
+
|
|
93
|
+
socket.onopen = () => {
|
|
94
|
+
socket.send(JSON.stringify({type: 'register', url: window.location.href}))
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
socket.onmessage = async (event) => {
|
|
98
|
+
console.log('Got message', event.data)
|
|
99
|
+
let {type, requestId, chart} = JSON.parse(event.data)
|
|
100
|
+
|
|
101
|
+
if (type === 'view') {
|
|
102
|
+
let {screenshot, stillLoading} = await takeScreenshot(chart)
|
|
103
|
+
socket.send(JSON.stringify({type: 'viewResponse', requestId, screenshot, stillLoading, errors: getErrors()}))
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@graphenedata/cli",
|
|
3
|
+
"main": "cli.ts",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"author": "Graphene Systems Inc",
|
|
6
|
+
"version": "0.0.1",
|
|
7
|
+
"license": "Elastic-2.0",
|
|
8
|
+
"engines": {
|
|
9
|
+
"node": ">=16"
|
|
10
|
+
},
|
|
11
|
+
"files": [
|
|
12
|
+
"dist",
|
|
13
|
+
"package.json",
|
|
14
|
+
"LICENSE.md",
|
|
15
|
+
"THIRD_PARTY_NOTICES.md"
|
|
16
|
+
],
|
|
17
|
+
"bin": {
|
|
18
|
+
"graphene": "./dist/cli/cli.js"
|
|
19
|
+
},
|
|
20
|
+
"exports": {
|
|
21
|
+
"./ui": {
|
|
22
|
+
"browser": "./dist/ui/web.js"
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
"dependencies": {
|
|
26
|
+
"@duckdb/node-api": "1.3.2-alpha.26",
|
|
27
|
+
"@google-cloud/bigquery": "^8.1.1",
|
|
28
|
+
"@lezer/common": "^1.2.3",
|
|
29
|
+
"@lezer/lr": "^1.4.2",
|
|
30
|
+
"@graphenedata/malloy": "0.0.304",
|
|
31
|
+
"@sveltejs/vite-plugin-svelte": "3.1.2",
|
|
32
|
+
"@tidyjs/tidy": "^2.5.2",
|
|
33
|
+
"chalk": "^5.3.0",
|
|
34
|
+
"chokidar": "3.6.0",
|
|
35
|
+
"chroma-js": "2.4.2",
|
|
36
|
+
"cli-table3": "^0.6.3",
|
|
37
|
+
"commander": "^11.0.0",
|
|
38
|
+
"debounce": "^1.2.1",
|
|
39
|
+
"dompurify": "^3.2.7",
|
|
40
|
+
"echarts": "^5.5.0",
|
|
41
|
+
"fs-extra": "11.2.0",
|
|
42
|
+
"glob": "^11.0.3",
|
|
43
|
+
"html2canvas": "^1.4.1",
|
|
44
|
+
"marked": "^16.3.0",
|
|
45
|
+
"mdsvex": "^0.12.6",
|
|
46
|
+
"nanoid": "3.3.8",
|
|
47
|
+
"rehype-stringify": "^10.0.1",
|
|
48
|
+
"remark": "^15.0.1",
|
|
49
|
+
"remark-mdx": "^3.1.1",
|
|
50
|
+
"remark-rehype": "^11.1.2",
|
|
51
|
+
"ssf": "^0.11.2",
|
|
52
|
+
"svelte": "4.2.19",
|
|
53
|
+
"unist-util-visit": "4.1.2",
|
|
54
|
+
"vite": "5.4.14",
|
|
55
|
+
"ws": "^8.18.0"
|
|
56
|
+
},
|
|
57
|
+
"devDependencies": {
|
|
58
|
+
"@types/fs-extra": "^11.0.4",
|
|
59
|
+
"@types/node": "^20.0.0",
|
|
60
|
+
"@types/ws": "^8.18.1",
|
|
61
|
+
"esbuild": "^0.21.5",
|
|
62
|
+
"vitest": "3.0.5",
|
|
63
|
+
"vscode-languageserver-types": "^3.17.0"
|
|
64
|
+
},
|
|
65
|
+
"scripts": {
|
|
66
|
+
"build": "rm -rf dist && node ./esbuild.mjs",
|
|
67
|
+
"test": "vitest run --reporter=default --reporter=json --outputFile=/tmp/graphene-test-results.json",
|
|
68
|
+
"test-one": "DEBUG=1 node --inspect ../scripts/turboTest.js",
|
|
69
|
+
"prepack": "pnpm run build"
|
|
70
|
+
}
|
|
71
|
+
}
|