├── .github └── workflows │ └── pages.yaml ├── .gitignore ├── .node-version ├── Dockerfile ├── README.md ├── Taskfile.yaml ├── assets └── demo.png ├── cmd ├── goggle │ └── main.go ├── indexer │ └── main.go ├── mirror │ └── main.go └── repl │ └── main.go ├── frontend ├── index.html ├── package.json ├── src │ ├── App.tsx │ ├── Bar.tsx │ ├── Search.tsx │ ├── goggle.ts │ ├── index.css │ ├── main.tsx │ ├── syntaxck.d.ts │ ├── theme.ts │ ├── vite-env.d.ts │ └── wasm.ts ├── tsconfig.json ├── tsconfig.node.json └── vite.config.ts ├── go.mod ├── go.sum ├── pkg ├── core │ ├── lib.go │ ├── marshal.go │ ├── signature.go │ ├── sim.go │ └── types.go ├── eval │ └── lib.go ├── goggle │ └── lib.go ├── index │ ├── README.md │ ├── indexer.go │ ├── lib.go │ ├── marshal.go │ ├── mirror.go │ ├── mirror_test.go │ └── types.go └── query │ └── query.go ├── pnpm-lock.yaml ├── pnpm-workspace.yaml └── wasm └── syntaxck └── main.go /.github/workflows/pages.yaml: -------------------------------------------------------------------------------- 1 | name: Deploy frontend 2 | on: 3 | push: 4 | branches: 5 | - main 6 | workflow_dispatch: 7 | concurrency: 8 | group: pages 9 | cancel-in-progress: true 10 | jobs: 11 | build: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - name: Checkout 15 | uses: actions/checkout@v4 16 | - name: Install Task 17 | uses: arduino/setup-task@v1 18 | - name: Setup pnpm 19 | uses: pnpm/action-setup@v2.4.0 20 | with: 21 | version: 8.10.2 22 | - name: Setup Node.js environment 23 | uses: actions/setup-node@v4.0.0 24 | with: 25 | node-version-file: .node-version 26 | cache: pnpm 27 | - name: Setup Go environment 28 | uses: actions/setup-go@v5.0.0 29 | with: 30 | go-version-file: go.mod 31 | - name: Setup Binaryen 32 | uses: Aandreba/setup-binaryen@v1.0.0 33 | with: 34 | token: ${{ secrets.GITHUB_TOKEN }} 35 | - name: Install dependencies 36 | run: pnpm install 37 | - name: Compile WASM 38 | run: task wasm-exec syntaxck 39 | - name: Build frontend 40 | run: pnpm build 41 | working-directory: frontend 42 | env: 43 | NODE_ENV: production 44 | VITE_EXTERN_ENDPOINT: ${{ vars.EXTERN_ENDPOINT }} 45 | - name: Setup Pages 46 | uses: actions/configure-pages@v3 47 | - name: Upload artifact 48 | uses: actions/upload-pages-artifact@v2 49 | with: 50 | path: ./frontend/dist 51 | deploy: 52 | needs: build 53 | permissions: 54 | contents: read 55 | pages: write 56 | id-token: write 57 | environment: 58 | name: GitHub Pages 59 | url: ${{ steps.deployment.outputs.page_url }} 60 | runs-on: ubuntu-latest 61 | steps: 62 | - name: Deploy to GitHub Pages 63 | id: deployment 64 | uses: actions/deploy-pages@v3.0.1 65 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | dist-ssr 4 | *.local 5 | 6 | # Logs 7 | logs 8 | *.log 9 | npm-debug.log* 10 | yarn-debug.log* 11 | yarn-error.log* 12 | pnpm-debug.log* 13 | lerna-debug.log* 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | 26 | /goggle 27 | /ignored 28 | /index.json 29 | /index.gob 30 | *.task 31 | *.br 32 | *.wasm 33 | /frontend/src/wasm_exec.js 34 | /mirror.json 35 | 36 | *.env 37 | -------------------------------------------------------------------------------- /.node-version: -------------------------------------------------------------------------------- 1 | v20.10.0 2 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # syntax=docker/dockerfile:1 2 | 3 | FROM golang:1.21.5-alpine3.18 as indexer 4 | 5 | WORKDIR /app 6 | 7 | COPY go.mod go.sum ./ 8 | 9 | RUN ["go", "mod", "download"] 10 | 11 | COPY ./pkg ./pkg 12 | COPY ./cmd ./cmd 13 | 14 | RUN ["go", "run", "./cmd/indexer"] 15 | 16 | RUN ["go", "build", "./cmd/goggle"] 17 | 18 | FROM alpine:3.18 as runner 19 | 20 | WORKDIR /app 21 | 22 | COPY --from=indexer /app/index.gob /app/goggle ./ 23 | 24 | EXPOSE 6099 25 | 26 | CMD ["./goggle"] 27 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

Goggle 🥽

2 |

Search your api through types, with speed

3 | 4 | ## Overview 5 | 6 | Goggle is a type-directed search engine like [hoogle](https://github.com/ndmitchell/hoogle) but for [Go](https://go.dev/) 7 | 8 | ## :tada: Try Goggle now! 9 | 10 | Try Goggle for yourself! You can now visit [here](https://abiriadev.github.io/goggle/) to see Goggle in action. 11 | 12 | ## :camera: Demo 13 | 14 | ![](./assets/demo.png) 15 | 16 | ## :mag: Query 17 | 18 | You can type query to search and filter results. 19 | 20 | The most simplest form is just Go's standard function definition. 21 | 22 | ```go 23 | func length(s string) int 24 | ``` 25 | 26 | But we can omit a function name, to retrieve results whose name does not match with `length`. 27 | 28 | ```go 29 | func (s string) int 30 | ``` 31 | 32 | We can omit a `func` keyword too. 33 | 34 | ```go 35 | (s string) int 36 | ``` 37 | 38 | Finally, we can omit argument names. 39 | 40 | ```go 41 | (string) int 42 | ``` 43 | 44 | ### Query syntax definition 45 | 46 | ```ebnf 47 | Type = Primitives | SliceType | PointerType | identifier . 48 | Primitives = "bool" | Int | UInt | "float32" | "float64" | "complex64" | "complex128" . 49 | Int = "int" | "int8" | "int16" | "int32" | "int64" . 50 | UInt = "uint" | "uint8" | "uint16" | "uint32" | "uint64" | "uintptr" . 51 | 52 | SliceType = "[" "]" Type . 53 | 54 | Parameters = "(" [ Type { "," Type } ] ")" . 55 | Query = [ "func" ] [ identifier ] Parameters [ Type ] . 56 | ``` 57 | 58 | ## Build Manually 59 | 60 | ```sh 61 | $ git clone https://github.com/abiriadev/goggle && cd goggle 62 | ``` 63 | 64 | ### Build indexer from source and index custom packages 65 | 66 | ```sh 67 | $ go run ./cmd/indexer 68 | # or 69 | $ go run ./cmd/indexer 70 | ``` 71 | 72 | See help page for more information: 73 | 74 | ```sh 75 | Usage of indexer: 76 | -f string 77 | index format (default "gob") 78 | -o string 79 | path to save index file 80 | ``` 81 | 82 | ### Build and run REPL 83 | 84 | ```sh 85 | $ go run ./cmd/repl 86 | # or optionally pass a path to index file to use 87 | $ go run ./cmd/repl 88 | ``` 89 | 90 | It will then show you a prompt starting with `λ`. 91 | 92 | Type any query(like `() bool`) and enter to see the results. 93 | 94 | ```go 95 | λ () bool 96 | func utf8.FullRune() bool // FullRune reports whether the bytes in p begin with a full UTF-8 encoding of a rune. 97 | func nettest.TestableAddress() bool // TestableAddress reports whether address of network is testable on the current platform configuration. 98 | func nettest.SupportsRawSocket() bool // SupportsRawSocket reports whether the current session is available to use raw sockets. 99 | func nettest.SupportsIPv6() bool // SupportsIPv6 reports whether the platform supports IPv6 networking functionality. 100 | func nettest.SupportsIPv4() bool // SupportsIPv4 reports whether the platform supports IPv4 networking functionality. 101 | func signal.Ignored() bool // Ignored reports whether sig is currently ignored. 102 | func slices.Equal() bool // Equal reports whether two slices are equal: the same length and all elements equal. 103 | func testenv.OptimizationOff() bool // OptimizationOff reports whether optimization is disabled. 104 | func testenv.HasSymlink() bool // HasSymlink reports whether the current system can use os.Symlink. 105 | func testenv.HasSrc() bool // HasSrc reports whether the entire source tree is available under GOROOT. 106 | ``` 107 | 108 | ### Build and run Goggle server 109 | 110 | ```sh 111 | $ go run ./cmd/goggle 112 | ``` 113 | 114 | The default port number is `6099`(L33T or `Gogg`). You can pass `-port` option to change it. 115 | 116 | ```sh 117 | Usage of goggle: 118 | -port int 119 | port number to bind (default 6099) 120 | ``` 121 | 122 | Try requesting from terminal: 123 | 124 | ```sh 125 | $ http :6099/search q='() bool' -v 126 | 127 | POST /search HTTP/1.1 128 | Accept: application/json, */*;q=0.5 129 | Accept-Encoding: gzip, deflate, br 130 | Connection: keep-alive 131 | Content-Length: 15 132 | Content-Type: application/json 133 | Host: localhost:6099 134 | User-Agent: HTTPie/3.2.1 135 | 136 | { 137 | "q": "() bool" 138 | } 139 | 140 | HTTP/1.1 200 OK 141 | Access-Control-Allow-Origin: * 142 | Content-Length: 1970 143 | Content-Type: text/plain; charset=utf-8 144 | Date: Tue, 12 Dec 2023 04:12:01 GMT 145 | 146 | { 147 | "items": [ 148 | { 149 | "sim": 0, 150 | "sig": "func utf8.FullRune() bool", 151 | "summary": "FullRune reports whether the bytes in p begin with a full UTF-8 encoding of a rune.", 152 | "link": "https://pkg.go.dev/unicode/utf8#FullRune" 153 | }, 154 | { 155 | "sim": 0, 156 | "sig": "func nettest.TestableAddress() bool", 157 | "summary": "TestableAddress reports whether address of network is testable on the current platform configuration.", 158 | "link": "https://pkg.go.dev/golang.org/x/net/nettest#TestableAddress" 159 | }, 160 | ... 161 | ] 162 | } 163 | ``` 164 | 165 | ### Build and run frontend 166 | 167 | Ensure that you have [Go](https://go.dev), [Task](https://github.com/go-task/task), [Node.js](https://nodejs.org), and [Binaryen](https://github.com/WebAssembly/binaryen) installed. 168 | 169 | Then, execuate the following commands: 170 | 171 | ```sh 172 | $ task wasm-exec syntaxck 173 | $ corepack enable 174 | $ pnpm install --frozen-lockfile 175 | $ cd frontend 176 | ``` 177 | 178 | If you don't want to have local Goggle proxy, you can specify your already-deployed endpoint by setting `VITE_EXTERN_ENDPOINT` variable. 179 | 180 | ```sh 181 | $ echo 'VITE_EXTERN_ENDPOINT=' > .env.production 182 | ``` 183 | 184 | Then, run! 185 | 186 | ```sh 187 | $ pnpm dev 188 | # Or, to use an external endpoint: 189 | $ pnpm dev --mode production 190 | ``` 191 | 192 | For building the frontend for deployment or serving: 193 | 194 | ```sh 195 | $ pnpm build 196 | ``` 197 | 198 | ## :memo: TODO 199 | 200 | - [ ] Index 201 | - [x] Portable index file 202 | - [ ] Index popular packages 203 | - [ ] Incremental search 204 | - [ ] Frontend 205 | - [ ] Standalone result view 206 | - [x] Link to pkg.go.dev 207 | - [x] Brief description 208 | - [ ] Syntax hightlighting for search result 209 | - [ ] Use dedicated search bar component 210 | - [ ] Query 211 | - [ ] Compound types 212 | - [ ] Array 213 | - [ ] Slice 214 | - [ ] Pointer type 215 | - [ ] Inline struct type 216 | - [ ] Interface resolution 217 | - [ ] Method 218 | - [ ] Multiple return 219 | - [ ] Parameter collaping 220 | - [ ] Spread syntax 221 | - [ ] Generics 222 | - [ ] Constraints 223 | - [x] Levenshtein distance 224 | - [ ] Argument-level similarity comparison 225 | - [ ] Hoogle-like structured edit distance 226 | - [ ] Subtype polymorphic edit distance 227 | - [x] GHA CD automation 228 | - [ ] External tools 229 | - [x] REPL 230 | - [ ] vscode extension 231 | - [ ] neovim LSP support? 232 | 233 | ## :grinning: This is really awwwesome!! How can I help? 234 | 235 | There are many ways to support and contribute to the ongoing maintenance and improvement of Goggle. Any support is greatly appreciated! 236 | 237 | - **Spread the world.** Share Goggle with your co-workers, students, and community so that they can find it useful as well! 238 | - **Report bugs.** If you encounter any unexpected behavior or runtime panics, please open an issue to report and document them. 239 | - **Make your document cleaner.** Although Goggle can find items without documentation, it doesn't have a power to generate intuitive identifiers and descriptive summaries. So it's a good idea to document you package thoroughly to enhance developer experience. 240 | - **Suggest better idea.** Currently, Goggle's approximate search doesn't support structural edit-distance, and there are still a lot of missing features. Your suggestions for more accurate and efficient implementations are always welcome. 241 | - **Build creative tools on top of Goggle.** Goggle currently supports web search and REPL, but the possibilities for its application are limitless. Ideas like a vscode extension, LSP autocompletion, etc., could significantly expand its ecosystem. 242 | -------------------------------------------------------------------------------- /Taskfile.yaml: -------------------------------------------------------------------------------- 1 | version: 3 2 | 3 | tasks: 4 | dist: 5 | cmds: 6 | - mkdir -p ./dist 7 | wasm-exec: 8 | cmds: 9 | - cp $(go env GOROOT)/misc/wasm/wasm_exec.js ./frontend/src/wasm_exec.js 10 | generates: 11 | - ./frontend/src/wasm_exec.js 12 | syntaxck: 13 | env: 14 | GOARCH: wasm 15 | GOOS: js 16 | cmds: 17 | - go build -o ./dist/syntaxck.wasm ./wasm/syntaxck 18 | - wasm-opt ./dist/syntaxck.wasm --enable-bulk-memory -Oz -o ./dist/syntaxck.pack.wasm 19 | - mkdir -p ./frontend/src/assets/ 20 | - cp ./dist/syntaxck.pack.wasm ./frontend/src/assets/syntaxck.pack.wasm 21 | sources: 22 | - ./wasm/syntaxck/*.go 23 | generates: 24 | - ./dist/syntaxck.wasm 25 | - ./dist/syntaxck.pack.wasm 26 | - ./frontend/src/assets/syntaxck.pack.wasm 27 | -------------------------------------------------------------------------------- /assets/demo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abiriadev/goggle/5a5a6d55b6a5a1b2df9e5e8d59f4a94e701d502e/assets/demo.png -------------------------------------------------------------------------------- /cmd/goggle/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/json" 5 | "flag" 6 | "fmt" 7 | "log" 8 | "log/slog" 9 | "net/http" 10 | "strconv" 11 | 12 | "github.com/abiriadev/goggle/pkg/goggle" 13 | ) 14 | 15 | func pathArg() string { 16 | flag.Parse() 17 | f := flag.Arg(0) 18 | 19 | if f == "" { 20 | return "index.gob" 21 | } 22 | 23 | return f 24 | } 25 | 26 | type QueryRequest struct { 27 | Q string `json:"q"` 28 | } 29 | 30 | type QueryHandler struct { 31 | Goggle goggle.Goggle 32 | } 33 | 34 | func (qh *QueryHandler) Query(query string) (goggle.ResultSet, error) { 35 | return qh.Goggle.Query(query) 36 | } 37 | 38 | func (qh *QueryHandler) ServeHTTP(rw http.ResponseWriter, r *http.Request) { 39 | slog.Info("Request", "method", r.Method, "path", r.URL) 40 | 41 | rw.Header().Set("Access-Control-Allow-Origin", "*") 42 | 43 | var req QueryRequest 44 | err := json.NewDecoder(r.Body).Decode(&req) 45 | if err != nil { 46 | rw.WriteHeader(400) 47 | rw.Write([]byte(err.Error())) 48 | return 49 | } 50 | 51 | slog.Info("Query", "q", req.Q) 52 | 53 | rs, err := qh.Query(req.Q) 54 | if err != nil { 55 | rw.WriteHeader(400) 56 | rw.Write([]byte(err.Error())) 57 | } 58 | 59 | slog.Info("ResultSet", "items found", len(rs.Results)) 60 | 61 | err = rs.MarshalJsonTo(rw) 62 | if err != nil { 63 | rw.WriteHeader(500) 64 | rw.Write([]byte(err.Error())) 65 | } 66 | } 67 | 68 | func main() { 69 | port := flag.Int("port", 6099, "port number to bind") 70 | f := pathArg() 71 | 72 | goggle, err := goggle.NewGoggle(goggle.Config{Limit: 10}) 73 | if err != nil { 74 | panic(err) 75 | } 76 | 77 | err = goggle.Load(f) 78 | if err != nil { 79 | panic(err) 80 | } 81 | 82 | qh := QueryHandler{Goggle: goggle} 83 | 84 | http.Handle("/search", &qh) 85 | 86 | slog.Info("server is listening on", "port", strconv.Itoa(*port)) 87 | log.Fatal(http.ListenAndServe(fmt.Sprintf(":%d", *port), nil)) 88 | } 89 | -------------------------------------------------------------------------------- /cmd/indexer/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | 6 | "github.com/abiriadev/goggle/pkg/index" 7 | ) 8 | 9 | func main() { 10 | indexDestFileName := flag.String("o", "", "path to save index file") 11 | format := flag.String("f", "gob", "index format") 12 | 13 | if *format != "json" && *format != "gob" { 14 | panic("invalid format: only `json` and `gob` are supported") 15 | } 16 | 17 | flag.Parse() 18 | 19 | target := flag.Args() 20 | if len(target) == 0 { 21 | target = append(target, "std") 22 | } 23 | 24 | index, err := index.NewIndexer().IndexPackages(target) 25 | if err != nil { 26 | panic(err) 27 | } 28 | 29 | if *format == "json" { 30 | if *indexDestFileName == "" { 31 | *indexDestFileName = "index.json" 32 | } 33 | 34 | err = index.SaveJson(*indexDestFileName) 35 | } else { 36 | if *indexDestFileName == "" { 37 | *indexDestFileName = "index.gob" 38 | } 39 | 40 | err = index.Save(*indexDestFileName) 41 | } 42 | 43 | if err != nil { 44 | panic(err) 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /cmd/mirror/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/json" 5 | "os" 6 | 7 | "github.com/abiriadev/goggle/pkg/index" 8 | ) 9 | 10 | func main() { 11 | m, err := index.ResolveFullIndex() 12 | if err != nil { 13 | panic(err) 14 | } 15 | 16 | f, err := os.Create("mirror.json") 17 | if err != nil { 18 | panic(err) 19 | } 20 | 21 | err = json.NewEncoder(f).Encode(m) 22 | if err != nil { 23 | panic(err) 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /cmd/repl/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | 7 | "github.com/abiriadev/goggle/pkg/goggle" 8 | "github.com/chzyer/readline" 9 | ) 10 | 11 | func pathArg() string { 12 | flag.Parse() 13 | f := flag.Arg(0) 14 | 15 | if f == "" { 16 | return "index.gob" 17 | } 18 | 19 | return f 20 | } 21 | 22 | func main() { 23 | f := pathArg() 24 | 25 | goggle, err := goggle.NewGoggle(goggle.Config{Limit: 10}) 26 | if err != nil { 27 | panic(err) 28 | } 29 | 30 | err = goggle.Load(f) 31 | if err != nil { 32 | panic(err) 33 | } 34 | 35 | rl, err := readline.New("λ ") 36 | if err != nil { 37 | panic(err) 38 | } 39 | defer rl.Close() 40 | 41 | for { 42 | line, err := rl.Readline() 43 | if err != nil { 44 | break 45 | } 46 | 47 | rs, err := goggle.Query(line) 48 | if err != nil { 49 | fmt.Println(err) 50 | } 51 | 52 | for _, ri := range rs.Results { 53 | fmt.Println(ri.SigWithComment()) 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /frontend/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | Goggle 10 | 11 | 12 |
13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /frontend/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "frontend", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "tsc && vite build", 9 | "preview": "vite preview" 10 | }, 11 | "dependencies": { 12 | "@emotion/react": "^11.11.1", 13 | "@emotion/styled": "^11.11.0", 14 | "@mui/icons-material": "^5.14.19", 15 | "@mui/material": "^5.14.20", 16 | "react": "^18.2.0", 17 | "react-dom": "^18.2.0" 18 | }, 19 | "devDependencies": { 20 | "@types/react": "^18.2.37", 21 | "@types/react-dom": "^18.2.15", 22 | "@vitejs/plugin-react-swc": "^3.5.0", 23 | "typescript": "^5.2.2", 24 | "vite": "^5.0.0" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /frontend/src/App.tsx: -------------------------------------------------------------------------------- 1 | import { Stack, Typography } from '@mui/material' 2 | import { Bar } from './Bar.tsx' 3 | import { Search } from './Search.tsx' 4 | 5 | function App() { 6 | return ( 7 | <> 8 | 9 | 15 | 22 | Goggle 23 | 24 | 25 | 26 | 27 | ) 28 | } 29 | 30 | export default App 31 | -------------------------------------------------------------------------------- /frontend/src/Bar.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | AppBar, 3 | Link, 4 | Toolbar, 5 | Typography, 6 | } from '@mui/material' 7 | import GitHubIcon from '@mui/icons-material/GitHub' 8 | 9 | export const Bar = () => ( 10 | 15 | 16 | 21 | Goggle 22 | 23 | 28 | 29 | 30 | 31 | 32 | ) 33 | -------------------------------------------------------------------------------- /frontend/src/Search.tsx: -------------------------------------------------------------------------------- 1 | import { useEffect, useState } from 'react' 2 | import { loadWasm } from './wasm' 3 | import { 4 | Autocomplete, 5 | Link, 6 | TextField, 7 | Typography, 8 | useTheme, 9 | } from '@mui/material' 10 | import './syntaxck.d.ts' 11 | import { ResultItem, query } from './goggle.ts' 12 | 13 | export const Search = () => { 14 | const theme = useTheme() 15 | const [wasmLoad, setWasmLoad] = useState(false) 16 | const [resultSet, setResultSet] = useState< 17 | Array 18 | >([]) 19 | const [inp, setInp] = useState('') 20 | 21 | useEffect(() => { 22 | ;(async () => { 23 | await loadWasm() 24 | setWasmLoad(true) 25 | })() 26 | }, []) 27 | 28 | useEffect(() => { 29 | if (!(wasmLoad && window.syntaxck(inp))) return 30 | ;(async () => { 31 | try { 32 | const rs = await query(inp) 33 | 34 | setResultSet(rs.items) 35 | } catch (e) { 36 | console.error(e) 37 | } 38 | })() 39 | }, [inp]) 40 | 41 | return ( 42 | _} 50 | getOptionLabel={o => 51 | typeof o === 'string' ? o : o.sig 52 | } 53 | componentsProps={{ 54 | popper: { 55 | modifiers: [ 56 | { name: 'flip', enabled: false }, 57 | { 58 | name: 'preventOverflow', 59 | enabled: false, 60 | }, 61 | ], 62 | }, 63 | }} 64 | onInputChange={(_, i) => setInp(i)} 65 | onChange={(_, o, r) => { 66 | if (r === 'selectOption') { 67 | window.open( 68 | typeof o === 'string' ? o : o?.link, 69 | '_blank', 70 | ) 71 | } 72 | }} 73 | renderInput={p => ( 74 | fieldset': { 78 | borderColor: 79 | theme.palette.primary 80 | .main, 81 | }, 82 | }, 83 | '& .MuiOutlinedInput-root:hover': { 84 | '& > fieldset': { 85 | borderColor: 86 | theme.palette.primary 87 | .main, 88 | }, 89 | }, 90 | }} 91 | {...p} 92 | /> 93 | )} 94 | renderOption={(p, o) => ( 95 |
  • 96 | 101 | 102 | {o.sig} 103 | 104 | 113 | {o.summary} 114 | 115 | 116 |
  • 117 | )} 118 | /> 119 | ) 120 | } 121 | -------------------------------------------------------------------------------- /frontend/src/goggle.ts: -------------------------------------------------------------------------------- 1 | export interface ResultSet { 2 | items: Array 3 | } 4 | 5 | export interface ResultItem { 6 | sim: number 7 | sig: string 8 | summary: string 9 | link: string 10 | } 11 | 12 | const host = 13 | import.meta.env.MODE === 'production' 14 | ? import.meta.env.VITE_EXTERN_ENDPOINT 15 | : '/api' 16 | 17 | console.log('host', host) 18 | console.log('mode', import.meta.env) 19 | 20 | export const query = async ( 21 | query: string, 22 | ): Promise => { 23 | const raw = await fetch(`${host}/search`, { 24 | method: 'POST', 25 | body: JSON.stringify({ q: query }), 26 | }) 27 | 28 | console.table(raw) 29 | 30 | if (!raw.ok) { 31 | throw new Error('response was not successful') 32 | } 33 | 34 | return await raw.json() 35 | } 36 | -------------------------------------------------------------------------------- /frontend/src/index.css: -------------------------------------------------------------------------------- 1 | :root { 2 | color-scheme: light dark; 3 | background-color: #202224; 4 | } 5 | 6 | body { 7 | margin: 0; 8 | } 9 | 10 | #root { 11 | width: 100vw; 12 | height: 100vh; 13 | } 14 | -------------------------------------------------------------------------------- /frontend/src/main.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom/client' 3 | import App from './App.tsx' 4 | import './index.css' 5 | import { ThemeProvider } from '@mui/material/styles' 6 | import { theme } from './theme.ts' 7 | 8 | ReactDOM.createRoot( 9 | document.getElementById('root')!, 10 | ).render( 11 | 12 | 13 | 14 | 15 | , 16 | ) 17 | -------------------------------------------------------------------------------- /frontend/src/syntaxck.d.ts: -------------------------------------------------------------------------------- 1 | declare global { 2 | export interface Window { 3 | Go: any 4 | syntaxck: (query: string) => bool 5 | } 6 | } 7 | 8 | export {} 9 | -------------------------------------------------------------------------------- /frontend/src/theme.ts: -------------------------------------------------------------------------------- 1 | import { createTheme } from '@mui/material/styles' 2 | 3 | export const theme = createTheme({ 4 | palette: { 5 | mode: 'dark', 6 | primary: { 7 | main: '#007d9c', 8 | light: '#50b7e0', 9 | }, 10 | }, 11 | }) 12 | -------------------------------------------------------------------------------- /frontend/src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | declare module '*.wasm' 4 | -------------------------------------------------------------------------------- /frontend/src/wasm.ts: -------------------------------------------------------------------------------- 1 | import './wasm_exec.js' 2 | import syntaxckPath from './assets/syntaxck.pack.wasm' 3 | 4 | export const loadWasm = async (): Promise => { 5 | const goWasm = new window.Go() 6 | const result = await WebAssembly.instantiateStreaming( 7 | fetch(syntaxckPath), 8 | goWasm.importObject, 9 | ) 10 | goWasm.run(result.instance) 11 | } 12 | -------------------------------------------------------------------------------- /frontend/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2020", 4 | "useDefineForClassFields": true, 5 | "lib": ["ES2020", "DOM", "DOM.Iterable"], 6 | "module": "ESNext", 7 | "skipLibCheck": true, 8 | 9 | /* Bundler mode */ 10 | "moduleResolution": "bundler", 11 | "allowImportingTsExtensions": true, 12 | "resolveJsonModule": true, 13 | "isolatedModules": true, 14 | "noEmit": true, 15 | "jsx": "react-jsx", 16 | 17 | /* Linting */ 18 | "strict": true, 19 | "noFallthroughCasesInSwitch": true 20 | }, 21 | "include": ["src"], 22 | "references": [{ "path": "./tsconfig.node.json" }] 23 | } 24 | -------------------------------------------------------------------------------- /frontend/tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "composite": true, 4 | "skipLibCheck": true, 5 | "module": "ESNext", 6 | "moduleResolution": "bundler", 7 | "allowSyntheticDefaultImports": true 8 | }, 9 | "include": ["vite.config.ts"] 10 | } 11 | -------------------------------------------------------------------------------- /frontend/vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import react from '@vitejs/plugin-react-swc' 3 | 4 | // https://vitejs.dev/config/ 5 | export default defineConfig(({ mode }) => ({ 6 | plugins: [react()], 7 | 8 | base: mode === 'development' ? '/' : '/goggle', 9 | 10 | server: { 11 | proxy: { 12 | '/api': { 13 | target: 'http://localhost:6099', 14 | changeOrigin: true, 15 | rewrite: p => p.replace(/^\/api/, ''), 16 | }, 17 | }, 18 | }, 19 | 20 | assetsInclude: ['**/*.wasm'], 21 | })) 22 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/abiriadev/goggle 2 | 3 | go 1.21.4 4 | 5 | require ( 6 | github.com/alecthomas/participle/v2 v2.1.1 7 | github.com/chzyer/readline v1.5.1 8 | ) 9 | 10 | require ( 11 | github.com/hbollon/go-edlib v1.6.0 // indirect 12 | github.com/kr/pretty v0.3.1 // indirect 13 | github.com/kr/text v0.2.0 // indirect 14 | github.com/repeale/fp-go v0.11.1 // indirect 15 | github.com/rogpeppe/go-internal v1.9.0 // indirect 16 | github.com/samber/mo v1.11.0 // indirect 17 | golang.org/x/mod v0.14.0 // indirect 18 | golang.org/x/sys v0.1.0 // indirect 19 | golang.org/x/tools v0.16.0 // indirect 20 | ) 21 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/alecthomas/assert/v2 v2.3.0 h1:mAsH2wmvjsuvyBvAmCtm7zFsBlb8mIHx5ySLVdDZXL0= 2 | github.com/alecthomas/assert/v2 v2.3.0/go.mod h1:pXcQ2Asjp247dahGEmsZ6ru0UVwnkhktn7S0bBDLxvQ= 3 | github.com/alecthomas/participle/v2 v2.1.1 h1:hrjKESvSqGHzRb4yW1ciisFJ4p3MGYih6icjJvbsmV8= 4 | github.com/alecthomas/participle/v2 v2.1.1/go.mod h1:Y1+hAs8DHPmc3YUFzqllV+eSQ9ljPTk0ZkPMtEdAx2c= 5 | github.com/alecthomas/repr v0.2.0 h1:HAzS41CIzNW5syS8Mf9UwXhNH1J9aix/BvDRf1Ml2Yk= 6 | github.com/alecthomas/repr v0.2.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4= 7 | github.com/chzyer/logex v1.2.1 h1:XHDu3E6q+gdHgsdTPH6ImJMIp436vR6MPtH8gP05QzM= 8 | github.com/chzyer/logex v1.2.1/go.mod h1:JLbx6lG2kDbNRFnfkgvh4eRJRPX1QCoOIWomwysCBrQ= 9 | github.com/chzyer/readline v1.5.1 h1:upd/6fQk4src78LMRzh5vItIt361/o4uq553V8B5sGI= 10 | github.com/chzyer/readline v1.5.1/go.mod h1:Eh+b79XXUwfKfcPLepksvw2tcLE/Ct21YObkaSkeBlk= 11 | github.com/chzyer/test v1.0.0 h1:p3BQDXSxOhOG0P9z6/hGnII4LGiEPOYBhs8asl/fC04= 12 | github.com/chzyer/test v1.0.0/go.mod h1:2JlltgoNkt4TW/z9V/IzDdFaMTM2JPIi26O1pF38GC8= 13 | github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= 14 | github.com/hbollon/go-edlib v1.6.0 h1:ga7AwwVIvP8mHm9GsPueC0d71cfRU/52hmPJ7Tprv4E= 15 | github.com/hbollon/go-edlib v1.6.0/go.mod h1:wnt6o6EIVEzUfgbUZY7BerzQ2uvzp354qmS2xaLkrhM= 16 | github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM= 17 | github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg= 18 | github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= 19 | github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= 20 | github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= 21 | github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= 22 | github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= 23 | github.com/repeale/fp-go v0.11.1 h1:Q/e+gNyyHaxKAyfdbBqvip3DxhVWH453R+kthvSr9Mk= 24 | github.com/repeale/fp-go v0.11.1/go.mod h1:4KrwQJB1VRY+06CA+jTc4baZetr6o2PeuqnKr5ybQUc= 25 | github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= 26 | github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= 27 | github.com/samber/mo v1.11.0 h1:ZOiSkrGGpNhVv/1dxP02risztdMTIwE8KSW9OG4k5bY= 28 | github.com/samber/mo v1.11.0/go.mod h1:BfkrCPuYzVG3ZljnZB783WIJIGk1mcZr9c9CPf8tAxs= 29 | golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0= 30 | golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= 31 | golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 32 | golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U= 33 | golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 34 | golang.org/x/tools v0.16.0 h1:GO788SKMRunPIBCXiQyo2AaexLstOrVhuAL5YwsckQM= 35 | golang.org/x/tools v0.16.0/go.mod h1:kYVVN6I1mBNoB1OX+noeBjbRk4IUEPa7JJ+TJMEooJ0= 36 | -------------------------------------------------------------------------------- /pkg/core/lib.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | import ( 4 | "github.com/samber/mo" 5 | ) 6 | 7 | type Arg struct { 8 | Name string `json:"name"` 9 | Type string `json:"type"` 10 | } 11 | 12 | type FuncDef struct { 13 | Package string `json:"pkg"` 14 | PackageMame string `json:"pkg_name"` 15 | Name string `json:"name"` 16 | Args []Arg `json:"args"` 17 | Return string `json:"ret"` 18 | Summary string `json:"sum"` 19 | Link string `json:"link"` 20 | } 21 | 22 | type MethodDef struct { 23 | Package string `json:"pkg"` 24 | PackageMame string `json:"pkg_name"` 25 | Name string `json:"name"` 26 | Receiver string `json:"recv"` 27 | Args []Arg `json:"args"` 28 | Return string `json:"ret"` 29 | Summary string `json:"sum"` 30 | Link string `json:"link"` 31 | } 32 | 33 | func (this *FuncDef) ToResult(sim Similarity) ResultItem { 34 | return ResultItem{ 35 | Similarity: sim, 36 | Sig: this.Signature(), 37 | Summary: this.Summary, 38 | Link: this.Link, 39 | } 40 | } 41 | 42 | type Item = mo.Either[FuncDef, MethodDef] 43 | 44 | type ResultItem struct { 45 | Similarity Similarity `json:"sim"` 46 | Sig string `json:"sig"` 47 | Summary string `json:"summary"` 48 | Link string `json:"link"` 49 | } 50 | 51 | type ResultSet struct { 52 | Results []ResultItem `json:"items"` 53 | } 54 | 55 | func NewResultSet() ResultSet { 56 | return ResultSet{make([]ResultItem, 0)} 57 | } 58 | -------------------------------------------------------------------------------- /pkg/core/marshal.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | import ( 4 | "encoding/json" 5 | "io" 6 | ) 7 | 8 | func (rs *ResultSet) MarshalJson() ([]byte, error) { 9 | return json.Marshal(rs) 10 | } 11 | 12 | func (rs *ResultSet) MarshalJsonTo(w io.Writer) error { 13 | return json.NewEncoder(w).Encode(rs) 14 | } 15 | -------------------------------------------------------------------------------- /pkg/core/signature.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | 7 | "github.com/repeale/fp-go" 8 | ) 9 | 10 | type ToSignature interface { 11 | Signature() string 12 | } 13 | 14 | func args(args []Arg) string { 15 | return fmt.Sprintf("(%s)", strings.Join(fp.Map(func(arg Arg) string { 16 | return fmt.Sprintf("%s %s", arg.Name, arg.Type) 17 | })(args), ", ")) 18 | } 19 | 20 | func (f *FuncDef) Signature() string { 21 | return fmt.Sprintf("func %s.%s%s %s", f.PackageMame, f.Name, args(f.Args), f.Return) 22 | } 23 | 24 | func (ri *ResultItem) Signature() string { 25 | return ri.Sig 26 | } 27 | 28 | func (ri *ResultItem) SigWithComment() string { 29 | return fmt.Sprintf("%s\t// %s", ri.Sig, ri.Summary) 30 | } 31 | -------------------------------------------------------------------------------- /pkg/core/sim.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | type Similarity float64 4 | 5 | const ( 6 | Equivalent Similarity = 0 7 | Subequal = 0.25 8 | Different = 1 9 | ) 10 | -------------------------------------------------------------------------------- /pkg/core/types.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | type Type interface { 4 | } 5 | 6 | type Array[T any] struct { 7 | t T 8 | } 9 | -------------------------------------------------------------------------------- /pkg/eval/lib.go: -------------------------------------------------------------------------------- 1 | package eval 2 | 3 | import ( 4 | "cmp" 5 | "slices" 6 | 7 | "github.com/abiriadev/goggle/pkg/core" 8 | "github.com/abiriadev/goggle/pkg/index" 9 | "github.com/abiriadev/goggle/pkg/query" 10 | "github.com/hbollon/go-edlib" 11 | ) 12 | 13 | func Lev(a, b string) core.Similarity { 14 | s, err := edlib.StringsSimilarity(a, b, edlib.Levenshtein) 15 | if err != nil { 16 | // unreachable error 17 | panic(err) 18 | } 19 | 20 | return core.Similarity(1 - s) 21 | } 22 | 23 | func AccSim(sims []core.Similarity) core.Similarity { 24 | acc := core.Equivalent 25 | 26 | for _, sim := range sims { 27 | acc += sim 28 | } 29 | 30 | return acc / core.Similarity(len(sims)) 31 | } 32 | 33 | func CombSim(a, b core.Similarity) core.Similarity { 34 | return (a + b) / 2 35 | } 36 | 37 | func EvaluateName(ident string, query string) core.Similarity { 38 | if query == "" { 39 | return core.Equivalent 40 | } else { 41 | return Lev(query, ident) 42 | } 43 | } 44 | 45 | func EvaluateArgs(args []core.Arg, query query.Query) core.Similarity { 46 | if len(query.Args) != len(args) { 47 | return core.Different 48 | } 49 | 50 | for i, arg := range query.Args { 51 | if args[i].Type != arg { 52 | return core.Different 53 | } 54 | } 55 | 56 | // TODO: qp does not support argument names yet 57 | return core.Equivalent 58 | 59 | // sarr := make([]core.Similarity, len(args)) 60 | // 61 | // for i, arg := range query.Args { 62 | // if args[i].Type != arg { 63 | // return core.Different 64 | // } 65 | // e := EvaluateName(arg, args[i].Name) 66 | // sarr = append( 67 | // sarr, 68 | // e, 69 | // ) 70 | // } 71 | // 72 | // return AccSim(sarr) 73 | } 74 | 75 | func EvaluateFunc(item *core.FuncDef, query query.Query) core.Similarity { 76 | if query.Ret == item.Return { 77 | argsSim := EvaluateArgs(item.Args, query) 78 | 79 | if argsSim == core.Different { 80 | return core.Different 81 | } 82 | 83 | namesSim := EvaluateName(item.Name, query.Name) 84 | 85 | return namesSim 86 | // return namesSim 87 | } 88 | return core.Different 89 | } 90 | 91 | // TODO: complete this function later 92 | func EvaluateMethod(item *core.MethodDef, query query.Query) core.Similarity { 93 | return core.Different 94 | } 95 | 96 | func limitSlice[T any](slice []T, limit int) []T { 97 | if len(slice) < limit { 98 | return slice 99 | } else { 100 | return slice[:limit] 101 | } 102 | } 103 | 104 | func Query(index *index.Index, query query.Query, limit int) core.ResultSet { 105 | rs := core.NewResultSet() 106 | 107 | for _, item := range index.FuncItems { 108 | sim := EvaluateFunc(&item, query) 109 | 110 | rs.Results = append(rs.Results, item.ToResult(sim)) 111 | } 112 | 113 | slices.SortFunc(rs.Results, func(a, b core.ResultItem) int { 114 | return cmp.Compare(a.Similarity, b.Similarity) 115 | }) 116 | 117 | rs.Results = limitSlice(rs.Results, limit) 118 | 119 | return rs 120 | } 121 | -------------------------------------------------------------------------------- /pkg/goggle/lib.go: -------------------------------------------------------------------------------- 1 | package goggle 2 | 3 | import ( 4 | "github.com/abiriadev/goggle/pkg/core" 5 | "github.com/abiriadev/goggle/pkg/eval" 6 | "github.com/abiriadev/goggle/pkg/index" 7 | "github.com/abiriadev/goggle/pkg/query" 8 | "github.com/alecthomas/participle/v2" 9 | ) 10 | 11 | // reexports: 12 | 13 | // ResultSet represents a query result ordered by accuracy 14 | type ResultSet = core.ResultSet 15 | 16 | // ResultItem represents a single item that matches to the query 17 | type ResultItem = core.ResultItem 18 | 19 | // Similarity represents how the results are accurate 20 | type Similarity = core.Similarity 21 | 22 | // Configuration for Goggle 23 | type Config struct { 24 | Limit int 25 | } 26 | 27 | // The main entry to the Goggle search engine 28 | type Goggle struct { 29 | index index.Index 30 | qp participle.Parser[query.Query] 31 | cfg Config 32 | } 33 | 34 | func NewGoggle(cfg Config) (Goggle, error) { 35 | qp, err := query.QueryParser() 36 | if err != nil { 37 | return Goggle{}, err 38 | } 39 | 40 | return Goggle{index.NewIndex(), *qp, cfg}, nil 41 | } 42 | 43 | // Initialize new Goggle search engine by loading index from the path 44 | func (g *Goggle) Load(indexFile string) error { 45 | index, err := index.Load(indexFile) 46 | if err != nil { 47 | return err 48 | } 49 | 50 | g.index = index 51 | return nil 52 | } 53 | 54 | // Query Goggle with a given query 55 | func (g *Goggle) Query(query string) (core.ResultSet, error) { 56 | q, err := g.qp.ParseString("", query) 57 | if err != nil { 58 | return core.ResultSet{}, err 59 | } 60 | 61 | return eval.Query(&g.index, *q, g.cfg.Limit), nil 62 | } 63 | -------------------------------------------------------------------------------- /pkg/index/README.md: -------------------------------------------------------------------------------- 1 | ![](https://pbs.twimg.com/media/AW9sSIRCQAAmJ2o?format=png) 2 | 3 | 인덱스 커엽다!! 4 | -------------------------------------------------------------------------------- /pkg/index/indexer.go: -------------------------------------------------------------------------------- 1 | package index 2 | 3 | import ( 4 | "fmt" 5 | "go/ast" 6 | "go/doc" 7 | "strings" 8 | 9 | "github.com/abiriadev/goggle/pkg/core" 10 | "github.com/repeale/fp-go" 11 | "golang.org/x/tools/go/packages" 12 | ) 13 | 14 | type Indexer struct{} 15 | 16 | func NewIndexer() Indexer { 17 | return Indexer{} 18 | } 19 | 20 | func (indexer Indexer) IndexPackages(pkgsToIndex []string) (Index, error) { 21 | pkgs, err := packages.Load( 22 | &packages.Config{ 23 | Mode: packages.NeedName | 24 | packages.NeedTypes | 25 | packages.NeedSyntax, 26 | }, 27 | pkgsToIndex..., 28 | ) 29 | if err != nil { 30 | return Index{}, nil 31 | } 32 | 33 | index := NewIndex() 34 | 35 | for _, pkg := range pkgs { 36 | d, err := doc.NewFromFiles(pkg.Fset, pkg.Syntax, pkg.PkgPath) 37 | if err != nil { 38 | continue 39 | } 40 | 41 | for _, f := range d.Funcs { 42 | r := f.Decl.Type.Results 43 | if r == nil || r.NumFields() != 1 { 44 | continue 45 | } 46 | 47 | v, b := r.List[0].Type.(*ast.Ident) 48 | if !b { 49 | continue 50 | } 51 | 52 | args := make([]core.Arg, 0) 53 | 54 | // analyzeFuncDecl(f.Decl) 55 | 56 | pl := f.Decl.Type.Params.List 57 | for _, arg := range pl { 58 | if arg != nil { 59 | a := arg.Names 60 | if len(a) != 1 { 61 | continue 62 | } 63 | 64 | a1 := a[0] 65 | 66 | t, isIdent := arg.Type.(*ast.Ident) 67 | if !isIdent { 68 | continue 69 | } 70 | 71 | args = append(args, core.Arg{ 72 | Name: a1.Name, 73 | Type: t.Name, 74 | }) 75 | } 76 | } 77 | 78 | var docLinkRoute string 79 | if docLinkRoute = d.ImportPath; isVendored(d.ImportPath) { 80 | docLinkRoute = strings.TrimPrefix(d.ImportPath, "vendor/") 81 | } 82 | 83 | index.FuncItems = append(index.FuncItems, core.FuncDef{ 84 | Package: d.ImportPath, 85 | PackageMame: pkg.Name, 86 | Name: f.Name, 87 | Args: args, 88 | Return: v.Name, 89 | Summary: d.Synopsis(f.Doc), 90 | Link: fmt.Sprintf("https://pkg.go.dev/%s#%s", docLinkRoute, f.Name), 91 | }) 92 | } 93 | } 94 | 95 | return index, nil 96 | } 97 | 98 | func analyzeFuncDecl(f *ast.FuncDecl) { 99 | // WARN: nilable 100 | // name := f.Name.Name 101 | 102 | // WARN: nilable 103 | ft := f.Type 104 | 105 | for _, field := range fp.Filter(func(n *ast.Field) bool { 106 | return n != nil 107 | })(ft.Params.List) { 108 | fty := field.Type 109 | 110 | fmt.Printf("fty: %T\n", fty) 111 | } 112 | } 113 | 114 | func isVendored(importPath string) bool { 115 | return strings.HasPrefix(importPath, "vendor/") 116 | // || strings.Contains(importPath, "/vendor/") 117 | } 118 | -------------------------------------------------------------------------------- /pkg/index/lib.go: -------------------------------------------------------------------------------- 1 | package index 2 | -------------------------------------------------------------------------------- /pkg/index/marshal.go: -------------------------------------------------------------------------------- 1 | package index 2 | 3 | import ( 4 | "encoding/gob" 5 | "encoding/json" 6 | "io" 7 | "os" 8 | ) 9 | 10 | func (index *Index) MarshalJson() ([]byte, error) { 11 | return json.Marshal(index) 12 | } 13 | 14 | func (index *Index) MarshalJsonTo(w io.Writer) error { 15 | return json.NewEncoder(w).Encode(index) 16 | } 17 | 18 | func (index *Index) SaveJson(filename string) error { 19 | w, e := os.Create(filename) 20 | if e != nil { 21 | return e 22 | } 23 | 24 | return index.MarshalJsonTo(w) 25 | } 26 | 27 | func UnmarshalJson(data []byte) (Index, error) { 28 | var idx Index 29 | return idx, json.Unmarshal(data, &idx) 30 | } 31 | 32 | func UnmarshalJsonFrom(r io.Reader) (Index, error) { 33 | var idx Index 34 | return idx, json.NewDecoder(r).Decode(&idx) 35 | } 36 | 37 | func LoadJson(filename string) (Index, error) { 38 | r, e := os.Open(filename) 39 | if e != nil { 40 | return Index{}, e 41 | } 42 | 43 | return UnmarshalJsonFrom(r) 44 | } 45 | 46 | func (index *Index) Save(filename string) error { 47 | w, err := os.Create(filename) 48 | if err != nil { 49 | return err 50 | } 51 | 52 | gob.Register(Index{}) 53 | 54 | enc := gob.NewEncoder(w) 55 | 56 | return enc.Encode(index) 57 | } 58 | 59 | func Load(filename string) (Index, error) { 60 | r, err := os.Open(filename) 61 | if err != nil { 62 | return Index{}, err 63 | } 64 | 65 | gob.Register(Index{}) 66 | 67 | dec := gob.NewDecoder(r) 68 | 69 | var index Index 70 | err = dec.Decode(&index) 71 | 72 | return index, err 73 | } 74 | -------------------------------------------------------------------------------- /pkg/index/mirror.go: -------------------------------------------------------------------------------- 1 | package index 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "io" 7 | "net/http" 8 | "strconv" 9 | "time" 10 | ) 11 | 12 | // maximum index server limit 13 | const LIMIT = 2000 14 | const INDEX_MAXIMUM_TAIL = 5 15 | 16 | func ResolveFullIndex() ([]ModuleIndex, error) { 17 | midxes := make([]ModuleIndex, 0) 18 | var inc *time.Time 19 | 20 | for { 21 | r, err := FetchModuleIndex(inc, LIMIT) 22 | if err != nil { 23 | return nil, err 24 | } 25 | 26 | m, err := ParseModuleIndex(r) 27 | if err != nil { 28 | return nil, err 29 | } 30 | 31 | midxes = append(midxes, m...) 32 | 33 | if len(m) >= INDEX_MAXIMUM_TAIL { 34 | t := IncTimeStamp(m[len(m)-1]) 35 | inc = &t 36 | 37 | fmt.Println(*inc) 38 | } else { 39 | break 40 | } 41 | } 42 | 43 | return midxes, nil 44 | } 45 | 46 | func IncTimeStamp(midxes ModuleIndex) time.Time { 47 | return midxes.Timestamp.Add(time.Nanosecond) 48 | } 49 | 50 | func FetchModuleIndex(since *time.Time, limit int) (io.Reader, error) { 51 | req, err := http.NewRequest("GET", "https://index.golang.org/index", nil) 52 | if err != nil { 53 | return nil, err 54 | } 55 | 56 | q := req.URL.Query() 57 | if since != nil { 58 | q.Add("since", since.Format(time.RFC3339)) 59 | } 60 | q.Add("limit", strconv.Itoa(limit)) 61 | req.URL.RawQuery = q.Encode() 62 | 63 | if err != nil { 64 | return nil, err 65 | } 66 | 67 | client := &http.Client{} 68 | 69 | res, err := client.Do(req) 70 | if err != nil { 71 | return nil, err 72 | } 73 | 74 | return res.Body, nil 75 | } 76 | 77 | type ModuleIndex struct { 78 | Path string 79 | Version string 80 | Timestamp time.Time 81 | } 82 | 83 | func ParseModuleIndex(r io.Reader) ([]ModuleIndex, error) { 84 | dec := json.NewDecoder(r) 85 | 86 | midxes := make([]ModuleIndex, 0) 87 | 88 | for { 89 | var v ModuleIndex 90 | err := dec.Decode(&v) 91 | if err != nil { 92 | if err == io.EOF { 93 | break 94 | } 95 | 96 | return nil, nil 97 | } 98 | 99 | midxes = append(midxes, v) 100 | } 101 | 102 | return midxes, nil 103 | } 104 | -------------------------------------------------------------------------------- /pkg/index/mirror_test.go: -------------------------------------------------------------------------------- 1 | package index 2 | 3 | import ( 4 | "strings" 5 | "testing" 6 | "time" 7 | ) 8 | 9 | func TestModuleIndexParser(t *testing.T) { 10 | raw := "{}\n{}\n{\"Timestamp\":\"2019-04-10T19:08:52.997264Z\"}" 11 | 12 | v, err := ParseModuleIndex(strings.NewReader(raw)) 13 | 14 | if err != nil { 15 | t.Fatal(err) 16 | } 17 | 18 | if len(v) != 3 { 19 | t.Fail() 20 | } 21 | 22 | if v[2].Timestamp != time.Date(2019, 04, 10, 19, 8, 52, 997264000, time.UTC) { 23 | t.Fatal(v[2].Timestamp) 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /pkg/index/types.go: -------------------------------------------------------------------------------- 1 | package index 2 | 3 | import ( 4 | "github.com/abiriadev/goggle/pkg/core" 5 | "github.com/repeale/fp-go" 6 | ) 7 | 8 | // The central struct for all kind of indexing and searching 9 | type Index struct { 10 | FuncItems []core.FuncDef `json:"func"` 11 | MethodItems []core.MethodDef `json:"met"` 12 | } 13 | 14 | // Create an empty index 15 | func NewIndex() Index { 16 | return Index{make([]core.FuncDef, 0), make([]core.MethodDef, 0)} 17 | } 18 | 19 | func ConcatSliceTo[T any](slices [][]T, dest []T) []T { 20 | for _, sl := range slices { 21 | dest = append(dest, sl...) 22 | } 23 | 24 | return dest 25 | } 26 | 27 | func ConcatSlice[T any](slices [][]T) []T { 28 | r := make([]T, 0) 29 | 30 | for _, sl := range slices { 31 | r = append(r, sl...) 32 | } 33 | 34 | return r 35 | } 36 | 37 | func MergeIndex(idxes []Index) Index { 38 | return Index{ 39 | FuncItems: ConcatSlice( 40 | fp.Map(func(idx Index) []core.FuncDef { 41 | return idx.FuncItems 42 | })(idxes), 43 | ), 44 | MethodItems: ConcatSlice( 45 | fp.Map(func(idx Index) []core.MethodDef { 46 | return idx.MethodItems 47 | })(idxes), 48 | ), 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /pkg/query/query.go: -------------------------------------------------------------------------------- 1 | package query 2 | 3 | import "github.com/alecthomas/participle/v2" 4 | 5 | type Query struct { 6 | Name string `"func"? @Ident?` 7 | Args []string `"(" ( @Ident ( "," @Ident )* )? ")"` 8 | Ret string `@Ident?` 9 | } 10 | 11 | func QueryParser() (*participle.Parser[Query], error) { 12 | p, err := participle.Build[Query]() 13 | return p, err 14 | } 15 | 16 | func QueryParserUnsafe() *participle.Parser[Query] { 17 | return participle.MustBuild[Query]() 18 | } 19 | -------------------------------------------------------------------------------- /pnpm-lock.yaml: -------------------------------------------------------------------------------- 1 | lockfileVersion: '6.0' 2 | 3 | settings: 4 | autoInstallPeers: true 5 | excludeLinksFromLockfile: false 6 | 7 | importers: 8 | 9 | frontend: 10 | dependencies: 11 | '@emotion/react': 12 | specifier: ^11.11.1 13 | version: 11.11.1(@types/react@18.2.42)(react@18.2.0) 14 | '@emotion/styled': 15 | specifier: ^11.11.0 16 | version: 11.11.0(@emotion/react@11.11.1)(@types/react@18.2.42)(react@18.2.0) 17 | '@mui/icons-material': 18 | specifier: ^5.14.19 19 | version: 5.14.19(@mui/material@5.14.20)(@types/react@18.2.42)(react@18.2.0) 20 | '@mui/material': 21 | specifier: ^5.14.20 22 | version: 5.14.20(@emotion/react@11.11.1)(@emotion/styled@11.11.0)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) 23 | react: 24 | specifier: ^18.2.0 25 | version: 18.2.0 26 | react-dom: 27 | specifier: ^18.2.0 28 | version: 18.2.0(react@18.2.0) 29 | devDependencies: 30 | '@types/react': 31 | specifier: ^18.2.37 32 | version: 18.2.42 33 | '@types/react-dom': 34 | specifier: ^18.2.15 35 | version: 18.2.17 36 | '@vitejs/plugin-react-swc': 37 | specifier: ^3.5.0 38 | version: 3.5.0(vite@5.0.5) 39 | typescript: 40 | specifier: ^5.2.2 41 | version: 5.3.2 42 | vite: 43 | specifier: ^5.0.0 44 | version: 5.0.5 45 | 46 | packages: 47 | 48 | /@babel/code-frame@7.23.5: 49 | resolution: {integrity: sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA==} 50 | engines: {node: '>=6.9.0'} 51 | dependencies: 52 | '@babel/highlight': 7.23.4 53 | chalk: 2.4.2 54 | dev: false 55 | 56 | /@babel/helper-module-imports@7.22.15: 57 | resolution: {integrity: sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w==} 58 | engines: {node: '>=6.9.0'} 59 | dependencies: 60 | '@babel/types': 7.23.5 61 | dev: false 62 | 63 | /@babel/helper-string-parser@7.23.4: 64 | resolution: {integrity: sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ==} 65 | engines: {node: '>=6.9.0'} 66 | dev: false 67 | 68 | /@babel/helper-validator-identifier@7.22.20: 69 | resolution: {integrity: sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==} 70 | engines: {node: '>=6.9.0'} 71 | dev: false 72 | 73 | /@babel/highlight@7.23.4: 74 | resolution: {integrity: sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A==} 75 | engines: {node: '>=6.9.0'} 76 | dependencies: 77 | '@babel/helper-validator-identifier': 7.22.20 78 | chalk: 2.4.2 79 | js-tokens: 4.0.0 80 | dev: false 81 | 82 | /@babel/runtime@7.23.5: 83 | resolution: {integrity: sha512-NdUTHcPe4C99WxPub+K9l9tK5/lV4UXIoaHSYgzco9BCyjKAAwzdBI+wWtYqHt7LJdbo74ZjRPJgzVweq1sz0w==} 84 | engines: {node: '>=6.9.0'} 85 | dependencies: 86 | regenerator-runtime: 0.14.0 87 | dev: false 88 | 89 | /@babel/types@7.23.5: 90 | resolution: {integrity: sha512-ON5kSOJwVO6xXVRTvOI0eOnWe7VdUcIpsovGo9U/Br4Ie4UVFQTboO2cYnDhAGU6Fp+UxSiT+pMft0SMHfuq6w==} 91 | engines: {node: '>=6.9.0'} 92 | dependencies: 93 | '@babel/helper-string-parser': 7.23.4 94 | '@babel/helper-validator-identifier': 7.22.20 95 | to-fast-properties: 2.0.0 96 | dev: false 97 | 98 | /@emotion/babel-plugin@11.11.0: 99 | resolution: {integrity: sha512-m4HEDZleaaCH+XgDDsPF15Ht6wTLsgDTeR3WYj9Q/k76JtWhrJjcP4+/XlG8LGT/Rol9qUfOIztXeA84ATpqPQ==} 100 | dependencies: 101 | '@babel/helper-module-imports': 7.22.15 102 | '@babel/runtime': 7.23.5 103 | '@emotion/hash': 0.9.1 104 | '@emotion/memoize': 0.8.1 105 | '@emotion/serialize': 1.1.2 106 | babel-plugin-macros: 3.1.0 107 | convert-source-map: 1.9.0 108 | escape-string-regexp: 4.0.0 109 | find-root: 1.1.0 110 | source-map: 0.5.7 111 | stylis: 4.2.0 112 | dev: false 113 | 114 | /@emotion/cache@11.11.0: 115 | resolution: {integrity: sha512-P34z9ssTCBi3e9EI1ZsWpNHcfY1r09ZO0rZbRO2ob3ZQMnFI35jB536qoXbkdesr5EUhYi22anuEJuyxifaqAQ==} 116 | dependencies: 117 | '@emotion/memoize': 0.8.1 118 | '@emotion/sheet': 1.2.2 119 | '@emotion/utils': 1.2.1 120 | '@emotion/weak-memoize': 0.3.1 121 | stylis: 4.2.0 122 | dev: false 123 | 124 | /@emotion/hash@0.9.1: 125 | resolution: {integrity: sha512-gJB6HLm5rYwSLI6PQa+X1t5CFGrv1J1TWG+sOyMCeKz2ojaj6Fnl/rZEspogG+cvqbt4AE/2eIyD2QfLKTBNlQ==} 126 | dev: false 127 | 128 | /@emotion/is-prop-valid@1.2.1: 129 | resolution: {integrity: sha512-61Mf7Ufx4aDxx1xlDeOm8aFFigGHE4z+0sKCa+IHCeZKiyP9RLD0Mmx7m8b9/Cf37f7NAvQOOJAbQQGVr5uERw==} 130 | dependencies: 131 | '@emotion/memoize': 0.8.1 132 | dev: false 133 | 134 | /@emotion/memoize@0.8.1: 135 | resolution: {integrity: sha512-W2P2c/VRW1/1tLox0mVUalvnWXxavmv/Oum2aPsRcoDJuob75FC3Y8FbpfLwUegRcxINtGUMPq0tFCvYNTBXNA==} 136 | dev: false 137 | 138 | /@emotion/react@11.11.1(@types/react@18.2.42)(react@18.2.0): 139 | resolution: {integrity: sha512-5mlW1DquU5HaxjLkfkGN1GA/fvVGdyHURRiX/0FHl2cfIfRxSOfmxEH5YS43edp0OldZrZ+dkBKbngxcNCdZvA==} 140 | peerDependencies: 141 | '@types/react': '*' 142 | react: '>=16.8.0' 143 | peerDependenciesMeta: 144 | '@types/react': 145 | optional: true 146 | dependencies: 147 | '@babel/runtime': 7.23.5 148 | '@emotion/babel-plugin': 11.11.0 149 | '@emotion/cache': 11.11.0 150 | '@emotion/serialize': 1.1.2 151 | '@emotion/use-insertion-effect-with-fallbacks': 1.0.1(react@18.2.0) 152 | '@emotion/utils': 1.2.1 153 | '@emotion/weak-memoize': 0.3.1 154 | '@types/react': 18.2.42 155 | hoist-non-react-statics: 3.3.2 156 | react: 18.2.0 157 | dev: false 158 | 159 | /@emotion/serialize@1.1.2: 160 | resolution: {integrity: sha512-zR6a/fkFP4EAcCMQtLOhIgpprZOwNmCldtpaISpvz348+DP4Mz8ZoKaGGCQpbzepNIUWbq4w6hNZkwDyKoS+HA==} 161 | dependencies: 162 | '@emotion/hash': 0.9.1 163 | '@emotion/memoize': 0.8.1 164 | '@emotion/unitless': 0.8.1 165 | '@emotion/utils': 1.2.1 166 | csstype: 3.1.2 167 | dev: false 168 | 169 | /@emotion/sheet@1.2.2: 170 | resolution: {integrity: sha512-0QBtGvaqtWi+nx6doRwDdBIzhNdZrXUppvTM4dtZZWEGTXL/XE/yJxLMGlDT1Gt+UHH5IX1n+jkXyytE/av7OA==} 171 | dev: false 172 | 173 | /@emotion/styled@11.11.0(@emotion/react@11.11.1)(@types/react@18.2.42)(react@18.2.0): 174 | resolution: {integrity: sha512-hM5Nnvu9P3midq5aaXj4I+lnSfNi7Pmd4EWk1fOZ3pxookaQTNew6bp4JaCBYM4HVFZF9g7UjJmsUmC2JlxOng==} 175 | peerDependencies: 176 | '@emotion/react': ^11.0.0-rc.0 177 | '@types/react': '*' 178 | react: '>=16.8.0' 179 | peerDependenciesMeta: 180 | '@types/react': 181 | optional: true 182 | dependencies: 183 | '@babel/runtime': 7.23.5 184 | '@emotion/babel-plugin': 11.11.0 185 | '@emotion/is-prop-valid': 1.2.1 186 | '@emotion/react': 11.11.1(@types/react@18.2.42)(react@18.2.0) 187 | '@emotion/serialize': 1.1.2 188 | '@emotion/use-insertion-effect-with-fallbacks': 1.0.1(react@18.2.0) 189 | '@emotion/utils': 1.2.1 190 | '@types/react': 18.2.42 191 | react: 18.2.0 192 | dev: false 193 | 194 | /@emotion/unitless@0.8.1: 195 | resolution: {integrity: sha512-KOEGMu6dmJZtpadb476IsZBclKvILjopjUii3V+7MnXIQCYh8W3NgNcgwo21n9LXZX6EDIKvqfjYxXebDwxKmQ==} 196 | dev: false 197 | 198 | /@emotion/use-insertion-effect-with-fallbacks@1.0.1(react@18.2.0): 199 | resolution: {integrity: sha512-jT/qyKZ9rzLErtrjGgdkMBn2OP8wl0G3sQlBb3YPryvKHsjvINUhVaPFfP+fpBcOkmrVOVEEHQFJ7nbj2TH2gw==} 200 | peerDependencies: 201 | react: '>=16.8.0' 202 | dependencies: 203 | react: 18.2.0 204 | dev: false 205 | 206 | /@emotion/utils@1.2.1: 207 | resolution: {integrity: sha512-Y2tGf3I+XVnajdItskUCn6LX+VUDmP6lTL4fcqsXAv43dnlbZiuW4MWQW38rW/BVWSE7Q/7+XQocmpnRYILUmg==} 208 | dev: false 209 | 210 | /@emotion/weak-memoize@0.3.1: 211 | resolution: {integrity: sha512-EsBwpc7hBUJWAsNPBmJy4hxWx12v6bshQsldrVmjxJoc3isbxhOrF2IcCpaXxfvq03NwkI7sbsOLXbYuqF/8Ww==} 212 | dev: false 213 | 214 | /@esbuild/android-arm64@0.19.8: 215 | resolution: {integrity: sha512-B8JbS61bEunhfx8kasogFENgQfr/dIp+ggYXwTqdbMAgGDhRa3AaPpQMuQU0rNxDLECj6FhDzk1cF9WHMVwrtA==} 216 | engines: {node: '>=12'} 217 | cpu: [arm64] 218 | os: [android] 219 | requiresBuild: true 220 | dev: true 221 | optional: true 222 | 223 | /@esbuild/android-arm@0.19.8: 224 | resolution: {integrity: sha512-31E2lxlGM1KEfivQl8Yf5aYU/mflz9g06H6S15ITUFQueMFtFjESRMoDSkvMo8thYvLBax+VKTPlpnx+sPicOA==} 225 | engines: {node: '>=12'} 226 | cpu: [arm] 227 | os: [android] 228 | requiresBuild: true 229 | dev: true 230 | optional: true 231 | 232 | /@esbuild/android-x64@0.19.8: 233 | resolution: {integrity: sha512-rdqqYfRIn4jWOp+lzQttYMa2Xar3OK9Yt2fhOhzFXqg0rVWEfSclJvZq5fZslnz6ypHvVf3CT7qyf0A5pM682A==} 234 | engines: {node: '>=12'} 235 | cpu: [x64] 236 | os: [android] 237 | requiresBuild: true 238 | dev: true 239 | optional: true 240 | 241 | /@esbuild/darwin-arm64@0.19.8: 242 | resolution: {integrity: sha512-RQw9DemMbIq35Bprbboyf8SmOr4UXsRVxJ97LgB55VKKeJOOdvsIPy0nFyF2l8U+h4PtBx/1kRf0BelOYCiQcw==} 243 | engines: {node: '>=12'} 244 | cpu: [arm64] 245 | os: [darwin] 246 | requiresBuild: true 247 | dev: true 248 | optional: true 249 | 250 | /@esbuild/darwin-x64@0.19.8: 251 | resolution: {integrity: sha512-3sur80OT9YdeZwIVgERAysAbwncom7b4bCI2XKLjMfPymTud7e/oY4y+ci1XVp5TfQp/bppn7xLw1n/oSQY3/Q==} 252 | engines: {node: '>=12'} 253 | cpu: [x64] 254 | os: [darwin] 255 | requiresBuild: true 256 | dev: true 257 | optional: true 258 | 259 | /@esbuild/freebsd-arm64@0.19.8: 260 | resolution: {integrity: sha512-WAnPJSDattvS/XtPCTj1tPoTxERjcTpH6HsMr6ujTT+X6rylVe8ggxk8pVxzf5U1wh5sPODpawNicF5ta/9Tmw==} 261 | engines: {node: '>=12'} 262 | cpu: [arm64] 263 | os: [freebsd] 264 | requiresBuild: true 265 | dev: true 266 | optional: true 267 | 268 | /@esbuild/freebsd-x64@0.19.8: 269 | resolution: {integrity: sha512-ICvZyOplIjmmhjd6mxi+zxSdpPTKFfyPPQMQTK/w+8eNK6WV01AjIztJALDtwNNfFhfZLux0tZLC+U9nSyA5Zg==} 270 | engines: {node: '>=12'} 271 | cpu: [x64] 272 | os: [freebsd] 273 | requiresBuild: true 274 | dev: true 275 | optional: true 276 | 277 | /@esbuild/linux-arm64@0.19.8: 278 | resolution: {integrity: sha512-z1zMZivxDLHWnyGOctT9JP70h0beY54xDDDJt4VpTX+iwA77IFsE1vCXWmprajJGa+ZYSqkSbRQ4eyLCpCmiCQ==} 279 | engines: {node: '>=12'} 280 | cpu: [arm64] 281 | os: [linux] 282 | requiresBuild: true 283 | dev: true 284 | optional: true 285 | 286 | /@esbuild/linux-arm@0.19.8: 287 | resolution: {integrity: sha512-H4vmI5PYqSvosPaTJuEppU9oz1dq2A7Mr2vyg5TF9Ga+3+MGgBdGzcyBP7qK9MrwFQZlvNyJrvz6GuCaj3OukQ==} 288 | engines: {node: '>=12'} 289 | cpu: [arm] 290 | os: [linux] 291 | requiresBuild: true 292 | dev: true 293 | optional: true 294 | 295 | /@esbuild/linux-ia32@0.19.8: 296 | resolution: {integrity: sha512-1a8suQiFJmZz1khm/rDglOc8lavtzEMRo0v6WhPgxkrjcU0LkHj+TwBrALwoz/OtMExvsqbbMI0ChyelKabSvQ==} 297 | engines: {node: '>=12'} 298 | cpu: [ia32] 299 | os: [linux] 300 | requiresBuild: true 301 | dev: true 302 | optional: true 303 | 304 | /@esbuild/linux-loong64@0.19.8: 305 | resolution: {integrity: sha512-fHZWS2JJxnXt1uYJsDv9+b60WCc2RlvVAy1F76qOLtXRO+H4mjt3Tr6MJ5l7Q78X8KgCFudnTuiQRBhULUyBKQ==} 306 | engines: {node: '>=12'} 307 | cpu: [loong64] 308 | os: [linux] 309 | requiresBuild: true 310 | dev: true 311 | optional: true 312 | 313 | /@esbuild/linux-mips64el@0.19.8: 314 | resolution: {integrity: sha512-Wy/z0EL5qZYLX66dVnEg9riiwls5IYnziwuju2oUiuxVc+/edvqXa04qNtbrs0Ukatg5HEzqT94Zs7J207dN5Q==} 315 | engines: {node: '>=12'} 316 | cpu: [mips64el] 317 | os: [linux] 318 | requiresBuild: true 319 | dev: true 320 | optional: true 321 | 322 | /@esbuild/linux-ppc64@0.19.8: 323 | resolution: {integrity: sha512-ETaW6245wK23YIEufhMQ3HSeHO7NgsLx8gygBVldRHKhOlD1oNeNy/P67mIh1zPn2Hr2HLieQrt6tWrVwuqrxg==} 324 | engines: {node: '>=12'} 325 | cpu: [ppc64] 326 | os: [linux] 327 | requiresBuild: true 328 | dev: true 329 | optional: true 330 | 331 | /@esbuild/linux-riscv64@0.19.8: 332 | resolution: {integrity: sha512-T2DRQk55SgoleTP+DtPlMrxi/5r9AeFgkhkZ/B0ap99zmxtxdOixOMI570VjdRCs9pE4Wdkz7JYrsPvsl7eESg==} 333 | engines: {node: '>=12'} 334 | cpu: [riscv64] 335 | os: [linux] 336 | requiresBuild: true 337 | dev: true 338 | optional: true 339 | 340 | /@esbuild/linux-s390x@0.19.8: 341 | resolution: {integrity: sha512-NPxbdmmo3Bk7mbNeHmcCd7R7fptJaczPYBaELk6NcXxy7HLNyWwCyDJ/Xx+/YcNH7Im5dHdx9gZ5xIwyliQCbg==} 342 | engines: {node: '>=12'} 343 | cpu: [s390x] 344 | os: [linux] 345 | requiresBuild: true 346 | dev: true 347 | optional: true 348 | 349 | /@esbuild/linux-x64@0.19.8: 350 | resolution: {integrity: sha512-lytMAVOM3b1gPypL2TRmZ5rnXl7+6IIk8uB3eLsV1JwcizuolblXRrc5ShPrO9ls/b+RTp+E6gbsuLWHWi2zGg==} 351 | engines: {node: '>=12'} 352 | cpu: [x64] 353 | os: [linux] 354 | requiresBuild: true 355 | dev: true 356 | optional: true 357 | 358 | /@esbuild/netbsd-x64@0.19.8: 359 | resolution: {integrity: sha512-hvWVo2VsXz/8NVt1UhLzxwAfo5sioj92uo0bCfLibB0xlOmimU/DeAEsQILlBQvkhrGjamP0/el5HU76HAitGw==} 360 | engines: {node: '>=12'} 361 | cpu: [x64] 362 | os: [netbsd] 363 | requiresBuild: true 364 | dev: true 365 | optional: true 366 | 367 | /@esbuild/openbsd-x64@0.19.8: 368 | resolution: {integrity: sha512-/7Y7u77rdvmGTxR83PgaSvSBJCC2L3Kb1M/+dmSIvRvQPXXCuC97QAwMugBNG0yGcbEGfFBH7ojPzAOxfGNkwQ==} 369 | engines: {node: '>=12'} 370 | cpu: [x64] 371 | os: [openbsd] 372 | requiresBuild: true 373 | dev: true 374 | optional: true 375 | 376 | /@esbuild/sunos-x64@0.19.8: 377 | resolution: {integrity: sha512-9Lc4s7Oi98GqFA4HzA/W2JHIYfnXbUYgekUP/Sm4BG9sfLjyv6GKKHKKVs83SMicBF2JwAX6A1PuOLMqpD001w==} 378 | engines: {node: '>=12'} 379 | cpu: [x64] 380 | os: [sunos] 381 | requiresBuild: true 382 | dev: true 383 | optional: true 384 | 385 | /@esbuild/win32-arm64@0.19.8: 386 | resolution: {integrity: sha512-rq6WzBGjSzihI9deW3fC2Gqiak68+b7qo5/3kmB6Gvbh/NYPA0sJhrnp7wgV4bNwjqM+R2AApXGxMO7ZoGhIJg==} 387 | engines: {node: '>=12'} 388 | cpu: [arm64] 389 | os: [win32] 390 | requiresBuild: true 391 | dev: true 392 | optional: true 393 | 394 | /@esbuild/win32-ia32@0.19.8: 395 | resolution: {integrity: sha512-AIAbverbg5jMvJznYiGhrd3sumfwWs8572mIJL5NQjJa06P8KfCPWZQ0NwZbPQnbQi9OWSZhFVSUWjjIrn4hSw==} 396 | engines: {node: '>=12'} 397 | cpu: [ia32] 398 | os: [win32] 399 | requiresBuild: true 400 | dev: true 401 | optional: true 402 | 403 | /@esbuild/win32-x64@0.19.8: 404 | resolution: {integrity: sha512-bfZ0cQ1uZs2PqpulNL5j/3w+GDhP36k1K5c38QdQg+Swy51jFZWWeIkteNsufkQxp986wnqRRsb/bHbY1WQ7TA==} 405 | engines: {node: '>=12'} 406 | cpu: [x64] 407 | os: [win32] 408 | requiresBuild: true 409 | dev: true 410 | optional: true 411 | 412 | /@floating-ui/core@1.5.2: 413 | resolution: {integrity: sha512-Ii3MrfY/GAIN3OhXNzpCKaLxHQfJF9qvwq/kEJYdqDxeIHa01K8sldugal6TmeeXl+WMvhv9cnVzUTaFFJF09A==} 414 | dependencies: 415 | '@floating-ui/utils': 0.1.6 416 | dev: false 417 | 418 | /@floating-ui/dom@1.5.3: 419 | resolution: {integrity: sha512-ClAbQnEqJAKCJOEbbLo5IUlZHkNszqhuxS4fHAVxRPXPya6Ysf2G8KypnYcOTpx6I8xcgF9bbHb6g/2KpbV8qA==} 420 | dependencies: 421 | '@floating-ui/core': 1.5.2 422 | '@floating-ui/utils': 0.1.6 423 | dev: false 424 | 425 | /@floating-ui/react-dom@2.0.4(react-dom@18.2.0)(react@18.2.0): 426 | resolution: {integrity: sha512-CF8k2rgKeh/49UrnIBs4BdxPUV6vize/Db1d/YbCLyp9GiVZ0BEwf5AiDSxJRCr6yOkGqTFHtmrULxkEfYZ7dQ==} 427 | peerDependencies: 428 | react: '>=16.8.0' 429 | react-dom: '>=16.8.0' 430 | dependencies: 431 | '@floating-ui/dom': 1.5.3 432 | react: 18.2.0 433 | react-dom: 18.2.0(react@18.2.0) 434 | dev: false 435 | 436 | /@floating-ui/utils@0.1.6: 437 | resolution: {integrity: sha512-OfX7E2oUDYxtBvsuS4e/jSn4Q9Qb6DzgeYtsAdkPZ47znpoNsMgZw0+tVijiv3uGNR6dgNlty6r9rzIzHjtd/A==} 438 | dev: false 439 | 440 | /@mui/base@5.0.0-beta.26(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0): 441 | resolution: {integrity: sha512-gPMRKC84VRw+tjqYoyBzyrBUqHQucMXdlBpYazHa5rCXrb91fYEQk5SqQ2U5kjxx9QxZxTBvWAmZ6DblIgaGhQ==} 442 | engines: {node: '>=12.0.0'} 443 | peerDependencies: 444 | '@types/react': ^17.0.0 || ^18.0.0 445 | react: ^17.0.0 || ^18.0.0 446 | react-dom: ^17.0.0 || ^18.0.0 447 | peerDependenciesMeta: 448 | '@types/react': 449 | optional: true 450 | dependencies: 451 | '@babel/runtime': 7.23.5 452 | '@floating-ui/react-dom': 2.0.4(react-dom@18.2.0)(react@18.2.0) 453 | '@mui/types': 7.2.10(@types/react@18.2.42) 454 | '@mui/utils': 5.14.20(@types/react@18.2.42)(react@18.2.0) 455 | '@popperjs/core': 2.11.8 456 | '@types/react': 18.2.42 457 | clsx: 2.0.0 458 | prop-types: 15.8.1 459 | react: 18.2.0 460 | react-dom: 18.2.0(react@18.2.0) 461 | dev: false 462 | 463 | /@mui/core-downloads-tracker@5.14.20: 464 | resolution: {integrity: sha512-fXoGe8VOrIYajqALysFuyal1q1YmBARqJ3tmnWYDVl0scu8f6h6tZQbS2K8BY28QwkWNGyv4WRfuUkzN5HR3Ow==} 465 | dev: false 466 | 467 | /@mui/icons-material@5.14.19(@mui/material@5.14.20)(@types/react@18.2.42)(react@18.2.0): 468 | resolution: {integrity: sha512-yjP8nluXxZGe3Y7pS+yxBV+hWZSsSBampCxkZwaw+1l+feL+rfP74vbEFbMrX/Kil9I/Y1tWfy5bs/eNvwNpWw==} 469 | engines: {node: '>=12.0.0'} 470 | peerDependencies: 471 | '@mui/material': ^5.0.0 472 | '@types/react': ^17.0.0 || ^18.0.0 473 | react: ^17.0.0 || ^18.0.0 474 | peerDependenciesMeta: 475 | '@types/react': 476 | optional: true 477 | dependencies: 478 | '@babel/runtime': 7.23.5 479 | '@mui/material': 5.14.20(@emotion/react@11.11.1)(@emotion/styled@11.11.0)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) 480 | '@types/react': 18.2.42 481 | react: 18.2.0 482 | dev: false 483 | 484 | /@mui/material@5.14.20(@emotion/react@11.11.1)(@emotion/styled@11.11.0)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0): 485 | resolution: {integrity: sha512-SUcPZnN6e0h1AtrDktEl76Dsyo/7pyEUQ+SAVe9XhHg/iliA0b4Vo+Eg4HbNkELsMbpDsUF4WHp7rgflPG7qYQ==} 486 | engines: {node: '>=12.0.0'} 487 | peerDependencies: 488 | '@emotion/react': ^11.5.0 489 | '@emotion/styled': ^11.3.0 490 | '@types/react': ^17.0.0 || ^18.0.0 491 | react: ^17.0.0 || ^18.0.0 492 | react-dom: ^17.0.0 || ^18.0.0 493 | peerDependenciesMeta: 494 | '@emotion/react': 495 | optional: true 496 | '@emotion/styled': 497 | optional: true 498 | '@types/react': 499 | optional: true 500 | dependencies: 501 | '@babel/runtime': 7.23.5 502 | '@emotion/react': 11.11.1(@types/react@18.2.42)(react@18.2.0) 503 | '@emotion/styled': 11.11.0(@emotion/react@11.11.1)(@types/react@18.2.42)(react@18.2.0) 504 | '@mui/base': 5.0.0-beta.26(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) 505 | '@mui/core-downloads-tracker': 5.14.20 506 | '@mui/system': 5.14.20(@emotion/react@11.11.1)(@emotion/styled@11.11.0)(@types/react@18.2.42)(react@18.2.0) 507 | '@mui/types': 7.2.10(@types/react@18.2.42) 508 | '@mui/utils': 5.14.20(@types/react@18.2.42)(react@18.2.0) 509 | '@types/react': 18.2.42 510 | '@types/react-transition-group': 4.4.10 511 | clsx: 2.0.0 512 | csstype: 3.1.2 513 | prop-types: 15.8.1 514 | react: 18.2.0 515 | react-dom: 18.2.0(react@18.2.0) 516 | react-is: 18.2.0 517 | react-transition-group: 4.4.5(react-dom@18.2.0)(react@18.2.0) 518 | dev: false 519 | 520 | /@mui/private-theming@5.14.20(@types/react@18.2.42)(react@18.2.0): 521 | resolution: {integrity: sha512-WV560e1vhs2IHCh0pgUaWHznrcrVoW9+cDCahU1VTkuwPokWVvb71ccWQ1f8Y3tRBPPcNkU2dChkkRJChLmQlQ==} 522 | engines: {node: '>=12.0.0'} 523 | peerDependencies: 524 | '@types/react': ^17.0.0 || ^18.0.0 525 | react: ^17.0.0 || ^18.0.0 526 | peerDependenciesMeta: 527 | '@types/react': 528 | optional: true 529 | dependencies: 530 | '@babel/runtime': 7.23.5 531 | '@mui/utils': 5.14.20(@types/react@18.2.42)(react@18.2.0) 532 | '@types/react': 18.2.42 533 | prop-types: 15.8.1 534 | react: 18.2.0 535 | dev: false 536 | 537 | /@mui/styled-engine@5.14.20(@emotion/react@11.11.1)(@emotion/styled@11.11.0)(react@18.2.0): 538 | resolution: {integrity: sha512-Vs4nGptd9wRslo9zeRkuWcZeIEp+oYbODy+fiZKqqr4CH1Gfi9fdP0Q1tGYk8OiJ2EPB/tZSAyOy62Hyp/iP7g==} 539 | engines: {node: '>=12.0.0'} 540 | peerDependencies: 541 | '@emotion/react': ^11.4.1 542 | '@emotion/styled': ^11.3.0 543 | react: ^17.0.0 || ^18.0.0 544 | peerDependenciesMeta: 545 | '@emotion/react': 546 | optional: true 547 | '@emotion/styled': 548 | optional: true 549 | dependencies: 550 | '@babel/runtime': 7.23.5 551 | '@emotion/cache': 11.11.0 552 | '@emotion/react': 11.11.1(@types/react@18.2.42)(react@18.2.0) 553 | '@emotion/styled': 11.11.0(@emotion/react@11.11.1)(@types/react@18.2.42)(react@18.2.0) 554 | csstype: 3.1.2 555 | prop-types: 15.8.1 556 | react: 18.2.0 557 | dev: false 558 | 559 | /@mui/system@5.14.20(@emotion/react@11.11.1)(@emotion/styled@11.11.0)(@types/react@18.2.42)(react@18.2.0): 560 | resolution: {integrity: sha512-jKOGtK4VfYZG5kdaryUHss4X6hzcfh0AihT8gmnkfqRtWP7xjY+vPaUhhuSeibE5sqA5wCtdY75z6ep9pxFnIg==} 561 | engines: {node: '>=12.0.0'} 562 | peerDependencies: 563 | '@emotion/react': ^11.5.0 564 | '@emotion/styled': ^11.3.0 565 | '@types/react': ^17.0.0 || ^18.0.0 566 | react: ^17.0.0 || ^18.0.0 567 | peerDependenciesMeta: 568 | '@emotion/react': 569 | optional: true 570 | '@emotion/styled': 571 | optional: true 572 | '@types/react': 573 | optional: true 574 | dependencies: 575 | '@babel/runtime': 7.23.5 576 | '@emotion/react': 11.11.1(@types/react@18.2.42)(react@18.2.0) 577 | '@emotion/styled': 11.11.0(@emotion/react@11.11.1)(@types/react@18.2.42)(react@18.2.0) 578 | '@mui/private-theming': 5.14.20(@types/react@18.2.42)(react@18.2.0) 579 | '@mui/styled-engine': 5.14.20(@emotion/react@11.11.1)(@emotion/styled@11.11.0)(react@18.2.0) 580 | '@mui/types': 7.2.10(@types/react@18.2.42) 581 | '@mui/utils': 5.14.20(@types/react@18.2.42)(react@18.2.0) 582 | '@types/react': 18.2.42 583 | clsx: 2.0.0 584 | csstype: 3.1.2 585 | prop-types: 15.8.1 586 | react: 18.2.0 587 | dev: false 588 | 589 | /@mui/types@7.2.10(@types/react@18.2.42): 590 | resolution: {integrity: sha512-wX1vbDC+lzF7FlhT6A3ffRZgEoKWPF8VqRoTu4lZwouFX2t90KyCMsgepMw5DxLak1BSp/KP86CmtZttikb/gQ==} 591 | peerDependencies: 592 | '@types/react': ^17.0.0 || ^18.0.0 593 | peerDependenciesMeta: 594 | '@types/react': 595 | optional: true 596 | dependencies: 597 | '@types/react': 18.2.42 598 | dev: false 599 | 600 | /@mui/utils@5.14.20(@types/react@18.2.42)(react@18.2.0): 601 | resolution: {integrity: sha512-Y6yL5MoFmtQml20DZnaaK1znrCEwG6/vRSzW8PKOTrzhyqKIql0FazZRUR7sA5EPASgiyKZfq0FPwISRXm5NdA==} 602 | engines: {node: '>=12.0.0'} 603 | peerDependencies: 604 | '@types/react': ^17.0.0 || ^18.0.0 605 | react: ^17.0.0 || ^18.0.0 606 | peerDependenciesMeta: 607 | '@types/react': 608 | optional: true 609 | dependencies: 610 | '@babel/runtime': 7.23.5 611 | '@types/prop-types': 15.7.11 612 | '@types/react': 18.2.42 613 | prop-types: 15.8.1 614 | react: 18.2.0 615 | react-is: 18.2.0 616 | dev: false 617 | 618 | /@popperjs/core@2.11.8: 619 | resolution: {integrity: sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==} 620 | dev: false 621 | 622 | /@rollup/rollup-android-arm-eabi@4.6.1: 623 | resolution: {integrity: sha512-0WQ0ouLejaUCRsL93GD4uft3rOmB8qoQMU05Kb8CmMtMBe7XUDLAltxVZI1q6byNqEtU7N1ZX1Vw5lIpgulLQA==} 624 | cpu: [arm] 625 | os: [android] 626 | requiresBuild: true 627 | dev: true 628 | optional: true 629 | 630 | /@rollup/rollup-android-arm64@4.6.1: 631 | resolution: {integrity: sha512-1TKm25Rn20vr5aTGGZqo6E4mzPicCUD79k17EgTLAsXc1zysyi4xXKACfUbwyANEPAEIxkzwue6JZ+stYzWUTA==} 632 | cpu: [arm64] 633 | os: [android] 634 | requiresBuild: true 635 | dev: true 636 | optional: true 637 | 638 | /@rollup/rollup-darwin-arm64@4.6.1: 639 | resolution: {integrity: sha512-cEXJQY/ZqMACb+nxzDeX9IPLAg7S94xouJJCNVE5BJM8JUEP4HeTF+ti3cmxWeSJo+5D+o8Tc0UAWUkfENdeyw==} 640 | cpu: [arm64] 641 | os: [darwin] 642 | requiresBuild: true 643 | dev: true 644 | optional: true 645 | 646 | /@rollup/rollup-darwin-x64@4.6.1: 647 | resolution: {integrity: sha512-LoSU9Xu56isrkV2jLldcKspJ7sSXmZWkAxg7sW/RfF7GS4F5/v4EiqKSMCFbZtDu2Nc1gxxFdQdKwkKS4rwxNg==} 648 | cpu: [x64] 649 | os: [darwin] 650 | requiresBuild: true 651 | dev: true 652 | optional: true 653 | 654 | /@rollup/rollup-linux-arm-gnueabihf@4.6.1: 655 | resolution: {integrity: sha512-EfI3hzYAy5vFNDqpXsNxXcgRDcFHUWSx5nnRSCKwXuQlI5J9dD84g2Usw81n3FLBNsGCegKGwwTVsSKK9cooSQ==} 656 | cpu: [arm] 657 | os: [linux] 658 | requiresBuild: true 659 | dev: true 660 | optional: true 661 | 662 | /@rollup/rollup-linux-arm64-gnu@4.6.1: 663 | resolution: {integrity: sha512-9lhc4UZstsegbNLhH0Zu6TqvDfmhGzuCWtcTFXY10VjLLUe4Mr0Ye2L3rrtHaDd/J5+tFMEuo5LTCSCMXWfUKw==} 664 | cpu: [arm64] 665 | os: [linux] 666 | requiresBuild: true 667 | dev: true 668 | optional: true 669 | 670 | /@rollup/rollup-linux-arm64-musl@4.6.1: 671 | resolution: {integrity: sha512-FfoOK1yP5ksX3wwZ4Zk1NgyGHZyuRhf99j64I5oEmirV8EFT7+OhUZEnP+x17lcP/QHJNWGsoJwrz4PJ9fBEXw==} 672 | cpu: [arm64] 673 | os: [linux] 674 | requiresBuild: true 675 | dev: true 676 | optional: true 677 | 678 | /@rollup/rollup-linux-x64-gnu@4.6.1: 679 | resolution: {integrity: sha512-DNGZvZDO5YF7jN5fX8ZqmGLjZEXIJRdJEdTFMhiyXqyXubBa0WVLDWSNlQ5JR2PNgDbEV1VQowhVRUh+74D+RA==} 680 | cpu: [x64] 681 | os: [linux] 682 | requiresBuild: true 683 | dev: true 684 | optional: true 685 | 686 | /@rollup/rollup-linux-x64-musl@4.6.1: 687 | resolution: {integrity: sha512-RkJVNVRM+piYy87HrKmhbexCHg3A6Z6MU0W9GHnJwBQNBeyhCJG9KDce4SAMdicQnpURggSvtbGo9xAWOfSvIQ==} 688 | cpu: [x64] 689 | os: [linux] 690 | requiresBuild: true 691 | dev: true 692 | optional: true 693 | 694 | /@rollup/rollup-win32-arm64-msvc@4.6.1: 695 | resolution: {integrity: sha512-v2FVT6xfnnmTe3W9bJXl6r5KwJglMK/iRlkKiIFfO6ysKs0rDgz7Cwwf3tjldxQUrHL9INT/1r4VA0n9L/F1vQ==} 696 | cpu: [arm64] 697 | os: [win32] 698 | requiresBuild: true 699 | dev: true 700 | optional: true 701 | 702 | /@rollup/rollup-win32-ia32-msvc@4.6.1: 703 | resolution: {integrity: sha512-YEeOjxRyEjqcWphH9dyLbzgkF8wZSKAKUkldRY6dgNR5oKs2LZazqGB41cWJ4Iqqcy9/zqYgmzBkRoVz3Q9MLw==} 704 | cpu: [ia32] 705 | os: [win32] 706 | requiresBuild: true 707 | dev: true 708 | optional: true 709 | 710 | /@rollup/rollup-win32-x64-msvc@4.6.1: 711 | resolution: {integrity: sha512-0zfTlFAIhgz8V2G8STq8toAjsYYA6eci1hnXuyOTUFnymrtJwnS6uGKiv3v5UrPZkBlamLvrLV2iiaeqCKzb0A==} 712 | cpu: [x64] 713 | os: [win32] 714 | requiresBuild: true 715 | dev: true 716 | optional: true 717 | 718 | /@swc/core-darwin-arm64@1.3.100: 719 | resolution: {integrity: sha512-XVWFsKe6ei+SsDbwmsuRkYck1SXRpO60Hioa4hoLwR8fxbA9eVp6enZtMxzVVMBi8ej5seZ4HZQeAWepbukiBw==} 720 | engines: {node: '>=10'} 721 | cpu: [arm64] 722 | os: [darwin] 723 | requiresBuild: true 724 | dev: true 725 | optional: true 726 | 727 | /@swc/core-darwin-x64@1.3.100: 728 | resolution: {integrity: sha512-KF/MXrnH1nakm1wbt4XV8FS7kvqD9TGmVxeJ0U4bbvxXMvzeYUurzg3AJUTXYmXDhH/VXOYJE5N5RkwZZPs5iA==} 729 | engines: {node: '>=10'} 730 | cpu: [x64] 731 | os: [darwin] 732 | requiresBuild: true 733 | dev: true 734 | optional: true 735 | 736 | /@swc/core-linux-arm64-gnu@1.3.100: 737 | resolution: {integrity: sha512-p8hikNnAEJrw5vHCtKiFT4hdlQxk1V7vqPmvUDgL/qe2menQDK/i12tbz7/3BEQ4UqUPnvwpmVn2d19RdEMNxw==} 738 | engines: {node: '>=10'} 739 | cpu: [arm64] 740 | os: [linux] 741 | requiresBuild: true 742 | dev: true 743 | optional: true 744 | 745 | /@swc/core-linux-arm64-musl@1.3.100: 746 | resolution: {integrity: sha512-BWx/0EeY89WC4q3AaIaBSGfQxkYxIlS3mX19dwy2FWJs/O+fMvF9oLk/CyJPOZzbp+1DjGeeoGFuDYpiNO91JA==} 747 | engines: {node: '>=10'} 748 | cpu: [arm64] 749 | os: [linux] 750 | requiresBuild: true 751 | dev: true 752 | optional: true 753 | 754 | /@swc/core-linux-x64-gnu@1.3.100: 755 | resolution: {integrity: sha512-XUdGu3dxAkjsahLYnm8WijPfKebo+jHgHphDxaW0ovI6sTdmEGFDew7QzKZRlbYL2jRkUuuKuDGvD6lO5frmhA==} 756 | engines: {node: '>=10'} 757 | cpu: [x64] 758 | os: [linux] 759 | requiresBuild: true 760 | dev: true 761 | optional: true 762 | 763 | /@swc/core-linux-x64-musl@1.3.100: 764 | resolution: {integrity: sha512-PhoXKf+f0OaNW/GCuXjJ0/KfK9EJX7z2gko+7nVnEA0p3aaPtbP6cq1Ubbl6CMoPL+Ci3gZ7nYumDqXNc3CtLQ==} 765 | engines: {node: '>=10'} 766 | cpu: [x64] 767 | os: [linux] 768 | requiresBuild: true 769 | dev: true 770 | optional: true 771 | 772 | /@swc/core-win32-arm64-msvc@1.3.100: 773 | resolution: {integrity: sha512-PwLADZN6F9cXn4Jw52FeP/MCLVHm8vwouZZSOoOScDtihjY495SSjdPnlosMaRSR4wJQssGwiD/4MbpgQPqbAw==} 774 | engines: {node: '>=10'} 775 | cpu: [arm64] 776 | os: [win32] 777 | requiresBuild: true 778 | dev: true 779 | optional: true 780 | 781 | /@swc/core-win32-ia32-msvc@1.3.100: 782 | resolution: {integrity: sha512-0f6nicKSLlDKlyPRl2JEmkpBV4aeDfRQg6n8mPqgL7bliZIcDahG0ej+HxgNjZfS3e0yjDxsNRa6sAqWU2Z60A==} 783 | engines: {node: '>=10'} 784 | cpu: [ia32] 785 | os: [win32] 786 | requiresBuild: true 787 | dev: true 788 | optional: true 789 | 790 | /@swc/core-win32-x64-msvc@1.3.100: 791 | resolution: {integrity: sha512-b7J0rPoMkRTa3XyUGt8PwCaIBuYWsL2DqbirrQKRESzgCvif5iNpqaM6kjIjI/5y5q1Ycv564CB51YDpiS8EtQ==} 792 | engines: {node: '>=10'} 793 | cpu: [x64] 794 | os: [win32] 795 | requiresBuild: true 796 | dev: true 797 | optional: true 798 | 799 | /@swc/core@1.3.100: 800 | resolution: {integrity: sha512-7dKgTyxJjlrMwFZYb1auj3Xq0D8ZBe+5oeIgfMlRU05doXZypYJe0LAk0yjj3WdbwYzpF+T1PLxwTWizI0pckw==} 801 | engines: {node: '>=10'} 802 | requiresBuild: true 803 | peerDependencies: 804 | '@swc/helpers': ^0.5.0 805 | peerDependenciesMeta: 806 | '@swc/helpers': 807 | optional: true 808 | dependencies: 809 | '@swc/counter': 0.1.2 810 | '@swc/types': 0.1.5 811 | optionalDependencies: 812 | '@swc/core-darwin-arm64': 1.3.100 813 | '@swc/core-darwin-x64': 1.3.100 814 | '@swc/core-linux-arm64-gnu': 1.3.100 815 | '@swc/core-linux-arm64-musl': 1.3.100 816 | '@swc/core-linux-x64-gnu': 1.3.100 817 | '@swc/core-linux-x64-musl': 1.3.100 818 | '@swc/core-win32-arm64-msvc': 1.3.100 819 | '@swc/core-win32-ia32-msvc': 1.3.100 820 | '@swc/core-win32-x64-msvc': 1.3.100 821 | dev: true 822 | 823 | /@swc/counter@0.1.2: 824 | resolution: {integrity: sha512-9F4ys4C74eSTEUNndnER3VJ15oru2NumfQxS8geE+f3eB5xvfxpWyqE5XlVnxb/R14uoXi6SLbBwwiDSkv+XEw==} 825 | dev: true 826 | 827 | /@swc/types@0.1.5: 828 | resolution: {integrity: sha512-myfUej5naTBWnqOCc/MdVOLVjXUXtIA+NpDrDBKJtLLg2shUjBu3cZmB/85RyitKc55+lUUyl7oRfLOvkr2hsw==} 829 | dev: true 830 | 831 | /@types/parse-json@4.0.2: 832 | resolution: {integrity: sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==} 833 | dev: false 834 | 835 | /@types/prop-types@15.7.11: 836 | resolution: {integrity: sha512-ga8y9v9uyeiLdpKddhxYQkxNDrfvuPrlFb0N1qnZZByvcElJaXthF1UhvCh9TLWJBEHeNtdnbysW7Y6Uq8CVng==} 837 | 838 | /@types/react-dom@18.2.17: 839 | resolution: {integrity: sha512-rvrT/M7Df5eykWFxn6MYt5Pem/Dbyc1N8Y0S9Mrkw2WFCRiqUgw9P7ul2NpwsXCSM1DVdENzdG9J5SreqfAIWg==} 840 | dependencies: 841 | '@types/react': 18.2.42 842 | dev: true 843 | 844 | /@types/react-transition-group@4.4.10: 845 | resolution: {integrity: sha512-hT/+s0VQs2ojCX823m60m5f0sL5idt9SO6Tj6Dg+rdphGPIeJbJ6CxvBYkgkGKrYeDjvIpKTR38UzmtHJOGW3Q==} 846 | dependencies: 847 | '@types/react': 18.2.42 848 | dev: false 849 | 850 | /@types/react@18.2.42: 851 | resolution: {integrity: sha512-c1zEr96MjakLYus/wPnuWDo1/zErfdU9rNsIGmE+NV71nx88FG9Ttgo5dqorXTu/LImX2f63WBP986gJkMPNbA==} 852 | dependencies: 853 | '@types/prop-types': 15.7.11 854 | '@types/scheduler': 0.16.8 855 | csstype: 3.1.2 856 | 857 | /@types/scheduler@0.16.8: 858 | resolution: {integrity: sha512-WZLiwShhwLRmeV6zH+GkbOFT6Z6VklCItrDioxUnv+u4Ll+8vKeFySoFyK/0ctcRpOmwAicELfmys1sDc/Rw+A==} 859 | 860 | /@vitejs/plugin-react-swc@3.5.0(vite@5.0.5): 861 | resolution: {integrity: sha512-1PrOvAaDpqlCV+Up8RkAh9qaiUjoDUcjtttyhXDKw53XA6Ve16SOp6cCOpRs8Dj8DqUQs6eTW5YkLcLJjrXAig==} 862 | peerDependencies: 863 | vite: ^4 || ^5 864 | dependencies: 865 | '@swc/core': 1.3.100 866 | vite: 5.0.5 867 | transitivePeerDependencies: 868 | - '@swc/helpers' 869 | dev: true 870 | 871 | /ansi-styles@3.2.1: 872 | resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==} 873 | engines: {node: '>=4'} 874 | dependencies: 875 | color-convert: 1.9.3 876 | dev: false 877 | 878 | /babel-plugin-macros@3.1.0: 879 | resolution: {integrity: sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==} 880 | engines: {node: '>=10', npm: '>=6'} 881 | dependencies: 882 | '@babel/runtime': 7.23.5 883 | cosmiconfig: 7.1.0 884 | resolve: 1.22.8 885 | dev: false 886 | 887 | /callsites@3.1.0: 888 | resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} 889 | engines: {node: '>=6'} 890 | dev: false 891 | 892 | /chalk@2.4.2: 893 | resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} 894 | engines: {node: '>=4'} 895 | dependencies: 896 | ansi-styles: 3.2.1 897 | escape-string-regexp: 1.0.5 898 | supports-color: 5.5.0 899 | dev: false 900 | 901 | /clsx@2.0.0: 902 | resolution: {integrity: sha512-rQ1+kcj+ttHG0MKVGBUXwayCCF1oh39BF5COIpRzuCEv8Mwjv0XucrI2ExNTOn9IlLifGClWQcU9BrZORvtw6Q==} 903 | engines: {node: '>=6'} 904 | dev: false 905 | 906 | /color-convert@1.9.3: 907 | resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} 908 | dependencies: 909 | color-name: 1.1.3 910 | dev: false 911 | 912 | /color-name@1.1.3: 913 | resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==} 914 | dev: false 915 | 916 | /convert-source-map@1.9.0: 917 | resolution: {integrity: sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==} 918 | dev: false 919 | 920 | /cosmiconfig@7.1.0: 921 | resolution: {integrity: sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==} 922 | engines: {node: '>=10'} 923 | dependencies: 924 | '@types/parse-json': 4.0.2 925 | import-fresh: 3.3.0 926 | parse-json: 5.2.0 927 | path-type: 4.0.0 928 | yaml: 1.10.2 929 | dev: false 930 | 931 | /csstype@3.1.2: 932 | resolution: {integrity: sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==} 933 | 934 | /dom-helpers@5.2.1: 935 | resolution: {integrity: sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==} 936 | dependencies: 937 | '@babel/runtime': 7.23.5 938 | csstype: 3.1.2 939 | dev: false 940 | 941 | /error-ex@1.3.2: 942 | resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==} 943 | dependencies: 944 | is-arrayish: 0.2.1 945 | dev: false 946 | 947 | /esbuild@0.19.8: 948 | resolution: {integrity: sha512-l7iffQpT2OrZfH2rXIp7/FkmaeZM0vxbxN9KfiCwGYuZqzMg/JdvX26R31Zxn/Pxvsrg3Y9N6XTcnknqDyyv4w==} 949 | engines: {node: '>=12'} 950 | hasBin: true 951 | requiresBuild: true 952 | optionalDependencies: 953 | '@esbuild/android-arm': 0.19.8 954 | '@esbuild/android-arm64': 0.19.8 955 | '@esbuild/android-x64': 0.19.8 956 | '@esbuild/darwin-arm64': 0.19.8 957 | '@esbuild/darwin-x64': 0.19.8 958 | '@esbuild/freebsd-arm64': 0.19.8 959 | '@esbuild/freebsd-x64': 0.19.8 960 | '@esbuild/linux-arm': 0.19.8 961 | '@esbuild/linux-arm64': 0.19.8 962 | '@esbuild/linux-ia32': 0.19.8 963 | '@esbuild/linux-loong64': 0.19.8 964 | '@esbuild/linux-mips64el': 0.19.8 965 | '@esbuild/linux-ppc64': 0.19.8 966 | '@esbuild/linux-riscv64': 0.19.8 967 | '@esbuild/linux-s390x': 0.19.8 968 | '@esbuild/linux-x64': 0.19.8 969 | '@esbuild/netbsd-x64': 0.19.8 970 | '@esbuild/openbsd-x64': 0.19.8 971 | '@esbuild/sunos-x64': 0.19.8 972 | '@esbuild/win32-arm64': 0.19.8 973 | '@esbuild/win32-ia32': 0.19.8 974 | '@esbuild/win32-x64': 0.19.8 975 | dev: true 976 | 977 | /escape-string-regexp@1.0.5: 978 | resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} 979 | engines: {node: '>=0.8.0'} 980 | dev: false 981 | 982 | /escape-string-regexp@4.0.0: 983 | resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} 984 | engines: {node: '>=10'} 985 | dev: false 986 | 987 | /find-root@1.1.0: 988 | resolution: {integrity: sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==} 989 | dev: false 990 | 991 | /fsevents@2.3.3: 992 | resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} 993 | engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} 994 | os: [darwin] 995 | requiresBuild: true 996 | dev: true 997 | optional: true 998 | 999 | /function-bind@1.1.2: 1000 | resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} 1001 | dev: false 1002 | 1003 | /has-flag@3.0.0: 1004 | resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==} 1005 | engines: {node: '>=4'} 1006 | dev: false 1007 | 1008 | /hasown@2.0.0: 1009 | resolution: {integrity: sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==} 1010 | engines: {node: '>= 0.4'} 1011 | dependencies: 1012 | function-bind: 1.1.2 1013 | dev: false 1014 | 1015 | /hoist-non-react-statics@3.3.2: 1016 | resolution: {integrity: sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==} 1017 | dependencies: 1018 | react-is: 16.13.1 1019 | dev: false 1020 | 1021 | /import-fresh@3.3.0: 1022 | resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==} 1023 | engines: {node: '>=6'} 1024 | dependencies: 1025 | parent-module: 1.0.1 1026 | resolve-from: 4.0.0 1027 | dev: false 1028 | 1029 | /is-arrayish@0.2.1: 1030 | resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} 1031 | dev: false 1032 | 1033 | /is-core-module@2.13.1: 1034 | resolution: {integrity: sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==} 1035 | dependencies: 1036 | hasown: 2.0.0 1037 | dev: false 1038 | 1039 | /js-tokens@4.0.0: 1040 | resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} 1041 | dev: false 1042 | 1043 | /json-parse-even-better-errors@2.3.1: 1044 | resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} 1045 | dev: false 1046 | 1047 | /lines-and-columns@1.2.4: 1048 | resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} 1049 | dev: false 1050 | 1051 | /loose-envify@1.4.0: 1052 | resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} 1053 | hasBin: true 1054 | dependencies: 1055 | js-tokens: 4.0.0 1056 | dev: false 1057 | 1058 | /nanoid@3.3.7: 1059 | resolution: {integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==} 1060 | engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} 1061 | hasBin: true 1062 | dev: true 1063 | 1064 | /object-assign@4.1.1: 1065 | resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} 1066 | engines: {node: '>=0.10.0'} 1067 | dev: false 1068 | 1069 | /parent-module@1.0.1: 1070 | resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} 1071 | engines: {node: '>=6'} 1072 | dependencies: 1073 | callsites: 3.1.0 1074 | dev: false 1075 | 1076 | /parse-json@5.2.0: 1077 | resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} 1078 | engines: {node: '>=8'} 1079 | dependencies: 1080 | '@babel/code-frame': 7.23.5 1081 | error-ex: 1.3.2 1082 | json-parse-even-better-errors: 2.3.1 1083 | lines-and-columns: 1.2.4 1084 | dev: false 1085 | 1086 | /path-parse@1.0.7: 1087 | resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} 1088 | dev: false 1089 | 1090 | /path-type@4.0.0: 1091 | resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} 1092 | engines: {node: '>=8'} 1093 | dev: false 1094 | 1095 | /picocolors@1.0.0: 1096 | resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==} 1097 | dev: true 1098 | 1099 | /postcss@8.4.32: 1100 | resolution: {integrity: sha512-D/kj5JNu6oo2EIy+XL/26JEDTlIbB8hw85G8StOE6L74RQAVVP5rej6wxCNqyMbR4RkPfqvezVbPw81Ngd6Kcw==} 1101 | engines: {node: ^10 || ^12 || >=14} 1102 | dependencies: 1103 | nanoid: 3.3.7 1104 | picocolors: 1.0.0 1105 | source-map-js: 1.0.2 1106 | dev: true 1107 | 1108 | /prop-types@15.8.1: 1109 | resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==} 1110 | dependencies: 1111 | loose-envify: 1.4.0 1112 | object-assign: 4.1.1 1113 | react-is: 16.13.1 1114 | dev: false 1115 | 1116 | /react-dom@18.2.0(react@18.2.0): 1117 | resolution: {integrity: sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==} 1118 | peerDependencies: 1119 | react: ^18.2.0 1120 | dependencies: 1121 | loose-envify: 1.4.0 1122 | react: 18.2.0 1123 | scheduler: 0.23.0 1124 | dev: false 1125 | 1126 | /react-is@16.13.1: 1127 | resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} 1128 | dev: false 1129 | 1130 | /react-is@18.2.0: 1131 | resolution: {integrity: sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==} 1132 | dev: false 1133 | 1134 | /react-transition-group@4.4.5(react-dom@18.2.0)(react@18.2.0): 1135 | resolution: {integrity: sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==} 1136 | peerDependencies: 1137 | react: '>=16.6.0' 1138 | react-dom: '>=16.6.0' 1139 | dependencies: 1140 | '@babel/runtime': 7.23.5 1141 | dom-helpers: 5.2.1 1142 | loose-envify: 1.4.0 1143 | prop-types: 15.8.1 1144 | react: 18.2.0 1145 | react-dom: 18.2.0(react@18.2.0) 1146 | dev: false 1147 | 1148 | /react@18.2.0: 1149 | resolution: {integrity: sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==} 1150 | engines: {node: '>=0.10.0'} 1151 | dependencies: 1152 | loose-envify: 1.4.0 1153 | dev: false 1154 | 1155 | /regenerator-runtime@0.14.0: 1156 | resolution: {integrity: sha512-srw17NI0TUWHuGa5CFGGmhfNIeja30WMBfbslPNhf6JrqQlLN5gcrvig1oqPxiVaXb0oW0XRKtH6Nngs5lKCIA==} 1157 | dev: false 1158 | 1159 | /resolve-from@4.0.0: 1160 | resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} 1161 | engines: {node: '>=4'} 1162 | dev: false 1163 | 1164 | /resolve@1.22.8: 1165 | resolution: {integrity: sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==} 1166 | hasBin: true 1167 | dependencies: 1168 | is-core-module: 2.13.1 1169 | path-parse: 1.0.7 1170 | supports-preserve-symlinks-flag: 1.0.0 1171 | dev: false 1172 | 1173 | /rollup@4.6.1: 1174 | resolution: {integrity: sha512-jZHaZotEHQaHLgKr8JnQiDT1rmatjgKlMekyksz+yk9jt/8z9quNjnKNRoaM0wd9DC2QKXjmWWuDYtM3jfF8pQ==} 1175 | engines: {node: '>=18.0.0', npm: '>=8.0.0'} 1176 | hasBin: true 1177 | optionalDependencies: 1178 | '@rollup/rollup-android-arm-eabi': 4.6.1 1179 | '@rollup/rollup-android-arm64': 4.6.1 1180 | '@rollup/rollup-darwin-arm64': 4.6.1 1181 | '@rollup/rollup-darwin-x64': 4.6.1 1182 | '@rollup/rollup-linux-arm-gnueabihf': 4.6.1 1183 | '@rollup/rollup-linux-arm64-gnu': 4.6.1 1184 | '@rollup/rollup-linux-arm64-musl': 4.6.1 1185 | '@rollup/rollup-linux-x64-gnu': 4.6.1 1186 | '@rollup/rollup-linux-x64-musl': 4.6.1 1187 | '@rollup/rollup-win32-arm64-msvc': 4.6.1 1188 | '@rollup/rollup-win32-ia32-msvc': 4.6.1 1189 | '@rollup/rollup-win32-x64-msvc': 4.6.1 1190 | fsevents: 2.3.3 1191 | dev: true 1192 | 1193 | /scheduler@0.23.0: 1194 | resolution: {integrity: sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==} 1195 | dependencies: 1196 | loose-envify: 1.4.0 1197 | dev: false 1198 | 1199 | /source-map-js@1.0.2: 1200 | resolution: {integrity: sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==} 1201 | engines: {node: '>=0.10.0'} 1202 | dev: true 1203 | 1204 | /source-map@0.5.7: 1205 | resolution: {integrity: sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==} 1206 | engines: {node: '>=0.10.0'} 1207 | dev: false 1208 | 1209 | /stylis@4.2.0: 1210 | resolution: {integrity: sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==} 1211 | dev: false 1212 | 1213 | /supports-color@5.5.0: 1214 | resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==} 1215 | engines: {node: '>=4'} 1216 | dependencies: 1217 | has-flag: 3.0.0 1218 | dev: false 1219 | 1220 | /supports-preserve-symlinks-flag@1.0.0: 1221 | resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} 1222 | engines: {node: '>= 0.4'} 1223 | dev: false 1224 | 1225 | /to-fast-properties@2.0.0: 1226 | resolution: {integrity: sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==} 1227 | engines: {node: '>=4'} 1228 | dev: false 1229 | 1230 | /typescript@5.3.2: 1231 | resolution: {integrity: sha512-6l+RyNy7oAHDfxC4FzSJcz9vnjTKxrLpDG5M2Vu4SHRVNg6xzqZp6LYSR9zjqQTu8DU/f5xwxUdADOkbrIX2gQ==} 1232 | engines: {node: '>=14.17'} 1233 | hasBin: true 1234 | dev: true 1235 | 1236 | /vite@5.0.5: 1237 | resolution: {integrity: sha512-OekeWqR9Ls56f3zd4CaxzbbS11gqYkEiBtnWFFgYR2WV8oPJRRKq0mpskYy/XaoCL3L7VINDhqqOMNDiYdGvGg==} 1238 | engines: {node: ^18.0.0 || >=20.0.0} 1239 | hasBin: true 1240 | peerDependencies: 1241 | '@types/node': ^18.0.0 || >=20.0.0 1242 | less: '*' 1243 | lightningcss: ^1.21.0 1244 | sass: '*' 1245 | stylus: '*' 1246 | sugarss: '*' 1247 | terser: ^5.4.0 1248 | peerDependenciesMeta: 1249 | '@types/node': 1250 | optional: true 1251 | less: 1252 | optional: true 1253 | lightningcss: 1254 | optional: true 1255 | sass: 1256 | optional: true 1257 | stylus: 1258 | optional: true 1259 | sugarss: 1260 | optional: true 1261 | terser: 1262 | optional: true 1263 | dependencies: 1264 | esbuild: 0.19.8 1265 | postcss: 8.4.32 1266 | rollup: 4.6.1 1267 | optionalDependencies: 1268 | fsevents: 2.3.3 1269 | dev: true 1270 | 1271 | /yaml@1.10.2: 1272 | resolution: {integrity: sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==} 1273 | engines: {node: '>= 6'} 1274 | dev: false 1275 | -------------------------------------------------------------------------------- /pnpm-workspace.yaml: -------------------------------------------------------------------------------- 1 | packages: 2 | - frontend 3 | -------------------------------------------------------------------------------- /wasm/syntaxck/main.go: -------------------------------------------------------------------------------- 1 | //go:build wasm 2 | 3 | package main 4 | 5 | import ( 6 | "syscall/js" 7 | 8 | "github.com/abiriadev/goggle/pkg/query" 9 | ) 10 | 11 | // global-lifetime query parser 12 | // TODO: use closure later? 13 | var qp = query.QueryParserUnsafe() 14 | 15 | func main() { 16 | js.Global().Set("syntaxck", js.FuncOf(syntaxckSys)) 17 | 18 | <-make(chan struct{}, 0) 19 | } 20 | 21 | // string -> bool 22 | func syntaxckSys(_ js.Value, args []js.Value) any { 23 | // SAFETY: check array boundaries 24 | if len(args) != 1 { 25 | return "only one argument needed" 26 | } 27 | 28 | return syntaxck(args[0].String()) 29 | } 30 | 31 | func syntaxck(query string) bool { 32 | _, err := qp.ParseString("", query) 33 | return err == nil 34 | } 35 | --------------------------------------------------------------------------------