cmd/wasm/main.go (107 lines of code) (raw):

// Licensed to Elasticsearch B.V. under one or more contributor // license agreements. See the NOTICE file distributed with // this work for additional information regarding copyright // ownership. Elasticsearch B.V. licenses this file to you under // the Apache License, Version 2.0 (the "License"); you may // not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, // software distributed under the License is distributed on an // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. //go:build js && wasm // The wasm package provides a WebAssembly module that can be used to format // CEL (Common Expression Language) programs in a canonical format. It exposes // two functions to JavaScript -- celFmt and celModuleBuildMetadata. // // # celFmt // // The celFmt function formats a given CEL program to canonical format. // It requires one argument, which must be a string. The function returns an // object with either a 'formatted' attribute containing the formatted CEL // program or an 'error' attribute containing the error message. // // # celModuleBuildMetadata // // The celModuleBuildMetadata function returns an object containing build and // dependency metadata. This is for diagnostic purposes. package main import ( "bytes" "encoding/json" "fmt" "io" "runtime" "runtime/debug" "strings" "syscall/js" "github.com/elastic/mito/lib" "github.com/google/cel-go/cel" "github.com/google/cel-go/checker/decls" "github.com/google/cel-go/common" "github.com/elastic/celfmt" ) //go:generate cp "$GOROOT/lib/wasm/wasm_exec.js" "$PWD/assets" func compileAndFormat(dst io.Writer, src string) error { xmlHelper, err := lib.XML(nil, nil) if err != nil { return fmt.Errorf("failed to initialize xml helper: %w", err) } env, err := cel.NewEnv( cel.Declarations(decls.NewVar("state", decls.Dyn)), lib.Collections(), lib.Crypto(), lib.JSON(nil), lib.Time(), lib.Try(), lib.Debug(func(_ string, _ any) {}), lib.File(nil), lib.MIME(nil), lib.HTTP(nil, nil, nil), lib.Limit(nil), lib.Strings(), xmlHelper, cel.OptionalTypes(cel.OptionalTypesVersion(1)), cel.EnableMacroCallTracking(), ) if err != nil { return fmt.Errorf("failed to create env: %w", err) } ast, iss := env.Compile(src) if iss != nil { return fmt.Errorf("failed to parse program: %v", iss) } return celfmt.Format(dst, ast.NativeRep(), common.NewTextSource(src), celfmt.Pretty(), celfmt.AlwaysComma()) } type celFmtResult struct { Error string `json:"error,omitempty"` Formatted string `json:"formatted,omitempty"` } // celFmt formats a given string using our CEL (Common Expression Language) // formatting rules. It compiles the given program as part of the formatting // process. This function takes one argument, which must be a string. // // The function always returns an object. On success, the object contains one // attribute named 'formatted' which contains the formatted CEL program. If any // error occurs, then the object contains an attribute named 'error' whose value // is the string error message. func celFmt(_ js.Value, args []js.Value) any { if len(args) != 1 { return toObject(&celFmtResult{Error: "celFmt requires one argument"}) } if args[0].Type() != js.TypeString { return toObject(&celFmtResult{Error: "celFmt argument must be a string"}) } buf := new(bytes.Buffer) if err := compileAndFormat(buf, args[0].String()); err != nil { return toObject(&celFmtResult{Error: err.Error()}) } return toObject(&celFmtResult{Formatted: buf.String()}) } // toObject converts a struct to a map[string]any using JSON marshal/unmarshal. func toObject(v any) map[string]any { data, err := json.Marshal(v) if err != nil { panic(err) } var out map[string]any if err = json.Unmarshal(data, &out); err != nil { panic(err) } return out } // moduleBuildMetadata returns a map containing build and dependency metadata // about WebAssembly module. This includes version numbers for specific modules // and other relevant settings such as commit hash and time. // // It then returns this map, which may contain the following keys: // // - go: The Go language version used to build the program (without the "go" prefix). // - celfmt: The version of the main module in the build information. // - mito: The version of the "github.com/elastic/mito" module. // - cel-go: The version of the "github.com/google/cel-go" module. // - commit: The VCS revision (commit hash) from the build settings, if available. // - commit_time: The timestamp of the VCS revision from the build settings, if available. func moduleBuildMetadata(_ js.Value, _ []js.Value) any { info, ok := debug.ReadBuildInfo() if !ok { return nil } meta := map[string]any{ "go": strings.TrimPrefix(runtime.Version(), "go"), "celfmt": info.Main.Version, } for _, m := range info.Deps { switch m.Path { case "github.com/elastic/mito": meta["mito"] = m.Version case "github.com/google/cel-go": meta["cel-go"] = m.Version } } for _, setting := range info.Settings { switch setting.Key { case "vcs.revision": meta["commit"] = setting.Value case "vcs.time": meta["commit_time"] = setting.Value } } return meta } func main() { js.Global().Set("celFmt", js.FuncOf(celFmt)) js.Global().Set("celModuleBuildMetadata", js.FuncOf(moduleBuildMetadata)) select {} }