NEZ-3109 feat:explore页面支持对 promql 格式校验及format

This commit is contained in:
zyh
2023-08-25 13:59:07 +08:00
parent 9d4e01e667
commit b56c3fd73e
6 changed files with 756 additions and 4 deletions

View File

@@ -3535,6 +3535,8 @@ import logTab from './logTab'
import promqlInputMixin from '@/components/common/mixin/promqlInput'
import chartRightBox from '@/components/common/rightBox/chart/chartRightBox'
import copy from '@/components/common/copy'
import './promqlparser/wasm_exec.js'
export default {
name: 'exploreItem',
components: {
@@ -3624,19 +3626,33 @@ export default {
scrollbarWrap: null
}
},
created () {
async created () {
this.getPanelData()
this.resetExpression()
await this.loadWebAssembly()
this.initQueryFromPath()
},
mounted () {
this.scrollbarWrap = this.$refs.exploreScrollbar
this.scrollbarWrap.addEventListener('scroll', this.onScroll)
this.initQueryFromPath()
},
beforeDestroy () {
this.scrollbarWrap.removeEventListener('scroll', this.onScroll)
},
methods: {
async loadWebAssembly () {
try {
// eslint-disable-next-line no-undef
const go = new Go()
const result = await WebAssembly.instantiateStreaming(fetch('/static/promqlparser.wasm'), go.importObject)
go.run(result.instance)
// eslint-disable-next-line no-undef
this.parsePromQL = parsePromQL
} catch (error) {
console.error(error)
}
},
parsePromQL () {},
onScroll: bus.debounce(function () {
this.showTopBtn = this.scrollbarWrap.scrollTop > 50
}, 300),
@@ -3953,7 +3969,7 @@ export default {
} else {
if (response.error) {
this.$refs['promql-' + promqlIndex][0].setError(response.error)
} else if(response.msg) {
} else if (response.msg) {
this.$refs['promql-' + promqlIndex][0].setError(response.msg)
} else {
this.$refs['promql-' + promqlIndex][0].setError(response)
@@ -4102,6 +4118,24 @@ export default {
this.setSearchTime(nowTimeType.type, nowTimeType.value)
}
if (this.expressions && this.expressions.length >= 1) {
let error = false
// 对 promql 格式校验及format
if (this.showMetrics) {
this.expressions.forEach((item, index) => {
if (item != '' && this.promqlKeys[index].state) {
const res = this.parsePromQL(item)
if (res.status === 'error') {
error = true
this.$refs['promql-' + index][0].setError(res.message)
} else {
this.$set(this.expressions, index, res.result)
this.$refs['promql-' + index][0].prettyCode()
this.$refs['promql-' + index][0].setError('')
}
}
})
}
if (error) { return }
if (this.showMetrics) {
this.queryTableData()
this.queryChartData()

View File

@@ -1158,10 +1158,20 @@ export default {
this.expressionList[this.index] = insertTxt
this.codeMirrorValue[this.index] = insertTxt
this.initCodeMirror()
}
},
/* setMsg:function(){
this.appendMsg
} */
prettyCode () {
this.$nextTick(() => {
const text = this.newView.state.doc.toString()
this.newView.dispatch(
this.newView.state.update({
changes: { from: 0, to: text.length, insert: this.codeMirrorValue[this.index] }
})
)
})
}
},
watch: {
dropDownVisible (n, o) {

View File

@@ -0,0 +1,30 @@
module test.go
go 1.20
require (
github.com/beorn7/perks v1.0.1 // indirect
github.com/cespare/xxhash/v2 v2.2.0 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/dennwc/varint v1.0.0 // indirect
github.com/go-kit/log v0.2.1 // indirect
github.com/go-logfmt/logfmt v0.6.0 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/protobuf v1.5.3 // indirect
github.com/grafana/regexp v0.0.0-20221122212121-6b5c0a4cb7fd // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/prometheus/client_golang v1.16.0 // indirect
github.com/prometheus/client_model v0.4.0 // indirect
github.com/prometheus/common v0.44.0 // indirect
github.com/prometheus/procfs v0.11.0 // indirect
github.com/prometheus/prometheus v0.46.0 // indirect
github.com/stretchr/testify v1.8.4 // indirect
go.uber.org/atomic v1.11.0 // indirect
go.uber.org/goleak v1.2.1 // indirect
golang.org/x/exp v0.0.0-20230713183714-613f0c0eb8a1 // indirect
golang.org/x/sys v0.10.0 // indirect
google.golang.org/protobuf v1.31.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

View File

@@ -0,0 +1,91 @@
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dennwc/varint v1.0.0 h1:kGNFFSSw8ToIy3obO/kKr8U9GZYUAxQEVuix4zfDWzE=
github.com/dennwc/varint v1.0.0/go.mod h1:hnItb35rvZvJrbTALZtY/iQfDs48JKRG1RPpgziApxA=
github.com/go-kit/log v0.2.1 h1:MRVx0/zhvdseW+Gza6N9rVzU/IVzaeE1SFI4raAhmBU=
github.com/go-kit/log v0.2.1/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0=
github.com/go-logfmt/logfmt v0.6.0 h1:wGYYu3uicYdqXVgoYbvnkrPVXkuLM1p1ifugDMEdRi4=
github.com/go-logfmt/logfmt v0.6.0/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs=
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/grafana/regexp v0.0.0-20221122212121-6b5c0a4cb7fd h1:PpuIBO5P3e9hpqBD0O/HjhShYuM6XE0i/lbE6J94kww=
github.com/grafana/regexp v0.0.0-20221122212121-6b5c0a4cb7fd/go.mod h1:M5qHK+eWfAv8VR/265dIuEpL3fNfeC21tXXp9itM24A=
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo=
github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_golang v1.16.0 h1:yk/hx9hDbrGHovbci4BY+pRMfSuuat626eFsHb7tmT8=
github.com/prometheus/client_golang v1.16.0/go.mod h1:Zsulrv/L9oM40tJ7T815tM89lFEugiJ9HzIqaAx4LKc=
github.com/prometheus/client_model v0.4.0 h1:5lQXD3cAg1OXBf4Wq03gTrXHeaV0TQvGfUooCfx1yqY=
github.com/prometheus/client_model v0.4.0/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU=
github.com/prometheus/common v0.44.0 h1:+5BrQJwiBB9xsMygAB3TNvpQKOwlkc25LbISbrdOOfY=
github.com/prometheus/common v0.44.0/go.mod h1:ofAIvZbQ1e/nugmZGz4/qCb9Ap1VoSTIO7x0VV9VvuY=
github.com/prometheus/procfs v0.11.0 h1:5EAgkfkMl659uZPbe9AS2N68a7Cc1TJbPEuGzFuRbyk=
github.com/prometheus/procfs v0.11.0/go.mod h1:nwNm2aOCAYw8uTR/9bWRREkZFxAUcWzPHWJq+XBB/FM=
github.com/prometheus/prometheus v0.46.0 h1:9JSdXnsuT6YsbODEhSQMwxNkGwPExfmzqG73vCMk/Kw=
github.com/prometheus/prometheus v0.46.0/go.mod h1:10L5IJE5CEsjee1FnOcVswYXlPIscDWWt3IJ2UDYrz4=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE=
go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A=
go.uber.org/goleak v1.2.1/go.mod h1:qlT2yGI9QafXHhZZLxlSuNsMw3FFLxBr+tBRlmO1xH4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/exp v0.0.0-20230713183714-613f0c0eb8a1 h1:MGwJjxBy0HJshjDNfLsYO8xppfqWlA5ZT9OhtUUhTNw=
golang.org/x/exp v0.0.0-20230713183714-613f0c0eb8a1/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.10.0 h1:SqMFp9UcQJZa+pmYuAKjd9xq1f0j5rLcDIk0mj4qAsA=
golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View File

@@ -0,0 +1,33 @@
// 编译为wasm文件 在页面中引入
package main
import (
"syscall/js"
"github.com/prometheus/prometheus/promql/parser"
)
func parsePromQL(this js.Value, args []js.Value) interface{} {
promql := args[0].String()
expr, err := parser.ParseExpr(promql)
if err != nil {
return map[string]interface{}{
"status": "error",
"message": err.Error(),
}
}
return map[string]interface{}{
"status": "success",
"result": expr.Pretty(0),
}
}
func main() {
c := make(chan struct{}, 0)
// 注册函数
js.Global().Set("parsePromQL", js.FuncOf(parsePromQL))
<-c
}

View File

@@ -0,0 +1,554 @@
// Copyright 2018 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
'use strict';
(() => {
const enosys = () => {
const err = new Error('not implemented')
err.code = 'ENOSYS'
return err
}
if (!globalThis.fs) {
let outputBuf = ''
globalThis.fs = {
constants: { O_WRONLY: -1, O_RDWR: -1, O_CREAT: -1, O_TRUNC: -1, O_APPEND: -1, O_EXCL: -1 }, // unused
writeSync (fd, buf) {
outputBuf += decoder.decode(buf)
const nl = outputBuf.lastIndexOf('\n')
if (nl != -1) {
console.log(outputBuf.substring(0, nl))
outputBuf = outputBuf.substring(nl + 1)
}
return buf.length
},
write (fd, buf, offset, length, position, callback) {
if (offset !== 0 || length !== buf.length || position !== null) {
callback(enosys())
return
}
const n = this.writeSync(fd, buf)
callback(null, n)
},
chmod (path, mode, callback) { callback(enosys()) },
chown (path, uid, gid, callback) { callback(enosys()) },
close (fd, callback) { callback(enosys()) },
fchmod (fd, mode, callback) { callback(enosys()) },
fchown (fd, uid, gid, callback) { callback(enosys()) },
fstat (fd, callback) { callback(enosys()) },
fsync (fd, callback) { callback(null) },
ftruncate (fd, length, callback) { callback(enosys()) },
lchown (path, uid, gid, callback) { callback(enosys()) },
link (path, link, callback) { callback(enosys()) },
lstat (path, callback) { callback(enosys()) },
mkdir (path, perm, callback) { callback(enosys()) },
open (path, flags, mode, callback) { callback(enosys()) },
read (fd, buffer, offset, length, position, callback) { callback(enosys()) },
readdir (path, callback) { callback(enosys()) },
readlink (path, callback) { callback(enosys()) },
rename (from, to, callback) { callback(enosys()) },
rmdir (path, callback) { callback(enosys()) },
stat (path, callback) { callback(enosys()) },
symlink (path, link, callback) { callback(enosys()) },
truncate (path, length, callback) { callback(enosys()) },
unlink (path, callback) { callback(enosys()) },
utimes (path, atime, mtime, callback) { callback(enosys()) }
}
}
if (!globalThis.process) {
globalThis.process = {
getuid () { return -1 },
getgid () { return -1 },
geteuid () { return -1 },
getegid () { return -1 },
getgroups () { throw enosys() },
pid: -1,
ppid: -1,
umask () { throw enosys() },
cwd () { throw enosys() },
chdir () { throw enosys() }
}
}
if (!globalThis.crypto) {
throw new Error('globalThis.crypto is not available, polyfill required (crypto.getRandomValues only)')
}
if (!globalThis.performance) {
throw new Error('globalThis.performance is not available, polyfill required (performance.now only)')
}
if (!globalThis.TextEncoder) {
throw new Error('globalThis.TextEncoder is not available, polyfill required')
}
if (!globalThis.TextDecoder) {
throw new Error('globalThis.TextDecoder is not available, polyfill required')
}
const encoder = new TextEncoder('utf-8')
const decoder = new TextDecoder('utf-8')
globalThis.Go = class {
constructor () {
this.argv = ['js']
this.env = {}
this.exit = (code) => {
if (code !== 0) {
console.warn('exit code:', code)
}
}
this._exitPromise = new Promise((resolve) => {
this._resolveExitPromise = resolve
})
this._pendingEvent = null
this._scheduledTimeouts = new Map()
this._nextCallbackTimeoutID = 1
const setInt64 = (addr, v) => {
this.mem.setUint32(addr + 0, v, true)
this.mem.setUint32(addr + 4, Math.floor(v / 4294967296), true)
}
const getInt64 = (addr) => {
const low = this.mem.getUint32(addr + 0, true)
const high = this.mem.getInt32(addr + 4, true)
return low + high * 4294967296
}
const loadValue = (addr) => {
const f = this.mem.getFloat64(addr, true)
if (f === 0) {
return undefined
}
if (!isNaN(f)) {
return f
}
const id = this.mem.getUint32(addr, true)
return this._values[id]
}
const storeValue = (addr, v) => {
const nanHead = 0x7FF80000
if (typeof v === 'number' && v !== 0) {
if (isNaN(v)) {
this.mem.setUint32(addr + 4, nanHead, true)
this.mem.setUint32(addr, 0, true)
return
}
this.mem.setFloat64(addr, v, true)
return
}
if (v === undefined) {
this.mem.setFloat64(addr, 0, true)
return
}
let id = this._ids.get(v)
if (id === undefined) {
id = this._idPool.pop()
if (id === undefined) {
id = this._values.length
}
this._values[id] = v
this._goRefCounts[id] = 0
this._ids.set(v, id)
}
this._goRefCounts[id]++
let typeFlag = 0
switch (typeof v) {
case 'object':
if (v !== null) {
typeFlag = 1
}
break
case 'string':
typeFlag = 2
break
case 'symbol':
typeFlag = 3
break
case 'function':
typeFlag = 4
break
}
this.mem.setUint32(addr + 4, nanHead | typeFlag, true)
this.mem.setUint32(addr, id, true)
}
const loadSlice = (addr) => {
const array = getInt64(addr + 0)
const len = getInt64(addr + 8)
return new Uint8Array(this._inst.exports.mem.buffer, array, len)
}
const loadSliceOfValues = (addr) => {
const array = getInt64(addr + 0)
const len = getInt64(addr + 8)
const a = new Array(len)
for (let i = 0; i < len; i++) {
a[i] = loadValue(array + i * 8)
}
return a
}
const loadString = (addr) => {
const saddr = getInt64(addr + 0)
const len = getInt64(addr + 8)
return decoder.decode(new DataView(this._inst.exports.mem.buffer, saddr, len))
}
const timeOrigin = Date.now() - performance.now()
this.importObject = {
go: {
// Go's SP does not change as long as no Go code is running. Some operations (e.g. calls, getters and setters)
// may synchronously trigger a Go event handler. This makes Go code get executed in the middle of the imported
// function. A goroutine can switch to a new stack if the current stack is too small (see morestack function).
// This changes the SP, thus we have to update the SP used by the imported function.
// func wasmExit(code int32)
'runtime.wasmExit': (sp) => {
sp >>>= 0
const code = this.mem.getInt32(sp + 8, true)
this.exited = true
delete this._inst
delete this._values
delete this._goRefCounts
delete this._ids
delete this._idPool
this.exit(code)
},
// func wasmWrite(fd uintptr, p unsafe.Pointer, n int32)
'runtime.wasmWrite': (sp) => {
sp >>>= 0
const fd = getInt64(sp + 8)
const p = getInt64(sp + 16)
const n = this.mem.getInt32(sp + 24, true)
fs.writeSync(fd, new Uint8Array(this._inst.exports.mem.buffer, p, n))
},
// func resetMemoryDataView()
'runtime.resetMemoryDataView': (sp) => {
sp >>>= 0
this.mem = new DataView(this._inst.exports.mem.buffer)
},
// func nanotime1() int64
'runtime.nanotime1': (sp) => {
sp >>>= 0
setInt64(sp + 8, (timeOrigin + performance.now()) * 1000000)
},
// func walltime() (sec int64, nsec int32)
'runtime.walltime': (sp) => {
sp >>>= 0
const msec = (new Date()).getTime()
setInt64(sp + 8, msec / 1000)
this.mem.setInt32(sp + 16, (msec % 1000) * 1000000, true)
},
// func scheduleTimeoutEvent(delay int64) int32
'runtime.scheduleTimeoutEvent': (sp) => {
sp >>>= 0
const id = this._nextCallbackTimeoutID
this._nextCallbackTimeoutID++
this._scheduledTimeouts.set(id, setTimeout(
() => {
this._resume()
while (this._scheduledTimeouts.has(id)) {
// for some reason Go failed to register the timeout event, log and try again
// (temporary workaround for https://github.com/golang/go/issues/28975)
console.warn('scheduleTimeoutEvent: missed timeout event')
this._resume()
}
},
getInt64(sp + 8) + 1 // setTimeout has been seen to fire up to 1 millisecond early
))
this.mem.setInt32(sp + 16, id, true)
},
// func clearTimeoutEvent(id int32)
'runtime.clearTimeoutEvent': (sp) => {
sp >>>= 0
const id = this.mem.getInt32(sp + 8, true)
clearTimeout(this._scheduledTimeouts.get(id))
this._scheduledTimeouts.delete(id)
},
// func getRandomData(r []byte)
'runtime.getRandomData': (sp) => {
sp >>>= 0
crypto.getRandomValues(loadSlice(sp + 8))
},
// func finalizeRef(v ref)
'syscall/js.finalizeRef': (sp) => {
sp >>>= 0
const id = this.mem.getUint32(sp + 8, true)
this._goRefCounts[id]--
if (this._goRefCounts[id] === 0) {
const v = this._values[id]
this._values[id] = null
this._ids.delete(v)
this._idPool.push(id)
}
},
// func stringVal(value string) ref
'syscall/js.stringVal': (sp) => {
sp >>>= 0
storeValue(sp + 24, loadString(sp + 8))
},
// func valueGet(v ref, p string) ref
'syscall/js.valueGet': (sp) => {
sp >>>= 0
const result = Reflect.get(loadValue(sp + 8), loadString(sp + 16))
sp = this._inst.exports.getsp() >>> 0 // see comment above
storeValue(sp + 32, result)
},
// func valueSet(v ref, p string, x ref)
'syscall/js.valueSet': (sp) => {
sp >>>= 0
Reflect.set(loadValue(sp + 8), loadString(sp + 16), loadValue(sp + 32))
},
// func valueDelete(v ref, p string)
'syscall/js.valueDelete': (sp) => {
sp >>>= 0
Reflect.deleteProperty(loadValue(sp + 8), loadString(sp + 16))
},
// func valueIndex(v ref, i int) ref
'syscall/js.valueIndex': (sp) => {
sp >>>= 0
storeValue(sp + 24, Reflect.get(loadValue(sp + 8), getInt64(sp + 16)))
},
// valueSetIndex(v ref, i int, x ref)
'syscall/js.valueSetIndex': (sp) => {
sp >>>= 0
Reflect.set(loadValue(sp + 8), getInt64(sp + 16), loadValue(sp + 24))
},
// func valueCall(v ref, m string, args []ref) (ref, bool)
'syscall/js.valueCall': (sp) => {
sp >>>= 0
try {
const v = loadValue(sp + 8)
const m = Reflect.get(v, loadString(sp + 16))
const args = loadSliceOfValues(sp + 32)
const result = Reflect.apply(m, v, args)
sp = this._inst.exports.getsp() >>> 0 // see comment above
storeValue(sp + 56, result)
this.mem.setUint8(sp + 64, 1)
} catch (err) {
sp = this._inst.exports.getsp() >>> 0 // see comment above
storeValue(sp + 56, err)
this.mem.setUint8(sp + 64, 0)
}
},
// func valueInvoke(v ref, args []ref) (ref, bool)
'syscall/js.valueInvoke': (sp) => {
sp >>>= 0
try {
const v = loadValue(sp + 8)
const args = loadSliceOfValues(sp + 16)
const result = Reflect.apply(v, undefined, args)
sp = this._inst.exports.getsp() >>> 0 // see comment above
storeValue(sp + 40, result)
this.mem.setUint8(sp + 48, 1)
} catch (err) {
sp = this._inst.exports.getsp() >>> 0 // see comment above
storeValue(sp + 40, err)
this.mem.setUint8(sp + 48, 0)
}
},
// func valueNew(v ref, args []ref) (ref, bool)
'syscall/js.valueNew': (sp) => {
sp >>>= 0
try {
const v = loadValue(sp + 8)
const args = loadSliceOfValues(sp + 16)
const result = Reflect.construct(v, args)
sp = this._inst.exports.getsp() >>> 0 // see comment above
storeValue(sp + 40, result)
this.mem.setUint8(sp + 48, 1)
} catch (err) {
sp = this._inst.exports.getsp() >>> 0 // see comment above
storeValue(sp + 40, err)
this.mem.setUint8(sp + 48, 0)
}
},
// func valueLength(v ref) int
'syscall/js.valueLength': (sp) => {
sp >>>= 0
setInt64(sp + 16, parseInt(loadValue(sp + 8).length))
},
// valuePrepareString(v ref) (ref, int)
'syscall/js.valuePrepareString': (sp) => {
sp >>>= 0
const str = encoder.encode(String(loadValue(sp + 8)))
storeValue(sp + 16, str)
setInt64(sp + 24, str.length)
},
// valueLoadString(v ref, b []byte)
'syscall/js.valueLoadString': (sp) => {
sp >>>= 0
const str = loadValue(sp + 8)
loadSlice(sp + 16).set(str)
},
// func valueInstanceOf(v ref, t ref) bool
'syscall/js.valueInstanceOf': (sp) => {
sp >>>= 0
this.mem.setUint8(sp + 24, (loadValue(sp + 8) instanceof loadValue(sp + 16)) ? 1 : 0)
},
// func copyBytesToGo(dst []byte, src ref) (int, bool)
'syscall/js.copyBytesToGo': (sp) => {
sp >>>= 0
const dst = loadSlice(sp + 8)
const src = loadValue(sp + 32)
if (!(src instanceof Uint8Array || src instanceof Uint8ClampedArray)) {
this.mem.setUint8(sp + 48, 0)
return
}
const toCopy = src.subarray(0, dst.length)
dst.set(toCopy)
setInt64(sp + 40, toCopy.length)
this.mem.setUint8(sp + 48, 1)
},
// func copyBytesToJS(dst ref, src []byte) (int, bool)
'syscall/js.copyBytesToJS': (sp) => {
sp >>>= 0
const dst = loadValue(sp + 8)
const src = loadSlice(sp + 16)
if (!(dst instanceof Uint8Array || dst instanceof Uint8ClampedArray)) {
this.mem.setUint8(sp + 48, 0)
return
}
const toCopy = src.subarray(0, dst.length)
dst.set(toCopy)
setInt64(sp + 40, toCopy.length)
this.mem.setUint8(sp + 48, 1)
},
debug: (value) => {
console.log(value)
}
}
}
}
async run (instance) {
if (!(instance instanceof WebAssembly.Instance)) {
throw new Error('Go.run: WebAssembly.Instance expected')
}
this._inst = instance
this.mem = new DataView(this._inst.exports.mem.buffer)
this._values = [ // JS values that Go currently has references to, indexed by reference id
NaN,
0,
null,
true,
false,
globalThis,
this
]
this._goRefCounts = new Array(this._values.length).fill(Infinity) // number of references that Go has to a JS value, indexed by reference id
this._ids = new Map([ // mapping from JS values to reference ids
[0, 1],
[null, 2],
[true, 3],
[false, 4],
[globalThis, 5],
[this, 6]
])
this._idPool = [] // unused ids that have been garbage collected
this.exited = false // whether the Go program has exited
// Pass command line arguments and environment variables to WebAssembly by writing them to the linear memory.
let offset = 4096
const strPtr = (str) => {
const ptr = offset
const bytes = encoder.encode(str + '\0')
new Uint8Array(this.mem.buffer, offset, bytes.length).set(bytes)
offset += bytes.length
if (offset % 8 !== 0) {
offset += 8 - (offset % 8)
}
return ptr
}
const argc = this.argv.length
const argvPtrs = []
this.argv.forEach((arg) => {
argvPtrs.push(strPtr(arg))
})
argvPtrs.push(0)
const keys = Object.keys(this.env).sort()
keys.forEach((key) => {
argvPtrs.push(strPtr(`${key}=${this.env[key]}`))
})
argvPtrs.push(0)
const argv = offset
argvPtrs.forEach((ptr) => {
this.mem.setUint32(offset, ptr, true)
this.mem.setUint32(offset + 4, 0, true)
offset += 8
})
// The linker guarantees global data starts from at least wasmMinDataAddr.
// Keep in sync with cmd/link/internal/ld/data.go:wasmMinDataAddr.
const wasmMinDataAddr = 4096 + 8192
if (offset >= wasmMinDataAddr) {
throw new Error('total length of command line and environment variables exceeds limit')
}
this._inst.exports.run(argc, argv)
if (this.exited) {
this._resolveExitPromise()
}
await this._exitPromise
}
_resume () {
if (this.exited) {
throw new Error('Go program has already exited')
}
this._inst.exports.resume()
if (this.exited) {
this._resolveExitPromise()
}
}
_makeFuncWrapper (id) {
const go = this
return function () {
const event = { id: id, this: this, args: arguments }
go._pendingEvent = event
go._resume()
return event.result
}
}
}
})()