@graphenedata/cli 0.0.4 → 0.0.6

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.
Files changed (74) hide show
  1. package/cli.ts +25 -55
  2. package/dist/cli/cli.js +1194 -435
  3. package/dist/docs/graphene.md +1074 -166
  4. package/dist/ui/component-utilities/echarts.js +3 -1
  5. package/dist/ui/component-utilities/inputUtils.ts +11 -0
  6. package/dist/ui/component-utilities/themeStores.ts +35 -7
  7. package/dist/ui/components/Area.svelte +6 -3
  8. package/dist/ui/components/AreaChart.svelte +3 -1
  9. package/dist/ui/components/Bar.svelte +14 -8
  10. package/dist/ui/components/BarChart.svelte +3 -1
  11. package/dist/ui/components/BigValue.svelte +1 -1
  12. package/dist/ui/components/Chart.svelte +57 -101
  13. package/dist/ui/components/Column.svelte +2 -0
  14. package/dist/ui/components/ECharts.svelte +2 -0
  15. package/dist/ui/components/Line.svelte +8 -5
  16. package/dist/ui/components/LineChart.svelte +3 -2
  17. package/dist/ui/components/PieChart.svelte +1 -1
  18. package/dist/ui/components/QueryLoad.svelte +5 -6
  19. package/dist/ui/components/TableRow.svelte +1 -1
  20. package/dist/ui/components/_Table.svelte +2 -0
  21. package/dist/ui/internal/queryEngine.ts +39 -15
  22. package/dist/ui/internal/telemetry.ts +5 -3
  23. package/dist/ui/web.js +28 -12
  24. package/package.json +3 -2
  25. package/dist/docs/data_apps/components/charts/annotations.md +0 -673
  26. package/dist/docs/data_apps/components/charts/area-chart.md +0 -202
  27. package/dist/docs/data_apps/components/charts/bar-chart.md +0 -317
  28. package/dist/docs/data_apps/components/charts/box-plot.md +0 -190
  29. package/dist/docs/data_apps/components/charts/bubble-chart.md +0 -151
  30. package/dist/docs/data_apps/components/charts/calendar-heatmap.md +0 -112
  31. package/dist/docs/data_apps/components/charts/custom-echarts.md +0 -308
  32. package/dist/docs/data_apps/components/charts/echarts-options.md +0 -217
  33. package/dist/docs/data_apps/components/charts/funnel-chart.md +0 -106
  34. package/dist/docs/data_apps/components/charts/heatmap.md +0 -180
  35. package/dist/docs/data_apps/components/charts/histogram.md +0 -107
  36. package/dist/docs/data_apps/components/charts/line-chart.md +0 -265
  37. package/dist/docs/data_apps/components/charts/mixed-type-charts.md +0 -240
  38. package/dist/docs/data_apps/components/charts/sankey-diagram.md +0 -301
  39. package/dist/docs/data_apps/components/charts/scatter-plot.md +0 -134
  40. package/dist/docs/data_apps/components/charts/sparkline.md +0 -68
  41. package/dist/docs/data_apps/components/data/big-value.md +0 -153
  42. package/dist/docs/data_apps/components/data/delta.md +0 -89
  43. package/dist/docs/data_apps/components/data/table.md +0 -470
  44. package/dist/docs/data_apps/components/data/value.md +0 -97
  45. package/dist/docs/data_apps/components/inputs/button-group.md +0 -154
  46. package/dist/docs/data_apps/components/inputs/checkbox.md +0 -52
  47. package/dist/docs/data_apps/components/inputs/date-input.md +0 -131
  48. package/dist/docs/data_apps/components/inputs/date-range.md +0 -124
  49. package/dist/docs/data_apps/components/inputs/dimension-grid.md +0 -67
  50. package/dist/docs/data_apps/components/inputs/dropdown.md +0 -199
  51. package/dist/docs/data_apps/components/inputs/index.md +0 -3
  52. package/dist/docs/data_apps/components/inputs/slider.md +0 -126
  53. package/dist/docs/data_apps/components/inputs/text-input.md +0 -86
  54. package/dist/docs/data_apps/components/maps/area-map.md +0 -397
  55. package/dist/docs/data_apps/components/maps/base-map.md +0 -269
  56. package/dist/docs/data_apps/components/maps/bubble-map.md +0 -361
  57. package/dist/docs/data_apps/components/maps/point-map.md +0 -326
  58. package/dist/docs/data_apps/components/maps/us-map.md +0 -167
  59. package/dist/docs/data_apps/components/ui/accordion.md +0 -116
  60. package/dist/docs/data_apps/components/ui/alert.md +0 -37
  61. package/dist/docs/data_apps/components/ui/big-link.md +0 -19
  62. package/dist/docs/data_apps/components/ui/details.md +0 -58
  63. package/dist/docs/data_apps/components/ui/download-data.md +0 -41
  64. package/dist/docs/data_apps/components/ui/embed.md +0 -47
  65. package/dist/docs/data_apps/components/ui/grid.md +0 -45
  66. package/dist/docs/data_apps/components/ui/image.md +0 -61
  67. package/dist/docs/data_apps/components/ui/info.md +0 -47
  68. package/dist/docs/data_apps/components/ui/last-refreshed.md +0 -28
  69. package/dist/docs/data_apps/components/ui/link-button.md +0 -20
  70. package/dist/docs/data_apps/components/ui/link.md +0 -40
  71. package/dist/docs/data_apps/components/ui/modal.md +0 -57
  72. package/dist/docs/data_apps/components/ui/note.md +0 -32
  73. package/dist/docs/data_apps/components/ui/print-format-components.md +0 -85
  74. package/dist/docs/data_apps/components/ui/tabs.md +0 -122
@@ -16,7 +16,7 @@
16
16
 
17
17
  <style></style>
18
18
 
19
- <QueryLoad data={data} fields={[category, value]} let:loaded>
19
+ <QueryLoad data={data} fields={{category, value}} let:loaded>
20
20
  <ECharts data={loaded} {echartsOptions} {seriesOptions} {seriesColors} config={{
21
21
  title: {
22
22
  text: title,
@@ -4,22 +4,21 @@
4
4
 
5
5
  export let data: string | {rows?: any[]}
6
6
  export let height = 200
7
- export let fields: string[] = []
7
+ export let fields: Record<string, string | string[]> = {}
8
8
 
9
9
  let errors: Error[] | null = null
10
10
  let loaded: any[] | null = null
11
11
 
12
- let handleResults = (data) => {
13
- errors = data.errors
14
- loaded = data.rows
12
+ let handleResults = (result) => {
13
+ errors = result.errors || null
14
+ loaded = result.rows
15
15
  }
16
16
 
17
17
  onMount(() => {
18
18
  if (typeof data !== 'string') {
19
19
  loaded = data.rows
20
20
  } else {
21
- let usedFields = fields.filter(f => !!f)
22
- if (usedFields.length == 0) usedFields = ['*']
21
+ let usedFields = Object.fromEntries(Object.entries(fields).filter(e => !!e[1]))
23
22
  window.$GRAPHENE.query(data, usedFields, handleResults)
24
23
  }
25
24
  })
@@ -251,7 +251,7 @@
251
251
  background: rgba(229, 231, 235, 0.6);
252
252
  }
253
253
 
254
- :global(.table-row--lined) td {
254
+ .table-row--lined :global(td) {
255
255
  border-bottom: 1px solid rgba(107, 114, 128, 0.2);
256
256
  }
257
257
 
@@ -15,6 +15,7 @@
15
15
  import {getFinalColumnOrder} from '../component-utilities/tableUtils'
16
16
  import {getThemeStores} from '../component-utilities/themeStores'
17
17
  import {toBoolean} from '../component-utilities/convert'
18
+ import {logError} from '../internal/telemetry.js'
18
19
 
19
20
  const {resolveColor} = getThemeStores()
20
21
 
@@ -141,6 +142,7 @@
141
142
  }
142
143
  } catch (thrown) {
143
144
  let message = thrown instanceof Error ? thrown.message : 'Unable to prepare dataset'
145
+ logError(thrown, {id: 'DataTable'})
144
146
  error = message
145
147
  if (strictBuild) throw thrown
146
148
  }
@@ -4,12 +4,6 @@
4
4
  import {cacheRead, cacheWrite, getHashes} from './clientCache'
5
5
  import {errorProvider} from './telemetry.ts'
6
6
 
7
- interface QueryError {
8
- file: string
9
- message: string
10
- from: {lineText: string}
11
- }
12
-
13
7
  interface QueryResult {
14
8
  rows?: any[]
15
9
  errors?: Error[]
@@ -25,10 +19,11 @@ type ResultHandler = (res: QueryResult) => void
25
19
 
26
20
  interface QueryNode {
27
21
  name?: string
22
+ source?: string
28
23
  contents: string
29
24
  callback?: ResultHandler
30
25
  loading: boolean
31
- fields: string[]
26
+ fields: Map<string, string | string[]>
32
27
  errors: Error[]
33
28
  }
34
29
 
@@ -38,7 +33,7 @@ let queries = [] as QueryNode[]
38
33
 
39
34
  function registerQuery (name: string, contents: string) {
40
35
  queries = queries.filter(q => q.name !== name)
41
- queries.push({name, contents, loading: false, fields: [], errors: []})
36
+ queries.push({name, contents, loading: false, fields: new Map(), errors: []})
42
37
  }
43
38
 
44
39
  function updateParam (name: string, value: any) {
@@ -46,9 +41,20 @@ function updateParam (name: string, value: any) {
46
41
  runAll() // for now, do the easy thing and reload it all
47
42
  }
48
43
 
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: []})
44
+ function query (source: string, fields: Record<string, string | string[]>, callback: ResultHandler) {
45
+ // using Map here because it preserves the order in which we add fields to the select, which we use when we get the result.
46
+ let map = new Map(Object.entries(fields))
47
+ let exprs: string[] = []
48
+ if (map.size > 0) {
49
+ map.forEach((value) => {
50
+ if (Array.isArray(value)) exprs.push(...value)
51
+ else exprs.push(value)
52
+ })
53
+ } else {
54
+ exprs = ['*']
55
+ }
56
+ let contents = `from ${source} select ${exprs.join(', ')}`
57
+ queries.push({contents, callback, loading: false, fields: map, errors: [], source})
52
58
  runAll()
53
59
  }
54
60
 
@@ -87,11 +93,24 @@ async function runNode (n: QueryNode) {
87
93
  } else { // request failed. Record it
88
94
  let isJson = response.headers.get('Content-Type') === 'application/json'
89
95
  let body = isJson ? await response.json() : await response.text()
90
- n.errors = Array.isArray(body) ? body : [body]
96
+ n.errors = Array.isArray(body) ? body : [{message: body}]
97
+
98
+ let fieldIds = Array.from(n.fields.entries()).flatMap(([name, val]) => {
99
+ if (Array.isArray(val)) {
100
+ if (val.length === 0) return [] as string[]
101
+ if (val.length === 1) return [`${name}="${val[0]}"`]
102
+ return [`${name}="${val.join(', ')}"`]
103
+ }
104
+ if (typeof val === 'string' && val.trim().length === 0) return [] as string[]
105
+ if (val == null) return [] as string[]
106
+ return [`${name}="${val}"`]
107
+ })
108
+ let idStr = `Query (data="${n.source}" ` + fieldIds.join(' ') + ')'
109
+ n.errors.forEach(e => (e as any).id = idStr)
91
110
  n.callback({errors: n.errors})
92
111
  }
93
112
  } catch (e) {
94
- n.errors = [e]
113
+ n.errors = [e as Error]
95
114
  } finally {
96
115
  n.loading = false
97
116
  }
@@ -113,13 +132,18 @@ function translateData (data: any, node: QueryNode) {
113
132
  let rows = data.rows || []
114
133
  rows.dataLoaded = true // evidence components need this to be set
115
134
  rows._evidenceColumnTypes = []
135
+ let requestFields: string[] = []
136
+ node.fields.forEach((value) => {
137
+ if (Array.isArray(value)) requestFields.push(...value)
138
+ else requestFields.push(value)
139
+ })
116
140
 
117
141
  data.fields.forEach((field, index) => {
118
142
  let name = field.name
119
143
 
120
144
  // server gives names like `col_1` to unnamed expressions but we translate it back into the original expression like `avg(price)`
121
145
  if (field.name.match(/col_\d+/)) {
122
- name = node.fields[index]
146
+ name = requestFields[index]
123
147
  rows.forEach(r => {
124
148
  r[name] = r[field.name]
125
149
  delete r[field.name]
@@ -140,7 +164,7 @@ errorProvider('queryEngine', () => {
140
164
  queries.flatMap(q => q.errors).filter(q => !!q).forEach(e => {
141
165
  unique[e.message + String((e as any).from?.lineText)] = e
142
166
  })
143
- return Object.values(unique)
167
+ return Object.values(unique) as Error[]
144
168
  })
145
169
 
146
170
  async function waitForQueries (timeout = 20_000) {
@@ -12,7 +12,9 @@ window.addEventListener('unhandledrejection', (event) => {
12
12
  staticErrors.push(event.reason)
13
13
  })
14
14
 
15
- export function error (e: Error, ctx?: any) {
15
+ export function logError (e: Error | string, ctx?: any) {
16
+ if (typeof e === 'string') e = new Error(e)
17
+ if (ctx) Object.assign(e, ctx)
16
18
  staticErrors.push(e)
17
19
  }
18
20
 
@@ -21,6 +23,6 @@ export function errorProvider (key:string, fn: ErrorProvider) {
21
23
  }
22
24
 
23
25
  export function getErrors (): Error[] {
24
- return staticErrors.concat(Object.values(errorProviders)
25
- .flatMap(p => p()))
26
+ let provided = Object.values(errorProviders).flatMap(p => p())
27
+ return staticErrors.concat(provided)
26
28
  }
package/dist/ui/web.js CHANGED
@@ -69,25 +69,42 @@ let socket = null
69
69
 
70
70
  connectWebSocket()
71
71
 
72
- async function takeScreenshot (chartName = null) {
72
+ async function captureChart (chartTitle) {
73
+ await waitForQueriesToFinish()
74
+ let errors = getErrors()
75
+ let escaped = window.CSS.escape(chartTitle)
76
+ let canvas = document.querySelector(`[data-chart-title="${escaped}"] canvas`)
77
+
78
+ if (!canvas) {
79
+ errors.push({message: `Could not find chart titled "${chartTitle}"`})
80
+ return {stillLoading: isLoading(), screenshot: null, errors}
81
+ }
82
+
83
+ return {stillLoading: isLoading(), screenshot: canvas.toDataURL('image/png'), errors}
84
+ }
85
+
86
+ async function takeScreenshot () {
87
+ await waitForQueriesToFinish()
73
88
  if (!window.html2canvas) {
74
- let html2canvas = await import('html2canvas')
89
+ let html2canvas = await import('@graphenedata/html2canvas')
75
90
  window.html2canvas = html2canvas.default
76
91
  }
77
92
 
78
- // wait some time for queries to finish loading
93
+ let canvas = await window.html2canvas(document.body, {useCORS: true, allowTaint: true, scale: 1, liveDOM: true})
94
+ let errors = getErrors().map(e => ({message: e.message, id: e.id}))
95
+ return {stillLoading: isLoading(), screenshot: canvas?.toDataURL('image/png'), errors}
96
+ }
97
+
98
+ async function waitForQueriesToFinish () {
79
99
  let startTime = Date.now()
80
100
  while (isLoading() && Date.now() - startTime < 20_000) {
81
101
  await new Promise(resolve => setTimeout(resolve, 100))
82
102
  }
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
103
  }
88
104
 
89
105
  function connectWebSocket () {
90
- socket = new WebSocket(`ws://${window.location.host}/graphene-ws`)
106
+ let wsUrl = `ws://${window.location.host}/_api/ws`
107
+ socket = new WebSocket(wsUrl)
91
108
  socket.onclose = () => setTimeout(connectWebSocket, 2000)
92
109
 
93
110
  socket.onopen = () => {
@@ -95,12 +112,11 @@ function connectWebSocket () {
95
112
  }
96
113
 
97
114
  socket.onmessage = async (event) => {
98
- console.log('Got message', event.data)
99
115
  let {type, requestId, chart} = JSON.parse(event.data)
100
116
 
101
- if (type === 'view') {
102
- let {screenshot, stillLoading} = await takeScreenshot(chart)
103
- socket.send(JSON.stringify({type: 'viewResponse', requestId, screenshot, stillLoading, errors: getErrors()}))
117
+ if (type === 'check') {
118
+ let result = chart ? await captureChart(chart) : await takeScreenshot()
119
+ socket.send(JSON.stringify({type: 'checkResponse', requestId, ...result}))
104
120
  }
105
121
  }
106
122
  }
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "main": "cli.ts",
4
4
  "type": "module",
5
5
  "author": "Graphene Systems Inc",
6
- "version": "0.0.4",
6
+ "version": "0.0.6",
7
7
  "license": "Elastic-2.0",
8
8
  "engines": {
9
9
  "node": ">=16"
@@ -39,11 +39,12 @@
39
39
  "echarts": "^5.5.0",
40
40
  "fs-extra": "11.2.0",
41
41
  "glob": "^11.0.3",
42
- "html2canvas": "^1.4.1",
42
+ "@graphenedata/html2canvas": "^1.4.1",
43
43
  "marked": "^16.3.0",
44
44
  "mdsvex": "^0.12.6",
45
45
  "nanoid": "3.3.8",
46
46
  "sanitize-html": "^2.17.0",
47
+ "snowflake-sdk": "^2.3.1",
47
48
  "ssf": "^0.11.2",
48
49
  "svelte": "4.2.19",
49
50
  "unist-util-visit": "4.1.2",