From af4cbabfd41c69b90cde359017da92c849d457bc Mon Sep 17 00:00:00 2001 From: "David E. Wheeler" Date: Sun, 27 Oct 2024 19:09:14 -0400 Subject: [PATCH 01/39] Create playground Use TinyGo to compile theory/jsonpath into a WASM file, then borrow from the Goldmark Playground and serde_json_path Sandbox to create it. --- .gitignore | 33 ++++ .golangci.yaml | 119 +++++++++++++ .pre-commit-config.yaml | 34 ++++ Makefile | 50 ++++++ README.md | 65 +++++++ go.mod | 5 + go.sum | 10 ++ src/index.html | 209 +++++++++++++++++++++++ src/main.go | 63 +++++++ src/play.css | 368 ++++++++++++++++++++++++++++++++++++++++ 10 files changed, 956 insertions(+) create mode 100644 .gitignore create mode 100644 .golangci.yaml create mode 100644 .pre-commit-config.yaml create mode 100644 Makefile create mode 100644 README.md create mode 100644 go.mod create mode 100644 go.sum create mode 100644 src/index.html create mode 100644 src/main.go create mode 100644 src/play.css diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1f0b939 --- /dev/null +++ b/.gitignore @@ -0,0 +1,33 @@ +# If you prefer the allow list template instead of the deny list, see community template: +# https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore +# +# Binaries for programs and plugins +*.exe +*.exe~ +*.dll +*.so +*.dylib + +# Test binary, built with `go test -c` +*.test + +# Output of the go coverage tool, specifically when used with LiteIDE +*.out + +# Dependency directories +vendor/ + +# Go workspace file +go.work + +# Editor files +.vscode/ + +# Build artifacts +pub/ + +# OS Stuff +.DS_Store + +# Temp files +*.tmp diff --git a/.golangci.yaml b/.golangci.yaml new file mode 100644 index 0000000..0f47239 --- /dev/null +++ b/.golangci.yaml @@ -0,0 +1,119 @@ +# Options for analysis running. +run: + # Timeout for analysis, e.g. 30s, 5m. + # Default: 1m + timeout: 5m + # Which dirs to skip: issues from them won't be reported. + # Can use regexp here: `generated.*`, regexp is applied on full path, + # including the path prefix if one is set. + # Default value is empty list, + # but default dirs are skipped independently of this option's value (see skip-dirs-use-default). + # "/" will be replaced by current OS file path separator to properly work on Windows. + # skip-dirs: [] + # Which files to skip: they will be analyzed, but issues from them won't be reported. + # Default value is empty list, + # but there is no need to include all autogenerated files, + # we confidently recognize autogenerated files. + # If it's not please let us know. + # "/" will be replaced by current OS file path separator to properly work on Windows. + # skip-files: [] + +# output configuration options +# output: +# Format: colored-line-number|line-number|json|colored-tab|tab|checkstyle|code-climate|junit-xml|github-actions|teamcity +# +# Multiple can be specified by separating them by comma, output can be provided +# for each of them by separating format name and path by colon symbol. +# Output path can be either `stdout`, `stderr` or path to the file to write to. +# Example: "checkstyle:report.xml,json:stdout,colored-line-number" +# +# Default: colored-line-number +# formats: colored-line-number + +# Re-enable excluded linters. Restores missing documentation checks, among +# others. +# https://golangci-lint.run/usage/false-positives/#default-exclusions +# https://github.com/golangci/golangci-lint/issues/456#issuecomment-986837325 +issues: + exclude-use-default: false + +linters: + # Enable specific linter + # https://golangci-lint.run/usage/linters/#enabled-by-default + # enable: [] + + # Enable all available linters. + # Default: false + enable-all: true + + # Disable specific linter + # https://golangci-lint.run/usage/linters/#disabled-by-default + disable: + # Deprecated + - execinquery # Archived by the owner + - gomnd # Renamed to mnd + + # Too strict. + - depguard + - gci + - typecheck # Ignore syscall/js + - cyclop # redundant, covered by gocyclo + - nlreturn + - wsl + - nestif # redundant, covered by gocyclo + + # Enable presets. + # https://golangci-lint.run/usage/linters + presets: [] + + # Run only fast linters from enabled linters set (first run won't be fast) + # Default: false + # fast: true +# linters-settings: +# ireturn: +# # ireturn does not allow using `allow` and `reject` settings at the same time. +# # Both settings are lists of the keywords and regular expressions matched to interface or package names. +# # keywords: +# # - `empty` for `any` +# # - `error` for errors +# # - `stdlib` for standard library +# # - `anon` for anonymous interfaces +# # - `generic` for generic interfaces added in go 1.18 + +# # By default, it allows using errors, empty interfaces, anonymous interfaces, +# # and interfaces provided by the standard library. +# allow: +# - anon +# - error +# - empty +# - stdlib +# - generic +# - Hark +# - protoreflect.ProtoMessage +# - tink\.(?:AEAD|MAC)$ +# - registry\.KMSClient$ +# - ast\.Node$ +# - types\.DateTime$ +# # You can specify idiomatic endings for interface +# # - (or|er)$ +# # reject-list of interfaces +# # reject: +# # - github.com\/user\/package\/v4\.Type +# errcheck: +# check-type-assertions: false +# check-blank: false +# disable-default-exclusions: false +# exclude-functions: +# - fmt.Fprintf +# - fmt.Fprintln +# - fmt.Fprint +# funlen: +# # Ignore comments when counting lines. +# ignore-comments: true +# nlreturn: +# # Size of the block (including return statement that is still "OK") +# # so no return split required. +# # Default: 1 +# block-size: 2 +# exhaustive: +# default-signifies-exhaustive: true diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..2c9a6a8 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,34 @@ +# auth-global hooks will be managed / updated across all auth repos. +repos: + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.5.0 + hooks: + - id: trailing-whitespace + name: Lint trailing whitespace + exclude_types: [image] + - id: end-of-file-fixer + name: Lint end-of-file newline + exclude_types: [image] + - id: check-added-large-files + name: Don't permit large files + exclude_types: [image] + + - repo: local + hooks: + - id: go-mod-tidy + name: Go mod tidy + language: system + entry: go mod tidy + pass_filenames: false + - id: golangci-lint + name: Go linting + language: system + entry: make golangci-lint + pass_filenames: false + + - repo: https://github.com/pre-commit/mirrors-prettier + rev: v3.1.0 + hooks: + - id: prettier + name: JSON and YAML formatting + types_or: [json, yaml] diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..8d7f322 --- /dev/null +++ b/Makefile @@ -0,0 +1,50 @@ +GO ?= go + +SRC_DIR := src +DST_DIR := pub +WASM_EXEC := $(shell tinygo env TINYGOROOT)/targets/wasm_exec.js +# WASM_EXEC := $(shell go env GOROOT)/misc/wasm/wasm_exec.js + +playground: $(DST_DIR)/play.wasm $(DST_DIR)/index.html $(DST_DIR)/wasm_exec.js $(DST_DIR)/play.css + +ROOT_DIR := $(dir $(realpath $(lastword $(MAKEFILE_LIST)))) +$(DST_DIR)/play.wasm: $(SRC_DIR)/main.go + @mkdir -p $(@D) + tinygo build -o $@ $< +# cd $(SRC_DIR); GOOS=js GOARCH=wasm go build -o $(ROOT_DIR)/$@ $$(basename "$<") + +$(DST_DIR)/play.css: $(SRC_DIR)/play.css + mkdir -p $(@D) + cp $< $@ + +$(DST_DIR)/index.html: $(SRC_DIR)/index.html + mkdir -p $(@D) + cp $< $@ + +$(DST_DIR)/wasm_exec.js: $(WASM_EXEC) + mkdir -p $(@D) + cp $< $@ + +.PHONY: run +run: playground + python3 -m http.server --directory $(DST_DIR) + +.PHONY: brew-lint-depends # Install linting tools from Homebrew +brew-lint-depends: + brew install golangci-lint + +.PHONY: debian-lint-depends # Install linting tools on Debian +debian-lint-depends: + curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sudo sh -s -- -b /usr/bin v1.59.0 + +.PHONY: lint # Lint the project +lint: .pre-commit-config.yaml + @pre-commit run --show-diff-on-failure --color=always --all-files + +.PHONY: golangci-lint # Run golangci-lint +golangci-lint: .golangci.yaml + golangci-lint run --fix --timeout=5m + +.PHONY: clean +clean: + rm -rf $(DST_DIR) diff --git a/README.md b/README.md new file mode 100644 index 0000000..4ea3988 --- /dev/null +++ b/README.md @@ -0,0 +1,65 @@ +Go RFC 9535 JSONPath Playground +=============================== + +The source for the [Go JSONPath Playground], a stateless single-page web site +for experimenting with the [Go RFC 9535 JSONPath] package. Compiled via +[TinyGo] into a ca. 3MB [Wasm] file and loaded directly into the page. All +functionality implemented in JavaScript and Go, heavily borrowed from the +[Goldmark Playground] and [serde_json_path Sandbox]. + +Usage +----- + +Write a JSONPath query in the input field and hit the "Run Query" button to +select values from the JSON in the "JSON Input" field. The results will appear +in the "Query Output" field. + +That's it. + +Read on for details and additional features. + +### Options + +Select options for execution and the display of results: + +* **Pretty**: Pretty-print the the JSON result. + +### Permalink + +Hit the "Permalink" button instead of "Run Query" to reload the page with a +URL that contains the contents the JSONPath, JSON, and options and executes +the results. Copy the URL to use it for sharing. + +Note that the Playground is stateless; no data is stored except in the +Permalink URL itself (and whatever data collection GitHub injects; see its +[privacy statement] for details). + +### Path + +Input the JSONPath query to execute into this field. On load, the app will +pre-load an example query. See [RFC 9535] for details on the jsonpath +language. + +### JSON Input + +Input the JSON against which to execute the JSONPath query. May be any kind +of JSON value, including objects, arrays, and scalar values. On load, the +field will contain the JSON object used in examples from [RFC 9535]. + +## Copyright and License + +Copyright (c) 2024 David E. Wheeler. Distributed under the [MIT License]. + +Based on [Goldmark Playground] the [serde_json_path Sandbox], both distributed +under the [MIT License]. + + [Go JSONPath Playground]: https://theory.github.io/jsonpath/playground + [Go RFC 9535 JSONPath]: https://pkg.go.dev/github.com/theory/jsonpath + "pkg.go.dev: github.com/theory/jsonpath" + [Wasm]: https://webassembly.org "WebAssembly" + [TinyGo]: https://tinygo.org + [Goldmark Playground]: https://yuin.github.io/goldmark/playground + [serde_json_path Sandbox]: https://serdejsonpath.live + [privacy statement]: https://docs.github.com/en/site-policy/privacy-policies/github-general-privacy-statement + [RFC 9535]: https://www.rfc-editor.org/rfc/rfc9535.html + [MIT License]: https://opensource.org/license/mit diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..177c1e5 --- /dev/null +++ b/go.mod @@ -0,0 +1,5 @@ +module main + +go 1.22.1 + +require github.com/theory/jsonpath v0.1.1 diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..8bbb0be --- /dev/null +++ b/go.sum @@ -0,0 +1,10 @@ +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/theory/jsonpath v0.1.1 h1:bVyAVgEl/DsdgjkXbED6wxgMAnzxNegeqlZSe+Cwb4M= +github.com/theory/jsonpath v0.1.1/go.mod h1:BcMmctdhgqIJDBtdRAfXDd6ePEjHpPgKAr2+LC7IoG8= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/src/index.html b/src/index.html new file mode 100644 index 0000000..a8b9909 --- /dev/null +++ b/src/index.html @@ -0,0 +1,209 @@ + + + + + Go JSONPath Playground + + + + + + + +
+

Go JSONPath Playground

+ +
+
+ About +

+ + + + + About +

+

This is the playground for github.com/theory/jsonpath, a Go package that executes RFC 9535 JSONPath queries to select values from arbitrary JSON data.

+

The playground runs entirely in the browser thanks to TinyGo, which compiles the github.com/theory/jsonpath package into Web Assembly.

+

Learn more about JSONPath by reading the IETF JSONPath Standard, and more about github.com/theory/jsonpath by following the links at the bottom of the page. The code for this website can be found on GitHub. +

+
+ +
+
+ + + +
+
+
+ +
+
+ + + +
+
+ + + + diff --git a/src/main.go b/src/main.go new file mode 100644 index 0000000..5b4d6ab --- /dev/null +++ b/src/main.go @@ -0,0 +1,63 @@ +package main + +import ( + "bytes" + "encoding/json" + "fmt" + + //nolint + "syscall/js" + + "github.com/theory/jsonpath" +) + +const ( + optIndent int = 1 << iota +) + +func main() { + c := make(chan struct{}, 0) + + js.Global().Set("query", js.FuncOf(query)) + js.Global().Set("optIndent", js.ValueOf(optIndent)) + + <-c +} + +func query(_ js.Value, args []js.Value) any { + query := args[0].String() + target := args[1].String() + opts := args[2].Int() + + return execute(query, target, opts) +} + +func execute(query, target string, opts int) string { + // Parse the JSON. + var value any + if err := json.Unmarshal([]byte(target), &value); err != nil { + return fmt.Sprintf("Error parsing JSON: %v", err) + } + + // Parse the SQL jsonpath query. + p, err := jsonpath.Parse(query) + if err != nil { + return fmt.Sprintf("Error parsing %v", err) + } + + // Execute the query against the JSON. + res := p.Select(value) + + // Serialize the result + var buf bytes.Buffer + enc := json.NewEncoder(&buf) + enc.SetEscapeHTML(false) + if opts&optIndent == optIndent { + enc.SetIndent("", " ") + } + if err := enc.Encode(res); err != nil { + return fmt.Sprintf("Error parsing results: %v", err) + } + + return buf.String() +} diff --git a/src/play.css b/src/play.css new file mode 100644 index 0000000..ade2ddb --- /dev/null +++ b/src/play.css @@ -0,0 +1,368 @@ +@media (prefers-color-scheme: dark) { + body { + color-scheme: dark; + --color-fg-default: #c9d1d9; + --color-fg-muted: #8b949e; + --color-fg-subtle: #484f58; + --color-canvas-default: #0d1117; + --color-canvas-subtle: #161b22; + --color-border-default: #30363d; + --color-border-muted: #21262d; + --color-neutral-muted: rgba(110,118,129,0.4); + --color-accent-fg: #58a6ff; + --color-accent-emphasis: #1f6feb; + --color-attention-subtle: rgba(187,128,9,0.15); + --color-danger-fg: #f85149; + --color-input-bg: #ccc; + } +} + +@media (prefers-color-scheme: light) { + body { + /* color-scheme: light; */ + --color-fg-default: #24292f; + --color-fg-muted: #57606a; + --color-fg-subtle: #6e7781; + --color-canvas-default: #ffffff; + --color-canvas-muted: #ededed; + --color-canvas-subtle: #f6f8fa; + --color-border-default: #d0d7de; + --color-border-muted: hsla(210,18%,87%,1); + --color-neutral-muted: rgba(175,184,193,0.2); + --color-accent-fg: #0969da; + --color-strong-fg: #118a7e; + --color-accent-emphasis: #0969da; + --color-attention-subtle: #fff8c5; + --color-danger-fg: #cf222e; + --color-button-lite: #bbb; + --color-button-lite-hover: #eee; + --color-input-bg: #ccc; + } +} + +body { + -ms-text-size-adjust: 100%; + -webkit-text-size-adjust: 100%; + --font: ui-monospace,SFMono-Regular,SF Mono,Menlo,Consolas,Liberation Mono,monospace; + --color-button: #eee; + --color-button-bg: #0d6efd; + --color-button-hover-bg: #0b5ed7; + --color-button-disabled-bg: #485871; margin: 2em; + color: var(--color-fg-default); + background-color: var(--color-canvas-subtle); + font-family: var(--font); + line-height: 1.5; + word-wrap: break-word; +} + +body [hidden] { + display: none !important; +} + +body a { + background-color: transparent; + color: var(--color-accent-fg); + text-decoration: underline; + text-underline-offset: 3px; +} + +body a:active, +body a:hover { + outline-width: 0; +} + +body a.disable{ + pointer-events: none; + color:var(--color-fg-subtle); +} + +body b, +body strong { + font-weight: 600; + color: var(--color-strong-fg); +} + +body dfn { + font-style: italic; +} + +body h1 { + margin: .67em 0; + font-weight: 600; + font-size: 2em; +} + +body header { + width: 100%; + display:flex; + flex-wrap: wrap; + align-items: center; + padding-bottom: .3em; + color: var(--color-strong-fg); +} + +body header h1 { + flex-direction: column; + width: 100%; + flex: 1 0; +} + +body header button { + flex: 0 0 auto; + padding: .5rem; + color: var(--color-button-lite); + background-color: var(--color-canvas-subtle); + border: 1px solid var(--color-button-lite); + border-radius: .5rem; +} + +body header button:hover { + background-color: var(--color-button-lite-hover); +} + +body hr { + box-sizing: content-box; + overflow: hidden; + background: transparent; + border-bottom: 1px solid var(--color-border-muted); + height: .25em; + padding: 0; + margin: 24px 0; + background-color: var(--color-border-default); + border: 0; +} + +body input, body button { + font: inherit; + margin: 0; + overflow: scroll; + font-family: inherit; + font-size: inherit; + line-height: inherit; +} + +body h1, +body h2, +body h3, +body h4, +body h5, +body h6 { + margin-top: 24px; + margin-bottom: 16px; + font-weight: 400; + line-height: 1.25; +} + +body h2 { + font-weight: 400; + padding-bottom: .3em; + font-size: 1.5em; +} + +body h3 { + font-weight: 400; + font-size: 1.25em; +} + +body h4 { + font-weight: 400; + font-size: 1em; +} + +body h5 { + font-weight: 400; + font-size: .875em; +} + +body h6 { + font-weight: 400; + font-size: .85em; + color: var(--color-fg-muted); +} + +body p { + margin-top: 0; + margin-bottom: 10px; +} + +body tt, +body code { + font-weight: 200; +} + +body textarea, body input { + font-family: var(--font); + font-size: inherit; + width:100%; +} + +body ::placeholder { + color: var(--color-fg-subtle); + opacity: 1; +} + +body p, +body blockquote, +body ul, +body ol, +body dl, +body table, +body pre, +body details { + margin-top: 0; + margin-bottom: 16px; +} + +@media screen and (min-width: 768px) { + #container { + display: flex; + flex-direction: row; + } + + #jsonpath { + flex-direction: column; + padding-top: 1em; + width: 50%; + } + + label { + font-weight: bold; + } + + #output { + flex-direction: column; + width: 50%; + height: 100%; + padding: 1em; + } +} + +@media screen and (max-width: 768px) { + #container { + display: flex; + flex-direction: column; + } + + #jsonpath { + flex-direction: column; + width: 100%; + } + + #output { + flex-direction: column; + width: 100%; + } +} + +#jsonpath div { + margin-bottom: 1em; +} + +#clear { + background-color: transparent; + color: var(--color-accent-fg); + text-decoration: none; + cursor: pointer; +} + +#clear:hover { + text-decoration: underline; +} + +#fluid { + width: 100%; + display:flex; + flex-wrap: wrap; +} + +#path { + width: 100%; + flex: 1 0; + padding-left: .5rem; + line-height: 1.5rem; + border: 1px solid var(--color-input-bg); + border-radius: .5rem; +} + +.btn { + flex: 0 0 auto; + line-height: 1.5rem; + margin-left: .5rem; + font-size: inherit; + padding: 0 1rem; + color: var(--color-button); + background-color: var(--color-button-bg); + border: 1px solid var(--color-button-bg); + border-radius: .5rem; +} + +.btn:hover { + background-color: var(--color-button-hover-bg) +} + +.btn:disabled { + background-color: var(--color-disabled-hover-bg) +} + +#message .warn { + padding-top: 1em; + color: var(--color-danger-fg); +} + +.nowrap { + white-space: nowrap; +} + +#actions > *, fieldset input { + margin-right: 0.5em; +} + +#optIndent { + width: auto; +} + +label[for=optIndent] { + float: right; +} + +#container textarea { + margin-top: .3rem; + background-color: var(--color-canvas-subtle); + border: 1px solid var(--color-input-bg); + padding: .5rem; + border-radius: .5rem; +} + +footer { + margin: 1rem; +} + +footer > * { + margin: 1rem 0; + display: flex; + align-items: center; + justify-content: center; +} + +footer > p { + white-space: pre-wrap; +} + +#refs a { + margin: 0 .3rem; +} + +#about[open] { + background-color: var(--color-canvas-muted); + padding: .75rem; + border: 1px solid var(--color-input-bg); + border-radius: .5rem; +} + +#about h2 { + margin-top: 0; +} + +#about summary { + display: none; +} From 5e010c8ca525eea1bfc2341639796c457e785c0b Mon Sep 17 00:00:00 2001 From: "David E. Wheeler" Date: Mon, 28 Oct 2024 10:31:05 -0400 Subject: [PATCH 02/39] Add LICENSE --- .gitignore | 2 ++ LICENSE | 21 +++++++++++++++++++++ 2 files changed, 23 insertions(+) create mode 100644 LICENSE diff --git a/.gitignore b/.gitignore index 1f0b939..928d96a 100644 --- a/.gitignore +++ b/.gitignore @@ -25,6 +25,8 @@ go.work # Build artifacts pub/ +_build/ +jsonpath-compliance-test-suite/ # OS Stuff .DS_Store diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..b4d2f21 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright 2024 David E. Wheeler + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the “Software”), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. From 794e61cb22dd0df8cbcb05ddeb25935f7abc50a4 Mon Sep 17 00:00:00 2001 From: "David E. Wheeler" Date: Mon, 28 Oct 2024 10:48:15 -0400 Subject: [PATCH 03/39] Upgrade jsonpath, remove debugging The v0.1.2 github.com/theory/jsonpath release fixes TinyGo support, so upgrade to it. Then modify the WASM compile to eliminate debugging symbols, reducing the WASM file size from ca 3MB to 641K. While at it, don't execute the query path on load; wait till the button is pressed. --- Makefile | 2 +- README.md | 2 +- go.mod | 4 ++-- go.sum | 4 ++-- src/index.html | 4 +--- 5 files changed, 7 insertions(+), 9 deletions(-) diff --git a/Makefile b/Makefile index 8d7f322..dfb9fff 100644 --- a/Makefile +++ b/Makefile @@ -10,7 +10,7 @@ playground: $(DST_DIR)/play.wasm $(DST_DIR)/index.html $(DST_DIR)/wasm_exec.js $ ROOT_DIR := $(dir $(realpath $(lastword $(MAKEFILE_LIST)))) $(DST_DIR)/play.wasm: $(SRC_DIR)/main.go @mkdir -p $(@D) - tinygo build -o $@ $< + GOOS=js GOARCH=wasm tinygo build -no-debug -size short -o $@ $< # cd $(SRC_DIR); GOOS=js GOARCH=wasm go build -o $(ROOT_DIR)/$@ $$(basename "$<") $(DST_DIR)/play.css: $(SRC_DIR)/play.css diff --git a/README.md b/README.md index 4ea3988..8b7f3c7 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ Go RFC 9535 JSONPath Playground The source for the [Go JSONPath Playground], a stateless single-page web site for experimenting with the [Go RFC 9535 JSONPath] package. Compiled via -[TinyGo] into a ca. 3MB [Wasm] file and loaded directly into the page. All +[TinyGo] into a ca. 640K [Wasm] file and loaded directly into the page. All functionality implemented in JavaScript and Go, heavily borrowed from the [Goldmark Playground] and [serde_json_path Sandbox]. diff --git a/go.mod b/go.mod index 177c1e5..7ba8d1f 100644 --- a/go.mod +++ b/go.mod @@ -1,5 +1,5 @@ module main -go 1.22.1 +go 1.22 -require github.com/theory/jsonpath v0.1.1 +require github.com/theory/jsonpath v0.1.2 diff --git a/go.sum b/go.sum index 8bbb0be..865c786 100644 --- a/go.sum +++ b/go.sum @@ -4,7 +4,7 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= -github.com/theory/jsonpath v0.1.1 h1:bVyAVgEl/DsdgjkXbED6wxgMAnzxNegeqlZSe+Cwb4M= -github.com/theory/jsonpath v0.1.1/go.mod h1:BcMmctdhgqIJDBtdRAfXDd6ePEjHpPgKAr2+LC7IoG8= +github.com/theory/jsonpath v0.1.2 h1:V0xNSmgrJxvgPk6rMxignjoZn8WeusP1cYDslVX5jYo= +github.com/theory/jsonpath v0.1.2/go.mod h1:BcMmctdhgqIJDBtdRAfXDd6ePEjHpPgKAr2+LC7IoG8= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/src/index.html b/src/index.html index a8b9909..fd59e98 100644 --- a/src/index.html +++ b/src/index.html @@ -125,8 +125,6 @@ ? decodeURIComponent(u.searchParams.get("j")) : JSON.stringify(EXAMPLE_JSON, null, 2); fit(json); - - execute() }; if ('instantiateStreaming' in WebAssembly) { @@ -187,7 +185,7 @@

Pretty - +
From a4e595393eb05324ab59b8e6501dee0abf74ba88 Mon Sep 17 00:00:00 2001 From: "David E. Wheeler" Date: Mon, 28 Oct 2024 11:11:04 -0400 Subject: [PATCH 04/39] Add GitHub action to publish playground --- .github/workflows/deploy.yml | 38 ++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 .github/workflows/deploy.yml diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml new file mode 100644 index 0000000..95f460d --- /dev/null +++ b/.github/workflows/deploy.yml @@ -0,0 +1,38 @@ +name: 🛝 Deploy Playground +on: + push: + # branches: [main] + +jobs: + build: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Setup Go + uses: actions/setup-go@v5 + with: { go-version: "1.23", check-latest: true } + - name: Setup TinyGo + uses: acifani/setup-tinygo@v2 + with: { tinygo-version: 0.34.0 } + - name: Generate App + run: make playground + - name: Upload Artifact + uses: actions/upload-pages-artifact@v3 + with: { path: ./pub } + + deploy: + needs: build + permissions: + pages: write + id-token: write + concurrency: + group: "pages" + cancel-in-progress: false + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + runs-on: ubuntu-latest + steps: + - id: deployment + uses: actions/deploy-pages@v4 From 6719d60f06ac7c5a1e9b179684910aed18edb560 Mon Sep 17 00:00:00 2001 From: "David E. Wheeler" Date: Mon, 28 Oct 2024 11:20:20 -0400 Subject: [PATCH 05/39] Adjust Wasm size in README.md Seems to be smaller on GitHub pages than on my Mac. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 8b7f3c7..fcafab9 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ Go RFC 9535 JSONPath Playground The source for the [Go JSONPath Playground], a stateless single-page web site for experimenting with the [Go RFC 9535 JSONPath] package. Compiled via -[TinyGo] into a ca. 640K [Wasm] file and loaded directly into the page. All +[TinyGo] into a ca. 254K [Wasm] file and loaded directly into the page. All functionality implemented in JavaScript and Go, heavily borrowed from the [Goldmark Playground] and [serde_json_path Sandbox]. From 8f2a8fa4450d3f30a9b68565091b6488ba378149 Mon Sep 17 00:00:00 2001 From: "David E. Wheeler" Date: Wed, 30 Oct 2024 12:33:33 -0400 Subject: [PATCH 06/39] Add cheat sheet and format HTML --- src/index.html | 220 ++++++++++++++++++++++++++++++++++++++----------- src/play.css | 33 ++++++-- 2 files changed, 195 insertions(+), 58 deletions(-) diff --git a/src/index.html b/src/index.html index fd59e98..a7cd5b7 100644 --- a/src/index.html +++ b/src/index.html @@ -7,7 +7,7 @@ @@ -147,31 +148,137 @@

Go JSONPath Playground

- +
+ + +
About

- + + About

-

This is the playground for github.com/theory/jsonpath, a Go package that executes RFC 9535 JSONPath queries to select values from arbitrary JSON data.

-

The playground runs entirely in the browser thanks to TinyGo, which compiles the github.com/theory/jsonpath package into Web Assembly.

-

Learn more about JSONPath by reading the IETF JSONPath Standard, and more about github.com/theory/jsonpath by following the links at the bottom of the page. The code for this website can be found on GitHub. +

This is the playground for github.com/theory/jsonpath, a Go package that executes RFC 9535 JSONPath queries to select values from arbitrary + JSON data.

+

The playground runs entirely in the browser thanks to TinyGo, which compiles the + github.com/theory/jsonpath package into Web Assembly.

+

Learn more about JSONPath by reading the IETF JSONPath + Standard, and more about github.com/theory/jsonpath by following the links at the bottom of + the page. The code for this website can be found on + GitHub. +

+
+ JSONPath Expressions +

+ + + + + JSONPath Expressions +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Syntax ElementDescription
$root node identifier
@current node identifier (valid only within filter selectors)
[<selectors>]child segment: selects zero or more children of a node
.nameshorthand for ['name']
.*shorthand for [*]
..[<selectors>]descendant segment: selects zero or more descendants of a node
..nameshorthand for ..['name']
..*shorthand for ..[*]
'name' or "name"name selector: selects a named child of an object
*wildcard selector: selects all children of a node
3index selector: selects an indexed child of an array (from 0)
0:100:5array slice selector: start:end:step for arrays
?<logical-expr>filter selector: selects particular children using a logical expression
length(@.foo)function extension: invokes a function in a filter expression
- + - +
@@ -192,16 +299,31 @@

Queries powered by the Go github.com/theory/jsonpath package:

-

+ diff --git a/src/play.css b/src/play.css index ade2ddb..dfe5e7f 100644 --- a/src/play.css +++ b/src/play.css @@ -132,7 +132,7 @@ body hr { border: 0; } -body input, body button { +body input, body button, body table { font: inherit; margin: 0; overflow: scroll; @@ -185,12 +185,7 @@ body p { margin-bottom: 10px; } -body tt, -body code { - font-weight: 200; -} - -body textarea, body input { +body textarea, body input, body tt, body code { font-family: var(--font); font-size: inherit; width:100%; @@ -352,7 +347,7 @@ footer > p { margin: 0 .3rem; } -#about[open] { +details[open] { background-color: var(--color-canvas-muted); padding: .75rem; border: 1px solid var(--color-input-bg); @@ -363,6 +358,26 @@ footer > p { margin-top: 0; } -#about summary { +details summary { display: none; } + +body table { + width: 100%; + border-collapse: collapse; +} + +body thead th { + border-bottom: 1px solid var(--color-fg-default); + padding-bottom: .5rem; + font-weight: bold; +} + +body tbody td { + border-bottom: 1px solid var(--color-border-default); + padding: .25rem; +} + +body tbody tr td:first-child{ + font-weight: bold; +} From a796f89a02ffdd0f77df3024c6be55820c3aa8d0 Mon Sep 17 00:00:00 2001 From: "David E. Wheeler" Date: Thu, 31 Oct 2024 11:31:10 -0400 Subject: [PATCH 07/39] Fix submit buttons on mobile devices --- src/index.html | 8 +++++--- src/play.css | 30 +++++++++++++++++++++++++----- 2 files changed, 30 insertions(+), 8 deletions(-) diff --git a/src/index.html b/src/index.html index a7cd5b7..2f83e0c 100644 --- a/src/index.html +++ b/src/index.html @@ -276,9 +276,11 @@

- - +
+ + +
diff --git a/src/play.css b/src/play.css index dfe5e7f..710b47a 100644 --- a/src/play.css +++ b/src/play.css @@ -228,7 +228,7 @@ body details { flex-direction: column; width: 50%; height: 100%; - padding: 1em; + padding: 1rem 0 .5rem 1rem; } } @@ -279,8 +279,11 @@ body details { border-radius: .5rem; } -.btn { - flex: 0 0 auto; +#go { + display: flex; +} + +#go button { line-height: 1.5rem; margin-left: .5rem; font-size: inherit; @@ -291,14 +294,31 @@ body details { border-radius: .5rem; } -.btn:hover { +#go button:hover { background-color: var(--color-button-hover-bg) } -.btn:disabled { +#go button:disabled { background-color: var(--color-disabled-hover-bg) } +@media screen and (max-width:576px) { + #fluid { + flex-flow: column; + } + #go { + padding: .5rem 0; + flex: 0 0 auto; + height: 2.5rem; + } + #go button { + flex: 1 0; + } + #go button:first-child { + margin: 0; + } +} + #message .warn { padding-top: 1em; color: var(--color-danger-fg); From 9aece2f664c4ff97318e9117da1a16c57e67d8f8 Mon Sep 17 00:00:00 2001 From: "David E. Wheeler" Date: Thu, 31 Oct 2024 11:40:34 -0400 Subject: [PATCH 08/39] Fix input/output spacing on narrow device --- src/index.html | 2 +- src/play.css | 14 +++++--------- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/src/index.html b/src/index.html index 2f83e0c..dc38ec4 100644 --- a/src/index.html +++ b/src/index.html @@ -283,7 +283,7 @@

-
+
diff --git a/src/play.css b/src/play.css index 710b47a..ec6c322 100644 --- a/src/play.css +++ b/src/play.css @@ -214,7 +214,7 @@ body details { flex-direction: row; } - #jsonpath { + #input { flex-direction: column; padding-top: 1em; width: 50%; @@ -238,18 +238,14 @@ body details { flex-direction: column; } - #jsonpath { - flex-direction: column; - width: 100%; - } - - #output { + #input, #output { flex-direction: column; width: 100%; + padding: 1rem 0; } } -#jsonpath div { +#input div { margin-bottom: 1em; } @@ -307,7 +303,7 @@ body details { flex-flow: column; } #go { - padding: .5rem 0; + padding-top: .5rem; flex: 0 0 auto; height: 2.5rem; } From 3f4d072405d5d22683d1801d4ff7805a026c9280 Mon Sep 17 00:00:00 2001 From: "David E. Wheeler" Date: Thu, 31 Oct 2024 11:51:44 -0400 Subject: [PATCH 09/39] Fix footer centering --- src/play.css | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/play.css b/src/play.css index ec6c322..a4b0230 100644 --- a/src/play.css +++ b/src/play.css @@ -346,21 +346,20 @@ label[for=optIndent] { footer { margin: 1rem; -} - -footer > * { - margin: 1rem 0; display: flex; - align-items: center; + flex-direction: row; + flex-wrap: wrap; justify-content: center; + align-items: center; + text-align: center; } -footer > p { - white-space: pre-wrap; +footer > * { + width: 100%; } #refs a { - margin: 0 .3rem; + text-decoration: none; } details[open] { From cc4c85051f9663e2437742b714793b698979da05 Mon Sep 17 00:00:00 2001 From: "David E. Wheeler" Date: Thu, 31 Oct 2024 12:03:14 -0400 Subject: [PATCH 10/39] Limit textarea heights --- src/play.css | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/play.css b/src/play.css index a4b0230..f27f800 100644 --- a/src/play.css +++ b/src/play.css @@ -214,6 +214,10 @@ body details { flex-direction: row; } + #container textarea { + max-height: 70vh !important; + } + #input { flex-direction: column; padding-top: 1em; @@ -342,6 +346,7 @@ label[for=optIndent] { border: 1px solid var(--color-input-bg); padding: .5rem; border-radius: .5rem; + max-height: 40vh; } footer { From 2690136b2107a5968e0501a884f37ade6193c0c4 Mon Sep 17 00:00:00 2001 From: "David E. Wheeler" Date: Fri, 1 Nov 2024 09:50:27 -0400 Subject: [PATCH 11/39] Update dark mode and align table text --- src/play.css | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/play.css b/src/play.css index f27f800..9bdefb8 100644 --- a/src/play.css +++ b/src/play.css @@ -5,15 +5,19 @@ --color-fg-muted: #8b949e; --color-fg-subtle: #484f58; --color-canvas-default: #0d1117; - --color-canvas-subtle: #161b22; + --color-canvas-subtle: #24292f; + --color-canvas-muted: #161c1c; --color-border-default: #30363d; --color-border-muted: #21262d; --color-neutral-muted: rgba(110,118,129,0.4); --color-accent-fg: #58a6ff; + --color-strong-fg: #118a7e; --color-accent-emphasis: #1f6feb; --color-attention-subtle: rgba(187,128,9,0.15); --color-danger-fg: #f85149; - --color-input-bg: #ccc; + --color-button-lite: #666; + --color-button-lite-hover: #161c1c; + --color-input-bg: #555; } } @@ -154,6 +158,7 @@ body h6 { } body h2 { + color: var(--color-strong-fg); font-weight: 400; padding-bottom: .3em; font-size: 1.5em; @@ -393,6 +398,10 @@ body thead th { font-weight: bold; } +td, th { + vertical-align:top; +} + body tbody td { border-bottom: 1px solid var(--color-border-default); padding: .25rem; From 59e100238e3d4be8de99736d8ff38ec3638209ea Mon Sep 17 00:00:00 2001 From: "David E. Wheeler" Date: Fri, 1 Nov 2024 11:00:01 -0400 Subject: [PATCH 12/39] Style noscript message, update and credit icons --- README.md | 5 +++-- src/index.html | 47 ++++++++++++++++------------------------------- src/play.css | 4 ++-- 3 files changed, 21 insertions(+), 35 deletions(-) diff --git a/README.md b/README.md index fcafab9..849b30e 100644 --- a/README.md +++ b/README.md @@ -50,8 +50,8 @@ field will contain the JSON object used in examples from [RFC 9535]. Copyright (c) 2024 David E. Wheeler. Distributed under the [MIT License]. -Based on [Goldmark Playground] the [serde_json_path Sandbox], both distributed -under the [MIT License]. +Based on [Goldmark Playground] the [serde_json_path Sandbox], with icons from +[Boxicons], all distributed under the [MIT License]. [Go JSONPath Playground]: https://theory.github.io/jsonpath/playground [Go RFC 9535 JSONPath]: https://pkg.go.dev/github.com/theory/jsonpath @@ -63,3 +63,4 @@ under the [MIT License]. [privacy statement]: https://docs.github.com/en/site-policy/privacy-policies/github-general-privacy-statement [RFC 9535]: https://www.rfc-editor.org/rfc/rfc9535.html [MIT License]: https://opensource.org/license/mit + [Boxicons]: https://boxicons.com diff --git a/src/index.html b/src/index.html index dc38ec4..9a1ac1b 100644 --- a/src/index.html +++ b/src/index.html @@ -150,35 +150,17 @@

Go JSONPath Playground

About

- - - - - + About

This is the playground for github.com/theory/jsonpath, a Go package that executes

JSONPath Expressions

- - - - + JSONPath Expressions

@@ -271,9 +247,18 @@

-
- -
+
diff --git a/src/play.css b/src/play.css index 9bdefb8..46c263f 100644 --- a/src/play.css +++ b/src/play.css @@ -208,7 +208,7 @@ body ol, body dl, body table, body pre, -body details { +body details, .warn { margin-top: 0; margin-bottom: 16px; } @@ -372,7 +372,7 @@ footer > * { text-decoration: none; } -details[open] { +details[open], .warn { background-color: var(--color-canvas-muted); padding: .75rem; border: 1px solid var(--color-input-bg); From d50c235049d71c7bf5b4c9688c88b318e59cd9f8 Mon Sep 17 00:00:00 2001 From: "David E. Wheeler" Date: Sat, 2 Nov 2024 22:35:49 -0400 Subject: [PATCH 13/39] Fix JS error --- src/index.html | 1 - src/play.css | 5 ----- 2 files changed, 6 deletions(-) diff --git a/src/index.html b/src/index.html index 9a1ac1b..d1c143c 100644 --- a/src/index.html +++ b/src/index.html @@ -84,7 +84,6 @@ }; const execute = function () { - $('message').innerText = ""; const options = getOptions(); const outputElm = $$('#values'); outputElm.value = query($('path').value, $('json').value, options); diff --git a/src/play.css b/src/play.css index 46c263f..eb173e4 100644 --- a/src/play.css +++ b/src/play.css @@ -324,11 +324,6 @@ body details, .warn { } } -#message .warn { - padding-top: 1em; - color: var(--color-danger-fg); -} - .nowrap { white-space: nowrap; } From 46de7ca2dc09b4e1248d72d8eed8685b2c210449 Mon Sep 17 00:00:00 2001 From: "David E. Wheeler" Date: Sun, 3 Nov 2024 11:32:23 -0500 Subject: [PATCH 14/39] Make path background color consistent It was correct on macOS and wrong on iOS, but not explicitly set. So set it explicitly, and fix the name of the variable used for border colors. --- src/play.css | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/play.css b/src/play.css index eb173e4..5569f01 100644 --- a/src/play.css +++ b/src/play.css @@ -17,7 +17,8 @@ --color-danger-fg: #f85149; --color-button-lite: #666; --color-button-lite-hover: #161c1c; - --color-input-bg: #555; + --color-input-bg: #1e1e1e; + --color-input-border: #555; } } @@ -27,7 +28,7 @@ --color-fg-default: #24292f; --color-fg-muted: #57606a; --color-fg-subtle: #6e7781; - --color-canvas-default: #ffffff; + --color-canvas-default: #fff; --color-canvas-muted: #ededed; --color-canvas-subtle: #f6f8fa; --color-border-default: #d0d7de; @@ -40,7 +41,8 @@ --color-danger-fg: #cf222e; --color-button-lite: #bbb; --color-button-lite-hover: #eee; - --color-input-bg: #ccc; + --color-input-bg: #fff; + --color-input-border: #ccc; } } @@ -279,8 +281,8 @@ body details, .warn { width: 100%; flex: 1 0; padding-left: .5rem; - line-height: 1.5rem; - border: 1px solid var(--color-input-bg); + background-color: var(--color-input-bg); + border: 1px solid var(--color-input-border); border-radius: .5rem; } @@ -343,7 +345,7 @@ label[for=optIndent] { #container textarea { margin-top: .3rem; background-color: var(--color-canvas-subtle); - border: 1px solid var(--color-input-bg); + border: 1px solid var(--color-input-border); padding: .5rem; border-radius: .5rem; max-height: 40vh; @@ -370,7 +372,7 @@ footer > * { details[open], .warn { background-color: var(--color-canvas-muted); padding: .75rem; - border: 1px solid var(--color-input-bg); + border: 1px solid var(--color-input-border); border-radius: .5rem; } From b631c9bce9900bd2995e044b3b0585bdc4a19ae6 Mon Sep 17 00:00:00 2001 From: "David E. Wheeler" Date: Sun, 3 Nov 2024 12:43:27 -0500 Subject: [PATCH 15/39] Clean up SVGs a bit --- src/index.html | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/index.html b/src/index.html index d1c143c..9f7959a 100644 --- a/src/index.html +++ b/src/index.html @@ -149,17 +149,17 @@

Go JSONPath Playground

About

- + About

This is the playground for github.com/theory/jsonpath, a Go package that executes

JSONPath Expressions

- + JSONPath Expressions

@@ -249,7 +249,7 @@