├── .gitattributes ├── demo ├── web │ ├── sf.jpg │ ├── paris.jpg │ ├── space.jpg │ ├── beijing.jpg │ ├── city.css │ └── hello.css ├── makefile ├── go.mod ├── app.yaml ├── app │ ├── main.go │ ├── hello.go │ └── city.go ├── README.md ├── .gcloudignore ├── server │ └── main.go └── go.sum ├── luck ├── web │ ├── bg.jpg │ ├── icon.png │ └── luck.css ├── app │ ├── main.go │ ├── home.go │ ├── select.go │ └── game.go ├── makefile ├── go.mod ├── app.yaml ├── README.md ├── .gcloudignore ├── server │ └── main.go └── go.sum ├── hello ├── makefile ├── go.mod ├── main.go ├── README.md └── go.sum ├── docs ├── makefile ├── go.mod ├── main.go ├── manifest.json ├── go.sum ├── app-worker.js ├── index.html ├── app.js ├── app.css └── wasm_exec.js ├── README.md ├── hello-gcloud-func ├── infra │ ├── google-cloud │ │ └── cors.json │ └── firebase │ │ ├── firebase.json │ │ ├── .gitignore │ │ └── public │ │ └── index2.html ├── go.mod ├── makefile ├── hello.go ├── .gcloudignore ├── .gitignore ├── go.sum └── README.md ├── hello-local-external-root ├── makefile ├── go.mod ├── main.go ├── README.md └── go.sum ├── hello-local ├── makefile ├── go.mod ├── main.go ├── README.md └── go.sum ├── hello-docker ├── makefile ├── go.mod ├── dockerfile ├── main.go ├── go.sum └── README.md ├── hello-gcloud-appengine ├── app.yaml ├── makefile ├── go.mod ├── main.go ├── .gcloudignore ├── go.sum └── README.md ├── .gitignore ├── makefile └── LICENSE /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /demo/web/sf.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxence-charriere/go-app-demo/HEAD/demo/web/sf.jpg -------------------------------------------------------------------------------- /luck/web/bg.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxence-charriere/go-app-demo/HEAD/luck/web/bg.jpg -------------------------------------------------------------------------------- /demo/web/paris.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxence-charriere/go-app-demo/HEAD/demo/web/paris.jpg -------------------------------------------------------------------------------- /demo/web/space.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxence-charriere/go-app-demo/HEAD/demo/web/space.jpg -------------------------------------------------------------------------------- /luck/web/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxence-charriere/go-app-demo/HEAD/luck/web/icon.png -------------------------------------------------------------------------------- /demo/web/beijing.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxence-charriere/go-app-demo/HEAD/demo/web/beijing.jpg -------------------------------------------------------------------------------- /hello/makefile: -------------------------------------------------------------------------------- 1 | build: 2 | GOARCH=wasm GOOS=js go build -o app.wasm 3 | 4 | clean: 5 | @go clean 6 | @-rm app.wasm 7 | -------------------------------------------------------------------------------- /docs/makefile: -------------------------------------------------------------------------------- 1 | build: 2 | go run main.go 3 | @cd ../hello && make build 4 | @cp ../hello/app.wasm ./web/ 5 | 6 | clean: 7 | @go clean 8 | @-rm app.wasm 9 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # go-app-demo 2 | 3 | This repository contains examples using [go-app](https://github.com/maxence-charriere/go-app) package that work on different platforms. 4 | -------------------------------------------------------------------------------- /docs/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/maxence-charriere/go-app-demo/doc 2 | 3 | go 1.14 4 | 5 | require github.com/maxence-charriere/go-app/v7 v7.0.0-20200626193706-6701325e7b21 // indirect 6 | -------------------------------------------------------------------------------- /hello-gcloud-func/infra/google-cloud/cors.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "maxAgeSeconds": 3600, 4 | "method": ["GET", "HEAD"], 5 | "origin": ["*"], 6 | "responseHeader": ["*"] 7 | } 8 | ] 9 | -------------------------------------------------------------------------------- /hello-local-external-root/makefile: -------------------------------------------------------------------------------- 1 | build: 2 | @cd ../hello-local && make build 3 | @go build 4 | 5 | run: build 6 | ./hello-local-external-root 7 | 8 | clean: 9 | @go clean -v 10 | -------------------------------------------------------------------------------- /hello-local/makefile: -------------------------------------------------------------------------------- 1 | build: 2 | @cd ../hello && make build 3 | @cp ../hello/app.wasm . 4 | @go build 5 | 6 | run: build 7 | ./hello-local 8 | 9 | clean: 10 | @go clean -v ./... 11 | @-rm app.wasm 12 | -------------------------------------------------------------------------------- /luck/app/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "github.com/maxence-charriere/go-app/v6/pkg/app" 4 | 5 | func main() { 6 | app.Route("/", &home{}) 7 | app.Route("/select", &gameSelect{}) 8 | app.Run() 9 | } 10 | -------------------------------------------------------------------------------- /demo/makefile: -------------------------------------------------------------------------------- 1 | build: 2 | @GOARCH=wasm GOOS=js go build -o app.wasm ./app 3 | @go build -o demo ./server 4 | 5 | run: build 6 | PORT=8000 ./demo 7 | 8 | deploy: build 9 | gcloud app deploy --project=murlok 10 | 11 | -------------------------------------------------------------------------------- /luck/makefile: -------------------------------------------------------------------------------- 1 | build: 2 | @GOARCH=wasm GOOS=js go build -o app.wasm ./app 3 | @go build -o luck ./server 4 | 5 | run: build 6 | PORT=9000 ./luck 7 | 8 | deploy: build 9 | gcloud app deploy --project=murlok 10 | 11 | -------------------------------------------------------------------------------- /hello-docker/makefile: -------------------------------------------------------------------------------- 1 | build: 2 | @cd ../hello && make build 3 | @cp ../hello/app.wasm . 4 | @docker build -t hello-docker . 5 | 6 | run: build 7 | docker run -p 7000:7000 hello-docker 8 | 9 | clean: 10 | @go clean 11 | @rm app.wasm 12 | -------------------------------------------------------------------------------- /demo/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/maxence-charriere/go-app-demo/demo 2 | 3 | go 1.14 4 | 5 | require ( 6 | github.com/davecgh/go-spew v1.1.1 // indirect 7 | github.com/maxence-charriere/go-app/v6 v6.4.1 8 | gopkg.in/yaml.v2 v2.2.8 // indirect 9 | ) 10 | -------------------------------------------------------------------------------- /luck/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/maxence-charriere/go-app-demo/luck 2 | 3 | go 1.14 4 | 5 | require ( 6 | github.com/davecgh/go-spew v1.1.1 // indirect 7 | github.com/maxence-charriere/go-app/v6 v6.4.1 8 | gopkg.in/yaml.v2 v2.2.8 // indirect 9 | ) 10 | -------------------------------------------------------------------------------- /hello/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/maxence-charriere/go-app-demo/hello 2 | 3 | go 1.14 4 | 5 | require ( 6 | github.com/davecgh/go-spew v1.1.1 // indirect 7 | github.com/maxence-charriere/go-app/v6 v6.4.1 8 | gopkg.in/yaml.v2 v2.2.8 // indirect 9 | ) 10 | -------------------------------------------------------------------------------- /hello-local/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/maxence-charriere/go-app-demo/hello-local 2 | 3 | go 1.14 4 | 5 | require ( 6 | github.com/davecgh/go-spew v1.1.1 // indirect 7 | github.com/maxence-charriere/go-app/v6 v6.4.1 8 | gopkg.in/yaml.v2 v2.2.8 // indirect 9 | ) 10 | -------------------------------------------------------------------------------- /luck/app.yaml: -------------------------------------------------------------------------------- 1 | service: luck 2 | runtime: go113 3 | main: ./server 4 | 5 | instance_class: F1 6 | automatic_scaling: 7 | max_instances: 1 8 | 9 | handlers: 10 | - url: /.* 11 | script: auto 12 | secure: always 13 | redirect_http_response_code: 301 14 | -------------------------------------------------------------------------------- /demo/app.yaml: -------------------------------------------------------------------------------- 1 | service: hello 2 | runtime: go113 3 | main: ./server 4 | 5 | instance_class: F1 6 | automatic_scaling: 7 | max_instances: 1 8 | 9 | handlers: 10 | - url: /.* 11 | script: auto 12 | secure: always 13 | redirect_http_response_code: 301 14 | -------------------------------------------------------------------------------- /hello-docker/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/maxence-charriere/go-app-demo/hello-docker 2 | 3 | go 1.14 4 | 5 | require ( 6 | github.com/davecgh/go-spew v1.1.1 // indirect 7 | github.com/maxence-charriere/go-app/v6 v6.4.1 8 | gopkg.in/yaml.v2 v2.2.8 // indirect 9 | ) 10 | -------------------------------------------------------------------------------- /hello-gcloud-appengine/app.yaml: -------------------------------------------------------------------------------- 1 | runtime: go113 2 | 3 | instance_class: F1 4 | automatic_scaling: 5 | max_instances: 1 6 | 7 | handlers: 8 | # Enforce https: 9 | - url: /.* 10 | script: auto 11 | secure: always 12 | redirect_http_response_code: 301 13 | -------------------------------------------------------------------------------- /hello-gcloud-appengine/makefile: -------------------------------------------------------------------------------- 1 | deploy: 2 | @cd ../hello && make build 3 | @cp ../hello/app.wasm . 4 | @-gcloud app deploy . --project go-app-demo-42 5 | 6 | run: 7 | @gcloud app browse --project=go-app-demo-42 8 | 9 | clean: 10 | @go clean 11 | @-rm app.wasm 12 | -------------------------------------------------------------------------------- /hello-gcloud-func/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/maxence-charriere/go-app/demo/hello-gcloud-func 2 | 3 | go 1.14 4 | 5 | require ( 6 | github.com/davecgh/go-spew v1.1.1 // indirect 7 | github.com/maxence-charriere/go-app/v6 v6.4.1 8 | gopkg.in/yaml.v2 v2.2.8 // indirect 9 | ) 10 | -------------------------------------------------------------------------------- /hello-gcloud-appengine/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/maxence-charriere/go-app-demo/hello-gcloud-appengine 2 | 3 | go 1.14 4 | 5 | require ( 6 | github.com/davecgh/go-spew v1.1.1 // indirect 7 | github.com/maxence-charriere/go-app/v6 v6.4.1 8 | gopkg.in/yaml.v2 v2.2.8 // indirect 9 | ) 10 | -------------------------------------------------------------------------------- /hello-local-external-root/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/maxence-charriere/go-app-demo/hello-local-external-root 2 | 3 | go 1.14 4 | 5 | require ( 6 | github.com/davecgh/go-spew v1.1.1 // indirect 7 | github.com/maxence-charriere/go-app/v6 v6.4.1 8 | gopkg.in/yaml.v2 v2.2.8 // indirect 9 | ) 10 | -------------------------------------------------------------------------------- /hello-gcloud-func/infra/firebase/firebase.json: -------------------------------------------------------------------------------- 1 | { 2 | "hosting": { 3 | "public": "public", 4 | "ignore": ["firebase.json", "**/.*", "**/node_modules/**"], 5 | "rewrites": [ 6 | { 7 | "source": "**", 8 | "function": "Hello" 9 | } 10 | ] 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /demo/app/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/maxence-charriere/go-app/v6/pkg/app" 5 | ) 6 | 7 | const ( 8 | icon = "https://storage.googleapis.com/murlok-github/icon-192.png" 9 | ) 10 | 11 | func main() { 12 | app.Route("/", &hello{}) 13 | app.Route("/city", &city{}) 14 | app.Run() 15 | } 16 | -------------------------------------------------------------------------------- /luck/README.md: -------------------------------------------------------------------------------- 1 | # luck 2 | 3 | [![luck](https://storage.googleapis.com/murlok-github/luck-thumb.png)](https://luck.murlok.io) 4 | 5 | Example that generates lottery numbers. 6 | 7 | ## Contribute 8 | 9 | Help to develop the [go-app](https://github.com/maxence-charriere/go-app) package by becoming a sponsor. 10 |
[Become a sponsor](https://opencollective.com/go-app). 11 | -------------------------------------------------------------------------------- /demo/README.md: -------------------------------------------------------------------------------- 1 | # demo 2 | 3 | [![luck](https://storage.googleapis.com/murlok-github/hello-thumb.png)](https://hello.murlok.io) 4 | 5 | Example that showcases a styled hello world, context menu and navigation. 6 | 7 | ## Contribute 8 | 9 | Help to develop the [go-app](https://github.com/maxence-charriere/go-app) package by becoming a sponsor. 10 |
[Become a sponsor](https://opencollective.com/go-app). 11 | -------------------------------------------------------------------------------- /docs/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/maxence-charriere/go-app/v7/pkg/app" 7 | ) 8 | 9 | func main() { 10 | err := app.GenerateStaticWebsite("", &app.Handler{ 11 | Name: "Github Pages Hello", 12 | Title: "Github Pages Hello", 13 | }) 14 | 15 | if err != nil { 16 | fmt.Println(err) 17 | return 18 | } 19 | 20 | fmt.Println("static website generated") 21 | } 22 | -------------------------------------------------------------------------------- /hello-local/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "net/http" 6 | 7 | "github.com/maxence-charriere/go-app/v6/pkg/app" 8 | ) 9 | 10 | func main() { 11 | fmt.Println("starting local server") 12 | 13 | h := &app.Handler{ 14 | Title: "Hello Demo", 15 | Author: "Maxence Charriere", 16 | } 17 | 18 | if err := http.ListenAndServe(":7000", h); err != nil { 19 | panic(err) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.exe~ 4 | *.dll 5 | *.so 6 | *.dylib 7 | *.wasm 8 | hello-local/hello-local 9 | luck/luck 10 | demo/demo 11 | 12 | # Test binary, built with `go test -c` 13 | *.test 14 | 15 | # Output of the go coverage tool, specifically when used with LiteIDE 16 | *.out 17 | 18 | # Dependency directories (remove the comment below to include it) 19 | # vendor/ 20 | .firebaserc 21 | -------------------------------------------------------------------------------- /hello-docker/dockerfile: -------------------------------------------------------------------------------- 1 | # Set the latest golang base image: 2 | FROM golang:latest 3 | 4 | # Set the Current Working Directory inside the container 5 | WORKDIR /app 6 | 7 | # Build server: 8 | COPY ./ ./ 9 | RUN go mod download 10 | RUN go build -o app . 11 | 12 | # Expose server port: 13 | EXPOSE 7000 14 | 15 | # App version (uncomment if the image is for non-development ends): 16 | # ENV APP_VERSION "YOUR_VERSION" 17 | 18 | # Run the server: 19 | CMD ["./app"] -------------------------------------------------------------------------------- /hello-docker/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "net/http" 6 | "os" 7 | 8 | "github.com/maxence-charriere/go-app/v6/pkg/app" 9 | ) 10 | 11 | func main() { 12 | fmt.Println("starting docker server") 13 | 14 | h := &app.Handler{ 15 | Title: "Hello Demo from Docker", 16 | Author: "Maxence Charriere", 17 | Version: os.Getenv("APP_VERSION"), 18 | } 19 | 20 | if err := http.ListenAndServe(":7000", h); err != nil { 21 | panic(err) 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /hello-local-external-root/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "net/http" 6 | 7 | "github.com/maxence-charriere/go-app/v6/pkg/app" 8 | ) 9 | 10 | func main() { 11 | fmt.Println("starting local server with external root") 12 | 13 | h := &app.Handler{ 14 | Title: "Hello Demo with external root", 15 | Author: "Maxence Charriere", 16 | RootDir: "../hello-local", 17 | } 18 | 19 | if err := http.ListenAndServe(":7000", h); err != nil { 20 | panic(err) 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /hello-gcloud-func/makefile: -------------------------------------------------------------------------------- 1 | deploy: 2 | @cd ../hello && make build 3 | @cp ../hello/app.wasm . 4 | @gsutil \ 5 | -h "Cache-Control:no-cache,max-age=0" \ 6 | -h "Content-Type:application/wasm" \ 7 | cp ./app.wasm gs://goapp-gcloud-function/app.wasm 8 | @gcloud functions deploy Hello --runtime go113 --trigger-http --project go-app-demo-42 9 | @cd ./infra/firebase && firebase deploy 10 | 11 | run: 12 | @open https://go-app-demo-42.firebaseapp.com 13 | 14 | clean: 15 | @go clean 16 | @-rm app.wasm -------------------------------------------------------------------------------- /hello-gcloud-func/hello.go: -------------------------------------------------------------------------------- 1 | package hello 2 | 3 | import ( 4 | "net/http" 5 | "os" 6 | 7 | "github.com/maxence-charriere/go-app/v6/pkg/app" 8 | ) 9 | 10 | var handler = &app.Handler{ 11 | Title: "Hello Google Cloud Function", 12 | Author: "Maxence Charriere", 13 | RootDir: "https://storage.googleapis.com/goapp-gcloud-function", 14 | Version: os.Getenv("K_REVISION"), 15 | } 16 | 17 | // Hello is the Google Cloud Function implementation. 18 | func Hello(w http.ResponseWriter, r *http.Request) { 19 | handler.ServeHTTP(w, r) 20 | } 21 | -------------------------------------------------------------------------------- /docs/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "Github Pages Hello", 3 | "name": "Github Pages Hello", 4 | "icons": [ 5 | { 6 | "src": "https://storage.googleapis.com/murlok-github/icon-192.png", 7 | "type": "image/png", 8 | "sizes": "192x192" 9 | }, 10 | { 11 | "src": "https://storage.googleapis.com/murlok-github/icon-512.png", 12 | "type": "image/png", 13 | "sizes": "512x512" 14 | } 15 | ], 16 | "start_url": "/", 17 | "background_color": "#2d2c2c", 18 | "display": "standalone", 19 | "theme_color": "#2d2c2c" 20 | } 21 | -------------------------------------------------------------------------------- /demo/web/city.css: -------------------------------------------------------------------------------- 1 | .city { 2 | position: fixed; 3 | top: 0; 4 | left: 0; 5 | width: 100%; 6 | height: 100%; 7 | overflow: hidden; 8 | 9 | display: flex; 10 | flex-direction: column; 11 | justify-content: center; 12 | align-items: center; 13 | 14 | background-size: cover; 15 | background-position: center; 16 | } 17 | 18 | .city-title { 19 | font-size: 56pt; 20 | font-weight: 200; 21 | text-align: center; 22 | } 23 | 24 | .city-description { 25 | max-width: 480px; 26 | text-align: justify; 27 | } 28 | 29 | .city-links { 30 | margin-top: 12px; 31 | text-align: center; 32 | } 33 | -------------------------------------------------------------------------------- /hello-gcloud-func/.gcloudignore: -------------------------------------------------------------------------------- 1 | # This file specifies files that are *not* uploaded to Google Cloud Platform 2 | # using gcloud. It follows the same syntax as .gitignore, with the addition of 3 | # "#!include" directives (which insert the entries of the given .gitignore-style 4 | # file at that point). 5 | # 6 | # For more information, run: 7 | # $ gcloud topic gcloudignore 8 | # 9 | .gcloudignore 10 | # If you would like to upload your .git directory, .gitignore file or files 11 | # from your .gitignore file, remove the corresponding line 12 | # below: 13 | .git 14 | .gitignore 15 | 16 | node_modules 17 | #!include:.gitignore 18 | -------------------------------------------------------------------------------- /hello-gcloud-appengine/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "net/http" 5 | "os" 6 | 7 | "github.com/maxence-charriere/go-app/v6/pkg/app" 8 | "github.com/maxence-charriere/go-app/v6/pkg/log" 9 | ) 10 | 11 | func main() { 12 | addr := ":" + os.Getenv("PORT") 13 | version := os.Getenv("GAE_VERSION") 14 | 15 | log.Info("stating app engine server"). 16 | T("addr", addr). 17 | T("version", version). 18 | T("exec", os.Args[0]) 19 | 20 | h := &app.Handler{ 21 | Title: "Hello Google App Engine", 22 | Author: "Maxence Charriere", 23 | Version: version, 24 | } 25 | 26 | if err := http.ListenAndServe(addr, h); err != nil { 27 | panic(err) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /makefile: -------------------------------------------------------------------------------- 1 | update: 2 | @cd hello && \ 3 | go get -u ./... && \ 4 | go mod tidy && \ 5 | GOARCH=wasm GOOS=js go build -o app.wasm 6 | @cd hello-docker && \ 7 | go get -u ./... && \ 8 | go mod tidy 9 | @cd hello-gcloud-appengine && \ 10 | go get -u ./... && \ 11 | go mod tidy 12 | @cd hello-gcloud-func && \ 13 | go get -u ./... && \ 14 | go mod tidy 15 | @cd hello-local && \ 16 | go get -u ./... && \ 17 | go mod tidy 18 | @cd hello-local-external-root && \ 19 | go get -u ./... && \ 20 | go mod tidy 21 | @cd luck && \ 22 | go get -u ./... && \ 23 | go mod tidy 24 | @cd demo && \ 25 | go get -u ./... && \ 26 | go mod tidy 27 | 28 | clean: 29 | @-rm -r */app.wasm 30 | @-rm ./luck/luck 31 | @-rm ./demo/demo 32 | go clean ./... -------------------------------------------------------------------------------- /demo/.gcloudignore: -------------------------------------------------------------------------------- 1 | # This file specifies files that are *not* uploaded to Google Cloud Platform 2 | # using gcloud. It follows the same syntax as .gitignore, with the addition of 3 | # "#!include" directives (which insert the entries of the given .gitignore-style 4 | # file at that point). 5 | # 6 | # For more information, run: 7 | # $ gcloud topic gcloudignore 8 | # 9 | .gcloudignore 10 | # If you would like to upload your .git directory, .gitignore file or files 11 | # from your .gitignore file, remove the corresponding line 12 | # below: 13 | .git 14 | .gitignore 15 | 16 | # Binaries for programs and plugins 17 | *.exe 18 | *.exe~ 19 | *.dll 20 | *.so 21 | *.dylib 22 | # Test binary, build with `go test -c` 23 | *.test 24 | # Output of the go coverage tool, specifically when used with LiteIDE 25 | *.out 26 | demo 27 | app -------------------------------------------------------------------------------- /hello-gcloud-appengine/.gcloudignore: -------------------------------------------------------------------------------- 1 | # This file specifies files that are *not* uploaded to Google Cloud Platform 2 | # using gcloud. It follows the same syntax as .gitignore, with the addition of 3 | # "#!include" directives (which insert the entries of the given .gitignore-style 4 | # file at that point). 5 | # 6 | # For more information, run: 7 | # $ gcloud topic gcloudignore 8 | # 9 | .gcloudignore 10 | # If you would like to upload your .git directory, .gitignore file or files 11 | # from your .gitignore file, remove the corresponding line 12 | # below: 13 | .git 14 | .gitignore 15 | 16 | # Binaries for programs and plugins 17 | *.exe 18 | *.exe~ 19 | *.dll 20 | *.so 21 | *.dylib 22 | # Test binary, build with `go test -c` 23 | *.test 24 | # Output of the go coverage tool, specifically when used with LiteIDE 25 | *.out -------------------------------------------------------------------------------- /luck/.gcloudignore: -------------------------------------------------------------------------------- 1 | # This file specifies files that are *not* uploaded to Google Cloud Platform 2 | # using gcloud. It follows the same syntax as .gitignore, with the addition of 3 | # "#!include" directives (which insert the entries of the given .gitignore-style 4 | # file at that point). 5 | # 6 | # For more information, run: 7 | # $ gcloud topic gcloudignore 8 | # 9 | .gcloudignore 10 | # If you would like to upload your .git directory, .gitignore file or files 11 | # from your .gitignore file, remove the corresponding line 12 | # below: 13 | .git 14 | .gitignore 15 | 16 | # Binaries for programs and plugins 17 | *.exe 18 | *.exe~ 19 | *.dll 20 | *.so 21 | *.dylib 22 | # Test binary, build with `go test -c` 23 | *.test 24 | # Output of the go coverage tool, specifically when used with LiteIDE 25 | *.out 26 | luck 27 | app -------------------------------------------------------------------------------- /hello/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/maxence-charriere/go-app/v6/pkg/app" 5 | ) 6 | 7 | type hello struct { 8 | app.Compo 9 | name string 10 | } 11 | 12 | func (h *hello) Render() app.UI { 13 | return app.Div().Body( 14 | app.Main().Body( 15 | app.H1().Body( 16 | app.Text("Hello, "), 17 | app.If(h.name != "", 18 | app.Text(h.name), 19 | ).Else( 20 | app.Text("World"), 21 | ), 22 | ), 23 | app.Input(). 24 | Value(h.name). 25 | Placeholder("What is your name?"). 26 | AutoFocus(true). 27 | OnChange(h.OnInputChange), 28 | ), 29 | ) 30 | } 31 | 32 | func (h *hello) OnInputChange(src app.Value, e app.Event) { 33 | h.name = src.Get("value").String() 34 | h.Update() 35 | } 36 | 37 | func main() { 38 | app.Route("/", &hello{}) 39 | app.Run() 40 | } 41 | -------------------------------------------------------------------------------- /hello/README.md: -------------------------------------------------------------------------------- 1 | # hello 2 | 3 | hello is a demo that shows how to use the [go-app package](https://github.com/maxence-charriere/go-app) to build a GUI. 4 | 5 | ## Build 6 | 7 | Go to the hello directory: 8 | 9 | ```sh 10 | cd $GOPATH/src/github.com/maxence-charriere/go-app-demo/hello 11 | ``` 12 | 13 | ```sh 14 | GOARCH=wasm GOOS=js go build -o app.wasm 15 | ``` 16 | 17 | Note that `app.wasm` binary requires to be moved at the server location that will serve it. See the other hello examples: 18 | 19 | - [hello-docker](https://github.com/maxence-charriere/go-app-demo/tree/v6/hello-docker) 20 | - [hello-local](https://github.com/maxence-charriere/go-app-demo/tree/v6/hello-local) 21 | 22 | ## Contribute 23 | 24 | Help to develop the [go-app](https://github.com/maxence-charriere/go-app) package by becoming a sponsor. 25 |
[Become a sponsor](https://opencollective.com/go-app). 26 | -------------------------------------------------------------------------------- /docs/go.sum: -------------------------------------------------------------------------------- 1 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 2 | github.com/maxence-charriere/go-app v1.3.6 h1:ivoXYI+Wf11vrmgoew5hcFDw4djFin2HzwBGjs8yV+4= 3 | github.com/maxence-charriere/go-app/v7 v7.0.0-20200626193706-6701325e7b21 h1:X2Eyf6dWw5UvNkMpybZnYMAlq3DJ+w+oxNcJdFGvBfo= 4 | github.com/maxence-charriere/go-app/v7 v7.0.0-20200626193706-6701325e7b21/go.mod h1:JTs+/Taw1Qz+NDOQsA0mQS0QuoEtD7kZuSpviWRKsjk= 5 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 6 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 7 | github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= 8 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 9 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 10 | -------------------------------------------------------------------------------- /demo/web/hello.css: -------------------------------------------------------------------------------- 1 | body { 2 | background-color: black; 3 | color: white; 4 | } 5 | 6 | .hello { 7 | position: fixed; 8 | top: 0; 9 | left: 0; 10 | width: 100%; 11 | height: 100%; 12 | overflow: hidden; 13 | 14 | display: flex; 15 | flex-direction: column; 16 | justify-content: center; 17 | align-items: center; 18 | 19 | background-image: url("/web/space.jpg"); 20 | background-size: cover; 21 | background-position: center; 22 | } 23 | 24 | .hello-title { 25 | max-width: 480px; 26 | font-size: 56pt; 27 | font-weight: 200; 28 | } 29 | 30 | .hello-input { 31 | max-width: 480px; 32 | font-size: 11pt; 33 | font-weight: 200; 34 | } 35 | 36 | .menu-button { 37 | position: fixed; 38 | left: 0; 39 | top: 0; 40 | padding: 12px; 41 | margin: 0 12px 12px; 42 | border: 0; 43 | z-index: 1; 44 | 45 | font-size: 24px; 46 | font-weight: 200; 47 | color: currentColor; 48 | cursor: pointer; 49 | } 50 | -------------------------------------------------------------------------------- /demo/server/main.go: -------------------------------------------------------------------------------- 1 | // +build !wasm 2 | 3 | package main 4 | 5 | import ( 6 | "net/http" 7 | "os" 8 | 9 | "github.com/maxence-charriere/go-app/v6/pkg/app" 10 | "github.com/maxence-charriere/go-app/v6/pkg/log" 11 | ) 12 | 13 | func main() { 14 | port := os.Getenv("PORT") 15 | if port == "" { 16 | port = "7777" 17 | } 18 | 19 | version := os.Getenv("GAE_VERSION") 20 | 21 | log.Info("starting server"). 22 | T("port", port). 23 | T("version", version) 24 | 25 | err := http.ListenAndServe(":"+port, &app.Handler{ 26 | Title: "App Demo", 27 | Styles: []string{ 28 | "/web/hello.css", 29 | "/web/city.css", 30 | }, 31 | CacheableResources: []string{ 32 | "/web/space.jpg", 33 | "/web/beijing.jpg", 34 | "/web/paris.jpg", 35 | "/web/sf.jpg", 36 | }, 37 | // UseMinimalDefaultStyles: true, 38 | Version: version, 39 | }) 40 | 41 | if err != nil { 42 | log.Error("server crashed").T("error", err) 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 maxence-charriere 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 | -------------------------------------------------------------------------------- /luck/server/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "net/http" 5 | "os" 6 | 7 | "github.com/maxence-charriere/go-app/v6/pkg/app" 8 | "github.com/maxence-charriere/go-app/v6/pkg/log" 9 | ) 10 | 11 | func main() { 12 | port := os.Getenv("PORT") 13 | if port == "" { 14 | port = "9000" 15 | } 16 | addr := ":" + port 17 | 18 | version := os.Getenv("GAE_VERSION") 19 | 20 | log.Info("starting server").T("addr", addr) 21 | 22 | if err := http.ListenAndServe(addr, &app.Handler{ 23 | Name: "Luck", 24 | Author: "Maxence Charriere", 25 | Description: "Lottery numbers generator.", 26 | Icon: app.Icon{ 27 | Default: "/web/icon.png", 28 | }, 29 | Keywords: []string{ 30 | "lottery", 31 | "EuroMillions", 32 | "Loto", 33 | "MEGA Millions", 34 | "Powerball", 35 | "SuperLotto Plus", 36 | }, 37 | ThemeColor: "#000000", 38 | BackgroundColor: "#000000", 39 | Styles: []string{ 40 | "/web/luck.css", 41 | }, 42 | CacheableResources: []string{ 43 | "/web/bg.jpg", 44 | }, 45 | Version: version, 46 | }); err != nil { 47 | log.Error("listening and serving http requests failed"). 48 | T("reason", err). 49 | T("addr", addr). 50 | T("version", version). 51 | Panic() 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /hello-local/README.md: -------------------------------------------------------------------------------- 1 | # hello-local 2 | 3 | hello-local is a demo that shows how to run a progressive web app created with the [go-app package](https://github.com/maxence-charriere/go-app) on your local machine. 4 | 5 | ## TLDR 6 | 7 | ```sh 8 | cd $GOPATH/src/github.com/maxence-charriere/go-app-demo/hello-local 9 | make run 10 | ``` 11 | 12 | ## Build and run 13 | 14 | Go to the hello-local directory: 15 | 16 | ```sh 17 | cd $GOPATH/src/github.com/maxence-charriere/go-app-demo/hello-local 18 | ``` 19 | 20 | Make sure the `hello` directory is built: 21 | 22 | ```sh 23 | cd ../hello && make build && cd - 24 | ``` 25 | 26 | Copy the hello wasm binary: 27 | 28 | ```sh 29 | cp ../hello/app.wasm . 30 | ``` 31 | 32 | Build the server: 33 | 34 | ```sh 35 | go build 36 | ``` 37 | 38 | The current directory should look like the following: 39 | 40 | ```sh 41 | # github.com/maxence-charriere/go-app-demo/hello-local 42 | . 43 | ├── README.md 44 | ├── app.wasm 45 | ├── go.mod 46 | ├── go.sum 47 | ├── hello-local 48 | └── main.go 49 | ``` 50 | 51 | Run the server: 52 | 53 | ```sh 54 | ./hello-local 55 | ``` 56 | 57 | ## Contribute 58 | 59 | Help to develop the [go-app](https://github.com/maxence-charriere/go-app) package by becoming a sponsor. 60 |
[Become a sponsor](https://opencollective.com/go-app). 61 | -------------------------------------------------------------------------------- /docs/app-worker.js: -------------------------------------------------------------------------------- 1 | const cacheName = "app-" + "286b5034b87c94b59b608e7ae260f7599b794b79"; 2 | 3 | self.addEventListener("install", event => { 4 | console.log("installing app worker 286b5034b87c94b59b608e7ae260f7599b794b79"); 5 | self.skipWaiting(); 6 | 7 | event.waitUntil( 8 | caches.open(cacheName).then(cache => { 9 | return cache.addAll([ 10 | "/", 11 | "/app.css", 12 | "/app.js", 13 | "/manifest.json", 14 | "/wasm_exec.js", 15 | "/web/app.wasm", 16 | "https://storage.googleapis.com/murlok-github/icon-192.png", 17 | "https://storage.googleapis.com/murlok-github/icon-512.png", 18 | 19 | ]); 20 | }) 21 | ); 22 | }); 23 | 24 | self.addEventListener("activate", event => { 25 | event.waitUntil( 26 | caches.keys().then(keyList => { 27 | return Promise.all( 28 | keyList.map(key => { 29 | if (key !== cacheName) { 30 | return caches.delete(key); 31 | } 32 | }) 33 | ); 34 | }) 35 | ); 36 | console.log("app worker 286b5034b87c94b59b608e7ae260f7599b794b79 is activated"); 37 | }); 38 | 39 | self.addEventListener("fetch", event => { 40 | event.respondWith( 41 | caches.match(event.request).then(response => { 42 | return response || fetch(event.request); 43 | }) 44 | ); 45 | }); 46 | -------------------------------------------------------------------------------- /hello-local-external-root/README.md: -------------------------------------------------------------------------------- 1 | # hello-local-external-root 2 | 3 | hello-local is a demo that shows how to run a progressive web app created with the [go-app package](https://github.com/maxence-charriere/go-app) on your local machine. It use the static files of the `hello-local` directory. 4 | 5 | ## TLDR 6 | 7 | ```sh 8 | cd $GOPATH/src/github.com/maxence-charriere/go-app-demo/hello-local-external-root 9 | make run 10 | ``` 11 | 12 | ## Build and run 13 | 14 | Go to the hello-local-external-root directory: 15 | 16 | ```sh 17 | cd $GOPATH/src/github.com/maxence-charriere/go-app-demo/hello-local-external-root 18 | ``` 19 | 20 | Make sure the `hello-local` directory is built: 21 | 22 | ```sh 23 | cd ../hello-local && make build && cd - 24 | ``` 25 | 26 | Build the server: 27 | 28 | ```sh 29 | go build 30 | ``` 31 | 32 | The current directory should look like the following: 33 | 34 | ```sh 35 | # github.com/maxence-charriere/go-app-demo/hello-local-external-root 36 | . 37 | ├── README.md 38 | ├── go.mod 39 | ├── go.sum 40 | ├── hello-local-external-root 41 | └── main.go 42 | ``` 43 | 44 | Run the server: 45 | 46 | ```sh 47 | ./hello-local-external-root 48 | ``` 49 | 50 | ## Contribute 51 | 52 | Help to develop the [go-app](https://github.com/maxence-charriere/go-app) package by becoming a sponsor. 53 |
[Become a sponsor](https://opencollective.com/go-app). 54 | -------------------------------------------------------------------------------- /hello-gcloud-func/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | firebase-debug.log* 8 | 9 | # Firebase cache 10 | .firebase/ 11 | 12 | # Firebase config 13 | 14 | # Uncomment this if you'd like others to create their own Firebase project. 15 | # For a team working on the same Firebase project(s), it is recommended to leave 16 | # it commented so all members can deploy to the same project(s) in .firebaserc. 17 | # .firebaserc 18 | 19 | # Runtime data 20 | pids 21 | *.pid 22 | *.seed 23 | *.pid.lock 24 | 25 | # Directory for instrumented libs generated by jscoverage/JSCover 26 | lib-cov 27 | 28 | # Coverage directory used by tools like istanbul 29 | coverage 30 | 31 | # nyc test coverage 32 | .nyc_output 33 | 34 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 35 | .grunt 36 | 37 | # Bower dependency directory (https://bower.io/) 38 | bower_components 39 | 40 | # node-waf configuration 41 | .lock-wscript 42 | 43 | # Compiled binary addons (http://nodejs.org/api/addons.html) 44 | build/Release 45 | 46 | # Dependency directories 47 | node_modules/ 48 | 49 | # Optional npm cache directory 50 | .npm 51 | 52 | # Optional eslint cache 53 | .eslintcache 54 | 55 | # Optional REPL history 56 | .node_repl_history 57 | 58 | # Output of 'npm pack' 59 | *.tgz 60 | 61 | # Yarn Integrity file 62 | .yarn-integrity 63 | 64 | # dotenv environment variables file 65 | .env 66 | -------------------------------------------------------------------------------- /hello-gcloud-func/infra/firebase/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | firebase-debug.log* 8 | 9 | # Firebase cache 10 | .firebase/ 11 | 12 | # Firebase config 13 | 14 | # Uncomment this if you'd like others to create their own Firebase project. 15 | # For a team working on the same Firebase project(s), it is recommended to leave 16 | # it commented so all members can deploy to the same project(s) in .firebaserc. 17 | # .firebaserc 18 | 19 | # Runtime data 20 | pids 21 | *.pid 22 | *.seed 23 | *.pid.lock 24 | 25 | # Directory for instrumented libs generated by jscoverage/JSCover 26 | lib-cov 27 | 28 | # Coverage directory used by tools like istanbul 29 | coverage 30 | 31 | # nyc test coverage 32 | .nyc_output 33 | 34 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 35 | .grunt 36 | 37 | # Bower dependency directory (https://bower.io/) 38 | bower_components 39 | 40 | # node-waf configuration 41 | .lock-wscript 42 | 43 | # Compiled binary addons (http://nodejs.org/api/addons.html) 44 | build/Release 45 | 46 | # Dependency directories 47 | node_modules/ 48 | 49 | # Optional npm cache directory 50 | .npm 51 | 52 | # Optional eslint cache 53 | .eslintcache 54 | 55 | # Optional REPL history 56 | .node_repl_history 57 | 58 | # Output of 'npm pack' 59 | *.tgz 60 | 61 | # Yarn Integrity file 62 | .yarn-integrity 63 | 64 | # dotenv environment variables file 65 | .env 66 | -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | Github Pages Hello 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 |
21 | 22 |

23 | Loading 24 |

25 |
26 |
27 | 28 | 29 |
30 | 31 | -------------------------------------------------------------------------------- /demo/go.sum: -------------------------------------------------------------------------------- 1 | github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= 2 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 3 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 4 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 5 | github.com/maxence-charriere/go-app/v6 v6.4.1 h1:YTBbL8qh89efBGm5wCz5J12mOUXKW4AWUxRumkjCApY= 6 | github.com/maxence-charriere/go-app/v6 v6.4.1/go.mod h1:lndc83s6AlsH9LmLR5JsttRzSwUVqOOYi3e/Kd5FfTU= 7 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 8 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 9 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 10 | github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4= 11 | github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= 12 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= 13 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 14 | gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= 15 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 16 | gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= 17 | gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 18 | -------------------------------------------------------------------------------- /hello/go.sum: -------------------------------------------------------------------------------- 1 | github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= 2 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 3 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 4 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 5 | github.com/maxence-charriere/go-app/v6 v6.4.1 h1:YTBbL8qh89efBGm5wCz5J12mOUXKW4AWUxRumkjCApY= 6 | github.com/maxence-charriere/go-app/v6 v6.4.1/go.mod h1:lndc83s6AlsH9LmLR5JsttRzSwUVqOOYi3e/Kd5FfTU= 7 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 8 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 9 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 10 | github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4= 11 | github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= 12 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= 13 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 14 | gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= 15 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 16 | gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= 17 | gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 18 | -------------------------------------------------------------------------------- /luck/go.sum: -------------------------------------------------------------------------------- 1 | github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= 2 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 3 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 4 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 5 | github.com/maxence-charriere/go-app/v6 v6.4.1 h1:YTBbL8qh89efBGm5wCz5J12mOUXKW4AWUxRumkjCApY= 6 | github.com/maxence-charriere/go-app/v6 v6.4.1/go.mod h1:lndc83s6AlsH9LmLR5JsttRzSwUVqOOYi3e/Kd5FfTU= 7 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 8 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 9 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 10 | github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4= 11 | github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= 12 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= 13 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 14 | gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= 15 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 16 | gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= 17 | gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 18 | -------------------------------------------------------------------------------- /hello-docker/go.sum: -------------------------------------------------------------------------------- 1 | github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= 2 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 3 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 4 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 5 | github.com/maxence-charriere/go-app/v6 v6.4.1 h1:YTBbL8qh89efBGm5wCz5J12mOUXKW4AWUxRumkjCApY= 6 | github.com/maxence-charriere/go-app/v6 v6.4.1/go.mod h1:lndc83s6AlsH9LmLR5JsttRzSwUVqOOYi3e/Kd5FfTU= 7 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 8 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 9 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 10 | github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4= 11 | github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= 12 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= 13 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 14 | gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= 15 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 16 | gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= 17 | gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 18 | -------------------------------------------------------------------------------- /hello-local/go.sum: -------------------------------------------------------------------------------- 1 | github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= 2 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 3 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 4 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 5 | github.com/maxence-charriere/go-app/v6 v6.4.1 h1:YTBbL8qh89efBGm5wCz5J12mOUXKW4AWUxRumkjCApY= 6 | github.com/maxence-charriere/go-app/v6 v6.4.1/go.mod h1:lndc83s6AlsH9LmLR5JsttRzSwUVqOOYi3e/Kd5FfTU= 7 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 8 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 9 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 10 | github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4= 11 | github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= 12 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= 13 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 14 | gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= 15 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 16 | gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= 17 | gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 18 | -------------------------------------------------------------------------------- /hello-gcloud-func/go.sum: -------------------------------------------------------------------------------- 1 | github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= 2 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 3 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 4 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 5 | github.com/maxence-charriere/go-app/v6 v6.4.1 h1:YTBbL8qh89efBGm5wCz5J12mOUXKW4AWUxRumkjCApY= 6 | github.com/maxence-charriere/go-app/v6 v6.4.1/go.mod h1:lndc83s6AlsH9LmLR5JsttRzSwUVqOOYi3e/Kd5FfTU= 7 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 8 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 9 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 10 | github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4= 11 | github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= 12 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= 13 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 14 | gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= 15 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 16 | gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= 17 | gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 18 | -------------------------------------------------------------------------------- /hello-gcloud-appengine/go.sum: -------------------------------------------------------------------------------- 1 | github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= 2 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 3 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 4 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 5 | github.com/maxence-charriere/go-app/v6 v6.4.1 h1:YTBbL8qh89efBGm5wCz5J12mOUXKW4AWUxRumkjCApY= 6 | github.com/maxence-charriere/go-app/v6 v6.4.1/go.mod h1:lndc83s6AlsH9LmLR5JsttRzSwUVqOOYi3e/Kd5FfTU= 7 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 8 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 9 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 10 | github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4= 11 | github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= 12 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= 13 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 14 | gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= 15 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 16 | gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= 17 | gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 18 | -------------------------------------------------------------------------------- /hello-local-external-root/go.sum: -------------------------------------------------------------------------------- 1 | github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= 2 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 3 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 4 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 5 | github.com/maxence-charriere/go-app/v6 v6.4.1 h1:YTBbL8qh89efBGm5wCz5J12mOUXKW4AWUxRumkjCApY= 6 | github.com/maxence-charriere/go-app/v6 v6.4.1/go.mod h1:lndc83s6AlsH9LmLR5JsttRzSwUVqOOYi3e/Kd5FfTU= 7 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 8 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 9 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 10 | github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4= 11 | github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= 12 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= 13 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 14 | gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= 15 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 16 | gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= 17 | gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 18 | -------------------------------------------------------------------------------- /hello-docker/README.md: -------------------------------------------------------------------------------- 1 | # hello-docker 2 | 3 | hello-docker is a demo that shows how to deploy a progressive web app created with the [app package](https://github.com/maxence-charriere/go-app) in a Docker container. 4 | 5 | ## Prerequisites 6 | 7 | - [Docker](https://www.docker.com) installed on your machine 8 | - A [Docker file](https://github.com/maxence-charriere/go-app-demo/tree/v6/hello-docker/dockerfile) 9 | 10 | ## TLDR 11 | 12 | ```sh 13 | cd $GOPATH/src/github.com/maxence-charriere/go-app-demo/hello-docker 14 | make run 15 | ``` 16 | 17 | ## Build and run Docker contrainer 18 | 19 | Go to the hello-docker directory: 20 | 21 | ```sh 22 | cd $GOPATH/src/github.com/maxence-charriere/go-app-demo/hello-docker 23 | ``` 24 | 25 | Make sure the `hello` directory is built: 26 | 27 | ```sh 28 | cd ../hello && make build && cd - 29 | ``` 30 | 31 | Copy the hello wasm binary: 32 | 33 | ```sh 34 | cp ../hello/app.wasm . 35 | ``` 36 | 37 | The current directory should look like the following: 38 | 39 | ```sh 40 | # github.com/maxence-charriere/go-app-demo/hello-docker 41 | . 42 | ├── README.md 43 | ├── app.wasm 44 | ├── dockerfile 45 | ├── go.mod 46 | ├── go.sum 47 | └── main.go 48 | ``` 49 | 50 | Build Docker image: 51 | 52 | ```sh 53 | docker build -t hello-docker . 54 | ``` 55 | 56 | Run the Docker container: 57 | 58 | ```sh 59 | docker run -p 7000:7000 hello-docker 60 | ``` 61 | 62 | ## Contribute 63 | 64 | Help to develop the [go-app](https://github.com/maxence-charriere/go-app) package by becoming a sponsor. 65 |
[Become a sponsor](https://opencollective.com/go-app). 66 | -------------------------------------------------------------------------------- /hello-gcloud-appengine/README.md: -------------------------------------------------------------------------------- 1 | # hello-gcloud-appengine 2 | 3 | hello-gcloud-appengine is a demo that shows how to run a progressive web app created with the [app package](https://github.com/maxence-charriere/go-app) on [Google Cloud App Engine](https://cloud.google.com/appengine). 4 | 5 | ## Prerequisites 6 | 7 | - [Google Cloud SDK](https://cloud.google.com/sdk) installed on your machine 8 | - A [Google Cloud project](https://console.cloud.google.com/cloud-resource-manager) 9 | - An [app.yaml file](https://github.com/maxence-charriere/go-app/tree/master/demo/hello-appengine/app.yaml) 10 | 11 | ## Build and deploy 12 | 13 | Go to the hello-gcloud-appengine directory: 14 | 15 | ```sh 16 | cd $GOPATH/src/github.com/maxence-charriere/go-app-demo/hello-gcloud-appengine 17 | ``` 18 | 19 | Make sure the `hello` directory is built: 20 | 21 | ```sh 22 | cd ../hello && make build && cd - 23 | ``` 24 | 25 | Copy the hello wasm binary: 26 | 27 | ```sh 28 | cp ../hello/app.wasm . 29 | ``` 30 | 31 | The current directory should look like the following: 32 | 33 | ```sh 34 | # github.com/maxence-charriere/go-app/demo/hello-appengine 35 | . 36 | ├── README.md 37 | ├── app.wasm 38 | ├── app.yaml 39 | ├── go.mod 40 | ├── go.sum 41 | └── main.go 42 | 43 | ``` 44 | 45 | Deploy on Google Cloud App Engine: 46 | 47 | ```sh 48 | gcloud app deploy . --project YOUR_PROJECT_ID 49 | ``` 50 | 51 | See the [live demo](https://go-app-demo-42.appspot.com/). 52 | 53 | ## Contribute 54 | 55 | Help to develop the [gp-app](https://github.com/maxence-charriere/go-app) package by becoming a sponsor. 56 |
[Become a sponsor](https://opencollective.com/go-app). 57 | -------------------------------------------------------------------------------- /luck/web/luck.css: -------------------------------------------------------------------------------- 1 | body { 2 | background-color: black; 3 | color: white; 4 | } 5 | 6 | .layout { 7 | position: fixed; 8 | top: 0; 9 | left: 0; 10 | width: 100%; 11 | height: 100%; 12 | background-image: url("/web/bg.jpg"); 13 | background-position: center; 14 | background-size: cover; 15 | } 16 | 17 | .background { 18 | position: relative; 19 | width: 100%; 20 | height: 100%; 21 | padding: 0; 22 | overflow-y: auto; 23 | 24 | display: flex; 25 | justify-content: center; 26 | align-items: center; 27 | 28 | background-image: linear-gradient( 29 | rgba(0, 0, 0, 1), 30 | rgba(0, 0, 0, 0.6), 31 | rgba(0, 0, 0, 1) 32 | ); 33 | } 34 | 35 | main { 36 | position: relative; 37 | max-width: 480px; 38 | max-height: 100%; 39 | 40 | display: flex; 41 | flex-direction: column; 42 | align-items: center; 43 | } 44 | 45 | h1 { 46 | font-size: 38px; 47 | text-align: center; 48 | } 49 | 50 | h2 { 51 | padding-bottom: 0; 52 | } 53 | 54 | main input { 55 | font-weight: 200; 56 | font-size: 16pt; 57 | } 58 | 59 | .section { 60 | margin-top: 18px; 61 | } 62 | 63 | .draw { 64 | font-size: 11pt; 65 | font-family: monospace; 66 | word-spacing: 6px; 67 | text-align: justify; 68 | } 69 | 70 | input:hover, 71 | input:focus { 72 | caret-color: #d17f2f; 73 | border-bottom-color: #d17f2f; 74 | } 75 | 76 | a:hover { 77 | color: #d17f2f; 78 | } 79 | 80 | .gamerow { 81 | cursor: pointer; 82 | } 83 | 84 | .gamerow:hover .title { 85 | color: #d17f2f; 86 | } 87 | 88 | .subtitle { 89 | font-size: 0.8em; 90 | color: darkgrey; 91 | } 92 | 93 | .accent { 94 | color: #d17f2f; 95 | } 96 | 97 | .selected { 98 | font-size: 1.2em; 99 | text-align: right; 100 | color: #d17f2f; 101 | } 102 | -------------------------------------------------------------------------------- /demo/app/hello.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/maxence-charriere/go-app/v6/pkg/app" 5 | ) 6 | 7 | type hello struct { 8 | app.Compo 9 | 10 | Name string 11 | } 12 | 13 | func (h *hello) Render() app.UI { 14 | return app.Div().Body( 15 | app.Div(). 16 | Class("menu-button"). 17 | OnClick(h.OnMenuClick). 18 | Body( 19 | app.Text("☰"), 20 | ), 21 | app.Main(). 22 | Class("hello"). 23 | Body( 24 | app.H1(). 25 | Class("hello-title"). 26 | Body( 27 | app.Text("Hello, "), 28 | app.If(h.Name != "", 29 | app.Text(h.Name), 30 | ).Else( 31 | app.Text("World"), 32 | ), 33 | ), 34 | app.Input(). 35 | Class("hello-input"). 36 | Value(h.Name). 37 | Placeholder("What is your name?"). 38 | AutoFocus(true). 39 | OnChange(h.OnInputChange), 40 | ), 41 | ) 42 | } 43 | 44 | func (h *hello) OnMenuClick(src app.Value, e app.Event) { 45 | app.NewContextMenu( 46 | app.MenuItem(). 47 | Label("Reload"). 48 | Keys("cmdorctrl+r"). 49 | OnClick(func(src app.Value, e app.Event) { 50 | app.Reload() 51 | }), 52 | app.MenuItem().Separator(), 53 | app.MenuItem(). 54 | Label("City demo"). 55 | OnClick(func(src app.Value, e app.Event) { 56 | app.Navigate("/city") 57 | }), 58 | app.MenuItem().Separator(), 59 | app.MenuItem(). 60 | Icon(icon). 61 | Label("Go to repository"). 62 | OnClick(func(src app.Value, e app.Event) { 63 | app.Navigate("https://github.com/maxence-charriere/go-app") 64 | }), 65 | app.MenuItem(). 66 | Icon(icon). 67 | Label("Demo sources"). 68 | OnClick(func(src app.Value, e app.Event) { 69 | app.Navigate("https://github.com/maxence-charriere/go-app-demo/tree/v6/demo") 70 | }), 71 | ) 72 | } 73 | 74 | func (h *hello) OnInputChange(src app.Value, e app.Event) { 75 | h.Name = src.Get("value").String() 76 | h.Update() 77 | } 78 | -------------------------------------------------------------------------------- /luck/app/home.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "net/url" 5 | "strconv" 6 | 7 | "github.com/maxence-charriere/go-app/v6/pkg/app" 8 | "github.com/maxence-charriere/go-app/v6/pkg/log" 9 | ) 10 | 11 | type home struct { 12 | app.Compo 13 | 14 | luckyNumber int 15 | games []game 16 | } 17 | 18 | func (h *home) OnNav(u *url.URL) { 19 | if err := app.LocalStorage.Get("luckyNumber", &h.luckyNumber); err != nil { 20 | log.Error("retrieving lucky number failed").T("error", err) 21 | } 22 | 23 | games := gameList(myGames) 24 | sortGameList(games) 25 | h.games = games 26 | h.Update() 27 | } 28 | 29 | func (h *home) Render() app.UI { 30 | return app.Div(). 31 | Class("layout"). 32 | Body( 33 | app.Div(). 34 | Class("background"). 35 | Body( 36 | app.Main().Body( 37 | app.H1().Body( 38 | app.Text("What is your lucky number?"), 39 | ), 40 | app.Input(). 41 | Class("section"). 42 | Type("number"). 43 | Placeholder("Lucky number"). 44 | Value(h.luckyNumber). 45 | OnChange(h.onLuckyNumberChange). 46 | OnKeyup(h.onLuckyNumberChange), 47 | app.Table(). 48 | Class("section"). 49 | Body( 50 | app.Range(h.games).Slice(func(i int) app.UI { 51 | game := h.games[i] 52 | if !game.Enabled { 53 | return nil 54 | } 55 | 56 | return app.Tr().Body( 57 | app.Td().Body( 58 | app.Div().Body(app.Text(game.Name)), 59 | app.Div(). 60 | Class("subtitle accent"). 61 | Body(app.Text(game.Location)), 62 | ), 63 | app.Td(). 64 | Class("draw"). 65 | Body( 66 | app.Text(game.draw(h.luckyNumber)), 67 | ), 68 | ) 69 | }), 70 | ), 71 | app.A(). 72 | Class("app-button section"). 73 | Href("select"). 74 | Body( 75 | app.Text("Select other games"), 76 | ), 77 | ), 78 | ), 79 | ) 80 | } 81 | 82 | func (h *home) onLuckyNumberChange(src app.Value, e app.Event) { 83 | number, _ := strconv.Atoi(src.Get("value").String()) 84 | h.luckyNumber = number 85 | h.Update() 86 | 87 | if err := app.LocalStorage.Set("luckyNumber", number); err != nil { 88 | log.Error("saving lucky number failed").T("error", err) 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /luck/app/select.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "net/url" 5 | 6 | "github.com/maxence-charriere/go-app/v6/pkg/app" 7 | "github.com/maxence-charriere/go-app/v6/pkg/log" 8 | ) 9 | 10 | type gameSelect struct { 11 | app.Compo 12 | 13 | filter string 14 | games []game 15 | } 16 | 17 | func (s *gameSelect) OnNav(u *url.URL) { 18 | s.showGames() 19 | } 20 | 21 | func (s *gameSelect) Render() app.UI { 22 | return app.Div(). 23 | Class("layout"). 24 | Body( 25 | app.Div(). 26 | Class("background"). 27 | Body( 28 | app.Main().Body( 29 | app.H1().Body( 30 | app.Text("Select games"), 31 | ), 32 | app.Input(). 33 | Class("section"). 34 | Type("text"). 35 | Placeholder("Filter"). 36 | Value(s.filter). 37 | OnKeyup(s.onFilterChange). 38 | OnChange(s.onFilterChange), 39 | app.Table(). 40 | Class("section"). 41 | Body( 42 | app.Range(s.games).Slice(func(i int) app.UI { 43 | game := s.games[i] 44 | 45 | return app.Tr(). 46 | Class("gamerow"). 47 | DataSet("id", game.ID). 48 | OnClick(s.onGameSelect). 49 | Body( 50 | app.Td().Body( 51 | app.Div(). 52 | Class("title"). 53 | Body( 54 | app.Text(game.Name), 55 | ), 56 | app.Div(). 57 | Class("subtitle"). 58 | Body( 59 | app.Text(game.Location), 60 | ), 61 | ), 62 | app.Td().Body( 63 | app.If(game.Enabled, 64 | app.Div(). 65 | Class("selected"). 66 | Body( 67 | app.Text("✓"), 68 | ), 69 | ), 70 | ), 71 | ) 72 | }), 73 | ), 74 | app.A(). 75 | Class("app-button section"). 76 | Href("/"). 77 | Body( 78 | app.Text("Done"), 79 | ), 80 | ), 81 | ), 82 | ) 83 | } 84 | 85 | func (s *gameSelect) showGames() { 86 | games := gameList(myGames) 87 | games = filterGameList(games, s.filter) 88 | sortGameList(games) 89 | s.games = games 90 | s.Update() 91 | } 92 | 93 | func (s *gameSelect) onFilterChange(src app.Value, e app.Event) { 94 | filter := src.Get("value").String() 95 | s.filter = filter 96 | s.Update() 97 | 98 | s.showGames() 99 | } 100 | 101 | func (s *gameSelect) onGameSelect(src app.Value, e app.Event) { 102 | id := src.Get("dataset").Get("id").String() 103 | game := myGames[id] 104 | game.Enabled = !game.Enabled 105 | myGames[id] = game 106 | 107 | if err := saveMyGames(myGames); err != nil { 108 | log.Error("saving games failed").T("error", err) 109 | } 110 | 111 | s.Update() 112 | s.showGames() 113 | } 114 | -------------------------------------------------------------------------------- /docs/app.js: -------------------------------------------------------------------------------- 1 | // ----------------------------------------------------------------------------- 2 | // Init service worker 3 | // ----------------------------------------------------------------------------- 4 | if ("serviceWorker" in navigator) { 5 | navigator.serviceWorker 6 | .register("/app-worker.js") 7 | .then(reg => { 8 | console.log("registering app service worker"); 9 | }) 10 | .catch(err => { 11 | console.error("offline service worker registration failed", err); 12 | }); 13 | } 14 | 15 | // ----------------------------------------------------------------------------- 16 | // Init progressive app 17 | // ----------------------------------------------------------------------------- 18 | const goappEnv = {"GOAPP_STATIC_RESOURCES_URL":"","GOAPP_VERSION":"286b5034b87c94b59b608e7ae260f7599b794b79"}; 19 | 20 | function goappGetenv(k) { 21 | return goappEnv[k]; 22 | } 23 | 24 | let deferredPrompt; 25 | 26 | window.addEventListener("beforeinstallprompt", e => { 27 | e.preventDefault(); 28 | deferredPrompt = e; 29 | }); 30 | 31 | // ----------------------------------------------------------------------------- 32 | // Init Web Assembly 33 | // ----------------------------------------------------------------------------- 34 | if (!WebAssembly.instantiateStreaming) { 35 | WebAssembly.instantiateStreaming = async (resp, importObject) => { 36 | const source = await (await resp).arrayBuffer(); 37 | return await WebAssembly.instantiate(source, importObject); 38 | }; 39 | } 40 | 41 | const go = new Go(); 42 | 43 | WebAssembly.instantiateStreaming(fetch("/web/app.wasm"), go.importObject) 44 | .then(result => { 45 | const loaderIcon = document.getElementById("app-wasm-loader-icon"); 46 | loaderIcon.className = "goapp-logo"; 47 | 48 | go.run(result.instance); 49 | }) 50 | .catch(err => { 51 | const loaderIcon = document.getElementById("app-wasm-loader-icon"); 52 | loaderIcon.className = "goapp-logo"; 53 | 54 | const loaderLabel = document.getElementById("app-wasm-loader-label"); 55 | loaderLabel.innerText = err; 56 | 57 | console.error("loading wasm failed: " + err); 58 | }); 59 | 60 | // ----------------------------------------------------------------------------- 61 | // Keep body clean 62 | // ----------------------------------------------------------------------------- 63 | function goappKeepBodyClean() { 64 | const body = document.body; 65 | const bodyChildrenCount = body.children.length; 66 | 67 | const mutationObserver = new MutationObserver(function (mutationList) { 68 | mutationList.forEach((mutation) => { 69 | switch (mutation.type) { 70 | case 'childList': 71 | while (body.children.length > bodyChildrenCount) { 72 | body.removeChild(body.lastChild); 73 | } 74 | break; 75 | } 76 | }); 77 | }); 78 | 79 | mutationObserver.observe(document.body, { 80 | childList: true, 81 | }); 82 | 83 | return () => mutationObserver.disconnect(); 84 | } 85 | -------------------------------------------------------------------------------- /hello-gcloud-func/infra/firebase/public/index2.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Welcome to Firebase Hosting 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 32 | 33 | 34 |
35 |

Welcome

36 |

Firebase Hosting Setup Complete

37 |

You're seeing this because you've successfully setup Firebase Hosting. Now it's time to go build something extraordinary!

38 | Open Hosting Documentation 39 |
40 |

Firebase SDK Loading…

41 | 42 | 64 | 65 | 66 | -------------------------------------------------------------------------------- /docs/app.css: -------------------------------------------------------------------------------- 1 | /* Loader */ 2 | .goapp-app-info { 3 | position: fixed; 4 | top: 0; 5 | left: 0; 6 | width: 100%; 7 | height: 100%; 8 | overflow: hidden; 9 | 10 | display: flex; 11 | flex-direction: column; 12 | justify-content: center; 13 | align-items: center; 14 | 15 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, 16 | Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif; 17 | font-size: 13px; 18 | font-weight: 400; 19 | color: white; 20 | background-color: #2d2c2c; 21 | } 22 | 23 | @media (prefers-color-scheme: light) { 24 | .goapp-app-info { 25 | color: black; 26 | background-color: #f6f6f6; 27 | } 28 | } 29 | 30 | .goapp-logo { 31 | max-width: 100px; 32 | max-height: 100px; 33 | user-select: none; 34 | -moz-user-select: none; 35 | -webkit-user-drag: none; 36 | -webkit-user-select: none; 37 | -ms-user-select: none; 38 | } 39 | 40 | .goapp-label { 41 | margin-top: 12px; 42 | font-size: 21px; 43 | font-weight: 100; 44 | letter-spacing: 1px; 45 | max-width: 480px; 46 | text-align: center; 47 | text-transform: lowercase; 48 | } 49 | 50 | .goapp-spin { 51 | animation: goapp-spin-frames 1.21s infinite linear; 52 | } 53 | 54 | @keyframes goapp-spin-frames { 55 | from { 56 | transform: rotate(0deg); 57 | } 58 | 59 | to { 60 | transform: rotate(360deg); 61 | } 62 | } 63 | 64 | /* Not found */ 65 | 66 | .goapp-notfound-title { 67 | display: flex; 68 | justify-content: center; 69 | align-items: center; 70 | font-size: 65pt; 71 | font-weight: 100; 72 | } 73 | 74 | /* Menu */ 75 | #app-contextmenu-background { 76 | position: fixed; 77 | width: 100%; 78 | height: 100%; 79 | overflow: hidden; 80 | left: 0; 81 | top: 0; 82 | z-index: 42000; 83 | } 84 | 85 | .goapp-contextmenu { 86 | position: fixed; 87 | min-width: 150px; 88 | max-width: 480px; 89 | padding: 6px 0; 90 | border-radius: 6px; 91 | border: solid 1px rgba(255, 255, 255, 0.1); 92 | background-color: rgba(40, 38, 37, 0.97); 93 | color: white; 94 | -webkit-box-shadow: -1px 12px 38px 0px rgba(0, 0, 0, 0.6); 95 | -moz-box-shadow: -1px 12px 38px 0px rgba(0, 0, 0, 0.6); 96 | box-shadow: -1px 12px 38px 0px rgba(0, 0, 0, 0.6); 97 | } 98 | 99 | @media (prefers-color-scheme: light) { 100 | .goapp-contextmenu { 101 | color: black; 102 | background-color: rgba(221, 221, 221, 0.97); 103 | border: solid 1px rgba(0, 0, 0, 0.2); 104 | } 105 | } 106 | 107 | .goapp-contextmenu-hidden { 108 | display: none; 109 | } 110 | 111 | .goapp-contextmenu-visible { 112 | display: block; 113 | } 114 | 115 | .goapp-menuitem { 116 | display: flex; 117 | align-items: center; 118 | padding: 3px 24px; 119 | margin: 0; 120 | border: none; 121 | width: 100%; 122 | cursor: pointer; 123 | outline: inherit; 124 | background-color: transparent; 125 | text-align: left; 126 | color: currentColor; 127 | } 128 | 129 | .goapp-menuitem:hover { 130 | background-color: deepskyblue; 131 | } 132 | 133 | .goapp-menuitem:disabled { 134 | opacity: 0.15; 135 | background-color: transparent; 136 | } 137 | 138 | .goapp-menuitem-separator { 139 | width: 100%; 140 | height: 0; 141 | margin: 6px 0; 142 | border-top: solid 1px rgba(255, 255, 255, 0.1); 143 | } 144 | 145 | @media (prefers-color-scheme: light) { 146 | .goapp-menuitem-separator { 147 | border-top: solid 1px rgba(0, 0, 0, 0.2); 148 | } 149 | } 150 | 151 | .goapp-menuitem-label { 152 | user-select: none; 153 | flex-grow: 1; 154 | } 155 | 156 | .goapp-menuitem-keys { 157 | flex-grow: 0; 158 | margin-left: 12px; 159 | text-transform: capitalize; 160 | } 161 | 162 | .goapp-menuitem-icon { 163 | width: 18px; 164 | height: 18px; 165 | margin-right: 12px; 166 | } 167 | -------------------------------------------------------------------------------- /demo/app/city.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "net/url" 6 | 7 | "github.com/maxence-charriere/go-app/v6/pkg/app" 8 | ) 9 | 10 | type cityData struct { 11 | name string 12 | description string 13 | image string 14 | } 15 | 16 | var cityStore = map[string]cityData{ 17 | "paris": { 18 | name: "Paris", 19 | image: "/web/paris.jpg", 20 | description: ` 21 | Paris, France's capital, is a major European city and a global center for art, 22 | fashion, gastronomy and culture. Its 19th-century cityscape is crisscrossed by 23 | wide boulevards and the River Seine. Beyond such landmarks as the Eiffel Tower 24 | and the 12th-century, Gothic Notre-Dame cathedral, the city is known for its 25 | cafe culture and designer boutiques along the Rue du Faubourg Saint-Honoré. 26 | `, 27 | }, 28 | "sf": { 29 | name: "San Francisco", 30 | image: "/web/sf.jpg", 31 | description: ` 32 | San Francisco, officially City and County of San Francisco and colloquially 33 | known as SF, San Fran or "The City", is a city in—and the cultural, commercial, 34 | and financial center of—Northern California. 35 | `, 36 | }, 37 | "beijing": { 38 | name: "北京市", 39 | image: "/web/beijing.jpg", 40 | description: ` 41 | Beijing, China’s sprawling capital, has history stretching back 3 millennia. Yet 42 | it’s known as much for modern architecture as its ancient sites such as the 43 | grand Forbidden City complex, the imperial palace during the Ming and Qing 44 | dynasties. Nearby, the massive Tiananmen Square pedestrian plaza is the site of 45 | Mao Zedong’s mausoleum and the National Museum of China, displaying a vast 46 | collection of cultural relics. 47 | `, 48 | }, 49 | } 50 | 51 | type city struct { 52 | app.Compo 53 | 54 | data cityData 55 | } 56 | 57 | func (c *city) OnNav(u *url.URL) { 58 | key := u.Query().Get("city") 59 | if key == "" { 60 | key = "paris" 61 | } 62 | 63 | data, ok := cityStore[key] 64 | if !ok { 65 | app.Navigate("/notfound") 66 | return 67 | } 68 | c.data = data 69 | c.Update() 70 | } 71 | 72 | func (c *city) Render() app.UI { 73 | return app.Div(). 74 | Body( 75 | app.Div(). 76 | Class("menu-button"). 77 | OnClick(c.OnMenuClick). 78 | Body( 79 | app.Text("☰"), 80 | ), 81 | app.Main(). 82 | Class("city"). 83 | Style("background-image", fmt.Sprintf("url('%s')", c.data.image)). 84 | Body( 85 | app.H1(). 86 | Class("city-title"). 87 | Body( 88 | app.Text(c.data.name), 89 | ), 90 | app.P(). 91 | Class("city-description"). 92 | Body( 93 | app.Text(c.data.description), 94 | ), 95 | app.P(). 96 | Class("city-links"). 97 | Body( 98 | app.A(). 99 | Class("app-button"). 100 | Href("/city?city=beijing"). 101 | Body( 102 | app.Text(cityStore["beijing"].name), 103 | ), 104 | app.A(). 105 | Class("app-button"). 106 | Href("/city?city=paris"). 107 | Body( 108 | app.Text(cityStore["paris"].name), 109 | ), 110 | app.A(). 111 | Class("app-button"). 112 | Href("/city?city=sf"). 113 | Body( 114 | app.Text(cityStore["sf"].name), 115 | ), 116 | ), 117 | ), 118 | ) 119 | } 120 | 121 | func (c *city) OnMenuClick(src app.Value, e app.Event) { 122 | app.NewContextMenu( 123 | app.MenuItem(). 124 | Label("Reload"). 125 | Keys("cmdorctrl+r"). 126 | OnClick(func(src app.Value, e app.Event) { 127 | app.Reload() 128 | }), 129 | app.MenuItem().Separator(), 130 | app.MenuItem(). 131 | Label("Hello demo"). 132 | OnClick(func(src app.Value, e app.Event) { 133 | app.Navigate("/") 134 | }), 135 | app.MenuItem().Separator(), 136 | app.MenuItem(). 137 | Icon(icon). 138 | Label("Go to repository"). 139 | OnClick(func(src app.Value, e app.Event) { 140 | app.Navigate("https://github.com/maxence-charriere/go-app") 141 | }), 142 | app.MenuItem(). 143 | Icon(icon). 144 | Label("Demo sources"). 145 | OnClick(func(src app.Value, e app.Event) { 146 | app.Navigate("https://github.com/maxence-charriere/go-app-demo/tree/v6/demo") 147 | }), 148 | ) 149 | } 150 | -------------------------------------------------------------------------------- /luck/app/game.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "math/rand" 6 | "sort" 7 | "strings" 8 | 9 | "github.com/maxence-charriere/go-app/v6/pkg/app" 10 | "github.com/maxence-charriere/go-app/v6/pkg/log" 11 | ) 12 | 13 | type game struct { 14 | ID string 15 | Name string 16 | Location string 17 | Enabled bool 18 | NotSorted bool 19 | Numbers []numberConfig 20 | } 21 | 22 | func (g game) draw(n int) string { 23 | r := rand.New(rand.NewSource(int64(n))) 24 | w := strings.Builder{} 25 | 26 | for i, cfg := range g.Numbers { 27 | if i != 0 { 28 | fmt.Fprint(&w, " - ") 29 | } 30 | 31 | numbers := make([]int, 0, cfg.Count) 32 | cache := make(map[int]struct{}, cfg.Count) 33 | 34 | for len(numbers) < cfg.Count { 35 | n := r.Intn(cfg.Max) + 1 36 | if _, ok := cache[n]; ok { 37 | continue 38 | } 39 | cache[n] = struct{}{} 40 | numbers = append(numbers, n) 41 | } 42 | 43 | sort.Ints(numbers) 44 | 45 | for _, n := range numbers { 46 | if n < 10 { 47 | fmt.Fprintf(&w, "0%v ", n) 48 | } else { 49 | fmt.Fprintf(&w, "%v ", n) 50 | } 51 | } 52 | } 53 | 54 | return strings.TrimSpace(w.String()) 55 | } 56 | 57 | type numberConfig struct { 58 | Count int 59 | Max int 60 | } 61 | 62 | var ( 63 | games = map[string]game{ 64 | "powerball": { 65 | ID: "powerball", 66 | Name: "Powerball", 67 | Location: "USA", 68 | Enabled: true, 69 | Numbers: []numberConfig{ 70 | { 71 | Count: 5, 72 | Max: 69, 73 | }, 74 | { 75 | Count: 1, 76 | Max: 26, 77 | }, 78 | }, 79 | }, 80 | "megamillions": { 81 | ID: "megamillions", 82 | Name: "MEGA Millions", 83 | Location: "USA", 84 | Numbers: []numberConfig{ 85 | { 86 | Count: 5, 87 | Max: 70, 88 | }, 89 | { 90 | Count: 1, 91 | Max: 25, 92 | }, 93 | }, 94 | }, 95 | "superlottoplus": { 96 | ID: "superlottoplus", 97 | Name: "SuperLotto Plus", 98 | Location: "USA", 99 | Numbers: []numberConfig{ 100 | { 101 | Count: 5, 102 | Max: 47, 103 | }, 104 | { 105 | Count: 1, 106 | Max: 27, 107 | }, 108 | }, 109 | }, 110 | "fantasy5": { 111 | ID: "fantasy5", 112 | Name: "Fantasy 5", 113 | Location: "USA", 114 | Numbers: []numberConfig{ 115 | { 116 | Count: 5, 117 | Max: 39, 118 | }, 119 | }, 120 | }, 121 | "daily3": { 122 | ID: "daily3", 123 | Name: "Daily 3", 124 | Location: "USA", 125 | NotSorted: true, 126 | Numbers: []numberConfig{ 127 | { 128 | Count: 3, 129 | Max: 9, 130 | }, 131 | }, 132 | }, 133 | "daily4": { 134 | ID: "daily4", 135 | Name: "Daily 4", 136 | Location: "USA", 137 | NotSorted: true, 138 | Numbers: []numberConfig{ 139 | { 140 | Count: 4, 141 | Max: 9, 142 | }, 143 | }, 144 | }, 145 | "euromillions": { 146 | ID: "euromillions", 147 | Name: "EuroMillions", 148 | Location: "Europe", 149 | Enabled: true, 150 | Numbers: []numberConfig{ 151 | { 152 | Count: 5, 153 | Max: 50, 154 | }, 155 | { 156 | Count: 2, 157 | Max: 12, 158 | }, 159 | }, 160 | }, 161 | "loto": { 162 | ID: "loto", 163 | Name: "Loto", 164 | Location: "France", 165 | Numbers: []numberConfig{ 166 | { 167 | Count: 5, 168 | Max: 49, 169 | }, 170 | { 171 | Count: 1, 172 | Max: 10, 173 | }, 174 | }, 175 | }, 176 | "loto6": { 177 | ID: "loto6", 178 | Name: "Loto 6", 179 | Location: "Japan", 180 | Enabled: true, 181 | Numbers: []numberConfig{ 182 | { 183 | Count: 6, 184 | Max: 43, 185 | }, 186 | }, 187 | }, 188 | "loto7": { 189 | ID: "loto7", 190 | Name: "Loto 7", 191 | Location: "Japan", 192 | Numbers: []numberConfig{ 193 | { 194 | Count: 7, 195 | Max: 37, 196 | }, 197 | }, 198 | }, 199 | } 200 | 201 | myGames map[string]game 202 | ) 203 | 204 | func init() { 205 | g, err := loadMyGames() 206 | if err != nil { 207 | g = games 208 | log.Error("loading my games failed").T("error", err) 209 | } 210 | 211 | myGames = g 212 | } 213 | 214 | func loadMyGames() (map[string]game, error) { 215 | myGames := make(map[string]game, len(games)) 216 | if err := app.LocalStorage.Get("myGames", &games); err != nil { 217 | return nil, err 218 | } 219 | 220 | for k := range myGames { 221 | if _, ok := games[k]; !ok { 222 | delete(myGames, k) 223 | } 224 | } 225 | 226 | for k, v := range games { 227 | if old, ok := myGames[k]; ok { 228 | v.Enabled = old.Enabled 229 | } 230 | myGames[k] = v 231 | } 232 | 233 | return myGames, nil 234 | } 235 | 236 | func saveMyGames(g map[string]game) error { 237 | return app.LocalStorage.Set("myGames", g) 238 | } 239 | 240 | func gameList(games map[string]game) []game { 241 | l := make([]game, 0, len(games)) 242 | for _, g := range games { 243 | l = append(l, g) 244 | } 245 | return l 246 | } 247 | 248 | func sortGameList(games []game) { 249 | sort.Slice(games, func(i, j int) bool { 250 | return strings.Compare(games[i].Name, games[j].Name) <= 0 251 | }) 252 | } 253 | 254 | func filterGameList(games []game, filter string) []game { 255 | var f []game 256 | for _, g := range games { 257 | if filter == "" || 258 | strings.Contains( 259 | strings.ToLower(g.Name), 260 | strings.ToLower(filter), 261 | ) || 262 | strings.Contains( 263 | strings.ToLower(g.Location), 264 | strings.ToLower(filter), 265 | ) { 266 | f = append(f, g) 267 | } 268 | } 269 | return f 270 | } 271 | -------------------------------------------------------------------------------- /hello-gcloud-func/README.md: -------------------------------------------------------------------------------- 1 | # hello-gcloud-func 2 | 3 | hello-gcloud-func is a demo that shows how to run a progressive web app created with the [app package](https://github.com/maxence-charriere/go-app) on a [Google Cloud Function](https://cloud.google.com/functions). 4 | 5 | ## Prerequisites 6 | 7 | - Go to the hello-gcloud-function directory: 8 | 9 | ```sh 10 | cd $GOPATH/src/github.com/maxence-charriere/go-app/demo/hello-gcloud-function 11 | ``` 12 | 13 | - [Google Cloud SDK](https://cloud.google.com/sdk) installed on your machine 14 | - A [Google Cloud project](https://console.cloud.google.com/cloud-resource-manager) 15 | - A public [Storage bucket](https://console.cloud.google.com/storage/browser) for hosting static files: 16 | 17 | 1. Create a bucket 18 | 2. Go to `Permissions` tab 19 | 3. Click on `Add members` 20 | 4. In the `New members` input, type `allUsers` 21 | 5. In the `Select a Role` dropdown, choose `Storage/Storage Object Viewer` 22 | 6. Click on `Save` 23 | 7. Click on `ALLOW PUBLIC ACCESS` 24 | 8. In a terminal, set the bucket CORS policies: 25 | 26 | ```sh 27 | gsutil cors set ./infra/google-cloud/cors.json gs://YOUR_BUCKET_NAME 28 | 29 | ``` 30 | 31 | 9. In `hello.go`, replace the handler `RootDir` by your bucket root: 32 | ```go 33 | RootDir: "https://storage.googleapis.com/YOUR_BUCKET_NAME", 34 | ``` 35 | 36 | - [Firebase CLI](https://firebase.google.com/docs/cli) installed on your machine 37 | - Set up Firebase project. Firebase is required in order to have the function operates under a single host and avoid CORS issues. 38 | 39 | 1. Go in `./infra/firebase` 40 | 41 | ```sh 42 | cd ./infra/firebase 43 | 44 | ``` 45 | 46 | 2. Remove all the files 47 | ```sh 48 | rm -r * 49 | ``` 50 | 3. Create a Firebase project: 51 | 52 | ```sh 53 | firebase init 54 | ``` 55 | 56 | When it ask for `associate this project directory with a Firebase project ... Please select an option`, choose either: 57 | 58 | - Add Firebase to an existing Google platform project 59 | - Use an existing project 60 | 61 | See the output below for other questions: 62 | 63 | ``` 64 | hello-gcloud-func/infra/firebase 65 | ▶ firebase init 66 | 67 | ######## #### ######## ######## ######## ### ###### ######## 68 | ## ## ## ## ## ## ## ## ## ## ## 69 | ###### ## ######## ###### ######## ######### ###### ###### 70 | ## ## ## ## ## ## ## ## ## ## ## 71 | ## #### ## ## ######## ######## ## ## ###### ######## 72 | 73 | You're about to initialize a Firebase project in this directory: 74 | 75 | /Users/maxence/dev/src/github.com/maxence-charriere/go-app-demo/hello-gcloud-func/infra/firebase 76 | 77 | ? Which Firebase CLI features do you want to set up for this folder? Press Space to select features, then Enter to confirm your 78 | choices. Hosting: Configure and deploy Firebase Hosting sites 79 | 80 | === Project Setup 81 | 82 | First, let's associate this project directory with a Firebase project. 83 | You can create multiple project aliases by running firebase use --add, 84 | but for now we'll just set up a default project. 85 | 86 | ? Please select an option: Use an existing project 87 | ? Select a default Firebase project for this directory: go-app-demo-42 (go-app-demo) 88 | i Using project go-app-demo-42 (go-app-demo) 89 | 90 | === Hosting Setup 91 | 92 | Your public directory is the folder (relative to your project directory) that 93 | will contain Hosting assets to be uploaded with firebase deploy. If you 94 | have a build process for your assets, use your build's output directory. 95 | 96 | ? What do you want to use as your public directory? public 97 | ? Configure as a single-page app (rewrite all urls to /index.html)? Yes 98 | ✔ Wrote public/index.html 99 | 100 | i Writing configuration info to firebase.json... 101 | i Writing project information to .firebaserc... 102 | i Writing gitignore file to .gitignore... 103 | 104 | ✔ Firebase initialization complete! 105 | ``` 106 | 107 | 4. In `firebase.json`, change the rewrite to make in point to your function: 108 | 109 | ```json 110 | { 111 | "hosting": { 112 | "public": "public", 113 | "ignore": ["firebase.json", "**/.*", "**/node_modules/**"], 114 | "rewrites": [ 115 | { 116 | "source": "**", 117 | "function": "Hello" 118 | } 119 | ] 120 | } 121 | } 122 | ``` 123 | 124 | 5. Rename `./public/index.html` to `./pubic/index2.html` (hack to make firebase redirect `/`): 125 | 126 | ```sh 127 | mv ./public/index.html ./pubic/index2.html 128 | ``` 129 | 130 | ## Build 131 | 132 | 1. Go back to the hello-gcloud-function directory: 133 | 134 | ```sh 135 | cd $GOPATH/src/github.com/maxence-charriere/go-app/demo/hello-gcloud-function 136 | ``` 137 | 138 | 2. Make sure the `hello` directory is built: 139 | 140 | ```sh 141 | cd ../hello && make build && cd - 142 | ``` 143 | 144 | 3. Copy the hello wasm binary: 145 | 146 | ```sh 147 | cp ../hello/app.wasm . 148 | ``` 149 | 150 | ## Deploy 151 | 152 | The current directory should look like the following: 153 | 154 | ```sh 155 | # github.com/maxence-charriere/go-app/demo/hello-gcloud-func 156 | . 157 | ├── README.md 158 | ├── go.mod 159 | ├── go.sum 160 | ├── hello.go 161 | └── infra 162 | ├── firebase 163 | │   ├── firebase.json 164 | │   └── public 165 | │   └── index2.html 166 | └── google-cloud 167 | └── cors.json 168 | ``` 169 | 170 | 1. Deploy `app.wasm` to the Google Cloud bucket: 171 | 172 | ``` 173 | gsutil \ 174 | -h "Cache-Control:no-cache,max-age=0" \ 175 | -h "Content-Type:application/wasm" \ 176 | cp ./app.wasm gs://YOUR_BUCKET_NAME/app.wasm 177 | ``` 178 | 179 | Setting the `Cache-Control` metadata will prevent Google Cloud Storage to store a version of the file for an hour in its cache. Forgetting to set this will make your PWA stuck with the same app.wasm version for a whole hour. 180 | 181 | The Content-Type metadata is to avoid the default `application/octet-stream` given by Google Cloud Storage. Browsers can't load a `.wasm` file if their mime-type is not `application/wasm`. 182 | 183 | 2. Deploy the Google Cloud Function: 184 | 185 | ```sh 186 | gcloud functions deploy Hello --runtime go113 --trigger-http --project YOUR_PROJECT_ID 187 | ``` 188 | 189 | Answer `y` if it asks for: 190 | 191 | ```sh 192 | API [cloudfunctions.googleapis.com] not enabled on project 193 | [xxx]. Would you like to enable and retry (this will take a 194 | few minutes)? (y/N)? 195 | ``` 196 | 197 | Answer `y` if it asks for: 198 | 199 | ```sh 200 | Allow unauthenticated invocations of new function [Hello]? (y/N)? 201 | ``` 202 | 203 | 3. Deploy Firebase project: 204 | 205 | ```sh 206 | cd ./infra/firebase && firebase deploy && cd - 207 | ``` 208 | 209 | ## Firebase 210 | 211 | Trying to reach the function as it is will result in CORS errors. 212 | You need to use [Firebase](https://firebase.google.com) in order to provide the function under a single host. 213 | 214 | The app should be now be accessible at your Firebase project URL available in the [Firebase consoler](https://console.firebase.google.com) in your project hosting section. 215 | 216 | See the [live demo](https://go-app-demo-42.firebaseapp.com/). 217 | 218 | ## Contribute 219 | 220 | Help to develop the [go-app](https://github.com/maxence-charriere/go-app) package by becoming a sponsor. 221 |
[Become a sponsor](https://opencollective.com/go-app). 222 | -------------------------------------------------------------------------------- /docs/wasm_exec.js: -------------------------------------------------------------------------------- 1 | // Copyright 2018 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 | (() => { 6 | // Map multiple JavaScript environments to a single common API, 7 | // preferring web standards over Node.js API. 8 | // 9 | // Environments considered: 10 | // - Browsers 11 | // - Node.js 12 | // - Electron 13 | // - Parcel 14 | 15 | if (typeof global !== "undefined") { 16 | // global already exists 17 | } else if (typeof window !== "undefined") { 18 | window.global = window; 19 | } else if (typeof self !== "undefined") { 20 | self.global = self; 21 | } else { 22 | throw new Error("cannot export Go (neither global, window nor self is defined)"); 23 | } 24 | 25 | if (!global.require && typeof require !== "undefined") { 26 | global.require = require; 27 | } 28 | 29 | if (!global.fs && global.require) { 30 | global.fs = require("fs"); 31 | } 32 | 33 | const enosys = () => { 34 | const err = new Error("not implemented"); 35 | err.code = "ENOSYS"; 36 | return err; 37 | }; 38 | 39 | if (!global.fs) { 40 | let outputBuf = ""; 41 | global.fs = { 42 | constants: { O_WRONLY: -1, O_RDWR: -1, O_CREAT: -1, O_TRUNC: -1, O_APPEND: -1, O_EXCL: -1 }, // unused 43 | writeSync(fd, buf) { 44 | outputBuf += decoder.decode(buf); 45 | const nl = outputBuf.lastIndexOf("\n"); 46 | if (nl != -1) { 47 | console.log(outputBuf.substr(0, nl)); 48 | outputBuf = outputBuf.substr(nl + 1); 49 | } 50 | return buf.length; 51 | }, 52 | write(fd, buf, offset, length, position, callback) { 53 | if (offset !== 0 || length !== buf.length || position !== null) { 54 | callback(enosys()); 55 | return; 56 | } 57 | const n = this.writeSync(fd, buf); 58 | callback(null, n); 59 | }, 60 | chmod(path, mode, callback) { callback(enosys()); }, 61 | chown(path, uid, gid, callback) { callback(enosys()); }, 62 | close(fd, callback) { callback(enosys()); }, 63 | fchmod(fd, mode, callback) { callback(enosys()); }, 64 | fchown(fd, uid, gid, callback) { callback(enosys()); }, 65 | fstat(fd, callback) { callback(enosys()); }, 66 | fsync(fd, callback) { callback(null); }, 67 | ftruncate(fd, length, callback) { callback(enosys()); }, 68 | lchown(path, uid, gid, callback) { callback(enosys()); }, 69 | link(path, link, callback) { callback(enosys()); }, 70 | lstat(path, callback) { callback(enosys()); }, 71 | mkdir(path, perm, callback) { callback(enosys()); }, 72 | open(path, flags, mode, callback) { callback(enosys()); }, 73 | read(fd, buffer, offset, length, position, callback) { callback(enosys()); }, 74 | readdir(path, callback) { callback(enosys()); }, 75 | readlink(path, callback) { callback(enosys()); }, 76 | rename(from, to, callback) { callback(enosys()); }, 77 | rmdir(path, callback) { callback(enosys()); }, 78 | stat(path, callback) { callback(enosys()); }, 79 | symlink(path, link, callback) { callback(enosys()); }, 80 | truncate(path, length, callback) { callback(enosys()); }, 81 | unlink(path, callback) { callback(enosys()); }, 82 | utimes(path, atime, mtime, callback) { callback(enosys()); }, 83 | }; 84 | } 85 | 86 | if (!global.process) { 87 | global.process = { 88 | getuid() { return -1; }, 89 | getgid() { return -1; }, 90 | geteuid() { return -1; }, 91 | getegid() { return -1; }, 92 | getgroups() { throw enosys(); }, 93 | pid: -1, 94 | ppid: -1, 95 | umask() { throw enosys(); }, 96 | cwd() { throw enosys(); }, 97 | chdir() { throw enosys(); }, 98 | } 99 | } 100 | 101 | if (!global.crypto) { 102 | const nodeCrypto = require("crypto"); 103 | global.crypto = { 104 | getRandomValues(b) { 105 | nodeCrypto.randomFillSync(b); 106 | }, 107 | }; 108 | } 109 | 110 | if (!global.performance) { 111 | global.performance = { 112 | now() { 113 | const [sec, nsec] = process.hrtime(); 114 | return sec * 1000 + nsec / 1000000; 115 | }, 116 | }; 117 | } 118 | 119 | if (!global.TextEncoder) { 120 | global.TextEncoder = require("util").TextEncoder; 121 | } 122 | 123 | if (!global.TextDecoder) { 124 | global.TextDecoder = require("util").TextDecoder; 125 | } 126 | 127 | // End of polyfills for common API. 128 | 129 | const encoder = new TextEncoder("utf-8"); 130 | const decoder = new TextDecoder("utf-8"); 131 | 132 | global.Go = class { 133 | constructor() { 134 | this.argv = ["js"]; 135 | this.env = {}; 136 | this.exit = (code) => { 137 | if (code !== 0) { 138 | console.warn("exit code:", code); 139 | } 140 | }; 141 | this._exitPromise = new Promise((resolve) => { 142 | this._resolveExitPromise = resolve; 143 | }); 144 | this._pendingEvent = null; 145 | this._scheduledTimeouts = new Map(); 146 | this._nextCallbackTimeoutID = 1; 147 | 148 | const setInt64 = (addr, v) => { 149 | this.mem.setUint32(addr + 0, v, true); 150 | this.mem.setUint32(addr + 4, Math.floor(v / 4294967296), true); 151 | } 152 | 153 | const getInt64 = (addr) => { 154 | const low = this.mem.getUint32(addr + 0, true); 155 | const high = this.mem.getInt32(addr + 4, true); 156 | return low + high * 4294967296; 157 | } 158 | 159 | const loadValue = (addr) => { 160 | const f = this.mem.getFloat64(addr, true); 161 | if (f === 0) { 162 | return undefined; 163 | } 164 | if (!isNaN(f)) { 165 | return f; 166 | } 167 | 168 | const id = this.mem.getUint32(addr, true); 169 | return this._values[id]; 170 | } 171 | 172 | const storeValue = (addr, v) => { 173 | const nanHead = 0x7FF80000; 174 | 175 | if (typeof v === "number") { 176 | if (isNaN(v)) { 177 | this.mem.setUint32(addr + 4, nanHead, true); 178 | this.mem.setUint32(addr, 0, true); 179 | return; 180 | } 181 | if (v === 0) { 182 | this.mem.setUint32(addr + 4, nanHead, true); 183 | this.mem.setUint32(addr, 1, true); 184 | return; 185 | } 186 | this.mem.setFloat64(addr, v, true); 187 | return; 188 | } 189 | 190 | switch (v) { 191 | case undefined: 192 | this.mem.setFloat64(addr, 0, true); 193 | return; 194 | case null: 195 | this.mem.setUint32(addr + 4, nanHead, true); 196 | this.mem.setUint32(addr, 2, true); 197 | return; 198 | case true: 199 | this.mem.setUint32(addr + 4, nanHead, true); 200 | this.mem.setUint32(addr, 3, true); 201 | return; 202 | case false: 203 | this.mem.setUint32(addr + 4, nanHead, true); 204 | this.mem.setUint32(addr, 4, true); 205 | return; 206 | } 207 | 208 | let id = this._ids.get(v); 209 | if (id === undefined) { 210 | id = this._idPool.pop(); 211 | if (id === undefined) { 212 | id = this._values.length; 213 | } 214 | this._values[id] = v; 215 | this._goRefCounts[id] = 0; 216 | this._ids.set(v, id); 217 | } 218 | this._goRefCounts[id]++; 219 | let typeFlag = 1; 220 | switch (typeof v) { 221 | case "string": 222 | typeFlag = 2; 223 | break; 224 | case "symbol": 225 | typeFlag = 3; 226 | break; 227 | case "function": 228 | typeFlag = 4; 229 | break; 230 | } 231 | this.mem.setUint32(addr + 4, nanHead | typeFlag, true); 232 | this.mem.setUint32(addr, id, true); 233 | } 234 | 235 | const loadSlice = (addr) => { 236 | const array = getInt64(addr + 0); 237 | const len = getInt64(addr + 8); 238 | return new Uint8Array(this._inst.exports.mem.buffer, array, len); 239 | } 240 | 241 | const loadSliceOfValues = (addr) => { 242 | const array = getInt64(addr + 0); 243 | const len = getInt64(addr + 8); 244 | const a = new Array(len); 245 | for (let i = 0; i < len; i++) { 246 | a[i] = loadValue(array + i * 8); 247 | } 248 | return a; 249 | } 250 | 251 | const loadString = (addr) => { 252 | const saddr = getInt64(addr + 0); 253 | const len = getInt64(addr + 8); 254 | return decoder.decode(new DataView(this._inst.exports.mem.buffer, saddr, len)); 255 | } 256 | 257 | const timeOrigin = Date.now() - performance.now(); 258 | this.importObject = { 259 | go: { 260 | // Go's SP does not change as long as no Go code is running. Some operations (e.g. calls, getters and setters) 261 | // may synchronously trigger a Go event handler. This makes Go code get executed in the middle of the imported 262 | // function. A goroutine can switch to a new stack if the current stack is too small (see morestack function). 263 | // This changes the SP, thus we have to update the SP used by the imported function. 264 | 265 | // func wasmExit(code int32) 266 | "runtime.wasmExit": (sp) => { 267 | const code = this.mem.getInt32(sp + 8, true); 268 | this.exited = true; 269 | delete this._inst; 270 | delete this._values; 271 | delete this._goRefCounts; 272 | delete this._ids; 273 | delete this._idPool; 274 | this.exit(code); 275 | }, 276 | 277 | // func wasmWrite(fd uintptr, p unsafe.Pointer, n int32) 278 | "runtime.wasmWrite": (sp) => { 279 | const fd = getInt64(sp + 8); 280 | const p = getInt64(sp + 16); 281 | const n = this.mem.getInt32(sp + 24, true); 282 | fs.writeSync(fd, new Uint8Array(this._inst.exports.mem.buffer, p, n)); 283 | }, 284 | 285 | // func resetMemoryDataView() 286 | "runtime.resetMemoryDataView": (sp) => { 287 | this.mem = new DataView(this._inst.exports.mem.buffer); 288 | }, 289 | 290 | // func nanotime1() int64 291 | "runtime.nanotime1": (sp) => { 292 | setInt64(sp + 8, (timeOrigin + performance.now()) * 1000000); 293 | }, 294 | 295 | // func walltime1() (sec int64, nsec int32) 296 | "runtime.walltime1": (sp) => { 297 | const msec = (new Date).getTime(); 298 | setInt64(sp + 8, msec / 1000); 299 | this.mem.setInt32(sp + 16, (msec % 1000) * 1000000, true); 300 | }, 301 | 302 | // func scheduleTimeoutEvent(delay int64) int32 303 | "runtime.scheduleTimeoutEvent": (sp) => { 304 | const id = this._nextCallbackTimeoutID; 305 | this._nextCallbackTimeoutID++; 306 | this._scheduledTimeouts.set(id, setTimeout( 307 | () => { 308 | this._resume(); 309 | while (this._scheduledTimeouts.has(id)) { 310 | // for some reason Go failed to register the timeout event, log and try again 311 | // (temporary workaround for https://github.com/golang/go/issues/28975) 312 | console.warn("scheduleTimeoutEvent: missed timeout event"); 313 | this._resume(); 314 | } 315 | }, 316 | getInt64(sp + 8) + 1, // setTimeout has been seen to fire up to 1 millisecond early 317 | )); 318 | this.mem.setInt32(sp + 16, id, true); 319 | }, 320 | 321 | // func clearTimeoutEvent(id int32) 322 | "runtime.clearTimeoutEvent": (sp) => { 323 | const id = this.mem.getInt32(sp + 8, true); 324 | clearTimeout(this._scheduledTimeouts.get(id)); 325 | this._scheduledTimeouts.delete(id); 326 | }, 327 | 328 | // func getRandomData(r []byte) 329 | "runtime.getRandomData": (sp) => { 330 | crypto.getRandomValues(loadSlice(sp + 8)); 331 | }, 332 | 333 | // func finalizeRef(v ref) 334 | "syscall/js.finalizeRef": (sp) => { 335 | const id = this.mem.getUint32(sp + 8, true); 336 | this._goRefCounts[id]--; 337 | if (this._goRefCounts[id] === 0) { 338 | const v = this._values[id]; 339 | this._values[id] = null; 340 | this._ids.delete(v); 341 | this._idPool.push(id); 342 | } 343 | }, 344 | 345 | // func stringVal(value string) ref 346 | "syscall/js.stringVal": (sp) => { 347 | storeValue(sp + 24, loadString(sp + 8)); 348 | }, 349 | 350 | // func valueGet(v ref, p string) ref 351 | "syscall/js.valueGet": (sp) => { 352 | const result = Reflect.get(loadValue(sp + 8), loadString(sp + 16)); 353 | sp = this._inst.exports.getsp(); // see comment above 354 | storeValue(sp + 32, result); 355 | }, 356 | 357 | // func valueSet(v ref, p string, x ref) 358 | "syscall/js.valueSet": (sp) => { 359 | Reflect.set(loadValue(sp + 8), loadString(sp + 16), loadValue(sp + 32)); 360 | }, 361 | 362 | // func valueDelete(v ref, p string) 363 | "syscall/js.valueDelete": (sp) => { 364 | Reflect.deleteProperty(loadValue(sp + 8), loadString(sp + 16)); 365 | }, 366 | 367 | // func valueIndex(v ref, i int) ref 368 | "syscall/js.valueIndex": (sp) => { 369 | storeValue(sp + 24, Reflect.get(loadValue(sp + 8), getInt64(sp + 16))); 370 | }, 371 | 372 | // valueSetIndex(v ref, i int, x ref) 373 | "syscall/js.valueSetIndex": (sp) => { 374 | Reflect.set(loadValue(sp + 8), getInt64(sp + 16), loadValue(sp + 24)); 375 | }, 376 | 377 | // func valueCall(v ref, m string, args []ref) (ref, bool) 378 | "syscall/js.valueCall": (sp) => { 379 | try { 380 | const v = loadValue(sp + 8); 381 | const m = Reflect.get(v, loadString(sp + 16)); 382 | const args = loadSliceOfValues(sp + 32); 383 | const result = Reflect.apply(m, v, args); 384 | sp = this._inst.exports.getsp(); // see comment above 385 | storeValue(sp + 56, result); 386 | this.mem.setUint8(sp + 64, 1); 387 | } catch (err) { 388 | storeValue(sp + 56, err); 389 | this.mem.setUint8(sp + 64, 0); 390 | } 391 | }, 392 | 393 | // func valueInvoke(v ref, args []ref) (ref, bool) 394 | "syscall/js.valueInvoke": (sp) => { 395 | try { 396 | const v = loadValue(sp + 8); 397 | const args = loadSliceOfValues(sp + 16); 398 | const result = Reflect.apply(v, undefined, args); 399 | sp = this._inst.exports.getsp(); // see comment above 400 | storeValue(sp + 40, result); 401 | this.mem.setUint8(sp + 48, 1); 402 | } catch (err) { 403 | storeValue(sp + 40, err); 404 | this.mem.setUint8(sp + 48, 0); 405 | } 406 | }, 407 | 408 | // func valueNew(v ref, args []ref) (ref, bool) 409 | "syscall/js.valueNew": (sp) => { 410 | try { 411 | const v = loadValue(sp + 8); 412 | const args = loadSliceOfValues(sp + 16); 413 | const result = Reflect.construct(v, args); 414 | sp = this._inst.exports.getsp(); // see comment above 415 | storeValue(sp + 40, result); 416 | this.mem.setUint8(sp + 48, 1); 417 | } catch (err) { 418 | storeValue(sp + 40, err); 419 | this.mem.setUint8(sp + 48, 0); 420 | } 421 | }, 422 | 423 | // func valueLength(v ref) int 424 | "syscall/js.valueLength": (sp) => { 425 | setInt64(sp + 16, parseInt(loadValue(sp + 8).length)); 426 | }, 427 | 428 | // valuePrepareString(v ref) (ref, int) 429 | "syscall/js.valuePrepareString": (sp) => { 430 | const str = encoder.encode(String(loadValue(sp + 8))); 431 | storeValue(sp + 16, str); 432 | setInt64(sp + 24, str.length); 433 | }, 434 | 435 | // valueLoadString(v ref, b []byte) 436 | "syscall/js.valueLoadString": (sp) => { 437 | const str = loadValue(sp + 8); 438 | loadSlice(sp + 16).set(str); 439 | }, 440 | 441 | // func valueInstanceOf(v ref, t ref) bool 442 | "syscall/js.valueInstanceOf": (sp) => { 443 | this.mem.setUint8(sp + 24, loadValue(sp + 8) instanceof loadValue(sp + 16)); 444 | }, 445 | 446 | // func copyBytesToGo(dst []byte, src ref) (int, bool) 447 | "syscall/js.copyBytesToGo": (sp) => { 448 | const dst = loadSlice(sp + 8); 449 | const src = loadValue(sp + 32); 450 | if (!(src instanceof Uint8Array)) { 451 | this.mem.setUint8(sp + 48, 0); 452 | return; 453 | } 454 | const toCopy = src.subarray(0, dst.length); 455 | dst.set(toCopy); 456 | setInt64(sp + 40, toCopy.length); 457 | this.mem.setUint8(sp + 48, 1); 458 | }, 459 | 460 | // func copyBytesToJS(dst ref, src []byte) (int, bool) 461 | "syscall/js.copyBytesToJS": (sp) => { 462 | const dst = loadValue(sp + 8); 463 | const src = loadSlice(sp + 16); 464 | if (!(dst instanceof Uint8Array)) { 465 | this.mem.setUint8(sp + 48, 0); 466 | return; 467 | } 468 | const toCopy = src.subarray(0, dst.length); 469 | dst.set(toCopy); 470 | setInt64(sp + 40, toCopy.length); 471 | this.mem.setUint8(sp + 48, 1); 472 | }, 473 | 474 | "debug": (value) => { 475 | console.log(value); 476 | }, 477 | } 478 | }; 479 | } 480 | 481 | async run(instance) { 482 | this._inst = instance; 483 | this.mem = new DataView(this._inst.exports.mem.buffer); 484 | this._values = [ // JS values that Go currently has references to, indexed by reference id 485 | NaN, 486 | 0, 487 | null, 488 | true, 489 | false, 490 | global, 491 | this, 492 | ]; 493 | this._goRefCounts = []; // number of references that Go has to a JS value, indexed by reference id 494 | this._ids = new Map(); // mapping from JS values to reference ids 495 | this._idPool = []; // unused ids that have been garbage collected 496 | this.exited = false; // whether the Go program has exited 497 | 498 | // Pass command line arguments and environment variables to WebAssembly by writing them to the linear memory. 499 | let offset = 4096; 500 | 501 | const strPtr = (str) => { 502 | const ptr = offset; 503 | const bytes = encoder.encode(str + "\0"); 504 | new Uint8Array(this.mem.buffer, offset, bytes.length).set(bytes); 505 | offset += bytes.length; 506 | if (offset % 8 !== 0) { 507 | offset += 8 - (offset % 8); 508 | } 509 | return ptr; 510 | }; 511 | 512 | const argc = this.argv.length; 513 | 514 | const argvPtrs = []; 515 | this.argv.forEach((arg) => { 516 | argvPtrs.push(strPtr(arg)); 517 | }); 518 | argvPtrs.push(0); 519 | 520 | const keys = Object.keys(this.env).sort(); 521 | keys.forEach((key) => { 522 | argvPtrs.push(strPtr(`${key}=${this.env[key]}`)); 523 | }); 524 | argvPtrs.push(0); 525 | 526 | const argv = offset; 527 | argvPtrs.forEach((ptr) => { 528 | this.mem.setUint32(offset, ptr, true); 529 | this.mem.setUint32(offset + 4, 0, true); 530 | offset += 8; 531 | }); 532 | 533 | this._inst.exports.run(argc, argv); 534 | if (this.exited) { 535 | this._resolveExitPromise(); 536 | } 537 | await this._exitPromise; 538 | } 539 | 540 | _resume() { 541 | if (this.exited) { 542 | throw new Error("Go program has already exited"); 543 | } 544 | this._inst.exports.resume(); 545 | if (this.exited) { 546 | this._resolveExitPromise(); 547 | } 548 | } 549 | 550 | _makeFuncWrapper(id) { 551 | const go = this; 552 | return function () { 553 | const event = { id: id, this: this, args: arguments }; 554 | go._pendingEvent = event; 555 | go._resume(); 556 | return event.result; 557 | }; 558 | } 559 | } 560 | 561 | if ( 562 | global.require && 563 | global.require.main === module && 564 | global.process && 565 | global.process.versions && 566 | !global.process.versions.electron 567 | ) { 568 | if (process.argv.length < 3) { 569 | console.error("usage: go_js_wasm_exec [wasm binary] [arguments]"); 570 | process.exit(1); 571 | } 572 | 573 | const go = new Go(); 574 | go.argv = process.argv.slice(2); 575 | go.env = Object.assign({ TMPDIR: require("os").tmpdir() }, process.env); 576 | go.exit = process.exit; 577 | WebAssembly.instantiate(fs.readFileSync(process.argv[2]), go.importObject).then((result) => { 578 | process.on("exit", (code) => { // Node.js exits if no event handler is pending 579 | if (code === 0 && !go.exited) { 580 | // deadlock, make Go print error and stack traces 581 | go._pendingEvent = { id: 0 }; 582 | go._resume(); 583 | } 584 | }); 585 | return go.run(result.instance); 586 | }).catch((err) => { 587 | console.error(err); 588 | process.exit(1); 589 | }); 590 | } 591 | })(); 592 | --------------------------------------------------------------------------------