├── .envrc ├── .github └── workflows │ └── docker.yml ├── .gitignore ├── .ocamlformat ├── LICENSE.md ├── Makefile ├── README.md ├── blogs ├── how_not_to_build_a_website.md ├── how_to_build_a_website.md └── to_learn_or_to_do.md ├── db ├── init.sql ├── insert.sh └── update.sh ├── dune-project ├── flake.lock ├── flake.nix ├── lib └── turso │ ├── Makefile │ ├── README.md │ ├── cache.ml │ ├── cache.mli │ ├── cmd │ └── C.go │ ├── dune │ ├── go.mod │ ├── go.sum │ ├── pkg │ ├── blog │ │ ├── model.go │ │ └── service.go │ └── database │ │ └── database.go │ ├── turso.ml │ ├── turso.mli │ └── vendor │ ├── github.com │ ├── antlr │ │ └── antlr4 │ │ │ └── runtime │ │ │ └── Go │ │ │ └── antlr │ │ │ └── v4 │ │ │ ├── LICENSE │ │ │ ├── antlrdoc.go │ │ │ ├── atn.go │ │ │ ├── atn_config.go │ │ │ ├── atn_config_set.go │ │ │ ├── atn_deserialization_options.go │ │ │ ├── atn_deserializer.go │ │ │ ├── atn_simulator.go │ │ │ ├── atn_state.go │ │ │ ├── atn_type.go │ │ │ ├── char_stream.go │ │ │ ├── common_token_factory.go │ │ │ ├── common_token_stream.go │ │ │ ├── comparators.go │ │ │ ├── dfa.go │ │ │ ├── dfa_serializer.go │ │ │ ├── dfa_state.go │ │ │ ├── diagnostic_error_listener.go │ │ │ ├── error_listener.go │ │ │ ├── error_strategy.go │ │ │ ├── errors.go │ │ │ ├── file_stream.go │ │ │ ├── input_stream.go │ │ │ ├── int_stream.go │ │ │ ├── interval_set.go │ │ │ ├── jcollect.go │ │ │ ├── lexer.go │ │ │ ├── lexer_action.go │ │ │ ├── lexer_action_executor.go │ │ │ ├── lexer_atn_simulator.go │ │ │ ├── ll1_analyzer.go │ │ │ ├── parser.go │ │ │ ├── parser_atn_simulator.go │ │ │ ├── parser_rule_context.go │ │ │ ├── prediction_context.go │ │ │ ├── prediction_mode.go │ │ │ ├── recognizer.go │ │ │ ├── rule_context.go │ │ │ ├── semantic_context.go │ │ │ ├── token.go │ │ │ ├── token_source.go │ │ │ ├── token_stream.go │ │ │ ├── tokenstream_rewriter.go │ │ │ ├── trace_listener.go │ │ │ ├── transition.go │ │ │ ├── tree.go │ │ │ ├── trees.go │ │ │ ├── utils.go │ │ │ └── utils_set.go │ ├── klauspost │ │ └── compress │ │ │ ├── LICENSE │ │ │ └── flate │ │ │ ├── deflate.go │ │ │ ├── dict_decoder.go │ │ │ ├── fast_encoder.go │ │ │ ├── huffman_bit_writer.go │ │ │ ├── huffman_code.go │ │ │ ├── huffman_sortByFreq.go │ │ │ ├── huffman_sortByLiteral.go │ │ │ ├── inflate.go │ │ │ ├── inflate_gen.go │ │ │ ├── level1.go │ │ │ ├── level2.go │ │ │ ├── level3.go │ │ │ ├── level4.go │ │ │ ├── level5.go │ │ │ ├── level6.go │ │ │ ├── regmask_amd64.go │ │ │ ├── regmask_other.go │ │ │ ├── stateless.go │ │ │ └── token.go │ ├── libsql │ │ └── sqlite-antlr4-parser │ │ │ ├── sqliteparser │ │ │ ├── SQLiteLexer.interp │ │ │ ├── SQLiteLexer.tokens │ │ │ ├── SQLiteParser.interp │ │ │ ├── SQLiteParser.tokens │ │ │ ├── sqlite_lexer.go │ │ │ ├── sqlite_parser.go │ │ │ ├── sqliteparser_base_listener.go │ │ │ └── sqliteparser_listener.go │ │ │ └── sqliteparserutils │ │ │ └── utils.go │ └── tursodatabase │ │ └── libsql-client-go │ │ ├── LICENSE │ │ └── libsql │ │ ├── internal │ │ ├── hrana │ │ │ ├── batch.go │ │ │ ├── batch_result.go │ │ │ ├── pipeline_request.go │ │ │ ├── pipeline_response.go │ │ │ ├── stmt.go │ │ │ ├── stmt_result.go │ │ │ ├── stream_request.go │ │ │ ├── stream_result.go │ │ │ └── value.go │ │ ├── http │ │ │ ├── driver.go │ │ │ ├── hranaV2 │ │ │ │ └── hranaV2.go │ │ │ └── shared │ │ │ │ ├── result.go │ │ │ │ ├── rows.go │ │ │ │ └── statement.go │ │ └── ws │ │ │ ├── driver.go │ │ │ └── websockets.go │ │ └── sql.go │ ├── golang.org │ └── x │ │ └── exp │ │ ├── LICENSE │ │ ├── PATENTS │ │ ├── constraints │ │ └── constraints.go │ │ └── slices │ │ ├── slices.go │ │ ├── sort.go │ │ ├── zsortfunc.go │ │ └── zsortordered.go │ ├── modules.txt │ └── nhooyr.io │ └── websocket │ ├── .gitignore │ ├── LICENSE.txt │ ├── README.md │ ├── accept.go │ ├── accept_js.go │ ├── close.go │ ├── close_notjs.go │ ├── compress.go │ ├── compress_notjs.go │ ├── conn.go │ ├── conn_notjs.go │ ├── dial.go │ ├── doc.go │ ├── frame.go │ ├── internal │ ├── bpool │ │ └── bpool.go │ ├── errd │ │ └── wrap.go │ ├── wsjs │ │ └── wsjs_js.go │ └── xsync │ │ ├── go.go │ │ └── int64.go │ ├── netconn.go │ ├── read.go │ ├── stringer.go │ ├── write.go │ ├── ws_js.go │ └── wsjson │ └── wsjson.go ├── nix ├── default.nix ├── docker.nix └── shell.nix ├── public ├── favicon │ ├── android-chrome-192x192.png │ ├── android-chrome-512x512.png │ ├── apple-touch-icon.png │ ├── favicon-16x16.png │ ├── favicon-32x32.png │ ├── favicon.ico │ └── site.webmanifest ├── fonts │ ├── fonts.css │ ├── inter-latin.woff2 │ ├── montserrat-latin-italic.woff2 │ └── montserrat-latin.woff2 ├── js │ ├── _hyperscript@0.9.12.min.js │ ├── antiThePrimeagen@0.0.2.js │ └── htmx@1.9.10.min.js ├── seo │ ├── robots.txt │ └── sitemap.xml ├── styles │ ├── abstract │ │ ├── index.css │ │ └── variables.css │ ├── base │ │ ├── base.css │ │ ├── index.css │ │ ├── reset.css │ │ └── util.css │ ├── components │ │ ├── call_to_action.css │ │ ├── card.css │ │ ├── fancy.css │ │ ├── footer.css │ │ ├── header.css │ │ ├── hero.css │ │ ├── icon.css │ │ ├── index.css │ │ ├── logo.css │ │ ├── search.css │ │ └── tags.css │ ├── main.css │ └── pages │ │ ├── blog.css │ │ ├── explore.css │ │ ├── home.css │ │ └── index.css └── svg │ ├── bmc-icon.svg │ ├── logo-github.svg │ ├── logo-twitter.svg │ ├── logo.svg │ └── mail.svg ├── src ├── components │ ├── blog.re │ ├── blog_cards.re │ ├── blog_section.re │ ├── call_to_action.re │ ├── doc.re │ ├── dune │ ├── footer.re │ ├── header.re │ ├── hero.re │ ├── icon.re │ ├── logo.re │ ├── search.re │ └── view.ml ├── dune ├── main.ml ├── pages │ ├── blog.ml │ ├── dune │ ├── explore.ml │ ├── index.ml │ └── view.ml └── search.ml └── webserver.opam /.envrc: -------------------------------------------------------------------------------- 1 | use flake 2 | dotenv 3 | -------------------------------------------------------------------------------- /.github/workflows/docker.yml: -------------------------------------------------------------------------------- 1 | name: Build and Push Nix Docker Image to GHCR 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | 8 | jobs: 9 | build-and-push: 10 | runs-on: ubuntu-latest 11 | permissions: 12 | contents: read 13 | packages: write 14 | 15 | steps: 16 | - name: Checkout 17 | uses: actions/checkout@v2 18 | 19 | - name: Setup Nix 20 | uses: cachix/install-nix-action@v16 21 | with: 22 | install_url: https://releases.nixos.org/nix/nix-2.19.3/install 23 | extra_nix_config: | 24 | experimental-features = nix-command flakes 25 | 26 | - name: Build Docker image with Nix 27 | run: nix build .#docker 28 | 29 | - name: Load Docker image 30 | run: | 31 | docker load < $(nix path-info .#docker) 32 | 33 | - name: Log in to GitHub Container Registry 34 | uses: docker/login-action@v2 35 | with: 36 | registry: ghcr.io 37 | username: ${{ github.repository_owner }} 38 | password: ${{ secrets.GITHUB_TOKEN }} 39 | 40 | - name: Push Docker image to GitHub Container Registry 41 | run: | 42 | IMAGE_ID=ghcr.io/${{ github.repository }}/webserver:latest 43 | docker tag $(docker images --format "{{.Repository}}:{{.Tag}}" | head -n 1) $IMAGE_ID 44 | docker push $IMAGE_ID 45 | 46 | - name: Trigger Render Deployment 47 | run: | 48 | curl -X POST ${{ secrets.RENDER_DEPLOY_HOOK }} 49 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .direnv/ 2 | _build/ 3 | result 4 | .env 5 | -------------------------------------------------------------------------------- /.ocamlformat: -------------------------------------------------------------------------------- 1 | profile = default 2 | version = 0.26.1 3 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Ethan Thoma 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: healthy 2 | 3 | IMAGE_NAME = webserver 4 | TAG = latest 5 | CONTAINER_NAME = "webserver-instance" 6 | LOCAL_HOST = 127.0.0.1:3000 7 | 8 | install: 9 | opam switch create . 5.1.1 -w --no-install -y 10 | opam install dream reason tyxml-jsx fuzzy_match omd ppx_string ocaml-lsp-server -y 11 | 12 | build: 13 | nix build .#docker 14 | 15 | run: build 16 | docker load < result 17 | docker run --env-file ./.env -d -t --name $(CONTAINER_NAME) -p $(LOCAL_HOST):3000 $(IMAGE_NAME):$(TAG) 18 | 19 | healthy: 20 | wget $(LOCAL_HOST)/healthy 21 | rm healthy 22 | 23 | clean: 24 | -docker stop $(CONTAINER_NAME) 25 | -docker rm $(CONTAINER_NAME) 26 | -docker rmi $(IMAGE_NAME):$(TAG) 27 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | Logo 6 |
7 | Blog and Personal Website 8 |

9 | 10 |

11 | 12 | 13 | 14 |

15 | 16 | 17 | ## 🤖 Stack 18 | 19 | | Tech | Stack | 20 | |----------------|----------| 21 | | Htmx | Frontend | 22 | | Ocaml & Reason | Backend | 23 | | Turso w/ Go | Database | 24 | 25 | ## 🚀 Building + Running 26 | 27 | The nix flake has two build targets: 28 | 1. dune project derivation: produces a binary that one can run as is 29 | 2. docker image: produces a minimal image that contains the derivation 30 | 31 | The derivation can be built with `nix build` and the image can be built with 32 | `nix build .#docker`. 33 | 34 | You can run it easily (assuming you have docker setup) with `make run`. If you 35 | want to clean up afterwards, use `make clean`. 36 | 37 | ## 🧮 Complications 38 | 39 | It uses Ocaml for the webserver and Reason for templating. I opted to use Turso 40 | for the DB but the client support for Ocaml is _poor_. So, I compiled Go to C 41 | and use Dune to bind the C code to Ocaml...probably not the best idea but it 42 | does work. Nix is used to build the code and with GitHub workflows, it gets 43 | deployed. Locally, I test using the Makefile. This will let you easily run and 44 | clean. 45 | -------------------------------------------------------------------------------- /blogs/to_learn_or_to_do.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ethanthoma/ocaml-webserver/9a5736548d4bc7b3cfc052c949a61f455276f2b2/blogs/to_learn_or_to_do.md -------------------------------------------------------------------------------- /db/init.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE blogs ( 2 | slug VARCHAR(255) NOT NULL UNIQUE, 3 | title VARCHAR(255) NOT NULL, 4 | description TEXT NOT NULL, 5 | content TEXT NOT NULL, 6 | date TIMESTAMP DEFAULT CURRENT_TIMESTAMP, 7 | tags TEXT NOT NULL, 8 | PRIMARY KEY (slug) 9 | ); 10 | -------------------------------------------------------------------------------- /db/insert.sh: -------------------------------------------------------------------------------- 1 | slug="how_to_build_a_website.md" 2 | title="How to Build a Website: Part Two" 3 | description="Combing Ocaml, HTMX, Go, NIx, etc to make a simple website." 4 | file_path="./blogs/${slug}" 5 | contents="$(cat "$file_path")" 6 | contents="$(echo "$contents" | sed "s/'/''/g")" 7 | tags="Nix, htmx, webdev, Ocaml, Go, C" 8 | 9 | sql="INSERT INTO blogs (title, slug, description, content, tags, date) VALUES('${title}', '${slug}', '${description}', '${contents}', '${tags}', date());" 10 | 11 | echo "$sql" | turso db shell $TURSO_DATABASE_NAME 12 | -------------------------------------------------------------------------------- /db/update.sh: -------------------------------------------------------------------------------- 1 | slug="how_to_build_a_website.md" 2 | file_path="./blogs/${slug}" 3 | contents="$(cat "$file_path")" 4 | contents="$(echo "$contents" | sed "s/'/''/g")" 5 | 6 | sql="UPDATE blogs SET content = " 7 | sql="${sql} '"${contents}"'" 8 | sql="${sql} WHERE slug = '${slug}'" 9 | 10 | echo "$sql" | turso db shell $TURSO_DATABASE_NAME 11 | -------------------------------------------------------------------------------- /dune-project: -------------------------------------------------------------------------------- 1 | (lang dune 3.7) 2 | 3 | (using ctypes 0.3) 4 | 5 | (name webserver) 6 | 7 | (generate_opam_files true) 8 | 9 | (source (github ethanthoma/ocaml-webserver)) 10 | 11 | (authors "Ethan Thoma") 12 | 13 | (maintainers "Ethan Thoma") 14 | 15 | (license LICENSE) 16 | 17 | (package 18 | (name webserver) 19 | (synopsis "Htmx webserver") 20 | (description "Htmx webserver") 21 | (depends 22 | ocaml 23 | dune 24 | dream 25 | reason 26 | tyxml-jsx 27 | fuzzy_match 28 | omd 29 | ppx_string 30 | ctypes 31 | ctypes-foreign 32 | crunch 33 | ) 34 | (tags (topics "Htmx" "JSX" "Ocaml")) 35 | ) 36 | -------------------------------------------------------------------------------- /flake.nix: -------------------------------------------------------------------------------- 1 | { 2 | inputs = { 3 | nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; 4 | opam-nix.url = "github:tweag/opam-nix"; 5 | flake-utils.url = "github:numtide/flake-utils"; 6 | }; 7 | 8 | outputs = { self, flake-utils, opam-nix, nixpkgs }: 9 | let 10 | package = "webserver"; 11 | src = ./.; 12 | in flake-utils.lib.eachDefaultSystem (system: 13 | let 14 | pkgs = nixpkgs.legacyPackages.${system}; 15 | 16 | goDeps = with pkgs; [ 17 | go 18 | libffi 19 | ]; 20 | 21 | cssDeps = with pkgs; [ 22 | lightningcss 23 | ]; 24 | 25 | deps = goDeps ++ cssDeps; 26 | 27 | on = opam-nix.lib.${system}; 28 | 29 | scope = on.buildDuneProject { } package src { 30 | ocaml-base-compiler = "*"; 31 | dream = "*"; 32 | reason = "*"; 33 | tyxml-jsx = "*"; 34 | fuzzy_match = "*"; 35 | omd = "*"; 36 | }; 37 | 38 | overlay = final: prev: { 39 | ${package} = prev.${package}.overrideAttrs (oa: { 40 | buildInputs = oa.buildInputs ++ [ 41 | final.dream 42 | final.reason 43 | final.tyxml-jsx 44 | final.fuzzy_match 45 | final.omd 46 | ]; 47 | nativeBuildInputs = oa.nativeBuildInputs ++ deps; 48 | buildPhase = "HOME=$TMPDIR dune build --release"; 49 | postInstall = '' 50 | cp -rf $src/public $out/public 51 | cp -rf $src/blogs $out/blogs 52 | ''; 53 | }); 54 | }; 55 | 56 | legacyPackages = scope.overrideScope' overlay; 57 | 58 | derivation = self.legacyPackages.${system}.${package}; 59 | in { 60 | inherit legacyPackages; 61 | 62 | packages.default = derivation; 63 | 64 | packages.docker = pkgs.callPackage ./nix/docker.nix { 65 | inherit pkgs package derivation; 66 | }; 67 | 68 | devShells.default = pkgs.callPackage ./nix/shell.nix { 69 | inherit pkgs; 70 | 71 | devDeps = deps; 72 | }; 73 | } 74 | ); 75 | } 76 | -------------------------------------------------------------------------------- /lib/turso/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: build 2 | 3 | build: 4 | go build -o dllgo_turso.so -buildmode=c-shared -mod=vendor ./cmd/ 5 | go build -o libgo_turso.a -buildmode=c-archive -mod=vendor ./cmd/ 6 | -------------------------------------------------------------------------------- /lib/turso/README.md: -------------------------------------------------------------------------------- 1 | # Turso Ocaml-bindings 2 | 3 | **NOTE: this library is setup just for my blogs db and is not generic** 4 | 5 | This is a simple go-to-c Turso library. The Ocaml code binds via Ctypes and Dune. 6 | 7 | Look at cmd/C.go to see how code is exported from the pkg directory. In the 8 | future, I will probably make this more generic but it is sufficient for now. 9 | 10 | If you are wanting to use this code in your own project, you will need to add 11 | this to your executable stanza for Dune: 12 | ```dune 13 | (link_flags -cclib -Wl,--whole-archive -cclib ./lib/turso/libgo_turso.a -cclib -Wl,--no-whole-archive) 14 | ``` 15 | -------------------------------------------------------------------------------- /lib/turso/cache.ml: -------------------------------------------------------------------------------- 1 | module Cache = struct 2 | type 'a t = { 3 | mutable data : 'a; 4 | mutable last_updated : float; 5 | update_function : unit -> 'a; 6 | max_age : float; 7 | } 8 | 9 | let needs_update cache = 10 | let now = Unix.gettimeofday () in 11 | now -. cache.last_updated > cache.max_age 12 | 13 | let update cache = 14 | Dream.log "Cache.update: Checking..."; 15 | if needs_update cache then ( 16 | Dream.log "Cache.update: Fetching from DB..."; 17 | let%lwt new_data = Lwt_preemptive.detach cache.update_function () in 18 | cache.data <- new_data; 19 | cache.last_updated <- Unix.gettimeofday (); 20 | Lwt.return_unit) 21 | else Lwt.return_unit 22 | 23 | let rec schedule_update cache = 24 | Lwt.bind 25 | (Lwt_unix.sleep (60. *. 60. *. 24.)) 26 | (fun () -> 27 | Dream.log "Cache.schedule_update: daily cache fetch"; 28 | let _ = update cache in 29 | schedule_update cache) 30 | 31 | let get cache = 32 | let current_data = cache.data in 33 | let _ = Lwt.async (fun () -> update cache) in 34 | current_data 35 | 36 | let init ~(update_function : unit -> 'a) ?(max_age : float = 60.) () = 37 | let cache = 38 | { 39 | data = update_function (); 40 | last_updated = Unix.gettimeofday (); 41 | update_function; 42 | max_age; 43 | } 44 | in 45 | let _ = Lwt.async (fun () -> schedule_update cache) in 46 | cache 47 | end 48 | 49 | module Blogs = struct 50 | let update () = 51 | Dream.log "Blogs: Updating blog cache..."; 52 | let blogs = Turso.get_blogs () in 53 | blogs 54 | 55 | let cache = Cache.init ~update_function:update ~max_age:(60. *. 5.) () 56 | let cache_by_slug = Hashtbl.create 5 57 | 58 | let update_by_slug slug = 59 | Dream.log "Blogs: Updating slug cache..."; 60 | Turso.get_blog_by_slug slug 61 | 62 | let get () = Cache.get cache 63 | 64 | let get_by_slug slug = 65 | try Cache.get (Hashtbl.find cache_by_slug slug) 66 | with Not_found -> 67 | let new_cache = 68 | Cache.init 69 | ~update_function:(fun () -> update_by_slug slug) 70 | ~max_age:(60. *. 60.) () 71 | in 72 | Hashtbl.add cache_by_slug slug new_cache; 73 | Cache.get new_cache 74 | end 75 | -------------------------------------------------------------------------------- /lib/turso/cache.mli: -------------------------------------------------------------------------------- 1 | module Blogs : sig 2 | val get : unit -> Turso.blog list 3 | val get_by_slug : string -> Turso.blog 4 | end 5 | -------------------------------------------------------------------------------- /lib/turso/cmd/C.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | // #cgo CFLAGS: -fpic 4 | import "C" 5 | 6 | import ( 7 | "fmt" 8 | "os" 9 | 10 | "github.com/ethanthoma/webserver/lib/turso/pkg/blog" 11 | db "github.com/ethanthoma/webserver/lib/turso/pkg/database" 12 | ) 13 | 14 | var database = os.Getenv("TURSO_DATABASE") 15 | var token = os.Getenv("TURSO_DATABASE_TOKEN") 16 | 17 | //export Init 18 | func Init() { 19 | db.Init(database, token) 20 | } 21 | 22 | //export GetBlogTable 23 | func GetBlogTable() *C.char { 24 | blogs, err := blog.GetBlogTable() 25 | if err != nil { 26 | fmt.Println("tb error") 27 | return nil 28 | } 29 | return C.CString(blogs) 30 | } 31 | 32 | //export GetBlogBySlug 33 | func GetBlogBySlug(slug *C.char) *C.char { 34 | blog, err := blog.GetBlogBySlug(C.GoString(slug)) 35 | if err != nil { 36 | return nil 37 | } 38 | return C.CString(blog) 39 | } 40 | 41 | func main() { } 42 | -------------------------------------------------------------------------------- /lib/turso/dune: -------------------------------------------------------------------------------- 1 | (library 2 | (name turso) 3 | (public_name webserver.turso) 4 | (foreign_archives go_turso) 5 | (libraries ctypes ctypes.foreign yojson dream) 6 | (preprocess 7 | (pps lwt_ppx)) 8 | (flags 9 | (:standard -w -49)) 10 | (wrapped false) 11 | (library_flags 12 | (-cclib -lgo_turso))) 13 | 14 | (rule 15 | (deps 16 | (source_tree .)) 17 | (targets libgo_turso.a dllgo_turso.so libgo_turso.h) 18 | (action 19 | (no-infer 20 | (ignore-stdout 21 | (progn 22 | (run make)))))) 23 | 24 | (env 25 | (utop 26 | (env-vars 27 | (OCAML_INTEROP_NO_CAML_STARTUP true)))) 28 | -------------------------------------------------------------------------------- /lib/turso/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/ethanthoma/webserver/lib/turso 2 | 3 | go 1.18 4 | 5 | require ( 6 | github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20230512164433-5d1fd1a340c9 // indirect 7 | github.com/klauspost/compress v1.15.15 // indirect 8 | github.com/libsql/sqlite-antlr4-parser v0.0.0-20230802215326-5cb5bb604475 // indirect 9 | github.com/tursodatabase/libsql-client-go v0.0.0-20240220085343-4ae0eb9d0898 // indirect 10 | golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e // indirect 11 | nhooyr.io/websocket v1.8.7 // indirect 12 | ) 13 | -------------------------------------------------------------------------------- /lib/turso/go.sum: -------------------------------------------------------------------------------- 1 | github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20230512164433-5d1fd1a340c9 h1:goHVqTbFX3AIo0tzGr14pgfAW2ZfPChKO21Z9MGf/gk= 2 | github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20230512164433-5d1fd1a340c9/go.mod h1:pSwJ0fSY5KhvocuWSx4fz3BA8OrA1bQn+K1Eli3BRwM= 3 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 4 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 5 | github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= 6 | github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M= 7 | github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= 8 | github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= 9 | github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= 10 | github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI= 11 | github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo= 12 | github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= 13 | github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM= 14 | github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= 15 | github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= 16 | github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 17 | github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= 18 | github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= 19 | github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= 20 | github.com/klauspost/compress v1.10.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= 21 | github.com/klauspost/compress v1.15.15 h1:EF27CXIuDsYJ6mmvtBRlEuB2UVOqHG1tAXgZ7yIO+lw= 22 | github.com/klauspost/compress v1.15.15/go.mod h1:ZcK2JAFqKOpnBlxcLsJzYfrS9X1akm9fHZNnD9+Vo/4= 23 | github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= 24 | github.com/libsql/sqlite-antlr4-parser v0.0.0-20230802215326-5cb5bb604475 h1:6PfEMwfInASh9hkN83aR0j4W/eKaAZt/AURtXAXlas0= 25 | github.com/libsql/sqlite-antlr4-parser v0.0.0-20230802215326-5cb5bb604475/go.mod h1:20nXSmcf0nAscrzqsXeC2/tA3KkV2eCiJqYuyAgl+ss= 26 | github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= 27 | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 28 | github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= 29 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 30 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 31 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 32 | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= 33 | github.com/tursodatabase/libsql-client-go v0.0.0-20240220085343-4ae0eb9d0898 h1:1MvEhzI5pvP27e9Dzz861mxk9WzXZLSJwzOU67cKTbU= 34 | github.com/tursodatabase/libsql-client-go v0.0.0-20240220085343-4ae0eb9d0898/go.mod h1:9bKuHS7eZh/0mJndbUOrCx8Ej3PlsRDszj4L7oVYMPQ= 35 | github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= 36 | github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= 37 | golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e h1:+WEEuIdZHnUeJJmEUjyYC2gfUMj69yZXw17EnHg/otA= 38 | golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e/go.mod h1:Kr81I6Kryrl9sr8s2FK3vxD90NdsKWRuOIl2O4CvYbA= 39 | golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 40 | golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= 41 | golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 42 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 43 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 44 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 45 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 46 | gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 47 | nhooyr.io/websocket v1.8.7 h1:usjR2uOr/zjjkVMy0lW+PPohFok7PCow5sDjLgX4P4g= 48 | nhooyr.io/websocket v1.8.7/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0= 49 | -------------------------------------------------------------------------------- /lib/turso/pkg/blog/model.go: -------------------------------------------------------------------------------- 1 | package blog 2 | 3 | import "time" 4 | 5 | type Blog struct { 6 | Slug string 7 | Title string 8 | Description string 9 | Content string 10 | Date time.Time 11 | Tags []string 12 | } 13 | -------------------------------------------------------------------------------- /lib/turso/pkg/blog/service.go: -------------------------------------------------------------------------------- 1 | package blog 2 | 3 | import ( 4 | "database/sql" 5 | "encoding/json" 6 | "fmt" 7 | "strings" 8 | 9 | db "github.com/ethanthoma/webserver/lib/turso/pkg/database" 10 | ) 11 | 12 | func rowsToBlogs (rows *sql.Rows) ([]Blog, error) { 13 | var blogs []Blog 14 | 15 | for rows.Next() { 16 | var blog Blog 17 | 18 | var tags string 19 | 20 | if err := rows.Scan( 21 | &blog.Slug, 22 | &blog.Title, 23 | &blog.Description, 24 | &blog.Content, 25 | &blog.Date, 26 | &tags, 27 | ); err != nil { 28 | fmt.Println("Error scanning row:", err) 29 | return nil, err 30 | } 31 | 32 | blog.Tags = strings.Split(tags, ",") 33 | 34 | blogs = append(blogs, blog) 35 | } 36 | 37 | if err := rows.Err(); err != nil { 38 | str := fmt.Sprint("Error during rows iteration:", err) 39 | fmt.Println(str) 40 | return nil, err 41 | } 42 | 43 | return blogs, nil 44 | } 45 | 46 | func GetBlogTable() (string, error) { 47 | rows, err := db.DB.Query(` 48 | SELECT 49 | * 50 | FROM 51 | blogs 52 | ORDER BY 53 | date 54 | DESC; 55 | `) 56 | if err != nil { 57 | return "", err 58 | } 59 | defer rows.Close() 60 | 61 | blogs, err := rowsToBlogs(rows) 62 | if err != nil { 63 | return "", err 64 | } 65 | 66 | jsonBlogs, err := json.Marshal(blogs) 67 | if err != nil { 68 | return "", err 69 | } 70 | 71 | return string(jsonBlogs), nil 72 | } 73 | 74 | func GetBlogBySlug(slug string) (string, error) { 75 | rows, err := db.DB.Query(fmt.Sprint(` 76 | SELECT 77 | * 78 | FROM 79 | blogs 80 | WHERE 81 | slug = '`,slug,`' 82 | LIMIT 83 | 1 84 | ; 85 | `)) 86 | if err != nil { 87 | return "", err 88 | } 89 | defer rows.Close() 90 | 91 | blogs, err := rowsToBlogs(rows) 92 | if err != nil { 93 | return "", err 94 | } 95 | 96 | if len(blogs) == 0 { 97 | return "", err 98 | } 99 | 100 | blog := blogs[0] 101 | 102 | jsonBlog, err := json.Marshal(blog) 103 | if err != nil { 104 | return "", err 105 | } 106 | 107 | return string(jsonBlog), nil 108 | } 109 | -------------------------------------------------------------------------------- /lib/turso/pkg/database/database.go: -------------------------------------------------------------------------------- 1 | package database 2 | 3 | import ( 4 | "database/sql" 5 | "fmt" 6 | "os" 7 | 8 | _ "github.com/tursodatabase/libsql-client-go/libsql" 9 | ) 10 | 11 | var DB *sql.DB 12 | 13 | func Init(database string, token string) { 14 | url := fmt.Sprintf("libsql://%s.turso.io?authToken=%s", database, token) 15 | 16 | var err error 17 | DB, err = sql.Open("libsql", url) 18 | if err != nil { 19 | fmt.Fprintf(os.Stderr, "failed to open db %s: %s", url, err) 20 | os.Exit(1) 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /lib/turso/turso.ml: -------------------------------------------------------------------------------- 1 | open Ctypes 2 | open Foreign 3 | 4 | let () = 5 | let init = foreign "Init" (void @-> returning void) in 6 | init () 7 | 8 | type blog = { 9 | filename : string; 10 | title : string; 11 | description : string; 12 | content : string; 13 | tags : string list; 14 | date : string; 15 | } 16 | 17 | let empty_blog = 18 | { 19 | filename = "not_yet_released.md"; 20 | title = "Not Yet Released"; 21 | description = "try again in the future!"; 22 | content = "# Not Yet Released\n\nPlease try again in the future!"; 23 | tags = []; 24 | date = "The Future"; 25 | } 26 | 27 | let json_to_blog json = 28 | let open Yojson.Basic.Util in 29 | let title = json |> member "Title" |> to_string in 30 | let filename = json |> member "Slug" |> to_string in 31 | let description = json |> member "Description" |> to_string in 32 | let content = json |> member "Content" |> to_string in 33 | let date = String.sub (json |> member "Date" |> to_string) 0 10 in 34 | let tags = json |> member "Tags" |> to_list |> filter_string in 35 | { filename; title; description; content; tags; date } 36 | 37 | let get_blogs () = 38 | let get_blog_table = foreign "GetBlogTable" (void @-> returning string) in 39 | let json = 40 | let str_json = get_blog_table () in 41 | if str_json <> "" then Some (Yojson.Basic.from_string str_json) else None 42 | in 43 | match json with 44 | | Some json -> ( 45 | let list_opt = 46 | Yojson.Basic.Util.to_option Yojson.Basic.Util.to_list json 47 | in 48 | match list_opt with Some list -> List.map json_to_blog list | None -> []) 49 | | None -> [] 50 | 51 | let get_blog_by_slug slug = 52 | let get_blog_by_slug = 53 | foreign "GetBlogBySlug" (string @-> returning string) 54 | in 55 | let json = 56 | let str_json = get_blog_by_slug slug in 57 | if str_json <> "" then Some (Yojson.Basic.from_string str_json) else None 58 | in 59 | match json with Some json -> json_to_blog json | None -> empty_blog 60 | -------------------------------------------------------------------------------- /lib/turso/turso.mli: -------------------------------------------------------------------------------- 1 | type blog = { 2 | filename : string; 3 | title : string; 4 | description : string; 5 | content : string; 6 | tags : string list; 7 | date : string; 8 | } 9 | 10 | val empty_blog : blog 11 | 12 | val get_blogs : unit -> blog list 13 | val get_blog_by_slug : string -> blog 14 | -------------------------------------------------------------------------------- /lib/turso/vendor/github.com/antlr/antlr4/runtime/Go/antlr/v4/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2021 The ANTLR Project 2 | 3 | Redistribution and use in source and binary forms, with or without modification, 4 | are permitted provided that the following conditions are met: 5 | 6 | 1. Redistributions of source code must retain the above copyright notice, 7 | this list of conditions and the following disclaimer. 8 | 9 | 2. Redistributions in binary form must reproduce the above copyright notice, 10 | this list of conditions and the following disclaimer in the documentation 11 | and/or other materials provided with the distribution. 12 | 13 | 3. Neither the name of the copyright holder nor the names of its 14 | contributors may be used to endorse or promote products derived from this 15 | software without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 18 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 19 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 20 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 21 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 23 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 24 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 25 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | -------------------------------------------------------------------------------- /lib/turso/vendor/github.com/antlr/antlr4/runtime/Go/antlr/v4/antlrdoc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Package antlr implements the Go version of the ANTLR 4 runtime. 3 | 4 | # The ANTLR Tool 5 | 6 | ANTLR (ANother Tool for Language Recognition) is a powerful parser generator for reading, processing, executing, 7 | or translating structured text or binary files. It's widely used to build languages, tools, and frameworks. 8 | From a grammar, ANTLR generates a parser that can build parse trees and also generates a listener interface 9 | (or visitor) that makes it easy to respond to the recognition of phrases of interest. 10 | 11 | # Code Generation 12 | 13 | ANTLR supports the generation of code in a number of [target languages], and the generated code is supported by a 14 | runtime library, written specifically to support the generated code in the target language. This library is the 15 | runtime for the Go target. 16 | 17 | To generate code for the go target, it is generally recommended to place the source grammar files in a package of 18 | their own, and use the `.sh` script method of generating code, using the go generate directive. In that same directory 19 | it is usual, though not required, to place the antlr tool that should be used to generate the code. That does mean 20 | that the antlr tool JAR file will be checked in to your source code control though, so you are free to use any other 21 | way of specifying the version of the ANTLR tool to use, such as aliasing in `.zshrc` or equivalent, or a profile in 22 | your IDE, or configuration in your CI system. 23 | 24 | Here is a general template for an ANTLR based recognizer in Go: 25 | 26 | . 27 | ├── myproject 28 | ├── parser 29 | │ ├── mygrammar.g4 30 | │ ├── antlr-4.12.0-complete.jar 31 | │ ├── error_listeners.go 32 | │ ├── generate.go 33 | │ ├── generate.sh 34 | ├── go.mod 35 | ├── go.sum 36 | ├── main.go 37 | └── main_test.go 38 | 39 | Make sure that the package statement in your grammar file(s) reflects the go package they exist in. 40 | The generate.go file then looks like this: 41 | 42 | package parser 43 | 44 | //go:generate ./generate.sh 45 | 46 | And the generate.sh file will look similar to this: 47 | 48 | #!/bin/sh 49 | 50 | alias antlr4='java -Xmx500M -cp "./antlr4-4.12.0-complete.jar:$CLASSPATH" org.antlr.v4.Tool' 51 | antlr4 -Dlanguage=Go -no-visitor -package parser *.g4 52 | 53 | depending on whether you want visitors or listeners or any other ANTLR options. 54 | 55 | From the command line at the root of your package “myproject” you can then simply issue the command: 56 | 57 | go generate ./... 58 | 59 | # Copyright Notice 60 | 61 | Copyright (c) 2012-2022 The ANTLR Project. All rights reserved. 62 | 63 | Use of this file is governed by the BSD 3-clause license, which can be found in the [LICENSE.txt] file in the project root. 64 | 65 | [target languages]: https://github.com/antlr/antlr4/tree/master/runtime 66 | [LICENSE.txt]: https://github.com/antlr/antlr4/blob/master/LICENSE.txt 67 | */ 68 | package antlr 69 | -------------------------------------------------------------------------------- /lib/turso/vendor/github.com/antlr/antlr4/runtime/Go/antlr/v4/atn_deserialization_options.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2012-2022 The ANTLR Project. All rights reserved. 2 | // Use of this file is governed by the BSD 3-clause license that 3 | // can be found in the LICENSE.txt file in the project root. 4 | 5 | package antlr 6 | 7 | import "errors" 8 | 9 | var defaultATNDeserializationOptions = ATNDeserializationOptions{true, true, false} 10 | 11 | type ATNDeserializationOptions struct { 12 | readOnly bool 13 | verifyATN bool 14 | generateRuleBypassTransitions bool 15 | } 16 | 17 | func (opts *ATNDeserializationOptions) ReadOnly() bool { 18 | return opts.readOnly 19 | } 20 | 21 | func (opts *ATNDeserializationOptions) SetReadOnly(readOnly bool) { 22 | if opts.readOnly { 23 | panic(errors.New("Cannot mutate read only ATNDeserializationOptions")) 24 | } 25 | opts.readOnly = readOnly 26 | } 27 | 28 | func (opts *ATNDeserializationOptions) VerifyATN() bool { 29 | return opts.verifyATN 30 | } 31 | 32 | func (opts *ATNDeserializationOptions) SetVerifyATN(verifyATN bool) { 33 | if opts.readOnly { 34 | panic(errors.New("Cannot mutate read only ATNDeserializationOptions")) 35 | } 36 | opts.verifyATN = verifyATN 37 | } 38 | 39 | func (opts *ATNDeserializationOptions) GenerateRuleBypassTransitions() bool { 40 | return opts.generateRuleBypassTransitions 41 | } 42 | 43 | func (opts *ATNDeserializationOptions) SetGenerateRuleBypassTransitions(generateRuleBypassTransitions bool) { 44 | if opts.readOnly { 45 | panic(errors.New("Cannot mutate read only ATNDeserializationOptions")) 46 | } 47 | opts.generateRuleBypassTransitions = generateRuleBypassTransitions 48 | } 49 | 50 | func DefaultATNDeserializationOptions() *ATNDeserializationOptions { 51 | return NewATNDeserializationOptions(&defaultATNDeserializationOptions) 52 | } 53 | 54 | func NewATNDeserializationOptions(other *ATNDeserializationOptions) *ATNDeserializationOptions { 55 | o := new(ATNDeserializationOptions) 56 | if other != nil { 57 | *o = *other 58 | o.readOnly = false 59 | } 60 | return o 61 | } 62 | -------------------------------------------------------------------------------- /lib/turso/vendor/github.com/antlr/antlr4/runtime/Go/antlr/v4/atn_simulator.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2012-2022 The ANTLR Project. All rights reserved. 2 | // Use of this file is governed by the BSD 3-clause license that 3 | // can be found in the LICENSE.txt file in the project root. 4 | 5 | package antlr 6 | 7 | var ATNSimulatorError = NewDFAState(0x7FFFFFFF, NewBaseATNConfigSet(false)) 8 | 9 | type IATNSimulator interface { 10 | SharedContextCache() *PredictionContextCache 11 | ATN() *ATN 12 | DecisionToDFA() []*DFA 13 | } 14 | 15 | type BaseATNSimulator struct { 16 | atn *ATN 17 | sharedContextCache *PredictionContextCache 18 | decisionToDFA []*DFA 19 | } 20 | 21 | func NewBaseATNSimulator(atn *ATN, sharedContextCache *PredictionContextCache) *BaseATNSimulator { 22 | b := new(BaseATNSimulator) 23 | 24 | b.atn = atn 25 | b.sharedContextCache = sharedContextCache 26 | 27 | return b 28 | } 29 | 30 | func (b *BaseATNSimulator) getCachedContext(context PredictionContext) PredictionContext { 31 | if b.sharedContextCache == nil { 32 | return context 33 | } 34 | 35 | visited := make(map[PredictionContext]PredictionContext) 36 | 37 | return getCachedBasePredictionContext(context, b.sharedContextCache, visited) 38 | } 39 | 40 | func (b *BaseATNSimulator) SharedContextCache() *PredictionContextCache { 41 | return b.sharedContextCache 42 | } 43 | 44 | func (b *BaseATNSimulator) ATN() *ATN { 45 | return b.atn 46 | } 47 | 48 | func (b *BaseATNSimulator) DecisionToDFA() []*DFA { 49 | return b.decisionToDFA 50 | } 51 | -------------------------------------------------------------------------------- /lib/turso/vendor/github.com/antlr/antlr4/runtime/Go/antlr/v4/atn_type.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2012-2022 The ANTLR Project. All rights reserved. 2 | // Use of this file is governed by the BSD 3-clause license that 3 | // can be found in the LICENSE.txt file in the project root. 4 | 5 | package antlr 6 | 7 | // Represent the type of recognizer an ATN applies to. 8 | const ( 9 | ATNTypeLexer = 0 10 | ATNTypeParser = 1 11 | ) 12 | -------------------------------------------------------------------------------- /lib/turso/vendor/github.com/antlr/antlr4/runtime/Go/antlr/v4/char_stream.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2012-2022 The ANTLR Project. All rights reserved. 2 | // Use of this file is governed by the BSD 3-clause license that 3 | // can be found in the LICENSE.txt file in the project root. 4 | 5 | package antlr 6 | 7 | type CharStream interface { 8 | IntStream 9 | GetText(int, int) string 10 | GetTextFromTokens(start, end Token) string 11 | GetTextFromInterval(*Interval) string 12 | } 13 | -------------------------------------------------------------------------------- /lib/turso/vendor/github.com/antlr/antlr4/runtime/Go/antlr/v4/common_token_factory.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2012-2022 The ANTLR Project. All rights reserved. 2 | // Use of this file is governed by the BSD 3-clause license that 3 | // can be found in the LICENSE.txt file in the project root. 4 | 5 | package antlr 6 | 7 | // TokenFactory creates CommonToken objects. 8 | type TokenFactory interface { 9 | Create(source *TokenSourceCharStreamPair, ttype int, text string, channel, start, stop, line, column int) Token 10 | } 11 | 12 | // CommonTokenFactory is the default TokenFactory implementation. 13 | type CommonTokenFactory struct { 14 | // copyText indicates whether CommonToken.setText should be called after 15 | // constructing tokens to explicitly set the text. This is useful for cases 16 | // where the input stream might not be able to provide arbitrary substrings of 17 | // text from the input after the lexer creates a token (e.g. the 18 | // implementation of CharStream.GetText in UnbufferedCharStream panics an 19 | // UnsupportedOperationException). Explicitly setting the token text allows 20 | // Token.GetText to be called at any time regardless of the input stream 21 | // implementation. 22 | // 23 | // The default value is false to avoid the performance and memory overhead of 24 | // copying text for every token unless explicitly requested. 25 | copyText bool 26 | } 27 | 28 | func NewCommonTokenFactory(copyText bool) *CommonTokenFactory { 29 | return &CommonTokenFactory{copyText: copyText} 30 | } 31 | 32 | // CommonTokenFactoryDEFAULT is the default CommonTokenFactory. It does not 33 | // explicitly copy token text when constructing tokens. 34 | var CommonTokenFactoryDEFAULT = NewCommonTokenFactory(false) 35 | 36 | func (c *CommonTokenFactory) Create(source *TokenSourceCharStreamPair, ttype int, text string, channel, start, stop, line, column int) Token { 37 | t := NewCommonToken(source, ttype, channel, start, stop) 38 | 39 | t.line = line 40 | t.column = column 41 | 42 | if text != "" { 43 | t.SetText(text) 44 | } else if c.copyText && source.charStream != nil { 45 | t.SetText(source.charStream.GetTextFromInterval(NewInterval(start, stop))) 46 | } 47 | 48 | return t 49 | } 50 | 51 | func (c *CommonTokenFactory) createThin(ttype int, text string) Token { 52 | t := NewCommonToken(nil, ttype, TokenDefaultChannel, -1, -1) 53 | t.SetText(text) 54 | 55 | return t 56 | } 57 | -------------------------------------------------------------------------------- /lib/turso/vendor/github.com/antlr/antlr4/runtime/Go/antlr/v4/dfa.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2012-2022 The ANTLR Project. All rights reserved. 2 | // Use of this file is governed by the BSD 3-clause license that 3 | // can be found in the LICENSE.txt file in the project root. 4 | 5 | package antlr 6 | 7 | type DFA struct { 8 | // atnStartState is the ATN state in which this was created 9 | atnStartState DecisionState 10 | 11 | decision int 12 | 13 | // states is all the DFA states. Use Map to get the old state back; Set can only 14 | // indicate whether it is there. Go maps implement key hash collisions and so on and are very 15 | // good, but the DFAState is an object and can't be used directly as the key as it can in say JAva 16 | // amd C#, whereby if the hashcode is the same for two objects, then Equals() is called against them 17 | // to see if they really are the same object. 18 | // 19 | // 20 | states *JStore[*DFAState, *ObjEqComparator[*DFAState]] 21 | 22 | numstates int 23 | 24 | s0 *DFAState 25 | 26 | // precedenceDfa is the backing field for isPrecedenceDfa and setPrecedenceDfa. 27 | // True if the DFA is for a precedence decision and false otherwise. 28 | precedenceDfa bool 29 | } 30 | 31 | func NewDFA(atnStartState DecisionState, decision int) *DFA { 32 | dfa := &DFA{ 33 | atnStartState: atnStartState, 34 | decision: decision, 35 | states: NewJStore[*DFAState, *ObjEqComparator[*DFAState]](dfaStateEqInst), 36 | } 37 | if s, ok := atnStartState.(*StarLoopEntryState); ok && s.precedenceRuleDecision { 38 | dfa.precedenceDfa = true 39 | dfa.s0 = NewDFAState(-1, NewBaseATNConfigSet(false)) 40 | dfa.s0.isAcceptState = false 41 | dfa.s0.requiresFullContext = false 42 | } 43 | return dfa 44 | } 45 | 46 | // getPrecedenceStartState gets the start state for the current precedence and 47 | // returns the start state corresponding to the specified precedence if a start 48 | // state exists for the specified precedence and nil otherwise. d must be a 49 | // precedence DFA. See also isPrecedenceDfa. 50 | func (d *DFA) getPrecedenceStartState(precedence int) *DFAState { 51 | if !d.getPrecedenceDfa() { 52 | panic("only precedence DFAs may contain a precedence start state") 53 | } 54 | 55 | // s0.edges is never nil for a precedence DFA 56 | if precedence < 0 || precedence >= len(d.getS0().getEdges()) { 57 | return nil 58 | } 59 | 60 | return d.getS0().getIthEdge(precedence) 61 | } 62 | 63 | // setPrecedenceStartState sets the start state for the current precedence. d 64 | // must be a precedence DFA. See also isPrecedenceDfa. 65 | func (d *DFA) setPrecedenceStartState(precedence int, startState *DFAState) { 66 | if !d.getPrecedenceDfa() { 67 | panic("only precedence DFAs may contain a precedence start state") 68 | } 69 | 70 | if precedence < 0 { 71 | return 72 | } 73 | 74 | // Synchronization on s0 here is ok. When the DFA is turned into a 75 | // precedence DFA, s0 will be initialized once and not updated again. s0.edges 76 | // is never nil for a precedence DFA. 77 | s0 := d.getS0() 78 | if precedence >= s0.numEdges() { 79 | edges := append(s0.getEdges(), make([]*DFAState, precedence+1-s0.numEdges())...) 80 | s0.setEdges(edges) 81 | d.setS0(s0) 82 | } 83 | 84 | s0.setIthEdge(precedence, startState) 85 | } 86 | 87 | func (d *DFA) getPrecedenceDfa() bool { 88 | return d.precedenceDfa 89 | } 90 | 91 | // setPrecedenceDfa sets whether d is a precedence DFA. If precedenceDfa differs 92 | // from the current DFA configuration, then d.states is cleared, the initial 93 | // state s0 is set to a new DFAState with an empty outgoing DFAState.edges to 94 | // store the start states for individual precedence values if precedenceDfa is 95 | // true or nil otherwise, and d.precedenceDfa is updated. 96 | func (d *DFA) setPrecedenceDfa(precedenceDfa bool) { 97 | if d.getPrecedenceDfa() != precedenceDfa { 98 | d.states = NewJStore[*DFAState, *ObjEqComparator[*DFAState]](dfaStateEqInst) 99 | d.numstates = 0 100 | 101 | if precedenceDfa { 102 | precedenceState := NewDFAState(-1, NewBaseATNConfigSet(false)) 103 | 104 | precedenceState.setEdges(make([]*DFAState, 0)) 105 | precedenceState.isAcceptState = false 106 | precedenceState.requiresFullContext = false 107 | d.setS0(precedenceState) 108 | } else { 109 | d.setS0(nil) 110 | } 111 | 112 | d.precedenceDfa = precedenceDfa 113 | } 114 | } 115 | 116 | func (d *DFA) getS0() *DFAState { 117 | return d.s0 118 | } 119 | 120 | func (d *DFA) setS0(s *DFAState) { 121 | d.s0 = s 122 | } 123 | 124 | // sortedStates returns the states in d sorted by their state number. 125 | func (d *DFA) sortedStates() []*DFAState { 126 | 127 | vs := d.states.SortedSlice(func(i, j *DFAState) bool { 128 | return i.stateNumber < j.stateNumber 129 | }) 130 | 131 | return vs 132 | } 133 | 134 | func (d *DFA) String(literalNames []string, symbolicNames []string) string { 135 | if d.getS0() == nil { 136 | return "" 137 | } 138 | 139 | return NewDFASerializer(d, literalNames, symbolicNames).String() 140 | } 141 | 142 | func (d *DFA) ToLexerString() string { 143 | if d.getS0() == nil { 144 | return "" 145 | } 146 | 147 | return NewLexerDFASerializer(d).String() 148 | } 149 | -------------------------------------------------------------------------------- /lib/turso/vendor/github.com/antlr/antlr4/runtime/Go/antlr/v4/dfa_serializer.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2012-2022 The ANTLR Project. All rights reserved. 2 | // Use of this file is governed by the BSD 3-clause license that 3 | // can be found in the LICENSE.txt file in the project root. 4 | 5 | package antlr 6 | 7 | import ( 8 | "fmt" 9 | "strconv" 10 | "strings" 11 | ) 12 | 13 | // DFASerializer is a DFA walker that knows how to dump them to serialized 14 | // strings. 15 | type DFASerializer struct { 16 | dfa *DFA 17 | literalNames []string 18 | symbolicNames []string 19 | } 20 | 21 | func NewDFASerializer(dfa *DFA, literalNames, symbolicNames []string) *DFASerializer { 22 | if literalNames == nil { 23 | literalNames = make([]string, 0) 24 | } 25 | 26 | if symbolicNames == nil { 27 | symbolicNames = make([]string, 0) 28 | } 29 | 30 | return &DFASerializer{ 31 | dfa: dfa, 32 | literalNames: literalNames, 33 | symbolicNames: symbolicNames, 34 | } 35 | } 36 | 37 | func (d *DFASerializer) String() string { 38 | if d.dfa.getS0() == nil { 39 | return "" 40 | } 41 | 42 | buf := "" 43 | states := d.dfa.sortedStates() 44 | 45 | for _, s := range states { 46 | if s.edges != nil { 47 | n := len(s.edges) 48 | 49 | for j := 0; j < n; j++ { 50 | t := s.edges[j] 51 | 52 | if t != nil && t.stateNumber != 0x7FFFFFFF { 53 | buf += d.GetStateString(s) 54 | buf += "-" 55 | buf += d.getEdgeLabel(j) 56 | buf += "->" 57 | buf += d.GetStateString(t) 58 | buf += "\n" 59 | } 60 | } 61 | } 62 | } 63 | 64 | if len(buf) == 0 { 65 | return "" 66 | } 67 | 68 | return buf 69 | } 70 | 71 | func (d *DFASerializer) getEdgeLabel(i int) string { 72 | if i == 0 { 73 | return "EOF" 74 | } else if d.literalNames != nil && i-1 < len(d.literalNames) { 75 | return d.literalNames[i-1] 76 | } else if d.symbolicNames != nil && i-1 < len(d.symbolicNames) { 77 | return d.symbolicNames[i-1] 78 | } 79 | 80 | return strconv.Itoa(i - 1) 81 | } 82 | 83 | func (d *DFASerializer) GetStateString(s *DFAState) string { 84 | var a, b string 85 | 86 | if s.isAcceptState { 87 | a = ":" 88 | } 89 | 90 | if s.requiresFullContext { 91 | b = "^" 92 | } 93 | 94 | baseStateStr := a + "s" + strconv.Itoa(s.stateNumber) + b 95 | 96 | if s.isAcceptState { 97 | if s.predicates != nil { 98 | return baseStateStr + "=>" + fmt.Sprint(s.predicates) 99 | } 100 | 101 | return baseStateStr + "=>" + fmt.Sprint(s.prediction) 102 | } 103 | 104 | return baseStateStr 105 | } 106 | 107 | type LexerDFASerializer struct { 108 | *DFASerializer 109 | } 110 | 111 | func NewLexerDFASerializer(dfa *DFA) *LexerDFASerializer { 112 | return &LexerDFASerializer{DFASerializer: NewDFASerializer(dfa, nil, nil)} 113 | } 114 | 115 | func (l *LexerDFASerializer) getEdgeLabel(i int) string { 116 | var sb strings.Builder 117 | sb.Grow(6) 118 | sb.WriteByte('\'') 119 | sb.WriteRune(rune(i)) 120 | sb.WriteByte('\'') 121 | return sb.String() 122 | } 123 | 124 | func (l *LexerDFASerializer) String() string { 125 | if l.dfa.getS0() == nil { 126 | return "" 127 | } 128 | 129 | buf := "" 130 | states := l.dfa.sortedStates() 131 | 132 | for i := 0; i < len(states); i++ { 133 | s := states[i] 134 | 135 | if s.edges != nil { 136 | n := len(s.edges) 137 | 138 | for j := 0; j < n; j++ { 139 | t := s.edges[j] 140 | 141 | if t != nil && t.stateNumber != 0x7FFFFFFF { 142 | buf += l.GetStateString(s) 143 | buf += "-" 144 | buf += l.getEdgeLabel(j) 145 | buf += "->" 146 | buf += l.GetStateString(t) 147 | buf += "\n" 148 | } 149 | } 150 | } 151 | } 152 | 153 | if len(buf) == 0 { 154 | return "" 155 | } 156 | 157 | return buf 158 | } 159 | -------------------------------------------------------------------------------- /lib/turso/vendor/github.com/antlr/antlr4/runtime/Go/antlr/v4/diagnostic_error_listener.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2012-2022 The ANTLR Project. All rights reserved. 2 | // Use of this file is governed by the BSD 3-clause license that 3 | // can be found in the LICENSE.txt file in the project root. 4 | 5 | package antlr 6 | 7 | import ( 8 | "strconv" 9 | ) 10 | 11 | // 12 | // This implementation of {@link ANTLRErrorListener} can be used to identify 13 | // certain potential correctness and performance problems in grammars. "reports" 14 | // are made by calling {@link Parser//NotifyErrorListeners} with the appropriate 15 | // message. 16 | // 17 | // 29 | 30 | type DiagnosticErrorListener struct { 31 | *DefaultErrorListener 32 | 33 | exactOnly bool 34 | } 35 | 36 | func NewDiagnosticErrorListener(exactOnly bool) *DiagnosticErrorListener { 37 | 38 | n := new(DiagnosticErrorListener) 39 | 40 | // whether all ambiguities or only exact ambiguities are Reported. 41 | n.exactOnly = exactOnly 42 | return n 43 | } 44 | 45 | func (d *DiagnosticErrorListener) ReportAmbiguity(recognizer Parser, dfa *DFA, startIndex, stopIndex int, exact bool, ambigAlts *BitSet, configs ATNConfigSet) { 46 | if d.exactOnly && !exact { 47 | return 48 | } 49 | msg := "reportAmbiguity d=" + 50 | d.getDecisionDescription(recognizer, dfa) + 51 | ": ambigAlts=" + 52 | d.getConflictingAlts(ambigAlts, configs).String() + 53 | ", input='" + 54 | recognizer.GetTokenStream().GetTextFromInterval(NewInterval(startIndex, stopIndex)) + "'" 55 | recognizer.NotifyErrorListeners(msg, nil, nil) 56 | } 57 | 58 | func (d *DiagnosticErrorListener) ReportAttemptingFullContext(recognizer Parser, dfa *DFA, startIndex, stopIndex int, conflictingAlts *BitSet, configs ATNConfigSet) { 59 | 60 | msg := "reportAttemptingFullContext d=" + 61 | d.getDecisionDescription(recognizer, dfa) + 62 | ", input='" + 63 | recognizer.GetTokenStream().GetTextFromInterval(NewInterval(startIndex, stopIndex)) + "'" 64 | recognizer.NotifyErrorListeners(msg, nil, nil) 65 | } 66 | 67 | func (d *DiagnosticErrorListener) ReportContextSensitivity(recognizer Parser, dfa *DFA, startIndex, stopIndex, prediction int, configs ATNConfigSet) { 68 | msg := "reportContextSensitivity d=" + 69 | d.getDecisionDescription(recognizer, dfa) + 70 | ", input='" + 71 | recognizer.GetTokenStream().GetTextFromInterval(NewInterval(startIndex, stopIndex)) + "'" 72 | recognizer.NotifyErrorListeners(msg, nil, nil) 73 | } 74 | 75 | func (d *DiagnosticErrorListener) getDecisionDescription(recognizer Parser, dfa *DFA) string { 76 | decision := dfa.decision 77 | ruleIndex := dfa.atnStartState.GetRuleIndex() 78 | 79 | ruleNames := recognizer.GetRuleNames() 80 | if ruleIndex < 0 || ruleIndex >= len(ruleNames) { 81 | return strconv.Itoa(decision) 82 | } 83 | ruleName := ruleNames[ruleIndex] 84 | if ruleName == "" { 85 | return strconv.Itoa(decision) 86 | } 87 | return strconv.Itoa(decision) + " (" + ruleName + ")" 88 | } 89 | 90 | // Computes the set of conflicting or ambiguous alternatives from a 91 | // configuration set, if that information was not already provided by the 92 | // parser. 93 | // 94 | // @param ReportedAlts The set of conflicting or ambiguous alternatives, as 95 | // Reported by the parser. 96 | // @param configs The conflicting or ambiguous configuration set. 97 | // @return Returns {@code ReportedAlts} if it is not {@code nil}, otherwise 98 | // returns the set of alternatives represented in {@code configs}. 99 | func (d *DiagnosticErrorListener) getConflictingAlts(ReportedAlts *BitSet, set ATNConfigSet) *BitSet { 100 | if ReportedAlts != nil { 101 | return ReportedAlts 102 | } 103 | result := NewBitSet() 104 | for _, c := range set.GetItems() { 105 | result.add(c.GetAlt()) 106 | } 107 | 108 | return result 109 | } 110 | -------------------------------------------------------------------------------- /lib/turso/vendor/github.com/antlr/antlr4/runtime/Go/antlr/v4/error_listener.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2012-2022 The ANTLR Project. All rights reserved. 2 | // Use of this file is governed by the BSD 3-clause license that 3 | // can be found in the LICENSE.txt file in the project root. 4 | 5 | package antlr 6 | 7 | import ( 8 | "fmt" 9 | "os" 10 | "strconv" 11 | ) 12 | 13 | // Provides an empty default implementation of {@link ANTLRErrorListener}. The 14 | // default implementation of each method does nothing, but can be overridden as 15 | // necessary. 16 | 17 | type ErrorListener interface { 18 | SyntaxError(recognizer Recognizer, offendingSymbol interface{}, line, column int, msg string, e RecognitionException) 19 | ReportAmbiguity(recognizer Parser, dfa *DFA, startIndex, stopIndex int, exact bool, ambigAlts *BitSet, configs ATNConfigSet) 20 | ReportAttemptingFullContext(recognizer Parser, dfa *DFA, startIndex, stopIndex int, conflictingAlts *BitSet, configs ATNConfigSet) 21 | ReportContextSensitivity(recognizer Parser, dfa *DFA, startIndex, stopIndex, prediction int, configs ATNConfigSet) 22 | } 23 | 24 | type DefaultErrorListener struct { 25 | } 26 | 27 | func NewDefaultErrorListener() *DefaultErrorListener { 28 | return new(DefaultErrorListener) 29 | } 30 | 31 | func (d *DefaultErrorListener) SyntaxError(recognizer Recognizer, offendingSymbol interface{}, line, column int, msg string, e RecognitionException) { 32 | } 33 | 34 | func (d *DefaultErrorListener) ReportAmbiguity(recognizer Parser, dfa *DFA, startIndex, stopIndex int, exact bool, ambigAlts *BitSet, configs ATNConfigSet) { 35 | } 36 | 37 | func (d *DefaultErrorListener) ReportAttemptingFullContext(recognizer Parser, dfa *DFA, startIndex, stopIndex int, conflictingAlts *BitSet, configs ATNConfigSet) { 38 | } 39 | 40 | func (d *DefaultErrorListener) ReportContextSensitivity(recognizer Parser, dfa *DFA, startIndex, stopIndex, prediction int, configs ATNConfigSet) { 41 | } 42 | 43 | type ConsoleErrorListener struct { 44 | *DefaultErrorListener 45 | } 46 | 47 | func NewConsoleErrorListener() *ConsoleErrorListener { 48 | return new(ConsoleErrorListener) 49 | } 50 | 51 | // Provides a default instance of {@link ConsoleErrorListener}. 52 | var ConsoleErrorListenerINSTANCE = NewConsoleErrorListener() 53 | 54 | // {@inheritDoc} 55 | // 56 | //

57 | // This implementation prints messages to {@link System//err} containing the 58 | // values of {@code line}, {@code charPositionInLine}, and {@code msg} using 59 | // the following format.

60 | // 61 | //
 62 | // line line:charPositionInLine msg
 63 | // 
64 | func (c *ConsoleErrorListener) SyntaxError(recognizer Recognizer, offendingSymbol interface{}, line, column int, msg string, e RecognitionException) { 65 | fmt.Fprintln(os.Stderr, "line "+strconv.Itoa(line)+":"+strconv.Itoa(column)+" "+msg) 66 | } 67 | 68 | type ProxyErrorListener struct { 69 | *DefaultErrorListener 70 | delegates []ErrorListener 71 | } 72 | 73 | func NewProxyErrorListener(delegates []ErrorListener) *ProxyErrorListener { 74 | if delegates == nil { 75 | panic("delegates is not provided") 76 | } 77 | l := new(ProxyErrorListener) 78 | l.delegates = delegates 79 | return l 80 | } 81 | 82 | func (p *ProxyErrorListener) SyntaxError(recognizer Recognizer, offendingSymbol interface{}, line, column int, msg string, e RecognitionException) { 83 | for _, d := range p.delegates { 84 | d.SyntaxError(recognizer, offendingSymbol, line, column, msg, e) 85 | } 86 | } 87 | 88 | func (p *ProxyErrorListener) ReportAmbiguity(recognizer Parser, dfa *DFA, startIndex, stopIndex int, exact bool, ambigAlts *BitSet, configs ATNConfigSet) { 89 | for _, d := range p.delegates { 90 | d.ReportAmbiguity(recognizer, dfa, startIndex, stopIndex, exact, ambigAlts, configs) 91 | } 92 | } 93 | 94 | func (p *ProxyErrorListener) ReportAttemptingFullContext(recognizer Parser, dfa *DFA, startIndex, stopIndex int, conflictingAlts *BitSet, configs ATNConfigSet) { 95 | for _, d := range p.delegates { 96 | d.ReportAttemptingFullContext(recognizer, dfa, startIndex, stopIndex, conflictingAlts, configs) 97 | } 98 | } 99 | 100 | func (p *ProxyErrorListener) ReportContextSensitivity(recognizer Parser, dfa *DFA, startIndex, stopIndex, prediction int, configs ATNConfigSet) { 101 | for _, d := range p.delegates { 102 | d.ReportContextSensitivity(recognizer, dfa, startIndex, stopIndex, prediction, configs) 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /lib/turso/vendor/github.com/antlr/antlr4/runtime/Go/antlr/v4/file_stream.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2012-2022 The ANTLR Project. All rights reserved. 2 | // Use of this file is governed by the BSD 3-clause license that 3 | // can be found in the LICENSE.txt file in the project root. 4 | 5 | package antlr 6 | 7 | import ( 8 | "bytes" 9 | "io" 10 | "os" 11 | ) 12 | 13 | // This is an InputStream that is loaded from a file all at once 14 | // when you construct the object. 15 | 16 | type FileStream struct { 17 | *InputStream 18 | 19 | filename string 20 | } 21 | 22 | func NewFileStream(fileName string) (*FileStream, error) { 23 | 24 | buf := bytes.NewBuffer(nil) 25 | 26 | f, err := os.Open(fileName) 27 | if err != nil { 28 | return nil, err 29 | } 30 | defer f.Close() 31 | _, err = io.Copy(buf, f) 32 | if err != nil { 33 | return nil, err 34 | } 35 | 36 | fs := new(FileStream) 37 | 38 | fs.filename = fileName 39 | s := string(buf.Bytes()) 40 | 41 | fs.InputStream = NewInputStream(s) 42 | 43 | return fs, nil 44 | 45 | } 46 | 47 | func (f *FileStream) GetSourceName() string { 48 | return f.filename 49 | } 50 | -------------------------------------------------------------------------------- /lib/turso/vendor/github.com/antlr/antlr4/runtime/Go/antlr/v4/input_stream.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2012-2022 The ANTLR Project. All rights reserved. 2 | // Use of this file is governed by the BSD 3-clause license that 3 | // can be found in the LICENSE.txt file in the project root. 4 | 5 | package antlr 6 | 7 | type InputStream struct { 8 | name string 9 | index int 10 | data []rune 11 | size int 12 | } 13 | 14 | func NewInputStream(data string) *InputStream { 15 | 16 | is := new(InputStream) 17 | 18 | is.name = "" 19 | is.index = 0 20 | is.data = []rune(data) 21 | is.size = len(is.data) // number of runes 22 | 23 | return is 24 | } 25 | 26 | func (is *InputStream) reset() { 27 | is.index = 0 28 | } 29 | 30 | func (is *InputStream) Consume() { 31 | if is.index >= is.size { 32 | // assert is.LA(1) == TokenEOF 33 | panic("cannot consume EOF") 34 | } 35 | is.index++ 36 | } 37 | 38 | func (is *InputStream) LA(offset int) int { 39 | 40 | if offset == 0 { 41 | return 0 // nil 42 | } 43 | if offset < 0 { 44 | offset++ // e.g., translate LA(-1) to use offset=0 45 | } 46 | pos := is.index + offset - 1 47 | 48 | if pos < 0 || pos >= is.size { // invalid 49 | return TokenEOF 50 | } 51 | 52 | return int(is.data[pos]) 53 | } 54 | 55 | func (is *InputStream) LT(offset int) int { 56 | return is.LA(offset) 57 | } 58 | 59 | func (is *InputStream) Index() int { 60 | return is.index 61 | } 62 | 63 | func (is *InputStream) Size() int { 64 | return is.size 65 | } 66 | 67 | // mark/release do nothing we have entire buffer 68 | func (is *InputStream) Mark() int { 69 | return -1 70 | } 71 | 72 | func (is *InputStream) Release(marker int) { 73 | } 74 | 75 | func (is *InputStream) Seek(index int) { 76 | if index <= is.index { 77 | is.index = index // just jump don't update stream state (line,...) 78 | return 79 | } 80 | // seek forward 81 | is.index = intMin(index, is.size) 82 | } 83 | 84 | func (is *InputStream) GetText(start int, stop int) string { 85 | if stop >= is.size { 86 | stop = is.size - 1 87 | } 88 | if start >= is.size { 89 | return "" 90 | } 91 | 92 | return string(is.data[start : stop+1]) 93 | } 94 | 95 | func (is *InputStream) GetTextFromTokens(start, stop Token) string { 96 | if start != nil && stop != nil { 97 | return is.GetTextFromInterval(NewInterval(start.GetTokenIndex(), stop.GetTokenIndex())) 98 | } 99 | 100 | return "" 101 | } 102 | 103 | func (is *InputStream) GetTextFromInterval(i *Interval) string { 104 | return is.GetText(i.Start, i.Stop) 105 | } 106 | 107 | func (*InputStream) GetSourceName() string { 108 | return "Obtained from string" 109 | } 110 | 111 | func (is *InputStream) String() string { 112 | return string(is.data) 113 | } 114 | -------------------------------------------------------------------------------- /lib/turso/vendor/github.com/antlr/antlr4/runtime/Go/antlr/v4/int_stream.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2012-2022 The ANTLR Project. All rights reserved. 2 | // Use of this file is governed by the BSD 3-clause license that 3 | // can be found in the LICENSE.txt file in the project root. 4 | 5 | package antlr 6 | 7 | type IntStream interface { 8 | Consume() 9 | LA(int) int 10 | Mark() int 11 | Release(marker int) 12 | Index() int 13 | Seek(index int) 14 | Size() int 15 | GetSourceName() string 16 | } 17 | -------------------------------------------------------------------------------- /lib/turso/vendor/github.com/antlr/antlr4/runtime/Go/antlr/v4/rule_context.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2012-2022 The ANTLR Project. All rights reserved. 2 | // Use of this file is governed by the BSD 3-clause license that 3 | // can be found in the LICENSE.txt file in the project root. 4 | 5 | package antlr 6 | 7 | // A rule context is a record of a single rule invocation. It knows 8 | // which context invoked it, if any. If there is no parent context, then 9 | // naturally the invoking state is not valid. The parent link 10 | // provides a chain upwards from the current rule invocation to the root 11 | // of the invocation tree, forming a stack. We actually carry no 12 | // information about the rule associated with b context (except 13 | // when parsing). We keep only the state number of the invoking state from 14 | // the ATN submachine that invoked b. Contrast b with the s 15 | // pointer inside ParserRuleContext that tracks the current state 16 | // being "executed" for the current rule. 17 | // 18 | // The parent contexts are useful for computing lookahead sets and 19 | // getting error information. 20 | // 21 | // These objects are used during parsing and prediction. 22 | // For the special case of parsers, we use the subclass 23 | // ParserRuleContext. 24 | // 25 | // @see ParserRuleContext 26 | // 27 | 28 | type RuleContext interface { 29 | RuleNode 30 | 31 | GetInvokingState() int 32 | SetInvokingState(int) 33 | 34 | GetRuleIndex() int 35 | IsEmpty() bool 36 | 37 | GetAltNumber() int 38 | SetAltNumber(altNumber int) 39 | 40 | String([]string, RuleContext) string 41 | } 42 | 43 | type BaseRuleContext struct { 44 | parentCtx RuleContext 45 | invokingState int 46 | RuleIndex int 47 | } 48 | 49 | func NewBaseRuleContext(parent RuleContext, invokingState int) *BaseRuleContext { 50 | 51 | rn := new(BaseRuleContext) 52 | 53 | // What context invoked b rule? 54 | rn.parentCtx = parent 55 | 56 | // What state invoked the rule associated with b context? 57 | // The "return address" is the followState of invokingState 58 | // If parent is nil, b should be -1. 59 | if parent == nil { 60 | rn.invokingState = -1 61 | } else { 62 | rn.invokingState = invokingState 63 | } 64 | 65 | return rn 66 | } 67 | 68 | func (b *BaseRuleContext) GetBaseRuleContext() *BaseRuleContext { 69 | return b 70 | } 71 | 72 | func (b *BaseRuleContext) SetParent(v Tree) { 73 | if v == nil { 74 | b.parentCtx = nil 75 | } else { 76 | b.parentCtx = v.(RuleContext) 77 | } 78 | } 79 | 80 | func (b *BaseRuleContext) GetInvokingState() int { 81 | return b.invokingState 82 | } 83 | 84 | func (b *BaseRuleContext) SetInvokingState(t int) { 85 | b.invokingState = t 86 | } 87 | 88 | func (b *BaseRuleContext) GetRuleIndex() int { 89 | return b.RuleIndex 90 | } 91 | 92 | func (b *BaseRuleContext) GetAltNumber() int { 93 | return ATNInvalidAltNumber 94 | } 95 | 96 | func (b *BaseRuleContext) SetAltNumber(altNumber int) {} 97 | 98 | // A context is empty if there is no invoking state meaning nobody call 99 | // current context. 100 | func (b *BaseRuleContext) IsEmpty() bool { 101 | return b.invokingState == -1 102 | } 103 | 104 | // Return the combined text of all child nodes. This method only considers 105 | // tokens which have been added to the parse tree. 106 | //

107 | // Since tokens on hidden channels (e.g. whitespace or comments) are not 108 | // added to the parse trees, they will not appear in the output of b 109 | // method. 110 | // 111 | 112 | func (b *BaseRuleContext) GetParent() Tree { 113 | return b.parentCtx 114 | } 115 | -------------------------------------------------------------------------------- /lib/turso/vendor/github.com/antlr/antlr4/runtime/Go/antlr/v4/token.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2012-2022 The ANTLR Project. All rights reserved. 2 | // Use of this file is governed by the BSD 3-clause license that 3 | // can be found in the LICENSE.txt file in the project root. 4 | 5 | package antlr 6 | 7 | import ( 8 | "strconv" 9 | "strings" 10 | ) 11 | 12 | type TokenSourceCharStreamPair struct { 13 | tokenSource TokenSource 14 | charStream CharStream 15 | } 16 | 17 | // A token has properties: text, type, line, character position in the line 18 | // (so we can ignore tabs), token channel, index, and source from which 19 | // we obtained this token. 20 | 21 | type Token interface { 22 | GetSource() *TokenSourceCharStreamPair 23 | GetTokenType() int 24 | GetChannel() int 25 | GetStart() int 26 | GetStop() int 27 | GetLine() int 28 | GetColumn() int 29 | 30 | GetText() string 31 | SetText(s string) 32 | 33 | GetTokenIndex() int 34 | SetTokenIndex(v int) 35 | 36 | GetTokenSource() TokenSource 37 | GetInputStream() CharStream 38 | } 39 | 40 | type BaseToken struct { 41 | source *TokenSourceCharStreamPair 42 | tokenType int // token type of the token 43 | channel int // The parser ignores everything not on DEFAULT_CHANNEL 44 | start int // optional return -1 if not implemented. 45 | stop int // optional return -1 if not implemented. 46 | tokenIndex int // from 0..n-1 of the token object in the input stream 47 | line int // line=1..n of the 1st character 48 | column int // beginning of the line at which it occurs, 0..n-1 49 | text string // text of the token. 50 | readOnly bool 51 | } 52 | 53 | const ( 54 | TokenInvalidType = 0 55 | 56 | // During lookahead operations, this "token" signifies we hit rule end ATN state 57 | // and did not follow it despite needing to. 58 | TokenEpsilon = -2 59 | 60 | TokenMinUserTokenType = 1 61 | 62 | TokenEOF = -1 63 | 64 | // All tokens go to the parser (unless Skip() is called in that rule) 65 | // on a particular "channel". The parser tunes to a particular channel 66 | // so that whitespace etc... can go to the parser on a "hidden" channel. 67 | 68 | TokenDefaultChannel = 0 69 | 70 | // Anything on different channel than DEFAULT_CHANNEL is not parsed 71 | // by parser. 72 | 73 | TokenHiddenChannel = 1 74 | ) 75 | 76 | func (b *BaseToken) GetChannel() int { 77 | return b.channel 78 | } 79 | 80 | func (b *BaseToken) GetStart() int { 81 | return b.start 82 | } 83 | 84 | func (b *BaseToken) GetStop() int { 85 | return b.stop 86 | } 87 | 88 | func (b *BaseToken) GetLine() int { 89 | return b.line 90 | } 91 | 92 | func (b *BaseToken) GetColumn() int { 93 | return b.column 94 | } 95 | 96 | func (b *BaseToken) GetTokenType() int { 97 | return b.tokenType 98 | } 99 | 100 | func (b *BaseToken) GetSource() *TokenSourceCharStreamPair { 101 | return b.source 102 | } 103 | 104 | func (b *BaseToken) GetTokenIndex() int { 105 | return b.tokenIndex 106 | } 107 | 108 | func (b *BaseToken) SetTokenIndex(v int) { 109 | b.tokenIndex = v 110 | } 111 | 112 | func (b *BaseToken) GetTokenSource() TokenSource { 113 | return b.source.tokenSource 114 | } 115 | 116 | func (b *BaseToken) GetInputStream() CharStream { 117 | return b.source.charStream 118 | } 119 | 120 | type CommonToken struct { 121 | *BaseToken 122 | } 123 | 124 | func NewCommonToken(source *TokenSourceCharStreamPair, tokenType, channel, start, stop int) *CommonToken { 125 | 126 | t := new(CommonToken) 127 | 128 | t.BaseToken = new(BaseToken) 129 | 130 | t.source = source 131 | t.tokenType = tokenType 132 | t.channel = channel 133 | t.start = start 134 | t.stop = stop 135 | t.tokenIndex = -1 136 | if t.source.tokenSource != nil { 137 | t.line = source.tokenSource.GetLine() 138 | t.column = source.tokenSource.GetCharPositionInLine() 139 | } else { 140 | t.column = -1 141 | } 142 | return t 143 | } 144 | 145 | // An empty {@link Pair} which is used as the default value of 146 | // {@link //source} for tokens that do not have a source. 147 | 148 | //CommonToken.EMPTY_SOURCE = [ nil, nil ] 149 | 150 | // Constructs a New{@link CommonToken} as a copy of another {@link Token}. 151 | // 152 | //

153 | // If {@code oldToken} is also a {@link CommonToken} instance, the newly 154 | // constructed token will share a reference to the {@link //text} field and 155 | // the {@link Pair} stored in {@link //source}. Otherwise, {@link //text} will 156 | // be assigned the result of calling {@link //GetText}, and {@link //source} 157 | // will be constructed from the result of {@link Token//GetTokenSource} and 158 | // {@link Token//GetInputStream}.

159 | // 160 | // @param oldToken The token to copy. 161 | func (c *CommonToken) clone() *CommonToken { 162 | t := NewCommonToken(c.source, c.tokenType, c.channel, c.start, c.stop) 163 | t.tokenIndex = c.GetTokenIndex() 164 | t.line = c.GetLine() 165 | t.column = c.GetColumn() 166 | t.text = c.GetText() 167 | return t 168 | } 169 | 170 | func (c *CommonToken) GetText() string { 171 | if c.text != "" { 172 | return c.text 173 | } 174 | input := c.GetInputStream() 175 | if input == nil { 176 | return "" 177 | } 178 | n := input.Size() 179 | if c.start < n && c.stop < n { 180 | return input.GetTextFromInterval(NewInterval(c.start, c.stop)) 181 | } 182 | return "" 183 | } 184 | 185 | func (c *CommonToken) SetText(text string) { 186 | c.text = text 187 | } 188 | 189 | func (c *CommonToken) String() string { 190 | txt := c.GetText() 191 | if txt != "" { 192 | txt = strings.Replace(txt, "\n", "\\n", -1) 193 | txt = strings.Replace(txt, "\r", "\\r", -1) 194 | txt = strings.Replace(txt, "\t", "\\t", -1) 195 | } else { 196 | txt = "" 197 | } 198 | 199 | var ch string 200 | if c.channel > 0 { 201 | ch = ",channel=" + strconv.Itoa(c.channel) 202 | } else { 203 | ch = "" 204 | } 205 | 206 | return "[@" + strconv.Itoa(c.tokenIndex) + "," + strconv.Itoa(c.start) + ":" + strconv.Itoa(c.stop) + "='" + 207 | txt + "',<" + strconv.Itoa(c.tokenType) + ">" + 208 | ch + "," + strconv.Itoa(c.line) + ":" + strconv.Itoa(c.column) + "]" 209 | } 210 | -------------------------------------------------------------------------------- /lib/turso/vendor/github.com/antlr/antlr4/runtime/Go/antlr/v4/token_source.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2012-2022 The ANTLR Project. All rights reserved. 2 | // Use of this file is governed by the BSD 3-clause license that 3 | // can be found in the LICENSE.txt file in the project root. 4 | 5 | package antlr 6 | 7 | type TokenSource interface { 8 | NextToken() Token 9 | Skip() 10 | More() 11 | GetLine() int 12 | GetCharPositionInLine() int 13 | GetInputStream() CharStream 14 | GetSourceName() string 15 | setTokenFactory(factory TokenFactory) 16 | GetTokenFactory() TokenFactory 17 | } 18 | -------------------------------------------------------------------------------- /lib/turso/vendor/github.com/antlr/antlr4/runtime/Go/antlr/v4/token_stream.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2012-2022 The ANTLR Project. All rights reserved. 2 | // Use of this file is governed by the BSD 3-clause license that 3 | // can be found in the LICENSE.txt file in the project root. 4 | 5 | package antlr 6 | 7 | type TokenStream interface { 8 | IntStream 9 | 10 | LT(k int) Token 11 | 12 | Get(index int) Token 13 | GetTokenSource() TokenSource 14 | SetTokenSource(TokenSource) 15 | 16 | GetAllText() string 17 | GetTextFromInterval(*Interval) string 18 | GetTextFromRuleContext(RuleContext) string 19 | GetTextFromTokens(Token, Token) string 20 | } 21 | -------------------------------------------------------------------------------- /lib/turso/vendor/github.com/antlr/antlr4/runtime/Go/antlr/v4/trace_listener.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2012-2022 The ANTLR Project. All rights reserved. 2 | // Use of this file is governed by the BSD 3-clause license that 3 | // can be found in the LICENSE.txt file in the project root. 4 | 5 | package antlr 6 | 7 | import "fmt" 8 | 9 | type TraceListener struct { 10 | parser *BaseParser 11 | } 12 | 13 | func NewTraceListener(parser *BaseParser) *TraceListener { 14 | tl := new(TraceListener) 15 | tl.parser = parser 16 | return tl 17 | } 18 | 19 | func (t *TraceListener) VisitErrorNode(_ ErrorNode) { 20 | } 21 | 22 | func (t *TraceListener) EnterEveryRule(ctx ParserRuleContext) { 23 | fmt.Println("enter " + t.parser.GetRuleNames()[ctx.GetRuleIndex()] + ", LT(1)=" + t.parser.input.LT(1).GetText()) 24 | } 25 | 26 | func (t *TraceListener) VisitTerminal(node TerminalNode) { 27 | fmt.Println("consume " + fmt.Sprint(node.GetSymbol()) + " rule " + t.parser.GetRuleNames()[t.parser.ctx.GetRuleIndex()]) 28 | } 29 | 30 | func (t *TraceListener) ExitEveryRule(ctx ParserRuleContext) { 31 | fmt.Println("exit " + t.parser.GetRuleNames()[ctx.GetRuleIndex()] + ", LT(1)=" + t.parser.input.LT(1).GetText()) 32 | } 33 | -------------------------------------------------------------------------------- /lib/turso/vendor/github.com/antlr/antlr4/runtime/Go/antlr/v4/trees.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2012-2022 The ANTLR Project. All rights reserved. 2 | // Use of this file is governed by the BSD 3-clause license that 3 | // can be found in the LICENSE.txt file in the project root. 4 | 5 | package antlr 6 | 7 | import "fmt" 8 | 9 | /** A set of utility routines useful for all kinds of ANTLR trees. */ 10 | 11 | // Print out a whole tree in LISP form. {@link //getNodeText} is used on the 12 | // 13 | // node payloads to get the text for the nodes. Detect 14 | // parse trees and extract data appropriately. 15 | func TreesStringTree(tree Tree, ruleNames []string, recog Recognizer) string { 16 | 17 | if recog != nil { 18 | ruleNames = recog.GetRuleNames() 19 | } 20 | 21 | s := TreesGetNodeText(tree, ruleNames, nil) 22 | 23 | s = EscapeWhitespace(s, false) 24 | c := tree.GetChildCount() 25 | if c == 0 { 26 | return s 27 | } 28 | res := "(" + s + " " 29 | if c > 0 { 30 | s = TreesStringTree(tree.GetChild(0), ruleNames, nil) 31 | res += s 32 | } 33 | for i := 1; i < c; i++ { 34 | s = TreesStringTree(tree.GetChild(i), ruleNames, nil) 35 | res += (" " + s) 36 | } 37 | res += ")" 38 | return res 39 | } 40 | 41 | func TreesGetNodeText(t Tree, ruleNames []string, recog Parser) string { 42 | if recog != nil { 43 | ruleNames = recog.GetRuleNames() 44 | } 45 | 46 | if ruleNames != nil { 47 | switch t2 := t.(type) { 48 | case RuleNode: 49 | t3 := t2.GetRuleContext() 50 | altNumber := t3.GetAltNumber() 51 | 52 | if altNumber != ATNInvalidAltNumber { 53 | return fmt.Sprintf("%s:%d", ruleNames[t3.GetRuleIndex()], altNumber) 54 | } 55 | return ruleNames[t3.GetRuleIndex()] 56 | case ErrorNode: 57 | return fmt.Sprint(t2) 58 | case TerminalNode: 59 | if t2.GetSymbol() != nil { 60 | return t2.GetSymbol().GetText() 61 | } 62 | } 63 | } 64 | 65 | // no recog for rule names 66 | payload := t.GetPayload() 67 | if p2, ok := payload.(Token); ok { 68 | return p2.GetText() 69 | } 70 | 71 | return fmt.Sprint(t.GetPayload()) 72 | } 73 | 74 | // Return ordered list of all children of this node 75 | func TreesGetChildren(t Tree) []Tree { 76 | list := make([]Tree, 0) 77 | for i := 0; i < t.GetChildCount(); i++ { 78 | list = append(list, t.GetChild(i)) 79 | } 80 | return list 81 | } 82 | 83 | // Return a list of all ancestors of this node. The first node of 84 | // 85 | // list is the root and the last is the parent of this node. 86 | func TreesgetAncestors(t Tree) []Tree { 87 | ancestors := make([]Tree, 0) 88 | t = t.GetParent() 89 | for t != nil { 90 | f := []Tree{t} 91 | ancestors = append(f, ancestors...) 92 | t = t.GetParent() 93 | } 94 | return ancestors 95 | } 96 | 97 | func TreesFindAllTokenNodes(t ParseTree, ttype int) []ParseTree { 98 | return TreesfindAllNodes(t, ttype, true) 99 | } 100 | 101 | func TreesfindAllRuleNodes(t ParseTree, ruleIndex int) []ParseTree { 102 | return TreesfindAllNodes(t, ruleIndex, false) 103 | } 104 | 105 | func TreesfindAllNodes(t ParseTree, index int, findTokens bool) []ParseTree { 106 | nodes := make([]ParseTree, 0) 107 | treesFindAllNodes(t, index, findTokens, &nodes) 108 | return nodes 109 | } 110 | 111 | func treesFindAllNodes(t ParseTree, index int, findTokens bool, nodes *[]ParseTree) { 112 | // check this node (the root) first 113 | 114 | t2, ok := t.(TerminalNode) 115 | t3, ok2 := t.(ParserRuleContext) 116 | 117 | if findTokens && ok { 118 | if t2.GetSymbol().GetTokenType() == index { 119 | *nodes = append(*nodes, t2) 120 | } 121 | } else if !findTokens && ok2 { 122 | if t3.GetRuleIndex() == index { 123 | *nodes = append(*nodes, t3) 124 | } 125 | } 126 | // check children 127 | for i := 0; i < t.GetChildCount(); i++ { 128 | treesFindAllNodes(t.GetChild(i).(ParseTree), index, findTokens, nodes) 129 | } 130 | } 131 | 132 | func TreesDescendants(t ParseTree) []ParseTree { 133 | nodes := []ParseTree{t} 134 | for i := 0; i < t.GetChildCount(); i++ { 135 | nodes = append(nodes, TreesDescendants(t.GetChild(i).(ParseTree))...) 136 | } 137 | return nodes 138 | } 139 | -------------------------------------------------------------------------------- /lib/turso/vendor/github.com/antlr/antlr4/runtime/Go/antlr/v4/utils_set.go: -------------------------------------------------------------------------------- 1 | package antlr 2 | 3 | import "math" 4 | 5 | const ( 6 | _initalCapacity = 16 7 | _initalBucketCapacity = 8 8 | _loadFactor = 0.75 9 | ) 10 | 11 | type Set interface { 12 | Add(value interface{}) (added interface{}) 13 | Len() int 14 | Get(value interface{}) (found interface{}) 15 | Contains(value interface{}) bool 16 | Values() []interface{} 17 | Each(f func(interface{}) bool) 18 | } 19 | 20 | type array2DHashSet struct { 21 | buckets [][]Collectable[any] 22 | hashcodeFunction func(interface{}) int 23 | equalsFunction func(Collectable[any], Collectable[any]) bool 24 | 25 | n int // How many elements in set 26 | threshold int // when to expand 27 | 28 | currentPrime int // jump by 4 primes each expand or whatever 29 | initialBucketCapacity int 30 | } 31 | 32 | func (as *array2DHashSet) Each(f func(interface{}) bool) { 33 | if as.Len() < 1 { 34 | return 35 | } 36 | 37 | for _, bucket := range as.buckets { 38 | for _, o := range bucket { 39 | if o == nil { 40 | break 41 | } 42 | if !f(o) { 43 | return 44 | } 45 | } 46 | } 47 | } 48 | 49 | func (as *array2DHashSet) Values() []interface{} { 50 | if as.Len() < 1 { 51 | return nil 52 | } 53 | 54 | values := make([]interface{}, 0, as.Len()) 55 | as.Each(func(i interface{}) bool { 56 | values = append(values, i) 57 | return true 58 | }) 59 | return values 60 | } 61 | 62 | func (as *array2DHashSet) Contains(value Collectable[any]) bool { 63 | return as.Get(value) != nil 64 | } 65 | 66 | func (as *array2DHashSet) Add(value Collectable[any]) interface{} { 67 | if as.n > as.threshold { 68 | as.expand() 69 | } 70 | return as.innerAdd(value) 71 | } 72 | 73 | func (as *array2DHashSet) expand() { 74 | old := as.buckets 75 | 76 | as.currentPrime += 4 77 | 78 | var ( 79 | newCapacity = len(as.buckets) << 1 80 | newTable = as.createBuckets(newCapacity) 81 | newBucketLengths = make([]int, len(newTable)) 82 | ) 83 | 84 | as.buckets = newTable 85 | as.threshold = int(float64(newCapacity) * _loadFactor) 86 | 87 | for _, bucket := range old { 88 | if bucket == nil { 89 | continue 90 | } 91 | 92 | for _, o := range bucket { 93 | if o == nil { 94 | break 95 | } 96 | 97 | b := as.getBuckets(o) 98 | bucketLength := newBucketLengths[b] 99 | var newBucket []Collectable[any] 100 | if bucketLength == 0 { 101 | // new bucket 102 | newBucket = as.createBucket(as.initialBucketCapacity) 103 | newTable[b] = newBucket 104 | } else { 105 | newBucket = newTable[b] 106 | if bucketLength == len(newBucket) { 107 | // expand 108 | newBucketCopy := make([]Collectable[any], len(newBucket)<<1) 109 | copy(newBucketCopy[:bucketLength], newBucket) 110 | newBucket = newBucketCopy 111 | newTable[b] = newBucket 112 | } 113 | } 114 | 115 | newBucket[bucketLength] = o 116 | newBucketLengths[b]++ 117 | } 118 | } 119 | } 120 | 121 | func (as *array2DHashSet) Len() int { 122 | return as.n 123 | } 124 | 125 | func (as *array2DHashSet) Get(o Collectable[any]) interface{} { 126 | if o == nil { 127 | return nil 128 | } 129 | 130 | b := as.getBuckets(o) 131 | bucket := as.buckets[b] 132 | if bucket == nil { // no bucket 133 | return nil 134 | } 135 | 136 | for _, e := range bucket { 137 | if e == nil { 138 | return nil // empty slot; not there 139 | } 140 | if as.equalsFunction(e, o) { 141 | return e 142 | } 143 | } 144 | 145 | return nil 146 | } 147 | 148 | func (as *array2DHashSet) innerAdd(o Collectable[any]) interface{} { 149 | b := as.getBuckets(o) 150 | 151 | bucket := as.buckets[b] 152 | 153 | // new bucket 154 | if bucket == nil { 155 | bucket = as.createBucket(as.initialBucketCapacity) 156 | bucket[0] = o 157 | 158 | as.buckets[b] = bucket 159 | as.n++ 160 | return o 161 | } 162 | 163 | // look for it in bucket 164 | for i := 0; i < len(bucket); i++ { 165 | existing := bucket[i] 166 | if existing == nil { // empty slot; not there, add. 167 | bucket[i] = o 168 | as.n++ 169 | return o 170 | } 171 | 172 | if as.equalsFunction(existing, o) { // found existing, quit 173 | return existing 174 | } 175 | } 176 | 177 | // full bucket, expand and add to end 178 | oldLength := len(bucket) 179 | bucketCopy := make([]Collectable[any], oldLength<<1) 180 | copy(bucketCopy[:oldLength], bucket) 181 | bucket = bucketCopy 182 | as.buckets[b] = bucket 183 | bucket[oldLength] = o 184 | as.n++ 185 | return o 186 | } 187 | 188 | func (as *array2DHashSet) getBuckets(value Collectable[any]) int { 189 | hash := as.hashcodeFunction(value) 190 | return hash & (len(as.buckets) - 1) 191 | } 192 | 193 | func (as *array2DHashSet) createBuckets(cap int) [][]Collectable[any] { 194 | return make([][]Collectable[any], cap) 195 | } 196 | 197 | func (as *array2DHashSet) createBucket(cap int) []Collectable[any] { 198 | return make([]Collectable[any], cap) 199 | } 200 | 201 | func newArray2DHashSetWithCap( 202 | hashcodeFunction func(interface{}) int, 203 | equalsFunction func(Collectable[any], Collectable[any]) bool, 204 | initCap int, 205 | initBucketCap int, 206 | ) *array2DHashSet { 207 | if hashcodeFunction == nil { 208 | hashcodeFunction = standardHashFunction 209 | } 210 | 211 | if equalsFunction == nil { 212 | equalsFunction = standardEqualsFunction 213 | } 214 | 215 | ret := &array2DHashSet{ 216 | hashcodeFunction: hashcodeFunction, 217 | equalsFunction: equalsFunction, 218 | 219 | n: 0, 220 | threshold: int(math.Floor(_initalCapacity * _loadFactor)), 221 | 222 | currentPrime: 1, 223 | initialBucketCapacity: initBucketCap, 224 | } 225 | 226 | ret.buckets = ret.createBuckets(initCap) 227 | return ret 228 | } 229 | 230 | func newArray2DHashSet( 231 | hashcodeFunction func(interface{}) int, 232 | equalsFunction func(Collectable[any], Collectable[any]) bool, 233 | ) *array2DHashSet { 234 | return newArray2DHashSetWithCap(hashcodeFunction, equalsFunction, _initalCapacity, _initalBucketCapacity) 235 | } 236 | -------------------------------------------------------------------------------- /lib/turso/vendor/github.com/klauspost/compress/flate/regmask_amd64.go: -------------------------------------------------------------------------------- 1 | package flate 2 | 3 | const ( 4 | // Masks for shifts with register sizes of the shift value. 5 | // This can be used to work around the x86 design of shifting by mod register size. 6 | // It can be used when a variable shift is always smaller than the register size. 7 | 8 | // reg8SizeMaskX - shift value is 8 bits, shifted is X 9 | reg8SizeMask8 = 7 10 | reg8SizeMask16 = 15 11 | reg8SizeMask32 = 31 12 | reg8SizeMask64 = 63 13 | 14 | // reg16SizeMaskX - shift value is 16 bits, shifted is X 15 | reg16SizeMask8 = reg8SizeMask8 16 | reg16SizeMask16 = reg8SizeMask16 17 | reg16SizeMask32 = reg8SizeMask32 18 | reg16SizeMask64 = reg8SizeMask64 19 | 20 | // reg32SizeMaskX - shift value is 32 bits, shifted is X 21 | reg32SizeMask8 = reg8SizeMask8 22 | reg32SizeMask16 = reg8SizeMask16 23 | reg32SizeMask32 = reg8SizeMask32 24 | reg32SizeMask64 = reg8SizeMask64 25 | 26 | // reg64SizeMaskX - shift value is 64 bits, shifted is X 27 | reg64SizeMask8 = reg8SizeMask8 28 | reg64SizeMask16 = reg8SizeMask16 29 | reg64SizeMask32 = reg8SizeMask32 30 | reg64SizeMask64 = reg8SizeMask64 31 | 32 | // regSizeMaskUintX - shift value is uint, shifted is X 33 | regSizeMaskUint8 = reg8SizeMask8 34 | regSizeMaskUint16 = reg8SizeMask16 35 | regSizeMaskUint32 = reg8SizeMask32 36 | regSizeMaskUint64 = reg8SizeMask64 37 | ) 38 | -------------------------------------------------------------------------------- /lib/turso/vendor/github.com/klauspost/compress/flate/regmask_other.go: -------------------------------------------------------------------------------- 1 | //go:build !amd64 2 | // +build !amd64 3 | 4 | package flate 5 | 6 | const ( 7 | // Masks for shifts with register sizes of the shift value. 8 | // This can be used to work around the x86 design of shifting by mod register size. 9 | // It can be used when a variable shift is always smaller than the register size. 10 | 11 | // reg8SizeMaskX - shift value is 8 bits, shifted is X 12 | reg8SizeMask8 = 0xff 13 | reg8SizeMask16 = 0xff 14 | reg8SizeMask32 = 0xff 15 | reg8SizeMask64 = 0xff 16 | 17 | // reg16SizeMaskX - shift value is 16 bits, shifted is X 18 | reg16SizeMask8 = 0xffff 19 | reg16SizeMask16 = 0xffff 20 | reg16SizeMask32 = 0xffff 21 | reg16SizeMask64 = 0xffff 22 | 23 | // reg32SizeMaskX - shift value is 32 bits, shifted is X 24 | reg32SizeMask8 = 0xffffffff 25 | reg32SizeMask16 = 0xffffffff 26 | reg32SizeMask32 = 0xffffffff 27 | reg32SizeMask64 = 0xffffffff 28 | 29 | // reg64SizeMaskX - shift value is 64 bits, shifted is X 30 | reg64SizeMask8 = 0xffffffffffffffff 31 | reg64SizeMask16 = 0xffffffffffffffff 32 | reg64SizeMask32 = 0xffffffffffffffff 33 | reg64SizeMask64 = 0xffffffffffffffff 34 | 35 | // regSizeMaskUintX - shift value is uint, shifted is X 36 | regSizeMaskUint8 = ^uint(0) 37 | regSizeMaskUint16 = ^uint(0) 38 | regSizeMaskUint32 = ^uint(0) 39 | regSizeMaskUint64 = ^uint(0) 40 | ) 41 | -------------------------------------------------------------------------------- /lib/turso/vendor/github.com/libsql/sqlite-antlr4-parser/sqliteparserutils/utils.go: -------------------------------------------------------------------------------- 1 | package sqliteparserutils 2 | 3 | import ( 4 | "github.com/antlr/antlr4/runtime/Go/antlr/v4" 5 | "github.com/libsql/sqlite-antlr4-parser/sqliteparser" 6 | ) 7 | 8 | // TODO: Shell test begin transaction on shell 9 | 10 | type SplitStatementExtraInfo struct { 11 | IncompleteCreateTriggerStatement bool 12 | IncompleteMultilineComment bool 13 | LastTokenType int 14 | } 15 | 16 | func SplitStatement(statement string) (stmts []string, extraInfo SplitStatementExtraInfo) { 17 | tokenStream := createTokenStream(statement) 18 | 19 | stmtIntervals := make([]*antlr.Interval, 0) 20 | currentIntervalStart := -1 21 | insideCreateTriggerStmt := false 22 | insideMultilineComment := false 23 | 24 | var previousToken antlr.Token 25 | var currentToken antlr.Token 26 | for currentToken = tokenStream.LT(1); currentToken.GetTokenType() != antlr.TokenEOF; currentToken = tokenStream.LT(1) { 27 | // We break loop here because we're sure multiline comment didn't finished, otherwise lexer would have just ignored 28 | // it 29 | if atIncompleteMultilineCommentStart(tokenStream) { 30 | insideMultilineComment = true 31 | break 32 | } 33 | 34 | if currentIntervalStart == -1 { 35 | if currentToken.GetTokenType() == sqliteparser.SQLiteLexerSCOL { 36 | previousToken = currentToken 37 | tokenStream.Consume() 38 | continue 39 | } 40 | currentIntervalStart = currentToken.GetTokenIndex() 41 | 42 | if atCreateTriggerStart(tokenStream) { 43 | insideCreateTriggerStmt = true 44 | previousToken = currentToken 45 | tokenStream.Consume() 46 | continue 47 | } 48 | 49 | } 50 | 51 | if insideCreateTriggerStmt { 52 | if currentToken.GetTokenType() == sqliteparser.SQLiteLexerEND_ { 53 | insideCreateTriggerStmt = false 54 | } 55 | } else if currentToken.GetTokenType() == sqliteparser.SQLiteLexerSCOL { 56 | stmtIntervals = append(stmtIntervals, antlr.NewInterval(currentIntervalStart, previousToken.GetTokenIndex())) 57 | currentIntervalStart = -1 58 | } 59 | 60 | previousToken = currentToken 61 | tokenStream.Consume() 62 | } 63 | 64 | if currentIntervalStart != -1 && previousToken != nil { 65 | stmtIntervals = append(stmtIntervals, antlr.NewInterval(currentIntervalStart, previousToken.GetTokenIndex())) 66 | } 67 | 68 | stmts = make([]string, 0) 69 | for _, stmtInterval := range stmtIntervals { 70 | stmts = append(stmts, tokenStream.GetTextFromInterval(stmtInterval)) 71 | } 72 | 73 | lastTokenType := antlr.TokenInvalidType 74 | if previousToken != nil { 75 | lastTokenType = previousToken.GetTokenType() 76 | } 77 | return stmts, SplitStatementExtraInfo{IncompleteCreateTriggerStatement: insideCreateTriggerStmt, IncompleteMultilineComment: insideMultilineComment, LastTokenType: lastTokenType} 78 | } 79 | 80 | func atCreateTriggerStart(tokenStream antlr.TokenStream) bool { 81 | if tokenStream.LT(1).GetTokenType() != sqliteparser.SQLiteLexerCREATE_ { 82 | return false 83 | } 84 | 85 | if tokenStream.LT(2).GetTokenType() == sqliteparser.SQLiteLexerTRIGGER_ { 86 | return true 87 | } 88 | 89 | if tokenStream.LT(2).GetTokenType() == sqliteparser.SQLiteLexerTEMP_ || tokenStream.LT(2).GetTokenType() == sqliteparser.SQLiteLexerTEMPORARY_ && 90 | tokenStream.LT(3).GetTokenType() == sqliteparser.SQLiteLexerTRIGGER_ { 91 | return true 92 | } 93 | 94 | return false 95 | 96 | } 97 | 98 | // Note: Only starts for incomplete multiline comments will be detected cause lexer automatically ignores complete 99 | // multiline comments 100 | func atIncompleteMultilineCommentStart(tokenStream antlr.TokenStream) bool { 101 | if tokenStream.LT(1).GetTokenType() != sqliteparser.SQLiteLexerDIV { 102 | return false 103 | } 104 | 105 | if tokenStream.LT(2).GetTokenType() == sqliteparser.SQLiteLexerSTAR { 106 | return true 107 | } 108 | 109 | return false 110 | } 111 | 112 | func createTokenStream(statement string) *antlr.CommonTokenStream { 113 | statementStream := antlr.NewInputStream(statement) 114 | 115 | lexer := sqliteparser.NewSQLiteLexer(statementStream) 116 | return antlr.NewCommonTokenStream(lexer, 0) 117 | } 118 | -------------------------------------------------------------------------------- /lib/turso/vendor/github.com/tursodatabase/libsql-client-go/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 libSQL 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /lib/turso/vendor/github.com/tursodatabase/libsql-client-go/libsql/internal/hrana/batch.go: -------------------------------------------------------------------------------- 1 | package hrana 2 | 3 | type Batch struct { 4 | Steps []BatchStep `json:"steps"` 5 | ReplicationIndex *uint64 `json:"replication_index,omitempty"` 6 | } 7 | 8 | type BatchStep struct { 9 | Stmt Stmt `json:"stmt"` 10 | Condition *BatchCondition `json:"condition,omitempty"` 11 | } 12 | 13 | type BatchCondition struct { 14 | Type string `json:"type"` 15 | Step *int32 `json:"step,omitempty"` 16 | Cond *BatchCondition `json:"cond,omitempty"` 17 | Conds []BatchCondition `json:"conds,omitempty"` 18 | } 19 | 20 | func (b *Batch) Add(stmt Stmt) { 21 | b.Steps = append(b.Steps, BatchStep{Stmt: stmt}) 22 | } 23 | -------------------------------------------------------------------------------- /lib/turso/vendor/github.com/tursodatabase/libsql-client-go/libsql/internal/hrana/batch_result.go: -------------------------------------------------------------------------------- 1 | package hrana 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "strconv" 7 | ) 8 | 9 | type BatchResult struct { 10 | StepResults []*StmtResult `json:"step_results"` 11 | StepErrors []*Error `json:"step_errors"` 12 | ReplicationIndex *uint64 `json:"replication_index"` 13 | } 14 | 15 | func (b *BatchResult) UnmarshalJSON(data []byte) error { 16 | type Alias BatchResult 17 | aux := &struct { 18 | ReplicationIndex interface{} `json:"replication_index,omitempty"` 19 | *Alias 20 | }{ 21 | Alias: (*Alias)(b), 22 | } 23 | 24 | if err := json.Unmarshal(data, &aux); err != nil { 25 | return err 26 | } 27 | 28 | if aux.ReplicationIndex == nil { 29 | return nil 30 | } 31 | 32 | switch v := aux.ReplicationIndex.(type) { 33 | case float64: 34 | repIndex := uint64(v) 35 | b.ReplicationIndex = &repIndex 36 | case string: 37 | if v == "" { 38 | return nil 39 | } 40 | repIndex, err := strconv.ParseUint(v, 10, 64) 41 | if err != nil { 42 | return err 43 | } 44 | b.ReplicationIndex = &repIndex 45 | default: 46 | return fmt.Errorf("invalid type for replication index: %T", v) 47 | } 48 | return nil 49 | } 50 | -------------------------------------------------------------------------------- /lib/turso/vendor/github.com/tursodatabase/libsql-client-go/libsql/internal/hrana/pipeline_request.go: -------------------------------------------------------------------------------- 1 | package hrana 2 | 3 | type PipelineRequest struct { 4 | Baton string `json:"baton,omitempty"` 5 | Requests []StreamRequest `json:"requests"` 6 | } 7 | 8 | func (pr *PipelineRequest) Add(request StreamRequest) { 9 | pr.Requests = append(pr.Requests, request) 10 | } 11 | -------------------------------------------------------------------------------- /lib/turso/vendor/github.com/tursodatabase/libsql-client-go/libsql/internal/hrana/pipeline_response.go: -------------------------------------------------------------------------------- 1 | package hrana 2 | 3 | type PipelineResponse struct { 4 | Baton string `json:"baton,omitempty"` 5 | BaseUrl string `json:"base_url,omitempty"` 6 | Results []StreamResult `json:"results"` 7 | } 8 | -------------------------------------------------------------------------------- /lib/turso/vendor/github.com/tursodatabase/libsql-client-go/libsql/internal/hrana/stmt.go: -------------------------------------------------------------------------------- 1 | package hrana 2 | 3 | import ( 4 | "github.com/tursodatabase/libsql-client-go/libsql/internal/http/shared" 5 | ) 6 | 7 | type Stmt struct { 8 | Sql *string `json:"sql,omitempty"` 9 | SqlId *int32 `json:"sql_id,omitempty"` 10 | Args []Value `json:"args,omitempty"` 11 | NamedArgs []NamedArg `json:"named_args,omitempty"` 12 | WantRows bool `json:"want_rows"` 13 | ReplicationIndex *uint64 `json:"replication_index,omitempty"` 14 | } 15 | 16 | type NamedArg struct { 17 | Name string `json:"name"` 18 | Value Value `json:"value"` 19 | } 20 | 21 | func (s *Stmt) AddArgs(params shared.Params) error { 22 | if len(params.Named()) > 0 { 23 | return s.AddNamedArgs(params.Named()) 24 | } else { 25 | return s.AddPositionalArgs(params.Positional()) 26 | } 27 | } 28 | 29 | func (s *Stmt) AddPositionalArgs(args []any) error { 30 | argValues := make([]Value, len(args)) 31 | for idx := range args { 32 | var err error 33 | if argValues[idx], err = ToValue(args[idx]); err != nil { 34 | return err 35 | } 36 | } 37 | s.Args = argValues 38 | return nil 39 | } 40 | 41 | func (s *Stmt) AddNamedArgs(args map[string]any) error { 42 | argValues := make([]NamedArg, len(args)) 43 | idx := 0 44 | for key, value := range args { 45 | var err error 46 | var v Value 47 | if v, err = ToValue(value); err != nil { 48 | return err 49 | } 50 | argValues[idx] = NamedArg{ 51 | Name: key, 52 | Value: v, 53 | } 54 | idx++ 55 | } 56 | s.NamedArgs = argValues 57 | return nil 58 | } 59 | -------------------------------------------------------------------------------- /lib/turso/vendor/github.com/tursodatabase/libsql-client-go/libsql/internal/hrana/stmt_result.go: -------------------------------------------------------------------------------- 1 | package hrana 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "strconv" 7 | ) 8 | 9 | type Column struct { 10 | Name *string `json:"name"` 11 | Type *string `json:"decltype"` 12 | } 13 | 14 | type StmtResult struct { 15 | Cols []Column `json:"cols"` 16 | Rows [][]Value `json:"rows"` 17 | AffectedRowCount int32 `json:"affected_row_count"` 18 | LastInsertRowId *string `json:"last_insert_rowid"` 19 | ReplicationIndex *uint64 `json:"replication_index"` 20 | } 21 | 22 | func (r *StmtResult) GetLastInsertRowId() int64 { 23 | if r.LastInsertRowId != nil { 24 | if integer, err := strconv.ParseInt(*r.LastInsertRowId, 10, 64); err == nil { 25 | return integer 26 | } 27 | } 28 | return 0 29 | } 30 | 31 | func (r *StmtResult) UnmarshalJSON(data []byte) error { 32 | type Alias StmtResult 33 | aux := &struct { 34 | ReplicationIndex interface{} `json:"replication_index,omitempty"` 35 | *Alias 36 | }{ 37 | Alias: (*Alias)(r), 38 | } 39 | 40 | if err := json.Unmarshal(data, &aux); err != nil { 41 | return err 42 | } 43 | 44 | if aux.ReplicationIndex == nil { 45 | return nil 46 | } 47 | 48 | switch v := aux.ReplicationIndex.(type) { 49 | case float64: 50 | repIndex := uint64(v) 51 | r.ReplicationIndex = &repIndex 52 | case string: 53 | if v == "" { 54 | return nil 55 | } 56 | repIndex, err := strconv.ParseUint(v, 10, 64) 57 | if err != nil { 58 | return err 59 | } 60 | r.ReplicationIndex = &repIndex 61 | default: 62 | return fmt.Errorf("invalid type for replication index: %T", v) 63 | } 64 | return nil 65 | } 66 | -------------------------------------------------------------------------------- /lib/turso/vendor/github.com/tursodatabase/libsql-client-go/libsql/internal/hrana/stream_request.go: -------------------------------------------------------------------------------- 1 | package hrana 2 | 3 | import ( 4 | "github.com/tursodatabase/libsql-client-go/libsql/internal/http/shared" 5 | ) 6 | 7 | type StreamRequest struct { 8 | Type string `json:"type"` 9 | Stmt *Stmt `json:"stmt,omitempty"` 10 | Batch *Batch `json:"batch,omitempty"` 11 | Sql *string `json:"sql,omitempty"` 12 | SqlId *int32 `json:"sql_id,omitempty"` 13 | } 14 | 15 | func CloseStream() StreamRequest { 16 | return StreamRequest{Type: "close"} 17 | } 18 | 19 | func ExecuteStream(sql string, params shared.Params, wantRows bool) (*StreamRequest, error) { 20 | stmt := &Stmt{ 21 | Sql: &sql, 22 | WantRows: wantRows, 23 | } 24 | if err := stmt.AddArgs(params); err != nil { 25 | return nil, err 26 | } 27 | return &StreamRequest{Type: "execute", Stmt: stmt}, nil 28 | } 29 | 30 | func ExecuteStoredStream(sqlId int32, params shared.Params, wantRows bool) (*StreamRequest, error) { 31 | stmt := &Stmt{ 32 | SqlId: &sqlId, 33 | WantRows: wantRows, 34 | } 35 | if err := stmt.AddArgs(params); err != nil { 36 | return nil, err 37 | } 38 | return &StreamRequest{Type: "execute", Stmt: stmt}, nil 39 | } 40 | 41 | func BatchStream(sqls []string, params []shared.Params, wantRows bool) (*StreamRequest, error) { 42 | batch := &Batch{} 43 | for idx, sql := range sqls { 44 | s := sql 45 | stmt := &Stmt{ 46 | Sql: &s, 47 | WantRows: wantRows, 48 | } 49 | if err := stmt.AddArgs(params[idx]); err != nil { 50 | return nil, err 51 | } 52 | batch.Add(*stmt) 53 | } 54 | return &StreamRequest{Type: "batch", Batch: batch}, nil 55 | } 56 | 57 | func StoreSqlStream(sql string, sqlId int32) StreamRequest { 58 | return StreamRequest{Type: "store_sql", Sql: &sql, SqlId: &sqlId} 59 | } 60 | 61 | func CloseStoredSqlStream(sqlId int32) StreamRequest { 62 | return StreamRequest{Type: "close_sql", SqlId: &sqlId} 63 | } 64 | -------------------------------------------------------------------------------- /lib/turso/vendor/github.com/tursodatabase/libsql-client-go/libsql/internal/hrana/stream_result.go: -------------------------------------------------------------------------------- 1 | package hrana 2 | 3 | import ( 4 | "encoding/json" 5 | "errors" 6 | "fmt" 7 | ) 8 | 9 | type StreamResult struct { 10 | Type string `json:"type"` 11 | Response *StreamResponse `json:"response,omitempty"` 12 | Error *Error `json:"error,omitempty"` 13 | } 14 | 15 | type StreamResponse struct { 16 | Type string `json:"type"` 17 | Result json.RawMessage `json:"result,omitempty"` 18 | } 19 | 20 | func (r *StreamResponse) ExecuteResult() (*StmtResult, error) { 21 | if r.Type != "execute" { 22 | return nil, fmt.Errorf("invalid response type: %s", r.Type) 23 | } 24 | 25 | var res StmtResult 26 | if err := json.Unmarshal(r.Result, &res); err != nil { 27 | return nil, err 28 | } 29 | return &res, nil 30 | } 31 | 32 | func (r *StreamResponse) BatchResult() (*BatchResult, error) { 33 | if r.Type != "batch" { 34 | return nil, fmt.Errorf("invalid response type: %s", r.Type) 35 | } 36 | 37 | var res BatchResult 38 | if err := json.Unmarshal(r.Result, &res); err != nil { 39 | return nil, err 40 | } 41 | for _, e := range res.StepErrors { 42 | if e != nil { 43 | return nil, errors.New(e.Message) 44 | } 45 | } 46 | return &res, nil 47 | } 48 | 49 | type Error struct { 50 | Message string `json:"message"` 51 | Code *string `json:"code,omitempty"` 52 | } 53 | -------------------------------------------------------------------------------- /lib/turso/vendor/github.com/tursodatabase/libsql-client-go/libsql/internal/hrana/value.go: -------------------------------------------------------------------------------- 1 | package hrana 2 | 3 | import ( 4 | "encoding/base64" 5 | "fmt" 6 | "strconv" 7 | "strings" 8 | "time" 9 | ) 10 | 11 | type Value struct { 12 | Type string `json:"type"` 13 | Value any `json:"value,omitempty"` 14 | Base64 string `json:"base64,omitempty"` 15 | } 16 | 17 | func (v Value) ToValue(columnType *string) any { 18 | if v.Type == "blob" { 19 | bytes, err := base64.StdEncoding.WithPadding(base64.NoPadding).DecodeString(v.Base64) 20 | if err != nil { 21 | return nil 22 | } 23 | return bytes 24 | } else if v.Type == "integer" { 25 | integer, err := strconv.ParseInt(v.Value.(string), 10, 64) 26 | if err != nil { 27 | return nil 28 | } 29 | return integer 30 | } else if columnType != nil { 31 | if (strings.ToLower(*columnType) == "timestamp" || strings.ToLower(*columnType) == "datetime") && v.Type == "text" { 32 | for _, format := range []string{ 33 | "2006-01-02 15:04:05.999999999-07:00", 34 | "2006-01-02T15:04:05.999999999-07:00", 35 | "2006-01-02 15:04:05.999999999", 36 | "2006-01-02T15:04:05.999999999", 37 | "2006-01-02 15:04:05", 38 | "2006-01-02T15:04:05", 39 | "2006-01-02 15:04", 40 | "2006-01-02T15:04", 41 | "2006-01-02", 42 | } { 43 | if t, err := time.ParseInLocation(format, v.Value.(string), time.UTC); err == nil { 44 | return t 45 | } 46 | } 47 | } 48 | } 49 | 50 | return v.Value 51 | } 52 | 53 | func ToValue(v any) (Value, error) { 54 | var res Value 55 | if v == nil { 56 | res.Type = "null" 57 | } else if integer, ok := v.(int64); ok { 58 | res.Type = "integer" 59 | res.Value = strconv.FormatInt(integer, 10) 60 | } else if integer, ok := v.(int); ok { 61 | res.Type = "integer" 62 | res.Value = strconv.FormatInt(int64(integer), 10) 63 | } else if text, ok := v.(string); ok { 64 | res.Type = "text" 65 | res.Value = text 66 | } else if blob, ok := v.([]byte); ok { 67 | res.Type = "blob" 68 | res.Base64 = base64.StdEncoding.WithPadding(base64.NoPadding).EncodeToString(blob) 69 | } else if float, ok := v.(float64); ok { 70 | res.Type = "float" 71 | res.Value = float 72 | } else if t, ok := v.(time.Time); ok { 73 | res.Type = "text" 74 | res.Value = t.Format("2006-01-02 15:04:05.999999999-07:00") 75 | } else if t, ok := v.(bool); ok { 76 | res.Type = "integer" 77 | res.Value = "0" 78 | if t { 79 | res.Value = "1" 80 | } 81 | } else { 82 | return res, fmt.Errorf("unsupported value type: %s", v) 83 | } 84 | return res, nil 85 | } 86 | -------------------------------------------------------------------------------- /lib/turso/vendor/github.com/tursodatabase/libsql-client-go/libsql/internal/http/driver.go: -------------------------------------------------------------------------------- 1 | package http 2 | 3 | import ( 4 | "database/sql/driver" 5 | 6 | "github.com/tursodatabase/libsql-client-go/libsql/internal/http/hranaV2" 7 | ) 8 | 9 | func Connect(url, jwt, host string) driver.Conn { 10 | return hranaV2.Connect(url, jwt, host) 11 | } 12 | -------------------------------------------------------------------------------- /lib/turso/vendor/github.com/tursodatabase/libsql-client-go/libsql/internal/http/shared/result.go: -------------------------------------------------------------------------------- 1 | package shared 2 | 3 | type result struct { 4 | id int64 5 | changes int64 6 | } 7 | 8 | func NewResult(id, changes int64) *result { 9 | return &result{id: id, changes: changes} 10 | } 11 | 12 | func (r *result) LastInsertId() (int64, error) { 13 | return r.id, nil 14 | } 15 | 16 | func (r *result) RowsAffected() (int64, error) { 17 | return r.changes, nil 18 | } 19 | -------------------------------------------------------------------------------- /lib/turso/vendor/github.com/tursodatabase/libsql-client-go/libsql/internal/http/shared/rows.go: -------------------------------------------------------------------------------- 1 | package shared 2 | 3 | import ( 4 | "database/sql/driver" 5 | "fmt" 6 | "io" 7 | ) 8 | 9 | type rowsProvider interface { 10 | SetsCount() int 11 | RowsCount(setIdx int) int 12 | Columns(setIdx int) []string 13 | FieldValue(setIdx, rowIdx int, columnIdx int) driver.Value 14 | Error(setIdx int) string 15 | HasResult(setIdx int) bool 16 | } 17 | 18 | func NewRows(result rowsProvider) driver.Rows { 19 | return &rows{result: result} 20 | } 21 | 22 | type rows struct { 23 | result rowsProvider 24 | currentResultSetIndex int 25 | currentRowIdx int 26 | } 27 | 28 | func (r *rows) Columns() []string { 29 | return r.result.Columns(r.currentResultSetIndex) 30 | } 31 | 32 | func (r *rows) Close() error { 33 | return nil 34 | } 35 | 36 | func (r *rows) Next(dest []driver.Value) error { 37 | if r.currentRowIdx == r.result.RowsCount(r.currentResultSetIndex) { 38 | return io.EOF 39 | } 40 | count := len(r.result.Columns(r.currentResultSetIndex)) 41 | for idx := 0; idx < count; idx++ { 42 | dest[idx] = r.result.FieldValue(r.currentResultSetIndex, r.currentRowIdx, idx) 43 | } 44 | r.currentRowIdx++ 45 | return nil 46 | } 47 | 48 | func (r *rows) HasNextResultSet() bool { 49 | return r.currentResultSetIndex < r.result.SetsCount()-1 50 | } 51 | 52 | func (r *rows) NextResultSet() error { 53 | if !r.HasNextResultSet() { 54 | return io.EOF 55 | } 56 | 57 | r.currentResultSetIndex++ 58 | r.currentRowIdx = 0 59 | 60 | errStr := r.result.Error(r.currentResultSetIndex) 61 | if errStr != "" { 62 | return fmt.Errorf("failed to execute statement\n%s", errStr) 63 | } 64 | if !r.result.HasResult(r.currentResultSetIndex) { 65 | return fmt.Errorf("no results for statement") 66 | } 67 | 68 | return nil 69 | } 70 | -------------------------------------------------------------------------------- /lib/turso/vendor/github.com/tursodatabase/libsql-client-go/libsql/internal/ws/driver.go: -------------------------------------------------------------------------------- 1 | package ws 2 | 3 | import ( 4 | "context" 5 | "database/sql/driver" 6 | "io" 7 | "sort" 8 | ) 9 | 10 | type result struct { 11 | id int64 12 | changes int64 13 | } 14 | 15 | func (r *result) LastInsertId() (int64, error) { 16 | return r.id, nil 17 | } 18 | 19 | func (r *result) RowsAffected() (int64, error) { 20 | return r.changes, nil 21 | } 22 | 23 | type rows struct { 24 | res *execResponse 25 | currentRowIdx int 26 | } 27 | 28 | func (r *rows) Columns() []string { 29 | return r.res.columns() 30 | } 31 | 32 | func (r *rows) Close() error { 33 | return nil 34 | } 35 | 36 | func (r *rows) Next(dest []driver.Value) error { 37 | if r.currentRowIdx == r.res.rowsCount() { 38 | return io.EOF 39 | } 40 | count := r.res.rowLen(r.currentRowIdx) 41 | for idx := 0; idx < count; idx++ { 42 | v, err := r.res.value(r.currentRowIdx, idx) 43 | if err != nil { 44 | return err 45 | } 46 | dest[idx] = v 47 | } 48 | r.currentRowIdx++ 49 | return nil 50 | } 51 | 52 | type conn struct { 53 | ws *websocketConn 54 | } 55 | 56 | func Connect(url string, jwt string) (*conn, error) { 57 | c, err := connect(url, jwt) 58 | if err != nil { 59 | return nil, err 60 | } 61 | return &conn{c}, nil 62 | } 63 | 64 | type stmt struct { 65 | c *conn 66 | query string 67 | } 68 | 69 | func (s stmt) Close() error { 70 | return nil 71 | } 72 | 73 | func (s stmt) NumInput() int { 74 | return -1 75 | } 76 | 77 | func convertToNamed(args []driver.Value) []driver.NamedValue { 78 | if len(args) == 0 { 79 | return nil 80 | } 81 | result := []driver.NamedValue{} 82 | for idx := range args { 83 | result = append(result, driver.NamedValue{Ordinal: idx, Value: args[idx]}) 84 | } 85 | return result 86 | } 87 | 88 | func (s stmt) Exec(args []driver.Value) (driver.Result, error) { 89 | return s.ExecContext(context.Background(), convertToNamed(args)) 90 | } 91 | 92 | func (s stmt) Query(args []driver.Value) (driver.Rows, error) { 93 | return s.QueryContext(context.Background(), convertToNamed(args)) 94 | } 95 | 96 | func (s stmt) ExecContext(ctx context.Context, args []driver.NamedValue) (driver.Result, error) { 97 | return s.c.ExecContext(ctx, s.query, args) 98 | } 99 | 100 | func (s stmt) QueryContext(ctx context.Context, args []driver.NamedValue) (driver.Rows, error) { 101 | return s.c.QueryContext(ctx, s.query, args) 102 | } 103 | 104 | func (c *conn) Ping() error { 105 | return c.PingContext(context.Background()) 106 | } 107 | 108 | func (c *conn) PingContext(ctx context.Context) error { 109 | _, err := c.ws.exec(ctx, "SELECT 1", params{}, false) 110 | return err 111 | } 112 | 113 | func (c *conn) Prepare(query string) (driver.Stmt, error) { 114 | return c.PrepareContext(context.Background(), query) 115 | } 116 | 117 | func (c *conn) PrepareContext(_ context.Context, query string) (driver.Stmt, error) { 118 | return stmt{c, query}, nil 119 | } 120 | 121 | func (c *conn) Close() error { 122 | return c.ws.Close() 123 | } 124 | 125 | type tx struct { 126 | c *conn 127 | } 128 | 129 | func (t tx) Commit() error { 130 | _, err := t.c.ExecContext(context.Background(), "COMMIT", nil) 131 | if err != nil { 132 | return err 133 | } 134 | return nil 135 | } 136 | 137 | func (t tx) Rollback() error { 138 | _, err := t.c.ExecContext(context.Background(), "ROLLBACK", nil) 139 | if err != nil { 140 | return err 141 | } 142 | return nil 143 | } 144 | 145 | func (c *conn) Begin() (driver.Tx, error) { 146 | return c.BeginTx(context.Background(), driver.TxOptions{}) 147 | } 148 | 149 | func (c *conn) BeginTx(ctx context.Context, _ driver.TxOptions) (driver.Tx, error) { 150 | _, err := c.ExecContext(ctx, "BEGIN", nil) 151 | if err != nil { 152 | return tx{nil}, err 153 | } 154 | return tx{c}, nil 155 | } 156 | 157 | func convertArgs(args []driver.NamedValue) params { 158 | if len(args) == 0 { 159 | return params{} 160 | } 161 | positionalArgs := [](*driver.NamedValue){} 162 | namedArgs := []namedParam{} 163 | for idx := range args { 164 | if len(args[idx].Name) > 0 { 165 | namedArgs = append(namedArgs, namedParam{args[idx].Name, args[idx].Value}) 166 | } else { 167 | positionalArgs = append(positionalArgs, &args[idx]) 168 | } 169 | } 170 | sort.Slice(positionalArgs, func(i, j int) bool { 171 | return positionalArgs[i].Ordinal < positionalArgs[j].Ordinal 172 | }) 173 | posArgs := [](any){} 174 | for idx := range positionalArgs { 175 | posArgs = append(posArgs, positionalArgs[idx].Value) 176 | } 177 | return params{PositinalArgs: posArgs, NamedArgs: namedArgs} 178 | } 179 | 180 | func (c *conn) ExecContext(ctx context.Context, query string, args []driver.NamedValue) (driver.Result, error) { 181 | res, err := c.ws.exec(ctx, query, convertArgs(args), false) 182 | if err != nil { 183 | return nil, err 184 | } 185 | return &result{res.lastInsertId(), res.affectedRowCount()}, nil 186 | } 187 | 188 | func (c *conn) QueryContext(ctx context.Context, query string, args []driver.NamedValue) (driver.Rows, error) { 189 | res, err := c.ws.exec(ctx, query, convertArgs(args), true) 190 | if err != nil { 191 | return nil, err 192 | } 193 | return &rows{res, 0}, nil 194 | } 195 | -------------------------------------------------------------------------------- /lib/turso/vendor/golang.org/x/exp/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2009 The Go Authors. All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are 5 | met: 6 | 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above 10 | copyright notice, this list of conditions and the following disclaimer 11 | in the documentation and/or other materials provided with the 12 | distribution. 13 | * Neither the name of Google Inc. nor the names of its 14 | contributors may be used to endorse or promote products derived from 15 | this software without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /lib/turso/vendor/golang.org/x/exp/PATENTS: -------------------------------------------------------------------------------- 1 | Additional IP Rights Grant (Patents) 2 | 3 | "This implementation" means the copyrightable works distributed by 4 | Google as part of the Go project. 5 | 6 | Google hereby grants to You a perpetual, worldwide, non-exclusive, 7 | no-charge, royalty-free, irrevocable (except as stated in this section) 8 | patent license to make, have made, use, offer to sell, sell, import, 9 | transfer and otherwise run, modify and propagate the contents of this 10 | implementation of Go, where such license applies only to those patent 11 | claims, both currently owned or controlled by Google and acquired in 12 | the future, licensable by Google that are necessarily infringed by this 13 | implementation of Go. This grant does not include claims that would be 14 | infringed only as a consequence of further modification of this 15 | implementation. If you or your agent or exclusive licensee institute or 16 | order or agree to the institution of patent litigation against any 17 | entity (including a cross-claim or counterclaim in a lawsuit) alleging 18 | that this implementation of Go or any code incorporated within this 19 | implementation of Go constitutes direct or contributory patent 20 | infringement, or inducement of patent infringement, then any patent 21 | rights granted to you under this License for this implementation of Go 22 | shall terminate as of the date such litigation is filed. 23 | -------------------------------------------------------------------------------- /lib/turso/vendor/golang.org/x/exp/constraints/constraints.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // Package constraints defines a set of useful constraints to be used 6 | // with type parameters. 7 | package constraints 8 | 9 | // Signed is a constraint that permits any signed integer type. 10 | // If future releases of Go add new predeclared signed integer types, 11 | // this constraint will be modified to include them. 12 | type Signed interface { 13 | ~int | ~int8 | ~int16 | ~int32 | ~int64 14 | } 15 | 16 | // Unsigned is a constraint that permits any unsigned integer type. 17 | // If future releases of Go add new predeclared unsigned integer types, 18 | // this constraint will be modified to include them. 19 | type Unsigned interface { 20 | ~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr 21 | } 22 | 23 | // Integer is a constraint that permits any integer type. 24 | // If future releases of Go add new predeclared integer types, 25 | // this constraint will be modified to include them. 26 | type Integer interface { 27 | Signed | Unsigned 28 | } 29 | 30 | // Float is a constraint that permits any floating-point type. 31 | // If future releases of Go add new predeclared floating-point types, 32 | // this constraint will be modified to include them. 33 | type Float interface { 34 | ~float32 | ~float64 35 | } 36 | 37 | // Complex is a constraint that permits any complex numeric type. 38 | // If future releases of Go add new predeclared complex numeric types, 39 | // this constraint will be modified to include them. 40 | type Complex interface { 41 | ~complex64 | ~complex128 42 | } 43 | 44 | // Ordered is a constraint that permits any ordered type: any type 45 | // that supports the operators < <= >= >. 46 | // If future releases of Go add new ordered types, 47 | // this constraint will be modified to include them. 48 | type Ordered interface { 49 | Integer | Float | ~string 50 | } 51 | -------------------------------------------------------------------------------- /lib/turso/vendor/golang.org/x/exp/slices/sort.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package slices 6 | 7 | import ( 8 | "math/bits" 9 | 10 | "golang.org/x/exp/constraints" 11 | ) 12 | 13 | // Sort sorts a slice of any ordered type in ascending order. 14 | // Sort may fail to sort correctly when sorting slices of floating-point 15 | // numbers containing Not-a-number (NaN) values. 16 | // Use slices.SortFunc(x, func(a, b float64) bool {return a < b || (math.IsNaN(a) && !math.IsNaN(b))}) 17 | // instead if the input may contain NaNs. 18 | func Sort[E constraints.Ordered](x []E) { 19 | n := len(x) 20 | pdqsortOrdered(x, 0, n, bits.Len(uint(n))) 21 | } 22 | 23 | // SortFunc sorts the slice x in ascending order as determined by the less function. 24 | // This sort is not guaranteed to be stable. 25 | // 26 | // SortFunc requires that less is a strict weak ordering. 27 | // See https://en.wikipedia.org/wiki/Weak_ordering#Strict_weak_orderings. 28 | func SortFunc[E any](x []E, less func(a, b E) bool) { 29 | n := len(x) 30 | pdqsortLessFunc(x, 0, n, bits.Len(uint(n)), less) 31 | } 32 | 33 | // SortStable sorts the slice x while keeping the original order of equal 34 | // elements, using less to compare elements. 35 | func SortStableFunc[E any](x []E, less func(a, b E) bool) { 36 | stableLessFunc(x, len(x), less) 37 | } 38 | 39 | // IsSorted reports whether x is sorted in ascending order. 40 | func IsSorted[E constraints.Ordered](x []E) bool { 41 | for i := len(x) - 1; i > 0; i-- { 42 | if x[i] < x[i-1] { 43 | return false 44 | } 45 | } 46 | return true 47 | } 48 | 49 | // IsSortedFunc reports whether x is sorted in ascending order, with less as the 50 | // comparison function. 51 | func IsSortedFunc[E any](x []E, less func(a, b E) bool) bool { 52 | for i := len(x) - 1; i > 0; i-- { 53 | if less(x[i], x[i-1]) { 54 | return false 55 | } 56 | } 57 | return true 58 | } 59 | 60 | // BinarySearch searches for target in a sorted slice and returns the position 61 | // where target is found, or the position where target would appear in the 62 | // sort order; it also returns a bool saying whether the target is really found 63 | // in the slice. The slice must be sorted in increasing order. 64 | func BinarySearch[E constraints.Ordered](x []E, target E) (int, bool) { 65 | // search returns the leftmost position where f returns true, or len(x) if f 66 | // returns false for all x. This is the insertion position for target in x, 67 | // and could point to an element that's either == target or not. 68 | pos := search(len(x), func(i int) bool { return x[i] >= target }) 69 | if pos >= len(x) || x[pos] != target { 70 | return pos, false 71 | } else { 72 | return pos, true 73 | } 74 | } 75 | 76 | // BinarySearchFunc works like BinarySearch, but uses a custom comparison 77 | // function. The slice must be sorted in increasing order, where "increasing" is 78 | // defined by cmp. cmp(a, b) is expected to return an integer comparing the two 79 | // parameters: 0 if a == b, a negative number if a < b and a positive number if 80 | // a > b. 81 | func BinarySearchFunc[E any](x []E, target E, cmp func(E, E) int) (int, bool) { 82 | pos := search(len(x), func(i int) bool { return cmp(x[i], target) >= 0 }) 83 | if pos >= len(x) || cmp(x[pos], target) != 0 { 84 | return pos, false 85 | } else { 86 | return pos, true 87 | } 88 | } 89 | 90 | func search(n int, f func(int) bool) int { 91 | // Define f(-1) == false and f(n) == true. 92 | // Invariant: f(i-1) == false, f(j) == true. 93 | i, j := 0, n 94 | for i < j { 95 | h := int(uint(i+j) >> 1) // avoid overflow when computing h 96 | // i ≤ h < j 97 | if !f(h) { 98 | i = h + 1 // preserves f(i-1) == false 99 | } else { 100 | j = h // preserves f(j) == true 101 | } 102 | } 103 | // i == j, f(i-1) == false, and f(j) (= f(i)) == true => answer is i. 104 | return i 105 | } 106 | 107 | type sortedHint int // hint for pdqsort when choosing the pivot 108 | 109 | const ( 110 | unknownHint sortedHint = iota 111 | increasingHint 112 | decreasingHint 113 | ) 114 | 115 | // xorshift paper: https://www.jstatsoft.org/article/view/v008i14/xorshift.pdf 116 | type xorshift uint64 117 | 118 | func (r *xorshift) Next() uint64 { 119 | *r ^= *r << 13 120 | *r ^= *r >> 17 121 | *r ^= *r << 5 122 | return uint64(*r) 123 | } 124 | 125 | func nextPowerOfTwo(length int) uint { 126 | return 1 << bits.Len(uint(length)) 127 | } 128 | -------------------------------------------------------------------------------- /lib/turso/vendor/modules.txt: -------------------------------------------------------------------------------- 1 | # github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20230512164433-5d1fd1a340c9 2 | ## explicit; go 1.18 3 | github.com/antlr/antlr4/runtime/Go/antlr/v4 4 | # github.com/klauspost/compress v1.15.15 5 | ## explicit; go 1.17 6 | github.com/klauspost/compress/flate 7 | # github.com/libsql/sqlite-antlr4-parser v0.0.0-20230802215326-5cb5bb604475 8 | ## explicit; go 1.19 9 | github.com/libsql/sqlite-antlr4-parser/sqliteparser 10 | github.com/libsql/sqlite-antlr4-parser/sqliteparserutils 11 | # github.com/tursodatabase/libsql-client-go v0.0.0-20240220085343-4ae0eb9d0898 12 | ## explicit; go 1.20 13 | github.com/tursodatabase/libsql-client-go/libsql 14 | github.com/tursodatabase/libsql-client-go/libsql/internal/hrana 15 | github.com/tursodatabase/libsql-client-go/libsql/internal/http 16 | github.com/tursodatabase/libsql-client-go/libsql/internal/http/hranaV2 17 | github.com/tursodatabase/libsql-client-go/libsql/internal/http/shared 18 | github.com/tursodatabase/libsql-client-go/libsql/internal/ws 19 | # golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e 20 | ## explicit; go 1.18 21 | golang.org/x/exp/constraints 22 | golang.org/x/exp/slices 23 | # nhooyr.io/websocket v1.8.7 24 | ## explicit; go 1.13 25 | nhooyr.io/websocket 26 | nhooyr.io/websocket/internal/bpool 27 | nhooyr.io/websocket/internal/errd 28 | nhooyr.io/websocket/internal/wsjs 29 | nhooyr.io/websocket/internal/xsync 30 | nhooyr.io/websocket/wsjson 31 | -------------------------------------------------------------------------------- /lib/turso/vendor/nhooyr.io/websocket/.gitignore: -------------------------------------------------------------------------------- 1 | websocket.test 2 | -------------------------------------------------------------------------------- /lib/turso/vendor/nhooyr.io/websocket/LICENSE.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Anmol Sethi 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /lib/turso/vendor/nhooyr.io/websocket/README.md: -------------------------------------------------------------------------------- 1 | # websocket 2 | 3 | [![godoc](https://godoc.org/nhooyr.io/websocket?status.svg)](https://pkg.go.dev/nhooyr.io/websocket) 4 | [![coverage](https://img.shields.io/badge/coverage-88%25-success)](https://nhooyrio-websocket-coverage.netlify.app) 5 | 6 | websocket is a minimal and idiomatic WebSocket library for Go. 7 | 8 | ## Install 9 | 10 | ```bash 11 | go get nhooyr.io/websocket 12 | ``` 13 | 14 | ## Highlights 15 | 16 | - Minimal and idiomatic API 17 | - First class [context.Context](https://blog.golang.org/context) support 18 | - Fully passes the WebSocket [autobahn-testsuite](https://github.com/crossbario/autobahn-testsuite) 19 | - [Single dependency](https://pkg.go.dev/nhooyr.io/websocket?tab=imports) 20 | - JSON and protobuf helpers in the [wsjson](https://pkg.go.dev/nhooyr.io/websocket/wsjson) and [wspb](https://pkg.go.dev/nhooyr.io/websocket/wspb) subpackages 21 | - Zero alloc reads and writes 22 | - Concurrent writes 23 | - [Close handshake](https://pkg.go.dev/nhooyr.io/websocket#Conn.Close) 24 | - [net.Conn](https://pkg.go.dev/nhooyr.io/websocket#NetConn) wrapper 25 | - [Ping pong](https://pkg.go.dev/nhooyr.io/websocket#Conn.Ping) API 26 | - [RFC 7692](https://tools.ietf.org/html/rfc7692) permessage-deflate compression 27 | - Compile to [Wasm](https://pkg.go.dev/nhooyr.io/websocket#hdr-Wasm) 28 | 29 | ## Roadmap 30 | 31 | - [ ] HTTP/2 [#4](https://github.com/nhooyr/websocket/issues/4) 32 | 33 | ## Examples 34 | 35 | For a production quality example that demonstrates the complete API, see the 36 | [echo example](./examples/echo). 37 | 38 | For a full stack example, see the [chat example](./examples/chat). 39 | 40 | ### Server 41 | 42 | ```go 43 | http.HandlerFunc(func (w http.ResponseWriter, r *http.Request) { 44 | c, err := websocket.Accept(w, r, nil) 45 | if err != nil { 46 | // ... 47 | } 48 | defer c.Close(websocket.StatusInternalError, "the sky is falling") 49 | 50 | ctx, cancel := context.WithTimeout(r.Context(), time.Second*10) 51 | defer cancel() 52 | 53 | var v interface{} 54 | err = wsjson.Read(ctx, c, &v) 55 | if err != nil { 56 | // ... 57 | } 58 | 59 | log.Printf("received: %v", v) 60 | 61 | c.Close(websocket.StatusNormalClosure, "") 62 | }) 63 | ``` 64 | 65 | ### Client 66 | 67 | ```go 68 | ctx, cancel := context.WithTimeout(context.Background(), time.Minute) 69 | defer cancel() 70 | 71 | c, _, err := websocket.Dial(ctx, "ws://localhost:8080", nil) 72 | if err != nil { 73 | // ... 74 | } 75 | defer c.Close(websocket.StatusInternalError, "the sky is falling") 76 | 77 | err = wsjson.Write(ctx, c, "hi") 78 | if err != nil { 79 | // ... 80 | } 81 | 82 | c.Close(websocket.StatusNormalClosure, "") 83 | ``` 84 | 85 | ## Comparison 86 | 87 | ### gorilla/websocket 88 | 89 | Advantages of [gorilla/websocket](https://github.com/gorilla/websocket): 90 | 91 | - Mature and widely used 92 | - [Prepared writes](https://pkg.go.dev/github.com/gorilla/websocket#PreparedMessage) 93 | - Configurable [buffer sizes](https://pkg.go.dev/github.com/gorilla/websocket#hdr-Buffers) 94 | 95 | Advantages of nhooyr.io/websocket: 96 | 97 | - Minimal and idiomatic API 98 | - Compare godoc of [nhooyr.io/websocket](https://pkg.go.dev/nhooyr.io/websocket) with [gorilla/websocket](https://pkg.go.dev/github.com/gorilla/websocket) side by side. 99 | - [net.Conn](https://pkg.go.dev/nhooyr.io/websocket#NetConn) wrapper 100 | - Zero alloc reads and writes ([gorilla/websocket#535](https://github.com/gorilla/websocket/issues/535)) 101 | - Full [context.Context](https://blog.golang.org/context) support 102 | - Dial uses [net/http.Client](https://golang.org/pkg/net/http/#Client) 103 | - Will enable easy HTTP/2 support in the future 104 | - Gorilla writes directly to a net.Conn and so duplicates features of net/http.Client. 105 | - Concurrent writes 106 | - Close handshake ([gorilla/websocket#448](https://github.com/gorilla/websocket/issues/448)) 107 | - Idiomatic [ping pong](https://pkg.go.dev/nhooyr.io/websocket#Conn.Ping) API 108 | - Gorilla requires registering a pong callback before sending a Ping 109 | - Can target Wasm ([gorilla/websocket#432](https://github.com/gorilla/websocket/issues/432)) 110 | - Transparent message buffer reuse with [wsjson](https://pkg.go.dev/nhooyr.io/websocket/wsjson) and [wspb](https://pkg.go.dev/nhooyr.io/websocket/wspb) subpackages 111 | - [1.75x](https://github.com/nhooyr/websocket/releases/tag/v1.7.4) faster WebSocket masking implementation in pure Go 112 | - Gorilla's implementation is slower and uses [unsafe](https://golang.org/pkg/unsafe/). 113 | - Full [permessage-deflate](https://tools.ietf.org/html/rfc7692) compression extension support 114 | - Gorilla only supports no context takeover mode 115 | - We use [klauspost/compress](https://github.com/klauspost/compress) for much lower memory usage ([gorilla/websocket#203](https://github.com/gorilla/websocket/issues/203)) 116 | - [CloseRead](https://pkg.go.dev/nhooyr.io/websocket#Conn.CloseRead) helper ([gorilla/websocket#492](https://github.com/gorilla/websocket/issues/492)) 117 | - Actively maintained ([gorilla/websocket#370](https://github.com/gorilla/websocket/issues/370)) 118 | 119 | #### golang.org/x/net/websocket 120 | 121 | [golang.org/x/net/websocket](https://pkg.go.dev/golang.org/x/net/websocket) is deprecated. 122 | See [golang/go/issues/18152](https://github.com/golang/go/issues/18152). 123 | 124 | The [net.Conn](https://pkg.go.dev/nhooyr.io/websocket#NetConn) can help in transitioning 125 | to nhooyr.io/websocket. 126 | 127 | #### gobwas/ws 128 | 129 | [gobwas/ws](https://github.com/gobwas/ws) has an extremely flexible API that allows it to be used 130 | in an event driven style for performance. See the author's [blog post](https://medium.freecodecamp.org/million-websockets-and-go-cc58418460bb). 131 | 132 | However when writing idiomatic Go, nhooyr.io/websocket will be faster and easier to use. 133 | -------------------------------------------------------------------------------- /lib/turso/vendor/nhooyr.io/websocket/accept_js.go: -------------------------------------------------------------------------------- 1 | package websocket 2 | 3 | import ( 4 | "errors" 5 | "net/http" 6 | ) 7 | 8 | // AcceptOptions represents Accept's options. 9 | type AcceptOptions struct { 10 | Subprotocols []string 11 | InsecureSkipVerify bool 12 | OriginPatterns []string 13 | CompressionMode CompressionMode 14 | CompressionThreshold int 15 | } 16 | 17 | // Accept is stubbed out for Wasm. 18 | func Accept(w http.ResponseWriter, r *http.Request, opts *AcceptOptions) (*Conn, error) { 19 | return nil, errors.New("unimplemented") 20 | } 21 | -------------------------------------------------------------------------------- /lib/turso/vendor/nhooyr.io/websocket/close.go: -------------------------------------------------------------------------------- 1 | package websocket 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | ) 7 | 8 | // StatusCode represents a WebSocket status code. 9 | // https://tools.ietf.org/html/rfc6455#section-7.4 10 | type StatusCode int 11 | 12 | // https://www.iana.org/assignments/websocket/websocket.xhtml#close-code-number 13 | // 14 | // These are only the status codes defined by the protocol. 15 | // 16 | // You can define custom codes in the 3000-4999 range. 17 | // The 3000-3999 range is reserved for use by libraries, frameworks and applications. 18 | // The 4000-4999 range is reserved for private use. 19 | const ( 20 | StatusNormalClosure StatusCode = 1000 21 | StatusGoingAway StatusCode = 1001 22 | StatusProtocolError StatusCode = 1002 23 | StatusUnsupportedData StatusCode = 1003 24 | 25 | // 1004 is reserved and so unexported. 26 | statusReserved StatusCode = 1004 27 | 28 | // StatusNoStatusRcvd cannot be sent in a close message. 29 | // It is reserved for when a close message is received without 30 | // a status code. 31 | StatusNoStatusRcvd StatusCode = 1005 32 | 33 | // StatusAbnormalClosure is exported for use only with Wasm. 34 | // In non Wasm Go, the returned error will indicate whether the 35 | // connection was closed abnormally. 36 | StatusAbnormalClosure StatusCode = 1006 37 | 38 | StatusInvalidFramePayloadData StatusCode = 1007 39 | StatusPolicyViolation StatusCode = 1008 40 | StatusMessageTooBig StatusCode = 1009 41 | StatusMandatoryExtension StatusCode = 1010 42 | StatusInternalError StatusCode = 1011 43 | StatusServiceRestart StatusCode = 1012 44 | StatusTryAgainLater StatusCode = 1013 45 | StatusBadGateway StatusCode = 1014 46 | 47 | // StatusTLSHandshake is only exported for use with Wasm. 48 | // In non Wasm Go, the returned error will indicate whether there was 49 | // a TLS handshake failure. 50 | StatusTLSHandshake StatusCode = 1015 51 | ) 52 | 53 | // CloseError is returned when the connection is closed with a status and reason. 54 | // 55 | // Use Go 1.13's errors.As to check for this error. 56 | // Also see the CloseStatus helper. 57 | type CloseError struct { 58 | Code StatusCode 59 | Reason string 60 | } 61 | 62 | func (ce CloseError) Error() string { 63 | return fmt.Sprintf("status = %v and reason = %q", ce.Code, ce.Reason) 64 | } 65 | 66 | // CloseStatus is a convenience wrapper around Go 1.13's errors.As to grab 67 | // the status code from a CloseError. 68 | // 69 | // -1 will be returned if the passed error is nil or not a CloseError. 70 | func CloseStatus(err error) StatusCode { 71 | var ce CloseError 72 | if errors.As(err, &ce) { 73 | return ce.Code 74 | } 75 | return -1 76 | } 77 | -------------------------------------------------------------------------------- /lib/turso/vendor/nhooyr.io/websocket/close_notjs.go: -------------------------------------------------------------------------------- 1 | // +build !js 2 | 3 | package websocket 4 | 5 | import ( 6 | "context" 7 | "encoding/binary" 8 | "errors" 9 | "fmt" 10 | "log" 11 | "time" 12 | 13 | "nhooyr.io/websocket/internal/errd" 14 | ) 15 | 16 | // Close performs the WebSocket close handshake with the given status code and reason. 17 | // 18 | // It will write a WebSocket close frame with a timeout of 5s and then wait 5s for 19 | // the peer to send a close frame. 20 | // All data messages received from the peer during the close handshake will be discarded. 21 | // 22 | // The connection can only be closed once. Additional calls to Close 23 | // are no-ops. 24 | // 25 | // The maximum length of reason must be 125 bytes. Avoid 26 | // sending a dynamic reason. 27 | // 28 | // Close will unblock all goroutines interacting with the connection once 29 | // complete. 30 | func (c *Conn) Close(code StatusCode, reason string) error { 31 | return c.closeHandshake(code, reason) 32 | } 33 | 34 | func (c *Conn) closeHandshake(code StatusCode, reason string) (err error) { 35 | defer errd.Wrap(&err, "failed to close WebSocket") 36 | 37 | writeErr := c.writeClose(code, reason) 38 | closeHandshakeErr := c.waitCloseHandshake() 39 | 40 | if writeErr != nil { 41 | return writeErr 42 | } 43 | 44 | if CloseStatus(closeHandshakeErr) == -1 { 45 | return closeHandshakeErr 46 | } 47 | 48 | return nil 49 | } 50 | 51 | var errAlreadyWroteClose = errors.New("already wrote close") 52 | 53 | func (c *Conn) writeClose(code StatusCode, reason string) error { 54 | c.closeMu.Lock() 55 | wroteClose := c.wroteClose 56 | c.wroteClose = true 57 | c.closeMu.Unlock() 58 | if wroteClose { 59 | return errAlreadyWroteClose 60 | } 61 | 62 | ce := CloseError{ 63 | Code: code, 64 | Reason: reason, 65 | } 66 | 67 | var p []byte 68 | var marshalErr error 69 | if ce.Code != StatusNoStatusRcvd { 70 | p, marshalErr = ce.bytes() 71 | if marshalErr != nil { 72 | log.Printf("websocket: %v", marshalErr) 73 | } 74 | } 75 | 76 | writeErr := c.writeControl(context.Background(), opClose, p) 77 | if CloseStatus(writeErr) != -1 { 78 | // Not a real error if it's due to a close frame being received. 79 | writeErr = nil 80 | } 81 | 82 | // We do this after in case there was an error writing the close frame. 83 | c.setCloseErr(fmt.Errorf("sent close frame: %w", ce)) 84 | 85 | if marshalErr != nil { 86 | return marshalErr 87 | } 88 | return writeErr 89 | } 90 | 91 | func (c *Conn) waitCloseHandshake() error { 92 | defer c.close(nil) 93 | 94 | ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) 95 | defer cancel() 96 | 97 | err := c.readMu.lock(ctx) 98 | if err != nil { 99 | return err 100 | } 101 | defer c.readMu.unlock() 102 | 103 | if c.readCloseFrameErr != nil { 104 | return c.readCloseFrameErr 105 | } 106 | 107 | for { 108 | h, err := c.readLoop(ctx) 109 | if err != nil { 110 | return err 111 | } 112 | 113 | for i := int64(0); i < h.payloadLength; i++ { 114 | _, err := c.br.ReadByte() 115 | if err != nil { 116 | return err 117 | } 118 | } 119 | } 120 | } 121 | 122 | func parseClosePayload(p []byte) (CloseError, error) { 123 | if len(p) == 0 { 124 | return CloseError{ 125 | Code: StatusNoStatusRcvd, 126 | }, nil 127 | } 128 | 129 | if len(p) < 2 { 130 | return CloseError{}, fmt.Errorf("close payload %q too small, cannot even contain the 2 byte status code", p) 131 | } 132 | 133 | ce := CloseError{ 134 | Code: StatusCode(binary.BigEndian.Uint16(p)), 135 | Reason: string(p[2:]), 136 | } 137 | 138 | if !validWireCloseCode(ce.Code) { 139 | return CloseError{}, fmt.Errorf("invalid status code %v", ce.Code) 140 | } 141 | 142 | return ce, nil 143 | } 144 | 145 | // See http://www.iana.org/assignments/websocket/websocket.xhtml#close-code-number 146 | // and https://tools.ietf.org/html/rfc6455#section-7.4.1 147 | func validWireCloseCode(code StatusCode) bool { 148 | switch code { 149 | case statusReserved, StatusNoStatusRcvd, StatusAbnormalClosure, StatusTLSHandshake: 150 | return false 151 | } 152 | 153 | if code >= StatusNormalClosure && code <= StatusBadGateway { 154 | return true 155 | } 156 | if code >= 3000 && code <= 4999 { 157 | return true 158 | } 159 | 160 | return false 161 | } 162 | 163 | func (ce CloseError) bytes() ([]byte, error) { 164 | p, err := ce.bytesErr() 165 | if err != nil { 166 | err = fmt.Errorf("failed to marshal close frame: %w", err) 167 | ce = CloseError{ 168 | Code: StatusInternalError, 169 | } 170 | p, _ = ce.bytesErr() 171 | } 172 | return p, err 173 | } 174 | 175 | const maxCloseReason = maxControlPayload - 2 176 | 177 | func (ce CloseError) bytesErr() ([]byte, error) { 178 | if len(ce.Reason) > maxCloseReason { 179 | return nil, fmt.Errorf("reason string max is %v but got %q with length %v", maxCloseReason, ce.Reason, len(ce.Reason)) 180 | } 181 | 182 | if !validWireCloseCode(ce.Code) { 183 | return nil, fmt.Errorf("status code %v cannot be set", ce.Code) 184 | } 185 | 186 | buf := make([]byte, 2+len(ce.Reason)) 187 | binary.BigEndian.PutUint16(buf, uint16(ce.Code)) 188 | copy(buf[2:], ce.Reason) 189 | return buf, nil 190 | } 191 | 192 | func (c *Conn) setCloseErr(err error) { 193 | c.closeMu.Lock() 194 | c.setCloseErrLocked(err) 195 | c.closeMu.Unlock() 196 | } 197 | 198 | func (c *Conn) setCloseErrLocked(err error) { 199 | if c.closeErr == nil { 200 | c.closeErr = fmt.Errorf("WebSocket closed: %w", err) 201 | } 202 | } 203 | 204 | func (c *Conn) isClosed() bool { 205 | select { 206 | case <-c.closed: 207 | return true 208 | default: 209 | return false 210 | } 211 | } 212 | -------------------------------------------------------------------------------- /lib/turso/vendor/nhooyr.io/websocket/compress.go: -------------------------------------------------------------------------------- 1 | package websocket 2 | 3 | // CompressionMode represents the modes available to the deflate extension. 4 | // See https://tools.ietf.org/html/rfc7692 5 | // 6 | // A compatibility layer is implemented for the older deflate-frame extension used 7 | // by safari. See https://tools.ietf.org/html/draft-tyoshino-hybi-websocket-perframe-deflate-06 8 | // It will work the same in every way except that we cannot signal to the peer we 9 | // want to use no context takeover on our side, we can only signal that they should. 10 | // It is however currently disabled due to Safari bugs. See https://github.com/nhooyr/websocket/issues/218 11 | type CompressionMode int 12 | 13 | const ( 14 | // CompressionNoContextTakeover grabs a new flate.Reader and flate.Writer as needed 15 | // for every message. This applies to both server and client side. 16 | // 17 | // This means less efficient compression as the sliding window from previous messages 18 | // will not be used but the memory overhead will be lower if the connections 19 | // are long lived and seldom used. 20 | // 21 | // The message will only be compressed if greater than 512 bytes. 22 | CompressionNoContextTakeover CompressionMode = iota 23 | 24 | // CompressionContextTakeover uses a flate.Reader and flate.Writer per connection. 25 | // This enables reusing the sliding window from previous messages. 26 | // As most WebSocket protocols are repetitive, this can be very efficient. 27 | // It carries an overhead of 8 kB for every connection compared to CompressionNoContextTakeover. 28 | // 29 | // If the peer negotiates NoContextTakeover on the client or server side, it will be 30 | // used instead as this is required by the RFC. 31 | CompressionContextTakeover 32 | 33 | // CompressionDisabled disables the deflate extension. 34 | // 35 | // Use this if you are using a predominantly binary protocol with very 36 | // little duplication in between messages or CPU and memory are more 37 | // important than bandwidth. 38 | CompressionDisabled 39 | ) 40 | -------------------------------------------------------------------------------- /lib/turso/vendor/nhooyr.io/websocket/compress_notjs.go: -------------------------------------------------------------------------------- 1 | // +build !js 2 | 3 | package websocket 4 | 5 | import ( 6 | "io" 7 | "net/http" 8 | "sync" 9 | 10 | "github.com/klauspost/compress/flate" 11 | ) 12 | 13 | func (m CompressionMode) opts() *compressionOptions { 14 | return &compressionOptions{ 15 | clientNoContextTakeover: m == CompressionNoContextTakeover, 16 | serverNoContextTakeover: m == CompressionNoContextTakeover, 17 | } 18 | } 19 | 20 | type compressionOptions struct { 21 | clientNoContextTakeover bool 22 | serverNoContextTakeover bool 23 | } 24 | 25 | func (copts *compressionOptions) setHeader(h http.Header) { 26 | s := "permessage-deflate" 27 | if copts.clientNoContextTakeover { 28 | s += "; client_no_context_takeover" 29 | } 30 | if copts.serverNoContextTakeover { 31 | s += "; server_no_context_takeover" 32 | } 33 | h.Set("Sec-WebSocket-Extensions", s) 34 | } 35 | 36 | // These bytes are required to get flate.Reader to return. 37 | // They are removed when sending to avoid the overhead as 38 | // WebSocket framing tell's when the message has ended but then 39 | // we need to add them back otherwise flate.Reader keeps 40 | // trying to return more bytes. 41 | const deflateMessageTail = "\x00\x00\xff\xff" 42 | 43 | type trimLastFourBytesWriter struct { 44 | w io.Writer 45 | tail []byte 46 | } 47 | 48 | func (tw *trimLastFourBytesWriter) reset() { 49 | if tw != nil && tw.tail != nil { 50 | tw.tail = tw.tail[:0] 51 | } 52 | } 53 | 54 | func (tw *trimLastFourBytesWriter) Write(p []byte) (int, error) { 55 | if tw.tail == nil { 56 | tw.tail = make([]byte, 0, 4) 57 | } 58 | 59 | extra := len(tw.tail) + len(p) - 4 60 | 61 | if extra <= 0 { 62 | tw.tail = append(tw.tail, p...) 63 | return len(p), nil 64 | } 65 | 66 | // Now we need to write as many extra bytes as we can from the previous tail. 67 | if extra > len(tw.tail) { 68 | extra = len(tw.tail) 69 | } 70 | if extra > 0 { 71 | _, err := tw.w.Write(tw.tail[:extra]) 72 | if err != nil { 73 | return 0, err 74 | } 75 | 76 | // Shift remaining bytes in tail over. 77 | n := copy(tw.tail, tw.tail[extra:]) 78 | tw.tail = tw.tail[:n] 79 | } 80 | 81 | // If p is less than or equal to 4 bytes, 82 | // all of it is is part of the tail. 83 | if len(p) <= 4 { 84 | tw.tail = append(tw.tail, p...) 85 | return len(p), nil 86 | } 87 | 88 | // Otherwise, only the last 4 bytes are. 89 | tw.tail = append(tw.tail, p[len(p)-4:]...) 90 | 91 | p = p[:len(p)-4] 92 | n, err := tw.w.Write(p) 93 | return n + 4, err 94 | } 95 | 96 | var flateReaderPool sync.Pool 97 | 98 | func getFlateReader(r io.Reader, dict []byte) io.Reader { 99 | fr, ok := flateReaderPool.Get().(io.Reader) 100 | if !ok { 101 | return flate.NewReaderDict(r, dict) 102 | } 103 | fr.(flate.Resetter).Reset(r, dict) 104 | return fr 105 | } 106 | 107 | func putFlateReader(fr io.Reader) { 108 | flateReaderPool.Put(fr) 109 | } 110 | 111 | type slidingWindow struct { 112 | buf []byte 113 | } 114 | 115 | var swPoolMu sync.RWMutex 116 | var swPool = map[int]*sync.Pool{} 117 | 118 | func slidingWindowPool(n int) *sync.Pool { 119 | swPoolMu.RLock() 120 | p, ok := swPool[n] 121 | swPoolMu.RUnlock() 122 | if ok { 123 | return p 124 | } 125 | 126 | p = &sync.Pool{} 127 | 128 | swPoolMu.Lock() 129 | swPool[n] = p 130 | swPoolMu.Unlock() 131 | 132 | return p 133 | } 134 | 135 | func (sw *slidingWindow) init(n int) { 136 | if sw.buf != nil { 137 | return 138 | } 139 | 140 | if n == 0 { 141 | n = 32768 142 | } 143 | 144 | p := slidingWindowPool(n) 145 | buf, ok := p.Get().([]byte) 146 | if ok { 147 | sw.buf = buf[:0] 148 | } else { 149 | sw.buf = make([]byte, 0, n) 150 | } 151 | } 152 | 153 | func (sw *slidingWindow) close() { 154 | if sw.buf == nil { 155 | return 156 | } 157 | 158 | swPoolMu.Lock() 159 | swPool[cap(sw.buf)].Put(sw.buf) 160 | swPoolMu.Unlock() 161 | sw.buf = nil 162 | } 163 | 164 | func (sw *slidingWindow) write(p []byte) { 165 | if len(p) >= cap(sw.buf) { 166 | sw.buf = sw.buf[:cap(sw.buf)] 167 | p = p[len(p)-cap(sw.buf):] 168 | copy(sw.buf, p) 169 | return 170 | } 171 | 172 | left := cap(sw.buf) - len(sw.buf) 173 | if left < len(p) { 174 | // We need to shift spaceNeeded bytes from the end to make room for p at the end. 175 | spaceNeeded := len(p) - left 176 | copy(sw.buf, sw.buf[spaceNeeded:]) 177 | sw.buf = sw.buf[:len(sw.buf)-spaceNeeded] 178 | } 179 | 180 | sw.buf = append(sw.buf, p...) 181 | } 182 | -------------------------------------------------------------------------------- /lib/turso/vendor/nhooyr.io/websocket/conn.go: -------------------------------------------------------------------------------- 1 | package websocket 2 | 3 | // MessageType represents the type of a WebSocket message. 4 | // See https://tools.ietf.org/html/rfc6455#section-5.6 5 | type MessageType int 6 | 7 | // MessageType constants. 8 | const ( 9 | // MessageText is for UTF-8 encoded text messages like JSON. 10 | MessageText MessageType = iota + 1 11 | // MessageBinary is for binary messages like protobufs. 12 | MessageBinary 13 | ) 14 | -------------------------------------------------------------------------------- /lib/turso/vendor/nhooyr.io/websocket/doc.go: -------------------------------------------------------------------------------- 1 | // +build !js 2 | 3 | // Package websocket implements the RFC 6455 WebSocket protocol. 4 | // 5 | // https://tools.ietf.org/html/rfc6455 6 | // 7 | // Use Dial to dial a WebSocket server. 8 | // 9 | // Use Accept to accept a WebSocket client. 10 | // 11 | // Conn represents the resulting WebSocket connection. 12 | // 13 | // The examples are the best way to understand how to correctly use the library. 14 | // 15 | // The wsjson and wspb subpackages contain helpers for JSON and protobuf messages. 16 | // 17 | // More documentation at https://nhooyr.io/websocket. 18 | // 19 | // Wasm 20 | // 21 | // The client side supports compiling to Wasm. 22 | // It wraps the WebSocket browser API. 23 | // 24 | // See https://developer.mozilla.org/en-US/docs/Web/API/WebSocket 25 | // 26 | // Some important caveats to be aware of: 27 | // 28 | // - Accept always errors out 29 | // - Conn.Ping is no-op 30 | // - HTTPClient, HTTPHeader and CompressionMode in DialOptions are no-op 31 | // - *http.Response from Dial is &http.Response{} with a 101 status code on success 32 | package websocket // import "nhooyr.io/websocket" 33 | -------------------------------------------------------------------------------- /lib/turso/vendor/nhooyr.io/websocket/internal/bpool/bpool.go: -------------------------------------------------------------------------------- 1 | package bpool 2 | 3 | import ( 4 | "bytes" 5 | "sync" 6 | ) 7 | 8 | var bpool sync.Pool 9 | 10 | // Get returns a buffer from the pool or creates a new one if 11 | // the pool is empty. 12 | func Get() *bytes.Buffer { 13 | b := bpool.Get() 14 | if b == nil { 15 | return &bytes.Buffer{} 16 | } 17 | return b.(*bytes.Buffer) 18 | } 19 | 20 | // Put returns a buffer into the pool. 21 | func Put(b *bytes.Buffer) { 22 | b.Reset() 23 | bpool.Put(b) 24 | } 25 | -------------------------------------------------------------------------------- /lib/turso/vendor/nhooyr.io/websocket/internal/errd/wrap.go: -------------------------------------------------------------------------------- 1 | package errd 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | // Wrap wraps err with fmt.Errorf if err is non nil. 8 | // Intended for use with defer and a named error return. 9 | // Inspired by https://github.com/golang/go/issues/32676. 10 | func Wrap(err *error, f string, v ...interface{}) { 11 | if *err != nil { 12 | *err = fmt.Errorf(f+": %w", append(v, *err)...) 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /lib/turso/vendor/nhooyr.io/websocket/internal/wsjs/wsjs_js.go: -------------------------------------------------------------------------------- 1 | // +build js 2 | 3 | // Package wsjs implements typed access to the browser javascript WebSocket API. 4 | // 5 | // https://developer.mozilla.org/en-US/docs/Web/API/WebSocket 6 | package wsjs 7 | 8 | import ( 9 | "syscall/js" 10 | ) 11 | 12 | func handleJSError(err *error, onErr func()) { 13 | r := recover() 14 | 15 | if jsErr, ok := r.(js.Error); ok { 16 | *err = jsErr 17 | 18 | if onErr != nil { 19 | onErr() 20 | } 21 | return 22 | } 23 | 24 | if r != nil { 25 | panic(r) 26 | } 27 | } 28 | 29 | // New is a wrapper around the javascript WebSocket constructor. 30 | func New(url string, protocols []string) (c WebSocket, err error) { 31 | defer handleJSError(&err, func() { 32 | c = WebSocket{} 33 | }) 34 | 35 | jsProtocols := make([]interface{}, len(protocols)) 36 | for i, p := range protocols { 37 | jsProtocols[i] = p 38 | } 39 | 40 | c = WebSocket{ 41 | v: js.Global().Get("WebSocket").New(url, jsProtocols), 42 | } 43 | 44 | c.setBinaryType("arraybuffer") 45 | 46 | return c, nil 47 | } 48 | 49 | // WebSocket is a wrapper around a javascript WebSocket object. 50 | type WebSocket struct { 51 | v js.Value 52 | } 53 | 54 | func (c WebSocket) setBinaryType(typ string) { 55 | c.v.Set("binaryType", string(typ)) 56 | } 57 | 58 | func (c WebSocket) addEventListener(eventType string, fn func(e js.Value)) func() { 59 | f := js.FuncOf(func(this js.Value, args []js.Value) interface{} { 60 | fn(args[0]) 61 | return nil 62 | }) 63 | c.v.Call("addEventListener", eventType, f) 64 | 65 | return func() { 66 | c.v.Call("removeEventListener", eventType, f) 67 | f.Release() 68 | } 69 | } 70 | 71 | // CloseEvent is the type passed to a WebSocket close handler. 72 | type CloseEvent struct { 73 | Code uint16 74 | Reason string 75 | WasClean bool 76 | } 77 | 78 | // OnClose registers a function to be called when the WebSocket is closed. 79 | func (c WebSocket) OnClose(fn func(CloseEvent)) (remove func()) { 80 | return c.addEventListener("close", func(e js.Value) { 81 | ce := CloseEvent{ 82 | Code: uint16(e.Get("code").Int()), 83 | Reason: e.Get("reason").String(), 84 | WasClean: e.Get("wasClean").Bool(), 85 | } 86 | fn(ce) 87 | }) 88 | } 89 | 90 | // OnError registers a function to be called when there is an error 91 | // with the WebSocket. 92 | func (c WebSocket) OnError(fn func(e js.Value)) (remove func()) { 93 | return c.addEventListener("error", fn) 94 | } 95 | 96 | // MessageEvent is the type passed to a message handler. 97 | type MessageEvent struct { 98 | // string or []byte. 99 | Data interface{} 100 | 101 | // There are more fields to the interface but we don't use them. 102 | // See https://developer.mozilla.org/en-US/docs/Web/API/MessageEvent 103 | } 104 | 105 | // OnMessage registers a function to be called when the WebSocket receives a message. 106 | func (c WebSocket) OnMessage(fn func(m MessageEvent)) (remove func()) { 107 | return c.addEventListener("message", func(e js.Value) { 108 | var data interface{} 109 | 110 | arrayBuffer := e.Get("data") 111 | if arrayBuffer.Type() == js.TypeString { 112 | data = arrayBuffer.String() 113 | } else { 114 | data = extractArrayBuffer(arrayBuffer) 115 | } 116 | 117 | me := MessageEvent{ 118 | Data: data, 119 | } 120 | fn(me) 121 | 122 | return 123 | }) 124 | } 125 | 126 | // Subprotocol returns the WebSocket subprotocol in use. 127 | func (c WebSocket) Subprotocol() string { 128 | return c.v.Get("protocol").String() 129 | } 130 | 131 | // OnOpen registers a function to be called when the WebSocket is opened. 132 | func (c WebSocket) OnOpen(fn func(e js.Value)) (remove func()) { 133 | return c.addEventListener("open", fn) 134 | } 135 | 136 | // Close closes the WebSocket with the given code and reason. 137 | func (c WebSocket) Close(code int, reason string) (err error) { 138 | defer handleJSError(&err, nil) 139 | c.v.Call("close", code, reason) 140 | return err 141 | } 142 | 143 | // SendText sends the given string as a text message 144 | // on the WebSocket. 145 | func (c WebSocket) SendText(v string) (err error) { 146 | defer handleJSError(&err, nil) 147 | c.v.Call("send", v) 148 | return err 149 | } 150 | 151 | // SendBytes sends the given message as a binary message 152 | // on the WebSocket. 153 | func (c WebSocket) SendBytes(v []byte) (err error) { 154 | defer handleJSError(&err, nil) 155 | c.v.Call("send", uint8Array(v)) 156 | return err 157 | } 158 | 159 | func extractArrayBuffer(arrayBuffer js.Value) []byte { 160 | uint8Array := js.Global().Get("Uint8Array").New(arrayBuffer) 161 | dst := make([]byte, uint8Array.Length()) 162 | js.CopyBytesToGo(dst, uint8Array) 163 | return dst 164 | } 165 | 166 | func uint8Array(src []byte) js.Value { 167 | uint8Array := js.Global().Get("Uint8Array").New(len(src)) 168 | js.CopyBytesToJS(uint8Array, src) 169 | return uint8Array 170 | } 171 | -------------------------------------------------------------------------------- /lib/turso/vendor/nhooyr.io/websocket/internal/xsync/go.go: -------------------------------------------------------------------------------- 1 | package xsync 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | // Go allows running a function in another goroutine 8 | // and waiting for its error. 9 | func Go(fn func() error) <-chan error { 10 | errs := make(chan error, 1) 11 | go func() { 12 | defer func() { 13 | r := recover() 14 | if r != nil { 15 | select { 16 | case errs <- fmt.Errorf("panic in go fn: %v", r): 17 | default: 18 | } 19 | } 20 | }() 21 | errs <- fn() 22 | }() 23 | 24 | return errs 25 | } 26 | -------------------------------------------------------------------------------- /lib/turso/vendor/nhooyr.io/websocket/internal/xsync/int64.go: -------------------------------------------------------------------------------- 1 | package xsync 2 | 3 | import ( 4 | "sync/atomic" 5 | ) 6 | 7 | // Int64 represents an atomic int64. 8 | type Int64 struct { 9 | // We do not use atomic.Load/StoreInt64 since it does not 10 | // work on 32 bit computers but we need 64 bit integers. 11 | i atomic.Value 12 | } 13 | 14 | // Load loads the int64. 15 | func (v *Int64) Load() int64 { 16 | i, _ := v.i.Load().(int64) 17 | return i 18 | } 19 | 20 | // Store stores the int64. 21 | func (v *Int64) Store(i int64) { 22 | v.i.Store(i) 23 | } 24 | -------------------------------------------------------------------------------- /lib/turso/vendor/nhooyr.io/websocket/netconn.go: -------------------------------------------------------------------------------- 1 | package websocket 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "io" 7 | "math" 8 | "net" 9 | "sync" 10 | "time" 11 | ) 12 | 13 | // NetConn converts a *websocket.Conn into a net.Conn. 14 | // 15 | // It's for tunneling arbitrary protocols over WebSockets. 16 | // Few users of the library will need this but it's tricky to implement 17 | // correctly and so provided in the library. 18 | // See https://github.com/nhooyr/websocket/issues/100. 19 | // 20 | // Every Write to the net.Conn will correspond to a message write of 21 | // the given type on *websocket.Conn. 22 | // 23 | // The passed ctx bounds the lifetime of the net.Conn. If cancelled, 24 | // all reads and writes on the net.Conn will be cancelled. 25 | // 26 | // If a message is read that is not of the correct type, the connection 27 | // will be closed with StatusUnsupportedData and an error will be returned. 28 | // 29 | // Close will close the *websocket.Conn with StatusNormalClosure. 30 | // 31 | // When a deadline is hit, the connection will be closed. This is 32 | // different from most net.Conn implementations where only the 33 | // reading/writing goroutines are interrupted but the connection is kept alive. 34 | // 35 | // The Addr methods will return a mock net.Addr that returns "websocket" for Network 36 | // and "websocket/unknown-addr" for String. 37 | // 38 | // A received StatusNormalClosure or StatusGoingAway close frame will be translated to 39 | // io.EOF when reading. 40 | func NetConn(ctx context.Context, c *Conn, msgType MessageType) net.Conn { 41 | nc := &netConn{ 42 | c: c, 43 | msgType: msgType, 44 | } 45 | 46 | var cancel context.CancelFunc 47 | nc.writeContext, cancel = context.WithCancel(ctx) 48 | nc.writeTimer = time.AfterFunc(math.MaxInt64, cancel) 49 | if !nc.writeTimer.Stop() { 50 | <-nc.writeTimer.C 51 | } 52 | 53 | nc.readContext, cancel = context.WithCancel(ctx) 54 | nc.readTimer = time.AfterFunc(math.MaxInt64, cancel) 55 | if !nc.readTimer.Stop() { 56 | <-nc.readTimer.C 57 | } 58 | 59 | return nc 60 | } 61 | 62 | type netConn struct { 63 | c *Conn 64 | msgType MessageType 65 | 66 | writeTimer *time.Timer 67 | writeContext context.Context 68 | 69 | readTimer *time.Timer 70 | readContext context.Context 71 | 72 | readMu sync.Mutex 73 | eofed bool 74 | reader io.Reader 75 | } 76 | 77 | var _ net.Conn = &netConn{} 78 | 79 | func (c *netConn) Close() error { 80 | return c.c.Close(StatusNormalClosure, "") 81 | } 82 | 83 | func (c *netConn) Write(p []byte) (int, error) { 84 | err := c.c.Write(c.writeContext, c.msgType, p) 85 | if err != nil { 86 | return 0, err 87 | } 88 | return len(p), nil 89 | } 90 | 91 | func (c *netConn) Read(p []byte) (int, error) { 92 | c.readMu.Lock() 93 | defer c.readMu.Unlock() 94 | 95 | if c.eofed { 96 | return 0, io.EOF 97 | } 98 | 99 | if c.reader == nil { 100 | typ, r, err := c.c.Reader(c.readContext) 101 | if err != nil { 102 | switch CloseStatus(err) { 103 | case StatusNormalClosure, StatusGoingAway: 104 | c.eofed = true 105 | return 0, io.EOF 106 | } 107 | return 0, err 108 | } 109 | if typ != c.msgType { 110 | err := fmt.Errorf("unexpected frame type read (expected %v): %v", c.msgType, typ) 111 | c.c.Close(StatusUnsupportedData, err.Error()) 112 | return 0, err 113 | } 114 | c.reader = r 115 | } 116 | 117 | n, err := c.reader.Read(p) 118 | if err == io.EOF { 119 | c.reader = nil 120 | err = nil 121 | } 122 | return n, err 123 | } 124 | 125 | type websocketAddr struct { 126 | } 127 | 128 | func (a websocketAddr) Network() string { 129 | return "websocket" 130 | } 131 | 132 | func (a websocketAddr) String() string { 133 | return "websocket/unknown-addr" 134 | } 135 | 136 | func (c *netConn) RemoteAddr() net.Addr { 137 | return websocketAddr{} 138 | } 139 | 140 | func (c *netConn) LocalAddr() net.Addr { 141 | return websocketAddr{} 142 | } 143 | 144 | func (c *netConn) SetDeadline(t time.Time) error { 145 | c.SetWriteDeadline(t) 146 | c.SetReadDeadline(t) 147 | return nil 148 | } 149 | 150 | func (c *netConn) SetWriteDeadline(t time.Time) error { 151 | if t.IsZero() { 152 | c.writeTimer.Stop() 153 | } else { 154 | c.writeTimer.Reset(t.Sub(time.Now())) 155 | } 156 | return nil 157 | } 158 | 159 | func (c *netConn) SetReadDeadline(t time.Time) error { 160 | if t.IsZero() { 161 | c.readTimer.Stop() 162 | } else { 163 | c.readTimer.Reset(t.Sub(time.Now())) 164 | } 165 | return nil 166 | } 167 | -------------------------------------------------------------------------------- /lib/turso/vendor/nhooyr.io/websocket/stringer.go: -------------------------------------------------------------------------------- 1 | // Code generated by "stringer -type=opcode,MessageType,StatusCode -output=stringer.go"; DO NOT EDIT. 2 | 3 | package websocket 4 | 5 | import "strconv" 6 | 7 | func _() { 8 | // An "invalid array index" compiler error signifies that the constant values have changed. 9 | // Re-run the stringer command to generate them again. 10 | var x [1]struct{} 11 | _ = x[opContinuation-0] 12 | _ = x[opText-1] 13 | _ = x[opBinary-2] 14 | _ = x[opClose-8] 15 | _ = x[opPing-9] 16 | _ = x[opPong-10] 17 | } 18 | 19 | const ( 20 | _opcode_name_0 = "opContinuationopTextopBinary" 21 | _opcode_name_1 = "opCloseopPingopPong" 22 | ) 23 | 24 | var ( 25 | _opcode_index_0 = [...]uint8{0, 14, 20, 28} 26 | _opcode_index_1 = [...]uint8{0, 7, 13, 19} 27 | ) 28 | 29 | func (i opcode) String() string { 30 | switch { 31 | case 0 <= i && i <= 2: 32 | return _opcode_name_0[_opcode_index_0[i]:_opcode_index_0[i+1]] 33 | case 8 <= i && i <= 10: 34 | i -= 8 35 | return _opcode_name_1[_opcode_index_1[i]:_opcode_index_1[i+1]] 36 | default: 37 | return "opcode(" + strconv.FormatInt(int64(i), 10) + ")" 38 | } 39 | } 40 | func _() { 41 | // An "invalid array index" compiler error signifies that the constant values have changed. 42 | // Re-run the stringer command to generate them again. 43 | var x [1]struct{} 44 | _ = x[MessageText-1] 45 | _ = x[MessageBinary-2] 46 | } 47 | 48 | const _MessageType_name = "MessageTextMessageBinary" 49 | 50 | var _MessageType_index = [...]uint8{0, 11, 24} 51 | 52 | func (i MessageType) String() string { 53 | i -= 1 54 | if i < 0 || i >= MessageType(len(_MessageType_index)-1) { 55 | return "MessageType(" + strconv.FormatInt(int64(i+1), 10) + ")" 56 | } 57 | return _MessageType_name[_MessageType_index[i]:_MessageType_index[i+1]] 58 | } 59 | func _() { 60 | // An "invalid array index" compiler error signifies that the constant values have changed. 61 | // Re-run the stringer command to generate them again. 62 | var x [1]struct{} 63 | _ = x[StatusNormalClosure-1000] 64 | _ = x[StatusGoingAway-1001] 65 | _ = x[StatusProtocolError-1002] 66 | _ = x[StatusUnsupportedData-1003] 67 | _ = x[statusReserved-1004] 68 | _ = x[StatusNoStatusRcvd-1005] 69 | _ = x[StatusAbnormalClosure-1006] 70 | _ = x[StatusInvalidFramePayloadData-1007] 71 | _ = x[StatusPolicyViolation-1008] 72 | _ = x[StatusMessageTooBig-1009] 73 | _ = x[StatusMandatoryExtension-1010] 74 | _ = x[StatusInternalError-1011] 75 | _ = x[StatusServiceRestart-1012] 76 | _ = x[StatusTryAgainLater-1013] 77 | _ = x[StatusBadGateway-1014] 78 | _ = x[StatusTLSHandshake-1015] 79 | } 80 | 81 | const _StatusCode_name = "StatusNormalClosureStatusGoingAwayStatusProtocolErrorStatusUnsupportedDatastatusReservedStatusNoStatusRcvdStatusAbnormalClosureStatusInvalidFramePayloadDataStatusPolicyViolationStatusMessageTooBigStatusMandatoryExtensionStatusInternalErrorStatusServiceRestartStatusTryAgainLaterStatusBadGatewayStatusTLSHandshake" 82 | 83 | var _StatusCode_index = [...]uint16{0, 19, 34, 53, 74, 88, 106, 127, 156, 177, 196, 220, 239, 259, 278, 294, 312} 84 | 85 | func (i StatusCode) String() string { 86 | i -= 1000 87 | if i < 0 || i >= StatusCode(len(_StatusCode_index)-1) { 88 | return "StatusCode(" + strconv.FormatInt(int64(i+1000), 10) + ")" 89 | } 90 | return _StatusCode_name[_StatusCode_index[i]:_StatusCode_index[i+1]] 91 | } 92 | -------------------------------------------------------------------------------- /lib/turso/vendor/nhooyr.io/websocket/wsjson/wsjson.go: -------------------------------------------------------------------------------- 1 | // Package wsjson provides helpers for reading and writing JSON messages. 2 | package wsjson // import "nhooyr.io/websocket/wsjson" 3 | 4 | import ( 5 | "context" 6 | "encoding/json" 7 | "fmt" 8 | 9 | "nhooyr.io/websocket" 10 | "nhooyr.io/websocket/internal/bpool" 11 | "nhooyr.io/websocket/internal/errd" 12 | ) 13 | 14 | // Read reads a JSON message from c into v. 15 | // It will reuse buffers in between calls to avoid allocations. 16 | func Read(ctx context.Context, c *websocket.Conn, v interface{}) error { 17 | return read(ctx, c, v) 18 | } 19 | 20 | func read(ctx context.Context, c *websocket.Conn, v interface{}) (err error) { 21 | defer errd.Wrap(&err, "failed to read JSON message") 22 | 23 | _, r, err := c.Reader(ctx) 24 | if err != nil { 25 | return err 26 | } 27 | 28 | b := bpool.Get() 29 | defer bpool.Put(b) 30 | 31 | _, err = b.ReadFrom(r) 32 | if err != nil { 33 | return err 34 | } 35 | 36 | err = json.Unmarshal(b.Bytes(), v) 37 | if err != nil { 38 | c.Close(websocket.StatusInvalidFramePayloadData, "failed to unmarshal JSON") 39 | return fmt.Errorf("failed to unmarshal JSON: %w", err) 40 | } 41 | 42 | return nil 43 | } 44 | 45 | // Write writes the JSON message v to c. 46 | // It will reuse buffers in between calls to avoid allocations. 47 | func Write(ctx context.Context, c *websocket.Conn, v interface{}) error { 48 | return write(ctx, c, v) 49 | } 50 | 51 | func write(ctx context.Context, c *websocket.Conn, v interface{}) (err error) { 52 | defer errd.Wrap(&err, "failed to write JSON message") 53 | 54 | w, err := c.Writer(ctx, websocket.MessageText) 55 | if err != nil { 56 | return err 57 | } 58 | 59 | // json.Marshal cannot reuse buffers between calls as it has to return 60 | // a copy of the byte slice but Encoder does as it directly writes to w. 61 | err = json.NewEncoder(w).Encode(v) 62 | if err != nil { 63 | return fmt.Errorf("failed to marshal JSON: %w", err) 64 | } 65 | 66 | return w.Close() 67 | } 68 | -------------------------------------------------------------------------------- /nix/default.nix: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ethanthoma/ocaml-webserver/9a5736548d4bc7b3cfc052c949a61f455276f2b2/nix/default.nix -------------------------------------------------------------------------------- /nix/docker.nix: -------------------------------------------------------------------------------- 1 | { pkgs, package, derivation }: 2 | 3 | let 4 | baseImage = pkgs.dockerTools.pullImage { 5 | imageName = "ghcr.io/tursodatabase/libsql-server"; 6 | imageDigest = "sha256:253207a90cdae061ce8702625011c62ff6b5725119199b2d079de06ae8001700"; 7 | sha256 = "8+nYYweux3KKtkip4GMtsZ/TmiYKueFCj3TQrUQ3GVM="; 8 | }; 9 | in pkgs.dockerTools.buildImage { 10 | name = package; 11 | tag = "latest"; 12 | created = "now"; 13 | fromImage = baseImage; 14 | copyToRoot = pkgs.buildEnv { 15 | name = "webserver"; 16 | paths = [ 17 | derivation 18 | pkgs.bash 19 | pkgs.getconf 20 | pkgs.turso-cli 21 | ]; 22 | pathsToLink = [ "/bin" "/public" "/blogs" ]; 23 | }; 24 | config = { 25 | Cmd = [ 26 | "${derivation}/bin/${package}" 27 | ]; 28 | Env = [ 29 | "TURSO_INSTALL_SKIP_SIGNUP=true" 30 | ]; 31 | ExposedPorts = { 32 | "3000/tcp" = {}; 33 | }; 34 | }; 35 | } 36 | -------------------------------------------------------------------------------- /nix/shell.nix: -------------------------------------------------------------------------------- 1 | { 2 | pkgs ? let 3 | nixpkgs = fetchTarball "https://github.com/NixOS/nixpkgs/tarball/nixpkgs-unstable"; 4 | in import nixpkgs {}, 5 | devDeps 6 | }: 7 | 8 | pkgs.mkShell { 9 | packages = with pkgs; [ 10 | ocaml 11 | ocamlformat 12 | ocamlPackages.findlib 13 | ocamlPackages.ocamlformat-rpc-lib 14 | ocamlPackages.utop 15 | opam 16 | turso-cli 17 | pkg-config 18 | openssl 19 | ] ++ devDeps; 20 | 21 | shellHook = '' 22 | turso config set token "$TURSO_TOKEN" 23 | eval $(opam env --switch=website) 24 | ''; 25 | } 26 | -------------------------------------------------------------------------------- /public/favicon/android-chrome-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ethanthoma/ocaml-webserver/9a5736548d4bc7b3cfc052c949a61f455276f2b2/public/favicon/android-chrome-192x192.png -------------------------------------------------------------------------------- /public/favicon/android-chrome-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ethanthoma/ocaml-webserver/9a5736548d4bc7b3cfc052c949a61f455276f2b2/public/favicon/android-chrome-512x512.png -------------------------------------------------------------------------------- /public/favicon/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ethanthoma/ocaml-webserver/9a5736548d4bc7b3cfc052c949a61f455276f2b2/public/favicon/apple-touch-icon.png -------------------------------------------------------------------------------- /public/favicon/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ethanthoma/ocaml-webserver/9a5736548d4bc7b3cfc052c949a61f455276f2b2/public/favicon/favicon-16x16.png -------------------------------------------------------------------------------- /public/favicon/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ethanthoma/ocaml-webserver/9a5736548d4bc7b3cfc052c949a61f455276f2b2/public/favicon/favicon-32x32.png -------------------------------------------------------------------------------- /public/favicon/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ethanthoma/ocaml-webserver/9a5736548d4bc7b3cfc052c949a61f455276f2b2/public/favicon/favicon.ico -------------------------------------------------------------------------------- /public/favicon/site.webmanifest: -------------------------------------------------------------------------------- 1 | {"name":"","short_name":"","icons":[{"src":"/android-chrome-192x192.png","sizes":"192x192","type":"image/png"},{"src":"/android-chrome-512x512.png","sizes":"512x512","type":"image/png"}],"theme_color":"#ffffff","background_color":"#ffffff","display":"standalone"} -------------------------------------------------------------------------------- /public/fonts/fonts.css: -------------------------------------------------------------------------------- 1 | /* latin */ 2 | @font-face { 3 | font-family: 'Montserrat'; 4 | font-style: normal; 5 | font-weight: 100 900; 6 | font-display: swap; 7 | src: url('./montserrat-latin.woff2') format('woff2'); 8 | unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; 9 | } 10 | /* latin */ 11 | @font-face { 12 | font-family: 'Montserrat'; 13 | font-style: italic; 14 | font-weight: 100 900; 15 | font-display: swap; 16 | src: url('./montserrat-latin-italic.woff2') format('woff2'); 17 | unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; 18 | } 19 | /* latin */ 20 | @font-face { 21 | font-family: 'Inter'; 22 | font-style: normal; 23 | font-weight: 100 900; 24 | font-display: swap; 25 | src: url('./inter-latin.woff2') format('woff2'); 26 | unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; 27 | } 28 | -------------------------------------------------------------------------------- /public/fonts/inter-latin.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ethanthoma/ocaml-webserver/9a5736548d4bc7b3cfc052c949a61f455276f2b2/public/fonts/inter-latin.woff2 -------------------------------------------------------------------------------- /public/fonts/montserrat-latin-italic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ethanthoma/ocaml-webserver/9a5736548d4bc7b3cfc052c949a61f455276f2b2/public/fonts/montserrat-latin-italic.woff2 -------------------------------------------------------------------------------- /public/fonts/montserrat-latin.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ethanthoma/ocaml-webserver/9a5736548d4bc7b3cfc052c949a61f455276f2b2/public/fonts/montserrat-latin.woff2 -------------------------------------------------------------------------------- /public/js/antiThePrimeagen@0.0.2.js: -------------------------------------------------------------------------------- 1 | /* The point of this script is to stop ThePrimeagen from hightlighting how he 2 | * wants. His twitter is here: https://twitter.com/ThePrimeagen 3 | */ 4 | 5 | // doesnt work across element tags. 6 | // TODO: get it to only take effect for text in blog-content and work acorss nodes 7 | let antiThePrimeagenEnabled = false; 8 | 9 | function mouseSelection(element) { 10 | const selection = window.getSelection(); 11 | if (!selection.isCollapsed) { 12 | const wordRange = document.createRange(); 13 | 14 | let startNode = selection.anchorNode, startOffset = selection.anchorOffset; 15 | let endNode = selection.focusNode, endOffset = selection.focusOffset; 16 | 17 | if (selection.anchorNode === selection.focusNode) { 18 | if (selection.anchorOffset > selection.focusOffset) { 19 | [startOffset, endOffset] = [endOffset, startOffset]; 20 | } 21 | } else if (selection.anchorNode.compareDocumentPosition(selection.focusNode) & Node.DOCUMENT_POSITION_FOLLOWING) { 22 | [startNode, endNode] = [endNode, startNode]; 23 | [startOffset, endOffset] = [endOffset, startOffset]; 24 | } 25 | 26 | let textContent = startNode.textContent; 27 | while (startOffset > 0 && /\S/.test(textContent[startOffset - 1])) { 28 | startOffset--; 29 | } 30 | 31 | textContent = endNode.textContent; 32 | while (endOffset < textContent.length && /\S/.test(textContent[endOffset])) { 33 | endOffset++; 34 | } 35 | 36 | wordRange.setStart(startNode, startOffset); 37 | wordRange.setEnd(endNode, endOffset); 38 | 39 | selection.removeAllRanges(); 40 | selection.addRange(wordRange); 41 | } 42 | } 43 | 44 | document.addEventListener('htmx:load', function(evt) { 45 | let els = document.getElementsByClassName('blog-content'); 46 | 47 | [...els].forEach((element, index, array) => { 48 | if (antiThePrimeagenEnabled) { 49 | element.addEventListener('mouseup', function(e) { 50 | mouseSelection(element); 51 | console.log("antiThePrimeagen: activated."); 52 | }); 53 | } 54 | }); 55 | }); 56 | -------------------------------------------------------------------------------- /public/seo/robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | Allow: / 3 | 4 | Sitemap: https://www.ethanthoma.com/sitemap.xml 5 | -------------------------------------------------------------------------------- /public/seo/sitemap.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | http://www.ethanthoma.com 6 | 7 | 8 | http://www.ethanthoma.com/explore 9 | 10 | 11 | http://www.ethanthoma.com/blogs/how_not_to_build_a_website.md 12 | 13 | 14 | -------------------------------------------------------------------------------- /public/styles/abstract/index.css: -------------------------------------------------------------------------------- 1 | @import "variables.css"; 2 | -------------------------------------------------------------------------------- /public/styles/base/base.css: -------------------------------------------------------------------------------- 1 | html { 2 | scroll-behavior: smooth; 3 | } 4 | 5 | body { 6 | display: flex; 7 | flex-direction: column; 8 | max-width: 100dvw; 9 | 10 | background: var(--clr-base); 11 | color: var(--clr-text); 12 | font-family: var(--ff-body); 13 | font-size: var(--fs-300); 14 | line-height: 1.4; 15 | letter-spacing: 0.05em; 16 | 17 | overflow-x: hidden; 18 | 19 | &>* { 20 | margin-inline: auto; 21 | width: 100%; 22 | } 23 | } 24 | 25 | h1, 26 | h2, 27 | h3 { 28 | font-family: var(--ff-heading); 29 | letter-spacing: -0.01em; 30 | line-height: 1.2; 31 | font-weight: 600; 32 | } 33 | 34 | h1 { 35 | font-size: var(--fs-700); 36 | } 37 | 38 | h2 { 39 | font-size: var(--fs-500); 40 | } 41 | 42 | h3 { 43 | font-size: var(--fs-400); 44 | } 45 | 46 | a { 47 | color: var(--clr-iris); 48 | border-radius: var(--space-3xs); 49 | cursor: pointer; 50 | 51 | &:hover, 52 | &:focus, 53 | &:focus-visible { 54 | color: var(--clr-gold); 55 | 56 | transition: color var(--animate-t) var(--animate-f); 57 | } 58 | 59 | &:focus-visible { 60 | outline-offset: 2px; 61 | outline: var(--clr-foam) solid 2px; 62 | } 63 | } 64 | 65 | ::-webkit-scrollbar { 66 | width: 15px; 67 | background: transparent; 68 | } 69 | 70 | ::-webkit-scrollbar-track { 71 | background: var(--clr-base); 72 | box-shadow: inset 0 0 0 1px var(--clr-high); 73 | border-radius: var(--space-3xs); 74 | } 75 | 76 | ::-webkit-scrollbar-thumb { 77 | background-color: var(--clr-muted); 78 | border-radius: var(--space-3xs); 79 | border: 3px solid transparent; 80 | 81 | background-clip: content-box; 82 | } 83 | 84 | ::-webkit-scrollbar-thumb:hover { 85 | background-color: var(--clr-subtle); 86 | } 87 | -------------------------------------------------------------------------------- /public/styles/base/index.css: -------------------------------------------------------------------------------- 1 | @import "reset.css"; 2 | @import "base.css"; 3 | @import "util.css"; 4 | -------------------------------------------------------------------------------- /public/styles/base/reset.css: -------------------------------------------------------------------------------- 1 | /* Remove default margin */ 2 | * { 3 | margin: 0; 4 | padding: 0; 5 | border: 0; 6 | font: inherit; 7 | font-size: 100%; 8 | vertical-align: baseline; 9 | text-rendering: optimizeLegibility; 10 | -webkit-font-smoothing: antialiased; 11 | text-size-adjust: none; 12 | } 13 | 14 | /* Box sizing rules */ 15 | *, 16 | *::before, 17 | *::after { 18 | box-sizing: border-box; 19 | } 20 | 21 | /* Remove default margin */ 22 | body, 23 | h1, 24 | h2, 25 | h3, 26 | h4, 27 | p, 28 | figure, 29 | blockquote, 30 | dl, 31 | dd { 32 | margin-block-end: 0; 33 | } 34 | 35 | /* Remove list styles on ul, ol elements with a list role, which suggests default styling will be removed */ 36 | ul[role='list'], 37 | ol[role='list'] { 38 | list-style: none; 39 | } 40 | 41 | /* Set core root defaults */ 42 | html:focus-within { 43 | scroll-behavior: smooth; 44 | } 45 | 46 | html { 47 | height: 100%; 48 | } 49 | 50 | /* Set core body defaults */ 51 | body { 52 | min-height: 100%; 53 | width: 100vw; 54 | max-width: 100%; 55 | box-sizing: border-box; 56 | position: relative; 57 | text-rendering: optimizeSpeed; 58 | line-height: 1.5; 59 | } 60 | 61 | /* A elements that don't have a class get default styles */ 62 | a:not([class]) { 63 | text-decoration-skip-ink: auto; 64 | } 65 | 66 | /* Make images easier to work with */ 67 | img, 68 | picture, 69 | svg { 70 | max-width: 100%; 71 | display: block; 72 | } 73 | 74 | footer, 75 | header, 76 | nav, 77 | section, 78 | main { 79 | display: block; 80 | } 81 | 82 | blockquote, 83 | q { 84 | quotes: none; 85 | } 86 | 87 | blockquote:before, 88 | blockquote:after, 89 | q:before, 90 | q:after { 91 | content: ''; 92 | content: none; 93 | } 94 | 95 | table { 96 | border-collapse: collapse; 97 | border-spacing: 0; 98 | } 99 | 100 | input { 101 | -webkit-appearance: none; 102 | appearance: none; 103 | border-radius: 0; 104 | } 105 | 106 | /* Remove all animations, transitions and smooth scroll for people that prefer not to see them */ 107 | @media (prefers-reduced-motion: reduce) { 108 | html:focus-within { 109 | scroll-behavior: auto; 110 | } 111 | 112 | *, 113 | *::before, 114 | *::after { 115 | animation-duration: 0.01ms !important; 116 | animation-iteration-count: 1 !important; 117 | transition-duration: 0.01ms !important; 118 | scroll-behavior: auto !important; 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /public/styles/base/util.css: -------------------------------------------------------------------------------- 1 | .hidden { 2 | border-width: 0 !important; 3 | clip: rect(0 0 0 0); 4 | clip-path: inset(50%); 5 | height: 0px; 6 | overflow: hidden; 7 | position: absolute; 8 | white-space: nowrap; 9 | width: 0px; 10 | } 11 | 12 | .italic, 13 | em { 14 | font-style: italic; 15 | } 16 | 17 | .bold, 18 | strong { 19 | font-weight: 600; 20 | font-style: normal; 21 | } 22 | 23 | .outline { 24 | transition: outline var(--animate-t) var(--animate-f); 25 | 26 | &:hover, 27 | &:focus, 28 | &:focus-visible { 29 | outline-offset: 2px; 30 | outline: var(--clr-foam) solid 2px; 31 | } 32 | } 33 | 34 | .border { 35 | transition: 36 | background-color var(--animate-t) var(--animate-f), 37 | box-shadow var(--animate-t) var(--animate-f); 38 | 39 | &:hover, 40 | &:focus, 41 | &:focus-visible { 42 | outline: unset; 43 | 44 | background-color: var(--clr-muted); 45 | box-shadow: inset 0 0 0 2px var(--clr-foam); 46 | } 47 | } 48 | 49 | .animate-fade-in { 50 | --animate-f: ease-in; 51 | 52 | opacity: 1; 53 | transition: opacity var(--animate-t) var(--animate-f); 54 | 55 | &.htmx-added { 56 | opacity: 0; 57 | } 58 | } 59 | 60 | @keyframes fadeInUp { 61 | from { 62 | opacity: 0; 63 | transform: translateY(20px); 64 | } 65 | 66 | to { 67 | opacity: 1; 68 | transform: translateY(0); 69 | } 70 | } 71 | 72 | .staggered-load { 73 | &>.staggered-load-child { 74 | opacity: 0; 75 | animation: fadeInUp 0.5s ease forwards; 76 | transform: translateY(20px); 77 | } 78 | 79 | &>.staggered-load-child:nth-child(1) { 80 | animation-delay: 0.15s; 81 | } 82 | 83 | &>.staggered-load-child:nth-child(2) { 84 | animation-delay: 0.3s; 85 | } 86 | 87 | &>.staggered-load-child:nth-child(3) { 88 | animation-delay: 0.45s; 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /public/styles/components/call_to_action.css: -------------------------------------------------------------------------------- 1 | .call-to-action { 2 | width: fit-content; 3 | 4 | text-align: center; 5 | display: inline-block; 6 | text-decoration: none; 7 | 8 | padding-block: var(--space-s); 9 | padding-inline: var(--space-l); 10 | 11 | color: var(--clr-base); 12 | background: var(--clr-foam); 13 | 14 | border-radius: var(--space-xs); 15 | 16 | box-shadow: var(--shadow-low); 17 | 18 | a { 19 | transition: unset; 20 | } 21 | 22 | &:hover, 23 | &:focus, 24 | &:focus-visible { 25 | outline: unset; 26 | 27 | transition: 28 | color var(--animate-t) var(--animate-f), 29 | background var(--animate-t) var(--animate-f), 30 | box-shadow var(--animate-t) var(--animate-f); 31 | 32 | color: var(--clr-text); 33 | background: var(--clr-surface); 34 | box-shadow: 35 | inset 0 0 0 2px var(--clr-foam), 36 | var(--shadow-low); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /public/styles/components/card.css: -------------------------------------------------------------------------------- 1 | .card { 2 | --animate-t: 0.2s; 3 | --animate-f: cubic-bezier(0, 0, .2, 1); 4 | --clr-bg: var(--clr-surface); 5 | --clr-fg: var(--clr-text); 6 | 7 | border-radius: var(--space-s); 8 | 9 | padding: var(--space-m); 10 | 11 | background-color: var(--clr-bg); 12 | color: var(--clr-fg); 13 | position: relative; 14 | 15 | transition: scale var(--animate-t) var(--animate-f); 16 | 17 | &:hover, 18 | &:focus, 19 | &:focus-visible, 20 | &:focus-within { 21 | --clr-bg: var(--clr-muted); 22 | } 23 | 24 | & a { 25 | color: inherit; 26 | display: flex; 27 | flex-direction: column; 28 | height: 100%; 29 | text-decoration: none; 30 | 31 | &:hover, 32 | &:focus, 33 | &:focus-visible { 34 | color: inherit; 35 | outline: unset; 36 | } 37 | 38 | & p { 39 | margin-top: var(--space-xs); 40 | } 41 | 42 | & div { 43 | margin-block-start: auto; 44 | padding-block-start: var(--space-m); 45 | 46 | color: var(--clr-subtle); 47 | } 48 | 49 | &::after { 50 | content: ''; 51 | position: absolute; 52 | inset: 0; 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /public/styles/components/fancy.css: -------------------------------------------------------------------------------- 1 | .fancy { 2 | white-space: nowrap; 3 | 4 | /* bg gradient */ 5 | background: linear-gradient(to right, 6 | var(--clr-rose), 7 | var(--clr-iris), 8 | var(--clr-foam), 9 | var(--clr-iris), 10 | var(--clr-rose)); 11 | background-size: 200%; 12 | -webkit-background-clip: text; 13 | background-clip: text; 14 | -webkit-text-fill-color: transparent; 15 | 16 | /* animation */ 17 | animation: background-pan 3s linear infinite; 18 | } 19 | 20 | @keyframes background-pan { 21 | from { 22 | background-position: 0% center; 23 | } 24 | 25 | to { 26 | background-position: -200% center; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /public/styles/components/footer.css: -------------------------------------------------------------------------------- 1 | body>footer { 2 | align-items: center; 3 | display: flex; 4 | flex-direction: column; 5 | 6 | --_col: color-mix(in srgb, color-mix(in srgb, var(--clr-text), var(--clr-base) 50%), black 60%); 7 | 8 | margin-block-start: auto; 9 | padding-block: var(--space-xl); 10 | 11 | background: color-mix(in srgb, var(--_col), black 60%); 12 | border-block-start: 2px solid var(--clr-high); 13 | color: color-mix(in srgb, var(--_col), white 60%); 14 | font-size: var(--fs-100); 15 | text-align: center; 16 | 17 | &>*+* { 18 | margin-block-start: var(--space-s); 19 | } 20 | 21 | & #nav { 22 | list-style: none; 23 | display: flex; 24 | gap: var(--space-s); 25 | } 26 | 27 | & #contact { 28 | list-style: none; 29 | display: flex; 30 | gap: var(--space-xs-s); 31 | align-items: center; 32 | justify-content: center; 33 | 34 | & a { 35 | display: flex; 36 | border-radius: var(--space-3xs); 37 | 38 | & .icon { 39 | height: 100%; 40 | aspect-ratio: 1; 41 | transition: scale var(--animate-t) var(--animate-f); 42 | font-size: var(--fs-400); 43 | } 44 | 45 | &:hover, 46 | &:focus, 47 | &:focus-visible { 48 | & .icon { 49 | scale: 0.7; 50 | } 51 | } 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /public/styles/components/header.css: -------------------------------------------------------------------------------- 1 | body>header { 2 | display: flex; 3 | 4 | flex-wrap: wrap; 5 | gap: var(--space-s-m); 6 | padding-block: var(--space-s-m); 7 | padding-inline: var(--padding-inline-l); 8 | 9 | font-weight: 400; 10 | font-family: var(--ff-heading); 11 | font-size: var(--fs-300); 12 | 13 | background: var(--clr-base); 14 | position: sticky; 15 | top: 0; 16 | z-index: 2; 17 | 18 | &>* { 19 | --clr-bg-parent: var(--clr-base); 20 | } 21 | 22 | & a { 23 | color: inherit; 24 | display: flex; 25 | align-items: center; 26 | justify-content: center; 27 | text-decoration: none; 28 | 29 | &:hover, 30 | &:focus, 31 | &:focus-visible { 32 | color: var(--clr-gold); 33 | } 34 | } 35 | 36 | & #left { 37 | display: flex; 38 | flex: 1; 39 | 40 | gap: var(--space-xs-s); 41 | align-items: center; 42 | justify-content: space-between; 43 | 44 | & .icon { 45 | height: 100%; 46 | width: unset; 47 | aspect-ratio: 1; 48 | transition: scale var(--animate-t) var(--animate-f); 49 | } 50 | } 51 | 52 | & nav { 53 | display: flex; 54 | gap: var(--space-s-l); 55 | flex: 2; 56 | justify-content: flex-end; 57 | align-items: center; 58 | 59 | & li { 60 | display: flex; 61 | align-items: center; 62 | justify-content: center; 63 | list-style: none; 64 | 65 | & a { 66 | border-radius: var(--space-3xs); 67 | 68 | &:hover, 69 | &:focus, 70 | &:focus-visible { 71 | & .icon { 72 | scale: 0.7; 73 | } 74 | } 75 | } 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /public/styles/components/hero.css: -------------------------------------------------------------------------------- 1 | .hero { 2 | display: grid; 3 | 4 | gap: var(--space-s-l); 5 | 6 | align-items: center; 7 | } 8 | 9 | .hero .text { 10 | display: flex; 11 | 12 | flex-direction: column; 13 | 14 | justify-content: center; 15 | margin-block-end: var(--space-s-l); 16 | 17 | &>*+* { 18 | margin-block-start: var(--space-xs-s); 19 | } 20 | 21 | & .subtitle { 22 | text-transform: uppercase; 23 | font-size: var(--fs-100); 24 | color: var(--clr-pine); 25 | font-weight: 700; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /public/styles/components/icon.css: -------------------------------------------------------------------------------- 1 | .icon { 2 | --animate-t: 0.5s; 3 | --animate-f: ease; 4 | 5 | display: inline-block; 6 | 7 | color: var(--clr-text); 8 | 9 | font-size: inherit; 10 | 11 | width: 1em; 12 | height: 1em; 13 | 14 | contain: strict; 15 | 16 | box-sizing: content-box !important; 17 | 18 | & .content { 19 | color: inherit; 20 | 21 | height: inherit; 22 | width: inherit; 23 | 24 | stroke: currentColor; 25 | font-size: inherit; 26 | fill: currentColor; 27 | 28 | & * { 29 | display: block; 30 | font-size: inherit; 31 | 32 | height: 100%; 33 | width: 100%; 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /public/styles/components/index.css: -------------------------------------------------------------------------------- 1 | @import "call_to_action.css"; 2 | @import "card.css"; 3 | @import "fancy.css"; 4 | @import "footer.css"; 5 | @import "header.css"; 6 | @import "icon.css"; 7 | @import "logo.css"; 8 | @import "search.css"; 9 | @import "tags.css"; 10 | -------------------------------------------------------------------------------- /public/styles/components/logo.css: -------------------------------------------------------------------------------- 1 | .logo { 2 | border-radius: var(--space-3xs); 3 | height: var(--space-l); 4 | 5 | background-color: var(--clr-bg); 6 | color: var(--clr-fg); 7 | box-shadow: 8 | inset 0 0 0 2px var(--clr-bg), 9 | inset 0 0 0 4px var(--clr-fg); 10 | 11 | & .icon { 12 | color: var(--clr-fg); 13 | stroke-width: 60px; 14 | stroke: currentColor; 15 | scale: 1.4; 16 | transition: unset; 17 | } 18 | 19 | &:hover, 20 | &:focus, 21 | &:focus-visible { 22 | outline-offset: -2px; 23 | outline: var(--clr-foam) solid 2px; 24 | } 25 | } 26 | 27 | /* bg should always be light, fg always dark */ 28 | @media (prefers-color-scheme: light) { 29 | .logo { 30 | --clr-bg: var(--clr-bg-parent, var(--clr-base)); 31 | --clr-fg: var(--clr-text); 32 | 33 | box-shadow: inset 0 0 0 2px var(--clr-fg); 34 | } 35 | } 36 | 37 | @media (prefers-color-scheme: dark) { 38 | .logo { 39 | --clr-bg: var(--clr-text); 40 | --clr-fg: var(--clr-bg-parent, var(--clr-base)); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /public/styles/components/search.css: -------------------------------------------------------------------------------- 1 | #search { 2 | --width: calc(8 * var(--space-l)); 3 | --height: var(--space-l); 4 | 5 | font-size: var(--fs-200); 6 | 7 | & #search-bar { 8 | display: flex; 9 | flex-direction: column; 10 | align-items: center; 11 | 12 | & input { 13 | width: var(--width); 14 | height: var(--height); 15 | 16 | border-radius: var(--space-3xs); 17 | 18 | padding-block: var(--space-3xs); 19 | padding-inline: var(--space-xs); 20 | 21 | background: var(--clr-text); 22 | color: var(--clr-base); 23 | 24 | &::placeholder { 25 | color: var(--clr-base); 26 | opacity: 1; 27 | } 28 | } 29 | } 30 | 31 | & #search-results { 32 | width: var(--width); 33 | } 34 | } 35 | 36 | #search-results-content { 37 | --max-count: 5; 38 | 39 | z-index: 1; 40 | 41 | width: calc(1 * var(--width)); 42 | max-height: calc(var(--max-count) * var(--height)); 43 | 44 | overflow-x: hidden; 45 | overflow-y: auto; 46 | 47 | position: absolute; 48 | transform: translateY(var(--height)); 49 | 50 | border-radius: var(--space-3xs); 51 | 52 | background: var(--clr-surface); 53 | box-shadow: inset 0 0 0 1px var(--clr-high); 54 | 55 | & li { 56 | display: block; 57 | width: 100%; 58 | position: relative; 59 | 60 | & button { 61 | cursor: pointer; 62 | width: inherit; 63 | height: var(--height); 64 | text-overflow: ellipsis; 65 | overflow: hidden; 66 | white-space: nowrap; 67 | 68 | padding-block: var(--space-3xs); 69 | padding-inline: var(--space-xs); 70 | 71 | text-align: start; 72 | color: inherit; 73 | background: inherit; 74 | border-radius: var(--space-3xs); 75 | 76 | &:hover, 77 | &:focus, 78 | &:focus-visible { 79 | background: var(--clr-overlay); 80 | } 81 | } 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /public/styles/components/tags.css: -------------------------------------------------------------------------------- 1 | .tags { 2 | margin-block-start: var(--space-s); 3 | list-style: none; 4 | display: flex; 5 | flex-wrap: wrap; 6 | gap: var(--space-2xs); 7 | 8 | & .tag { 9 | font-size: var(--fs-200); 10 | background-color: var(--clr-rose); 11 | color: var(--_clr); 12 | border-radius: var(--space-s); 13 | padding-block: var(--space-3xs); 14 | padding-inline: var(--space-xs); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /public/styles/main.css: -------------------------------------------------------------------------------- 1 | @import "abstract/index.css"; 2 | @import "base/index.css" layer(base); 3 | @import "components/index.css" layer(components); 4 | @import "pages/index.css" layer(pages); 5 | -------------------------------------------------------------------------------- /public/styles/pages/blog.css: -------------------------------------------------------------------------------- 1 | .blog-content { 2 | background: var(--clr-surface); 3 | 4 | border-radius: var(--space-3xs-2xs); 5 | 6 | padding-block: var(--space-m-l); 7 | padding-inline: var(--space-xs-xl); 8 | 9 | margin-block: var(--space-m-l); 10 | margin-inline: max(calc((100% - var(--max-width)) / 2), var(--space-xs-m)); 11 | 12 | /* margin spacing */ 13 | &>*+* { 14 | margin-block-start: var(--space-m-l); 15 | } 16 | 17 | &>button { 18 | margin-block-start: var(--space-l-xl); 19 | } 20 | 21 | & .date { 22 | margin-block-start: var(--space-3xs-2xs); 23 | text-align: center; 24 | font-family: var(--ff-heading); 25 | font-size: var(--fs-200); 26 | } 27 | 28 | & h1 { 29 | margin-block-start: var(--space-3xs-2xs); 30 | text-align: center; 31 | } 32 | 33 | & h1:after { 34 | --_width: 60; 35 | --_transx: calc((100 / (2 * var(--_width))) - 0.5); 36 | 37 | width: calc(var(--_width) * 1%); 38 | transform: translateX(calc(var(--_transx) * 100%)); 39 | 40 | content: ' '; 41 | margin-block-start: var(--space-m-l); 42 | display: block; 43 | border: 1px solid var(--clr-text); 44 | border-radius: var(--space-3xs); 45 | } 46 | 47 | & h2, 48 | & h3 { 49 | margin-block-start: var(--space-l-xl); 50 | } 51 | 52 | & ul, 53 | & ol { 54 | list-style-position: inside; 55 | 56 | &>li { 57 | margin-inline-start: var(--space-m); 58 | } 59 | 60 | &>*+li { 61 | margin-block-start: var(--space-2xs); 62 | } 63 | } 64 | 65 | & p>code { 66 | color: var(--clr-foam); 67 | font-style: italic; 68 | } 69 | 70 | & pre { 71 | padding: var(--space-xs); 72 | background: var(--clr-overlay); 73 | font-size: var(--fs-200); 74 | overflow-x: auto; 75 | 76 | &>code { 77 | word-wrap: break-word; 78 | } 79 | } 80 | 81 | & blockquote { 82 | border-inline-start: var(--space-3xs) solid var(--clr-high); 83 | padding-inline-start: var(--space-3xs); 84 | } 85 | 86 | .explore-footer { 87 | width: 100%; 88 | display: flex; 89 | align-items: center; 90 | justify-content: center; 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /public/styles/pages/explore.css: -------------------------------------------------------------------------------- 1 | .blog-cards { 2 | display: flex; 3 | flex-direction: column; 4 | gap: var(--space-m-l); 5 | align-items: center; 6 | margin-inline: max(calc((100% - var(--max-width)) / 2), var(--space-xs-m)); 7 | padding-block: var(--space-m-l); 8 | } 9 | 10 | .blog-card { 11 | width: 100%; 12 | 13 | & a { 14 | --_clr: var(--clr-surface); 15 | 16 | background: var(--_clr); 17 | border-radius: var(--space-s); 18 | color: var(--clr-text); 19 | display: flex; 20 | flex-direction: column; 21 | padding: var(--space-m); 22 | text-decoration: none; 23 | transition: transform 0.2s cubic-bezier(0, 0, .2, 1); 24 | 25 | &:hover, 26 | &:focus, 27 | &:focus-visible { 28 | --_clr: var(--clr-muted); 29 | transform: translateY(calc(-1 * var(--space-3xs))); 30 | } 31 | 32 | & p { 33 | margin-block-start: var(--space-xs); 34 | } 35 | 36 | & span { 37 | margin-block-start: var(--space-s); 38 | width: fit-content; 39 | color: var(--clr-iris); 40 | display: block; 41 | 42 | &::after { 43 | content: " →"; 44 | white-space: pre; 45 | } 46 | 47 | &:hover, 48 | &:focus, 49 | &:focus-visible { 50 | text-decoration-line: underline; 51 | } 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /public/styles/pages/home.css: -------------------------------------------------------------------------------- 1 | .home { 2 | &>* { 3 | padding-block: var(--space-l-2xl); 4 | padding-inline: var(--padding-inline-m) 5 | } 6 | 7 | & .hero-section { 8 | display: grid; 9 | 10 | gap: var(--space-s-l); 11 | 12 | align-items: center; 13 | 14 | & .text { 15 | display: flex; 16 | 17 | flex-direction: column; 18 | gap: var(--space-s-m); 19 | justify-content: center; 20 | 21 | &:not(:last-child) { 22 | margin-block-start: var(--space-xs-s); 23 | } 24 | 25 | & .subtitle { 26 | text-transform: uppercase; 27 | font-size: var(--fs-100); 28 | color: var(--clr-pine); 29 | font-weight: 700; 30 | } 31 | } 32 | } 33 | 34 | & .blog-section { 35 | padding-inline: var(--padding-inline-l); 36 | padding-block-start: calc(var(--space-l-2xl) / 2); 37 | 38 | background: var(--clr-overlay); 39 | 40 | display: flex; 41 | flex-direction: column; 42 | gap: calc(var(--space-l-2xl) / 2); 43 | align-items: center; 44 | 45 | & header { 46 | width: 100%; 47 | display: flex; 48 | justify-content: space-between; 49 | flex-wrap: wrap; 50 | column-gap: var(--space-3xl); 51 | row-gap: var(--space-m); 52 | 53 | & a { 54 | color: var(--clr-text); 55 | margin-block: auto; 56 | text-decoration: none; 57 | font-size: var(--fs-200); 58 | 59 | &:hover, 60 | &:focus, 61 | &:focus-visible { 62 | color: var(--clr-gold); 63 | } 64 | } 65 | } 66 | 67 | & section { 68 | --grid-gutter: var(--space-s-l, clamp(1.125rem, 0.6467rem + 2.3913vw, 2.5rem)); 69 | --content-width: calc(4 * var(--space-xl)); 70 | 71 | width: 100%; 72 | display: grid; 73 | grid-template-columns: repeat(var(--grid-columns), 1fr); 74 | grid-gap: var(--grid-gutter); 75 | } 76 | } 77 | } 78 | 79 | 80 | :root { 81 | --grid-columns: 4; 82 | } 83 | 84 | .blog-section .card { 85 | grid-column-end: span 4; 86 | } 87 | 88 | @media (min-width: 700px) { 89 | :root { 90 | --grid-columns: 12; 91 | } 92 | 93 | .blog-section .card { 94 | grid-column-end: span 6; 95 | } 96 | 97 | .blog-section .card:nth-child(3) { 98 | display: none; 99 | } 100 | } 101 | 102 | @media (min-width: 1200px) { 103 | .blog-section .card { 104 | grid-column-end: span 4; 105 | } 106 | 107 | .blog-section .card:nth-child(3) { 108 | display: unset; 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /public/styles/pages/index.css: -------------------------------------------------------------------------------- 1 | @import "blog.css"; 2 | @import "explore.css"; 3 | @import "home.css"; 4 | -------------------------------------------------------------------------------- /public/svg/logo-github.svg: -------------------------------------------------------------------------------- 1 | 2 | GitHub 3 | The remote source control company. 4 | 5 | 6 | -------------------------------------------------------------------------------- /public/svg/logo-twitter.svg: -------------------------------------------------------------------------------- 1 | 2 | Twitter logo 3 | Looks like a bird. Used to send short messages to others. 4 | 5 | 6 | -------------------------------------------------------------------------------- /public/svg/mail.svg: -------------------------------------------------------------------------------- 1 | 2 | E-Mail symbol 3 | An image of the email symbol. 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/components/blog.re: -------------------------------------------------------------------------------- 1 | open Tyxml; 2 | 3 | let component = (blog: Turso.blog) => 4 |
5 |
{Html.txt(blog.date)}
6 | {blog.content |> Omd.of_string |> Omd.to_html |> Html.Unsafe.data} 7 |
8 |
; 9 | -------------------------------------------------------------------------------- /src/components/blog_cards.re: -------------------------------------------------------------------------------- 1 | open Tyxml; 2 | 3 | let blog_card = (blog: Turso.blog) => 4 | ; 25 | 26 | let component = blogs => 27 |
28 | ...{List.map(blog_card, blogs)} 29 |
; 30 | -------------------------------------------------------------------------------- /src/components/blog_section.re: -------------------------------------------------------------------------------- 1 | open Tyxml; 2 | 3 | let blog_card = (blog: Turso.blog) => 4 | ; 17 | 18 | let component = blogs => 19 |
20 |
21 |

"Latest Blogs"

22 | 28 | "See All" 29 | 30 |
31 |
32 | ...{List.map(blog_card, blogs)} 33 |
34 |
; 35 | -------------------------------------------------------------------------------- /src/components/call_to_action.re: -------------------------------------------------------------------------------- 1 | open Tyxml; 2 | 3 | let createElement = () => { 4 | 13 | "Explore More" 14 | ; 15 | }; 16 | -------------------------------------------------------------------------------- /src/components/doc.re: -------------------------------------------------------------------------------- 1 | open Tyxml; 2 | 3 | let createElement = (~title: string, ~children, ()) => { 4 | let description = 5 | ; 9 | 10 | let fonts = ; 11 | 12 | let htmx =