├── src ├── templates │ ├── detail.rs │ ├── mod.rs │ ├── error.rs │ ├── home.rs │ ├── favorites.rs │ ├── goat_list.rs │ └── base.rs ├── utils.rs └── lib.rs ├── .gitignore ├── bin ├── wasm-bindgen └── wasm-bindgen-test-runner ├── pkg ├── the_best_goats.d.ts ├── the_best_goats_bg.wasm ├── package.json ├── README.md └── the_best_goats.js ├── tests └── web.rs ├── metadata.json ├── .appveyor.yml ├── LICENSE ├── Cargo.toml ├── README.md ├── .travis.yml └── index.js /src/templates/detail.rs: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | **/*.rs.bk 3 | Cargo.lock 4 | wasm-pack.log 5 | /TODO.md 6 | -------------------------------------------------------------------------------- /bin/wasm-bindgen: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jRiest/the-best-goats/HEAD/bin/wasm-bindgen -------------------------------------------------------------------------------- /pkg/the_best_goats.d.ts: -------------------------------------------------------------------------------- 1 | /* tslint:disable */ 2 | export function main(arg0: any): any; 3 | 4 | -------------------------------------------------------------------------------- /bin/wasm-bindgen-test-runner: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jRiest/the-best-goats/HEAD/bin/wasm-bindgen-test-runner -------------------------------------------------------------------------------- /pkg/the_best_goats_bg.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jRiest/the-best-goats/HEAD/pkg/the_best_goats_bg.wasm -------------------------------------------------------------------------------- /src/templates/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod base; 2 | pub mod error; 3 | pub mod favorites; 4 | pub mod goat_list; 5 | pub mod home; 6 | -------------------------------------------------------------------------------- /src/templates/error.rs: -------------------------------------------------------------------------------- 1 | pub static ERROR_PAGE: &'static str = r##"{{#*inline "page"}} 2 |

{{error_message}}

3 | {{/inline}} 4 | {{~> (parent)~}}"##; 5 | -------------------------------------------------------------------------------- /src/templates/home.rs: -------------------------------------------------------------------------------- 1 | pub static HOME_PAGE: &'static str = r##"{{#*inline "page"}} 2 |

Featured Goats

3 | {{~> (goat_list_template)~}} 4 | {{/inline}} 5 | {{~> (parent)~}}"##; 6 | -------------------------------------------------------------------------------- /src/templates/favorites.rs: -------------------------------------------------------------------------------- 1 | pub static FAVORITES_PAGE: &'static str = r##"{{#*inline "page"}} 2 |

Favorites

3 | {{#if has_favorites}} 4 | {{~> (goat_list_template)~}} 5 | {{else}} 6 |

No favorites yet :(

7 | {{/if}} 8 | {{/inline}} 9 | {{~> (parent)~}}"##; 10 | -------------------------------------------------------------------------------- /tests/web.rs: -------------------------------------------------------------------------------- 1 | //! Test suite for the Web and headless browsers. 2 | 3 | #![cfg(target_arch = "wasm32")] 4 | 5 | extern crate wasm_bindgen_test; 6 | use wasm_bindgen_test::*; 7 | 8 | wasm_bindgen_test_configure!(run_in_browser); 9 | 10 | #[wasm_bindgen_test] 11 | fn pass() { 12 | assert_eq!(1 + 1, 2); 13 | } 14 | -------------------------------------------------------------------------------- /metadata.json: -------------------------------------------------------------------------------- 1 | { 2 | "body_part": "script", 3 | "bindings": [ 4 | { "name": "WASM", "type": "wasm_module", "part": "wasm" }, 5 | { 6 | "name": "GoatsNs", 7 | "type": "kv_namespace", 8 | "namespace_id": "__id__" 9 | }, 10 | { 11 | "name": "FavoritesNs", 12 | "type": "kv_namespace", 13 | "namespace_id": "__id__" 14 | } 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /pkg/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "the-best-goats", 3 | "collaborators": [ 4 | "Jake Riesterer " 5 | ], 6 | "version": "0.1.0", 7 | "files": [ 8 | "the_best_goats_bg.wasm", 9 | "the_best_goats.js", 10 | "the_best_goats.d.ts" 11 | ], 12 | "module": "the_best_goats.js", 13 | "types": "the_best_goats.d.ts", 14 | "sideEffects": "false" 15 | } -------------------------------------------------------------------------------- /.appveyor.yml: -------------------------------------------------------------------------------- 1 | install: 2 | - appveyor-retry appveyor DownloadFile https://win.rustup.rs/ -FileName rustup-init.exe 3 | - if not defined RUSTFLAGS rustup-init.exe -y --default-host x86_64-pc-windows-msvc --default-toolchain nightly 4 | - set PATH=%PATH%;C:\Users\appveyor\.cargo\bin 5 | - rustc -V 6 | - cargo -V 7 | 8 | build: false 9 | 10 | test_script: 11 | - cargo test --locked 12 | -------------------------------------------------------------------------------- /src/utils.rs: -------------------------------------------------------------------------------- 1 | use cfg_if::cfg_if; 2 | 3 | cfg_if! { 4 | // When the `console_error_panic_hook` feature is enabled, we can call the 5 | // `set_panic_hook` function at least once during initialization, and then 6 | // we will get better error messages if our code ever panics. 7 | // 8 | // For more details see 9 | // https://github.com/rustwasm/console_error_panic_hook#readme 10 | if #[cfg(feature = "console_error_panic_hook")] { 11 | extern crate console_error_panic_hook; 12 | pub use self::console_error_panic_hook::set_once as set_panic_hook; 13 | } else { 14 | #[inline] 15 | pub fn set_panic_hook() {} 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/templates/goat_list.rs: -------------------------------------------------------------------------------- 1 | pub static GOAT_LIST_PARTIAL: &'static str = r##""##; 27 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2018, Cloudflare. All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 4 | 5 | 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 6 | 7 | 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 8 | 9 | 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 10 | 11 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "the-best-goats" 3 | version = "0.1.0" 4 | authors = ["Jake Riesterer "] 5 | 6 | [lib] 7 | crate-type = ["cdylib", "rlib"] 8 | 9 | [features] 10 | default = ["console_error_panic_hook", "wee_alloc"] 11 | 12 | [dependencies] 13 | cfg-if = "0.1.2" 14 | cookie = "0.11.0" 15 | futures = "0.1.25" 16 | handlebars = { version = "1.0.5", features = ["no_dir_source"], default-features = false } 17 | http = "0.1.13" 18 | js-sys = "0.3.2" 19 | lazy_static = "1.1.0" 20 | serde = "1.0.79" 21 | serde_derive = "1.0.79" 22 | serde_json = "1.0.32" 23 | time = "0.1.40" 24 | url = "1.7.1" 25 | wasm-bindgen = { version = "0.2.25", features = ["serde-serialize"] } 26 | wasm-bindgen-futures = "0.3.2" 27 | 28 | # The `console_error_panic_hook` crate provides better debugging of panics by 29 | # logging them with `console.error`. This is great for development, but requires 30 | # all the `std::fmt` and `std::panicking` infrastructure, so isn't great for 31 | # code size when deploying. 32 | console_error_panic_hook = { version = "0.1.1", optional = true } 33 | 34 | # `wee_alloc` is a tiny allocator for wasm that is only ~1K in code size 35 | # compared to the default allocator's ~10K. It is slower than the default 36 | # allocator, however. 37 | # 38 | # Unfortunately, `wee_alloc` requires nightly Rust when targeting wasm for now. 39 | wee_alloc = { version = "0.4.2", optional = true } 40 | 41 | [dependencies.web-sys] 42 | version = "0.3.2" 43 | features = [ 44 | 'FetchEvent', 45 | 'FormData', 46 | 'Headers', 47 | 'Request', 48 | 'ResponseInit', 49 | ] 50 | 51 | [dev-dependencies] 52 | wasm-bindgen-test = "0.2" 53 | 54 | [profile.release] 55 | # Tell `rustc` to optimize for small code size. 56 | opt-level = "s" 57 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # 🦀🕸️ `wasm-pack-template` 4 | 5 | A template for kick starting a Rust and WebAssembly project using 6 | [`wasm-pack`](https://github.com/rustwasm/wasm-pack). 7 | 8 | This template is designed for compiling Rust libraries into WebAssembly and 9 | publishing the resulting package to NPM. 10 | 11 | * Want to use the published NPM package in a Website? [Check out 12 | `create-wasm-app`.](https://github.com/rustwasm/create-wasm-app) 13 | * Want to make a monorepo-style Website without publishing to NPM? Check out 14 | [`rust-webpack-template`](https://github.com/rustwasm/rust-webpack-template) 15 | and/or 16 | [`rust-parcel-template`](https://github.com/rustwasm/rust-parcel-template). 17 | 18 | ## 🔋 Batteries Included 19 | 20 | * [`wasm-bindgen`](https://github.com/rustwasm/wasm-bindgen) for communicating 21 | between WebAssembly and JavaScript. 22 | * [`console_error_panic_hook`](https://github.com/rustwasm/console_error_panic_hook) 23 | for logging panic messages to the developer console. 24 | * [`wee_alloc`](https://github.com/rustwasm/wee_alloc), an allocator optimized 25 | for small code size. 26 | 27 | ## 🚴 Usage 28 | 29 | ### 🐑 Use `cargo generate` to Clone this Template 30 | 31 | [Learn more about `cargo generate` here.](https://github.com/ashleygwilliams/cargo-generate) 32 | 33 | ``` 34 | cargo generate --git https://github.com/rustwasm/wasm-pack-template.git --name my-project 35 | cd my-project 36 | ``` 37 | 38 | ### 🛠️ Build with `wasm-pack build` 39 | 40 | ``` 41 | wasm-pack build 42 | ``` 43 | 44 | ### 🔬 Test in Headless Browsers with `wasm-pack test` 45 | 46 | ``` 47 | wasm-pack test --headless --firefox 48 | ``` 49 | 50 | ### 🎁 Publish to NPM with `wasm-pack publish` 51 | 52 | ``` 53 | wasm-pack publish 54 | ``` 55 | -------------------------------------------------------------------------------- /pkg/README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # 🦀🕸️ `wasm-pack-template` 4 | 5 | A template for kick starting a Rust and WebAssembly project using 6 | [`wasm-pack`](https://github.com/rustwasm/wasm-pack). 7 | 8 | This template is designed for compiling Rust libraries into WebAssembly and 9 | publishing the resulting package to NPM. 10 | 11 | * Want to use the published NPM package in a Website? [Check out 12 | `create-wasm-app`.](https://github.com/rustwasm/create-wasm-app) 13 | * Want to make a monorepo-style Website without publishing to NPM? Check out 14 | [`rust-webpack-template`](https://github.com/rustwasm/rust-webpack-template) 15 | and/or 16 | [`rust-parcel-template`](https://github.com/rustwasm/rust-parcel-template). 17 | 18 | ## 🔋 Batteries Included 19 | 20 | * [`wasm-bindgen`](https://github.com/rustwasm/wasm-bindgen) for communicating 21 | between WebAssembly and JavaScript. 22 | * [`console_error_panic_hook`](https://github.com/rustwasm/console_error_panic_hook) 23 | for logging panic messages to the developer console. 24 | * [`wee_alloc`](https://github.com/rustwasm/wee_alloc), an allocator optimized 25 | for small code size. 26 | 27 | ## 🚴 Usage 28 | 29 | ### 🐑 Use `cargo generate` to Clone this Template 30 | 31 | [Learn more about `cargo generate` here.](https://github.com/ashleygwilliams/cargo-generate) 32 | 33 | ``` 34 | cargo generate --git https://github.com/rustwasm/wasm-pack-template.git --name my-project 35 | cd my-project 36 | ``` 37 | 38 | ### 🛠️ Build with `wasm-pack build` 39 | 40 | ``` 41 | wasm-pack build 42 | ``` 43 | 44 | ### 🔬 Test in Headless Browsers with `wasm-pack test` 45 | 46 | ``` 47 | wasm-pack test --headless --firefox 48 | ``` 49 | 50 | ### 🎁 Publish to NPM with `wasm-pack publish` 51 | 52 | ``` 53 | wasm-pack publish 54 | ``` 55 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: rust 2 | sudo: false 3 | 4 | cache: cargo 5 | 6 | matrix: 7 | include: 8 | 9 | # Builds with wasm-pack. 10 | - rust: beta 11 | env: RUST_BACKTRACE=1 12 | addons: 13 | firefox: latest 14 | chrome: stable 15 | before_script: 16 | - (test -x $HOME/.cargo/bin/cargo-install-update || cargo install cargo-update) 17 | - (test -x $HOME/.cargo/bin/cargo-generate || cargo install --vers "^0.2" cargo-generate) 18 | - cargo install-update -a 19 | - curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh -s -- -f 20 | script: 21 | - cargo generate --git . --name testing 22 | # Having a broken Cargo.toml (in that it has curlies in fields) anywhere 23 | # in any of our parent dirs is problematic. 24 | - mv Cargo.toml Cargo.toml.tmpl 25 | - cd testing 26 | - wasm-pack build 27 | - wasm-pack test --chrome --firefox --headless 28 | 29 | # Builds on nightly. 30 | - rust: nightly 31 | env: RUST_BACKTRACE=1 32 | before_script: 33 | - (test -x $HOME/.cargo/bin/cargo-install-update || cargo install cargo-update) 34 | - (test -x $HOME/.cargo/bin/cargo-generate || cargo install --vers "^0.2" cargo-generate) 35 | - cargo install-update -a 36 | - rustup target add wasm32-unknown-unknown 37 | script: 38 | - cargo generate --git . --name testing 39 | - mv Cargo.toml Cargo.toml.tmpl 40 | - cd testing 41 | - cargo check 42 | - cargo check --target wasm32-unknown-unknown 43 | - cargo check --no-default-features 44 | - cargo check --target wasm32-unknown-unknown --no-default-features 45 | - cargo check --no-default-features --features console_error_panic_hook 46 | - cargo check --target wasm32-unknown-unknown --no-default-features --features console_error_panic_hook 47 | - cargo check --no-default-features --features "console_error_panic_hook wee_alloc" 48 | - cargo check --target wasm32-unknown-unknown --no-default-features --features "console_error_panic_hook wee_alloc" 49 | 50 | # Builds on beta. 51 | - rust: beta 52 | env: RUST_BACKTRACE=1 53 | before_script: 54 | - (test -x $HOME/.cargo/bin/cargo-install-update || cargo install cargo-update) 55 | - (test -x $HOME/.cargo/bin/cargo-generate || cargo install --vers "^0.2" cargo-generate) 56 | - cargo install-update -a 57 | - rustup target add wasm32-unknown-unknown 58 | script: 59 | - cargo generate --git . --name testing 60 | - mv Cargo.toml Cargo.toml.tmpl 61 | - cd testing 62 | - cargo check 63 | - cargo check --target wasm32-unknown-unknown 64 | - cargo check --no-default-features 65 | - cargo check --target wasm32-unknown-unknown --no-default-features 66 | - cargo check --no-default-features --features console_error_panic_hook 67 | - cargo check --target wasm32-unknown-unknown --no-default-features --features console_error_panic_hook 68 | # Note: no enabling the `wee_alloc` feature here because it requires 69 | # nightly for now. 70 | -------------------------------------------------------------------------------- /src/templates/base.rs: -------------------------------------------------------------------------------- 1 | pub static BASE_LAYOUT: &'static str = r##" 2 | 3 | 4 | 5 | 6 | 7 | 8 | {{title}} 9 | 180 | 181 | 182 | 196 |
197 | {{~> page}} 198 |
199 | 200 | "##; 201 | -------------------------------------------------------------------------------- /pkg/the_best_goats.js: -------------------------------------------------------------------------------- 1 | /* tslint:disable */ 2 | import * as wasm from './the_best_goats_bg'; 3 | 4 | const __wbg_get_1ad4d2db49040ba1_target = GoatsNs.get.bind(GoatsNs) || function() { 5 | throw new Error(`wasm-bindgen: GoatsNs.get.bind(GoatsNs) does not exist`); 6 | }; 7 | 8 | let cachedTextDecoder = new TextDecoder('utf-8'); 9 | 10 | let cachegetUint8Memory = null; 11 | function getUint8Memory() { 12 | if (cachegetUint8Memory === null || cachegetUint8Memory.buffer !== wasm.memory.buffer) { 13 | cachegetUint8Memory = new Uint8Array(wasm.memory.buffer); 14 | } 15 | return cachegetUint8Memory; 16 | } 17 | 18 | function getStringFromWasm(ptr, len) { 19 | return cachedTextDecoder.decode(getUint8Memory().subarray(ptr, ptr + len)); 20 | } 21 | 22 | const slab = [{ obj: undefined }, { obj: null }, { obj: true }, { obj: false }]; 23 | 24 | let slab_next = slab.length; 25 | 26 | function addHeapObject(obj) { 27 | if (slab_next === slab.length) slab.push(slab.length + 1); 28 | const idx = slab_next; 29 | const next = slab[idx]; 30 | 31 | slab_next = next; 32 | 33 | slab[idx] = { obj, cnt: 1 }; 34 | return idx << 1; 35 | } 36 | 37 | export function __wbg_get_1ad4d2db49040ba1(arg0, arg1, arg2, arg3) { 38 | let varg0 = getStringFromWasm(arg0, arg1); 39 | let varg2 = getStringFromWasm(arg2, arg3); 40 | return addHeapObject(__wbg_get_1ad4d2db49040ba1_target(varg0, varg2)); 41 | } 42 | 43 | const __wbg_get_12b964c474bf3917_target = FavoritesNs.get.bind(FavoritesNs) || function() { 44 | throw new Error(`wasm-bindgen: FavoritesNs.get.bind(FavoritesNs) does not exist`); 45 | }; 46 | 47 | export function __wbg_get_12b964c474bf3917(arg0, arg1, arg2, arg3) { 48 | let varg0 = getStringFromWasm(arg0, arg1); 49 | let varg2 = getStringFromWasm(arg2, arg3); 50 | return addHeapObject(__wbg_get_12b964c474bf3917_target(varg0, varg2)); 51 | } 52 | 53 | const __wbg_put_93f63b3eb926f515_target = FavoritesNs.put.bind(FavoritesNs) || function() { 54 | throw new Error(`wasm-bindgen: FavoritesNs.put.bind(FavoritesNs) does not exist`); 55 | }; 56 | 57 | export function __wbg_put_93f63b3eb926f515(arg0, arg1, arg2, arg3) { 58 | let varg0 = getStringFromWasm(arg0, arg1); 59 | let varg2 = getStringFromWasm(arg2, arg3); 60 | return addHeapObject(__wbg_put_93f63b3eb926f515_target(varg0, varg2)); 61 | } 62 | 63 | const __wbg_delete_9fa50bcc3ac28ee0_target = FavoritesNs.delete.bind(FavoritesNs) || function() { 64 | throw new Error(`wasm-bindgen: FavoritesNs.delete.bind(FavoritesNs) does not exist`); 65 | }; 66 | 67 | export function __wbg_delete_9fa50bcc3ac28ee0(arg0, arg1) { 68 | let varg0 = getStringFromWasm(arg0, arg1); 69 | return addHeapObject(__wbg_delete_9fa50bcc3ac28ee0_target(varg0)); 70 | } 71 | 72 | const stack = []; 73 | 74 | function getObject(idx) { 75 | if ((idx & 1) === 1) { 76 | return stack[idx >> 1]; 77 | } else { 78 | const val = slab[idx >> 1]; 79 | 80 | return val.obj; 81 | 82 | } 83 | } 84 | 85 | export function __wbg_fetch_f97f38a52d09543e(arg0) { 86 | return addHeapObject(fetch(getObject(arg0))); 87 | } 88 | 89 | let cachedTextEncoder = new TextEncoder('utf-8'); 90 | 91 | function passStringToWasm(arg) { 92 | 93 | const buf = cachedTextEncoder.encode(arg); 94 | const ptr = wasm.__wbindgen_malloc(buf.length); 95 | getUint8Memory().set(buf, ptr); 96 | return [ptr, buf.length]; 97 | } 98 | 99 | let cachegetUint32Memory = null; 100 | function getUint32Memory() { 101 | if (cachegetUint32Memory === null || cachegetUint32Memory.buffer !== wasm.memory.buffer) { 102 | cachegetUint32Memory = new Uint32Array(wasm.memory.buffer); 103 | } 104 | return cachegetUint32Memory; 105 | } 106 | 107 | export function __wbg_generaterandomstr_8cd4e7c7c72c5d6f(ret) { 108 | 109 | const [retptr, retlen] = passStringToWasm(generate_random_str()); 110 | const mem = getUint32Memory(); 111 | mem[ret / 4] = retptr; 112 | mem[ret / 4 + 1] = retlen; 113 | 114 | } 115 | 116 | function dropRef(idx) { 117 | 118 | idx = idx >> 1; 119 | if (idx < 4) return; 120 | let obj = slab[idx]; 121 | 122 | obj.cnt -= 1; 123 | if (obj.cnt > 0) return; 124 | 125 | // If we hit 0 then free up our space in the slab 126 | slab[idx] = slab_next; 127 | slab_next = idx; 128 | } 129 | 130 | function takeObject(idx) { 131 | const ret = getObject(idx); 132 | dropRef(idx); 133 | return ret; 134 | } 135 | 136 | export function __wbg_new_0a15fc7e3d759804(arg0, arg1, arg2) { 137 | let varg0 = getStringFromWasm(arg0, arg1); 138 | return addHeapObject(new Response(varg0, takeObject(arg2))); 139 | } 140 | /** 141 | * @param {any} arg0 142 | * @returns {any} 143 | */ 144 | export function main(arg0) { 145 | return takeObject(wasm.main(addHeapObject(arg0))); 146 | } 147 | 148 | function GetOwnOrInheritedPropertyDescriptor(obj, id) { 149 | while (obj) { 150 | let desc = Object.getOwnPropertyDescriptor(obj, id); 151 | if (desc) return desc; 152 | obj = Object.getPrototypeOf(obj); 153 | } 154 | throw new Error(`descriptor for id='${id}' not found`); 155 | } 156 | 157 | const __widl_f_request_FetchEvent_target = GetOwnOrInheritedPropertyDescriptor(FetchEvent.prototype, 'request').get || function() { 158 | throw new Error(`wasm-bindgen: GetOwnOrInheritedPropertyDescriptor(FetchEvent.prototype, 'request').get does not exist`); 159 | }; 160 | 161 | export function __widl_f_request_FetchEvent(arg0) { 162 | return addHeapObject(__widl_f_request_FetchEvent_target.call(getObject(arg0))); 163 | } 164 | 165 | export function __widl_instanceof_FormData(idx) { 166 | return getObject(idx) instanceof FormData ? 1 : 0; 167 | } 168 | 169 | const __widl_f_get_FormData_target = FormData.prototype.get || function() { 170 | throw new Error(`wasm-bindgen: FormData.prototype.get does not exist`); 171 | }; 172 | 173 | export function __widl_f_get_FormData(arg0, arg1, arg2) { 174 | let varg1 = getStringFromWasm(arg1, arg2); 175 | return addHeapObject(__widl_f_get_FormData_target.call(getObject(arg0), varg1)); 176 | } 177 | 178 | export function __widl_f_new_Headers(exnptr) { 179 | try { 180 | return addHeapObject(new Headers()); 181 | } catch (e) { 182 | const view = getUint32Memory(); 183 | view[exnptr / 4] = 1; 184 | view[exnptr / 4 + 1] = addHeapObject(e); 185 | 186 | } 187 | } 188 | 189 | const __widl_f_append_Headers_target = Headers.prototype.append || function() { 190 | throw new Error(`wasm-bindgen: Headers.prototype.append does not exist`); 191 | }; 192 | 193 | export function __widl_f_append_Headers(arg0, arg1, arg2, arg3, arg4, exnptr) { 194 | let varg1 = getStringFromWasm(arg1, arg2); 195 | let varg3 = getStringFromWasm(arg3, arg4); 196 | try { 197 | __widl_f_append_Headers_target.call(getObject(arg0), varg1, varg3); 198 | } catch (e) { 199 | const view = getUint32Memory(); 200 | view[exnptr / 4] = 1; 201 | view[exnptr / 4 + 1] = addHeapObject(e); 202 | 203 | } 204 | } 205 | 206 | const __widl_f_get_Headers_target = Headers.prototype.get || function() { 207 | throw new Error(`wasm-bindgen: Headers.prototype.get does not exist`); 208 | }; 209 | 210 | function isLikeNone(x) { 211 | return x === undefined || x === null; 212 | } 213 | 214 | export function __widl_f_get_Headers(ret, arg0, arg1, arg2, exnptr) { 215 | let varg1 = getStringFromWasm(arg1, arg2); 216 | try { 217 | const val = __widl_f_get_Headers_target.call(getObject(arg0), varg1); 218 | const [retptr, retlen] = isLikeNone(val) ? [0, 0] : passStringToWasm(val); 219 | const mem = getUint32Memory(); 220 | mem[ret / 4] = retptr; 221 | mem[ret / 4 + 1] = retlen; 222 | 223 | } catch (e) { 224 | const view = getUint32Memory(); 225 | view[exnptr / 4] = 1; 226 | view[exnptr / 4 + 1] = addHeapObject(e); 227 | 228 | } 229 | } 230 | 231 | const __widl_f_set_Headers_target = Headers.prototype.set || function() { 232 | throw new Error(`wasm-bindgen: Headers.prototype.set does not exist`); 233 | }; 234 | 235 | export function __widl_f_set_Headers(arg0, arg1, arg2, arg3, arg4, exnptr) { 236 | let varg1 = getStringFromWasm(arg1, arg2); 237 | let varg3 = getStringFromWasm(arg3, arg4); 238 | try { 239 | __widl_f_set_Headers_target.call(getObject(arg0), varg1, varg3); 240 | } catch (e) { 241 | const view = getUint32Memory(); 242 | view[exnptr / 4] = 1; 243 | view[exnptr / 4 + 1] = addHeapObject(e); 244 | 245 | } 246 | } 247 | 248 | export function __widl_f_new_with_str_Request(arg0, arg1, exnptr) { 249 | let varg0 = getStringFromWasm(arg0, arg1); 250 | try { 251 | return addHeapObject(new Request(varg0)); 252 | } catch (e) { 253 | const view = getUint32Memory(); 254 | view[exnptr / 4] = 1; 255 | view[exnptr / 4 + 1] = addHeapObject(e); 256 | 257 | } 258 | } 259 | 260 | const __widl_f_method_Request_target = GetOwnOrInheritedPropertyDescriptor(Request.prototype, 'method').get || function() { 261 | throw new Error(`wasm-bindgen: GetOwnOrInheritedPropertyDescriptor(Request.prototype, 'method').get does not exist`); 262 | }; 263 | 264 | export function __widl_f_method_Request(ret, arg0) { 265 | 266 | const [retptr, retlen] = passStringToWasm(__widl_f_method_Request_target.call(getObject(arg0))); 267 | const mem = getUint32Memory(); 268 | mem[ret / 4] = retptr; 269 | mem[ret / 4 + 1] = retlen; 270 | 271 | } 272 | 273 | const __widl_f_url_Request_target = GetOwnOrInheritedPropertyDescriptor(Request.prototype, 'url').get || function() { 274 | throw new Error(`wasm-bindgen: GetOwnOrInheritedPropertyDescriptor(Request.prototype, 'url').get does not exist`); 275 | }; 276 | 277 | export function __widl_f_url_Request(ret, arg0) { 278 | 279 | const [retptr, retlen] = passStringToWasm(__widl_f_url_Request_target.call(getObject(arg0))); 280 | const mem = getUint32Memory(); 281 | mem[ret / 4] = retptr; 282 | mem[ret / 4 + 1] = retlen; 283 | 284 | } 285 | 286 | const __widl_f_headers_Request_target = GetOwnOrInheritedPropertyDescriptor(Request.prototype, 'headers').get || function() { 287 | throw new Error(`wasm-bindgen: GetOwnOrInheritedPropertyDescriptor(Request.prototype, 'headers').get does not exist`); 288 | }; 289 | 290 | export function __widl_f_headers_Request(arg0) { 291 | return addHeapObject(__widl_f_headers_Request_target.call(getObject(arg0))); 292 | } 293 | 294 | const __widl_f_form_data_Request_target = Request.prototype.formData || function() { 295 | throw new Error(`wasm-bindgen: Request.prototype.formData does not exist`); 296 | }; 297 | 298 | export function __widl_f_form_data_Request(arg0, exnptr) { 299 | try { 300 | return addHeapObject(__widl_f_form_data_Request_target.call(getObject(arg0))); 301 | } catch (e) { 302 | const view = getUint32Memory(); 303 | view[exnptr / 4] = 1; 304 | view[exnptr / 4 + 1] = addHeapObject(e); 305 | 306 | } 307 | } 308 | 309 | export function __wbg_new_7a259c7860f1b5c4() { 310 | return addHeapObject(new Array()); 311 | } 312 | 313 | export function __wbg_new_d85589be79b6c4df(arg0, arg1) { 314 | let varg0 = getStringFromWasm(arg0, arg1); 315 | return addHeapObject(new Error(varg0)); 316 | } 317 | 318 | export function __wbg_instanceof_Function_220b2df285bee4ce(idx) { 319 | return getObject(idx) instanceof Function ? 1 : 0; 320 | } 321 | 322 | const __wbg_call_eeece939fc9a62db_target = Function.prototype.call || function() { 323 | throw new Error(`wasm-bindgen: Function.prototype.call does not exist`); 324 | }; 325 | 326 | export function __wbg_call_eeece939fc9a62db(arg0, arg1, arg2, exnptr) { 327 | try { 328 | return addHeapObject(__wbg_call_eeece939fc9a62db_target.call(getObject(arg0), getObject(arg1), getObject(arg2))); 329 | } catch (e) { 330 | const view = getUint32Memory(); 331 | view[exnptr / 4] = 1; 332 | view[exnptr / 4 + 1] = addHeapObject(e); 333 | 334 | } 335 | } 336 | 337 | export function __wbg_new_6b7f4334f7b728fd() { 338 | return addHeapObject(new Object()); 339 | } 340 | 341 | const __wbg_get_b5fa2669cbf91d6f_target = Reflect.get.bind(Reflect) || function() { 342 | throw new Error(`wasm-bindgen: Reflect.get.bind(Reflect) does not exist`); 343 | }; 344 | 345 | export function __wbg_get_b5fa2669cbf91d6f(arg0, arg1, exnptr) { 346 | try { 347 | return addHeapObject(__wbg_get_b5fa2669cbf91d6f_target(getObject(arg0), getObject(arg1))); 348 | } catch (e) { 349 | const view = getUint32Memory(); 350 | view[exnptr / 4] = 1; 351 | view[exnptr / 4 + 1] = addHeapObject(e); 352 | 353 | } 354 | } 355 | 356 | const __wbg_set_6f4fee47694f666d_target = Reflect.set.bind(Reflect) || function() { 357 | throw new Error(`wasm-bindgen: Reflect.set.bind(Reflect) does not exist`); 358 | }; 359 | 360 | export function __wbg_set_6f4fee47694f666d(arg0, arg1, arg2, exnptr) { 361 | try { 362 | return __wbg_set_6f4fee47694f666d_target(getObject(arg0), getObject(arg1), getObject(arg2)) ? 1 : 0; 363 | } catch (e) { 364 | const view = getUint32Memory(); 365 | view[exnptr / 4] = 1; 366 | view[exnptr / 4 + 1] = addHeapObject(e); 367 | 368 | } 369 | } 370 | 371 | let cachedGlobalArgumentPtr = null; 372 | function globalArgumentPtr() { 373 | if (cachedGlobalArgumentPtr === null) { 374 | cachedGlobalArgumentPtr = wasm.__wbindgen_global_argument_ptr(); 375 | } 376 | return cachedGlobalArgumentPtr; 377 | } 378 | 379 | function getGlobalArgument(arg) { 380 | const idx = globalArgumentPtr() / 4 + arg; 381 | return getUint32Memory()[idx]; 382 | } 383 | 384 | export function __wbg_new_ea60e716adf807fe(arg0) { 385 | let cbarg0 = function(arg0, arg1) { 386 | let a = this.a; 387 | this.a = 0; 388 | try { 389 | return this.f(a, this.b, addHeapObject(arg0), addHeapObject(arg1)); 390 | 391 | } finally { 392 | this.a = a; 393 | 394 | } 395 | 396 | }; 397 | cbarg0.f = wasm.__wbg_function_table.get(arg0); 398 | cbarg0.a = getGlobalArgument(0); 399 | cbarg0.b = getGlobalArgument(0 + 1); 400 | try { 401 | return addHeapObject(new Promise(cbarg0.bind(cbarg0))); 402 | } finally { 403 | cbarg0.a = cbarg0.b = 0; 404 | 405 | } 406 | } 407 | 408 | const __wbg_reject_aa4c9a50081bb8a9_target = Promise.reject.bind(Promise) || function() { 409 | throw new Error(`wasm-bindgen: Promise.reject.bind(Promise) does not exist`); 410 | }; 411 | 412 | export function __wbg_reject_aa4c9a50081bb8a9(arg0) { 413 | return addHeapObject(__wbg_reject_aa4c9a50081bb8a9_target(getObject(arg0))); 414 | } 415 | 416 | const __wbg_resolve_a96e53ca1abfddb3_target = Promise.resolve.bind(Promise) || function() { 417 | throw new Error(`wasm-bindgen: Promise.resolve.bind(Promise) does not exist`); 418 | }; 419 | 420 | export function __wbg_resolve_a96e53ca1abfddb3(arg0) { 421 | return addHeapObject(__wbg_resolve_a96e53ca1abfddb3_target(getObject(arg0))); 422 | } 423 | 424 | const __wbg_then_074a42f44879a676_target = Promise.prototype.then || function() { 425 | throw new Error(`wasm-bindgen: Promise.prototype.then does not exist`); 426 | }; 427 | 428 | export function __wbg_then_074a42f44879a676(arg0, arg1, arg2) { 429 | return addHeapObject(__wbg_then_074a42f44879a676_target.call(getObject(arg0), getObject(arg1), getObject(arg2))); 430 | } 431 | 432 | export function __wbindgen_object_clone_ref(idx) { 433 | // If this object is on the stack promote it to the heap. 434 | if ((idx & 1) === 1) return addHeapObject(getObject(idx)); 435 | 436 | // Otherwise if the object is on the heap just bump the 437 | // refcount and move on 438 | const val = slab[idx >> 1]; 439 | val.cnt += 1; 440 | return idx; 441 | } 442 | 443 | export function __wbindgen_object_drop_ref(i) { 444 | dropRef(i); 445 | } 446 | 447 | export function __wbindgen_string_new(p, l) { 448 | return addHeapObject(getStringFromWasm(p, l)); 449 | } 450 | 451 | export function __wbindgen_number_new(i) { 452 | return addHeapObject(i); 453 | } 454 | 455 | export function __wbindgen_number_get(n, invalid) { 456 | let obj = getObject(n); 457 | if (typeof(obj) === 'number') return obj; 458 | getUint8Memory()[invalid] = 1; 459 | return 0; 460 | } 461 | 462 | export function __wbindgen_is_null(idx) { 463 | return getObject(idx) === null ? 1 : 0; 464 | } 465 | 466 | export function __wbindgen_is_undefined(idx) { 467 | return getObject(idx) === undefined ? 1 : 0; 468 | } 469 | 470 | export function __wbindgen_boolean_get(i) { 471 | let v = getObject(i); 472 | if (typeof(v) === 'boolean') { 473 | return v ? 1 : 0; 474 | } else { 475 | return 2; 476 | } 477 | } 478 | 479 | export function __wbindgen_is_symbol(i) { 480 | return typeof(getObject(i)) === 'symbol' ? 1 : 0; 481 | } 482 | 483 | export function __wbindgen_string_get(i, len_ptr) { 484 | let obj = getObject(i); 485 | if (typeof(obj) !== 'string') return 0; 486 | const [ptr, len] = passStringToWasm(obj); 487 | getUint32Memory()[len_ptr / 4] = len; 488 | return ptr; 489 | } 490 | 491 | export function __wbindgen_cb_drop(i) { 492 | const obj = getObject(i).original; 493 | dropRef(i); 494 | if (obj.cnt-- == 1) { 495 | obj.a = 0; 496 | return 1; 497 | } 498 | return 0; 499 | } 500 | 501 | export function __wbindgen_json_serialize(idx, ptrptr) { 502 | const [ptr, len] = passStringToWasm(JSON.stringify(getObject(idx))); 503 | getUint32Memory()[ptrptr / 4] = ptr; 504 | return len; 505 | } 506 | 507 | export function __wbindgen_closure_wrapper321(a, b, fi, di, _ignored) { 508 | const f = wasm.__wbg_function_table.get(fi); 509 | const d = wasm.__wbg_function_table.get(di); 510 | const cb = function(arg0) { 511 | this.cnt++; 512 | let a = this.a; 513 | this.a = 0; 514 | try { 515 | return f(a, b, addHeapObject(arg0)); 516 | 517 | } finally { 518 | this.a = a; 519 | if (this.cnt-- == 1) d(this.a, b); 520 | 521 | } 522 | 523 | }; 524 | cb.a = a; 525 | cb.cnt = 1; 526 | let real = cb.bind(cb); 527 | real.original = cb; 528 | return addHeapObject(real); 529 | } 530 | 531 | export function __wbindgen_throw(ptr, len) { 532 | throw new Error(getStringFromWasm(ptr, len)); 533 | } 534 | 535 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | extern crate cfg_if; 2 | extern crate cookie; 3 | extern crate futures; 4 | extern crate handlebars; 5 | extern crate http; 6 | extern crate js_sys; 7 | extern crate serde; 8 | extern crate time; 9 | extern crate url; 10 | extern crate wasm_bindgen; 11 | extern crate wasm_bindgen_futures; 12 | extern crate web_sys; 13 | #[macro_use] 14 | extern crate serde_derive; 15 | #[macro_use] 16 | extern crate lazy_static; 17 | 18 | mod templates; 19 | mod utils; 20 | 21 | use cfg_if::cfg_if; 22 | use cookie::Cookie; 23 | use futures::Future; 24 | use handlebars::Handlebars; 25 | use http::StatusCode; 26 | use js_sys::{Array, Error, Function, Promise, Reflect}; 27 | use std::fmt::Display; 28 | use time::Duration; 29 | use url::Url; 30 | use wasm_bindgen::prelude::*; 31 | use wasm_bindgen::JsCast; 32 | use wasm_bindgen_futures::future_to_promise; 33 | use wasm_bindgen_futures::JsFuture; 34 | use web_sys::{FetchEvent, FormData, Headers, Request, ResponseInit}; 35 | 36 | cfg_if! { 37 | // When the `wee_alloc` feature is enabled, use `wee_alloc` as the global 38 | // allocator. 39 | if #[cfg(feature = "wee_alloc")] { 40 | extern crate wee_alloc; 41 | #[global_allocator] 42 | static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT; 43 | } 44 | } 45 | 46 | type GoatId = u32; 47 | 48 | #[wasm_bindgen] 49 | extern "C" { 50 | type GoatsNs; 51 | 52 | #[wasm_bindgen(static_method_of = GoatsNs)] 53 | fn get(key: &str, data_type: &str) -> Promise; 54 | 55 | #[wasm_bindgen(static_method_of = GoatsNs)] 56 | fn put(key: &str, val: &str) -> Promise; 57 | 58 | #[wasm_bindgen(static_method_of = GoatsNs)] 59 | fn delete(key: &str) -> Promise; 60 | } 61 | 62 | #[wasm_bindgen] 63 | extern "C" { 64 | type FavoritesNs; 65 | 66 | #[wasm_bindgen(static_method_of = FavoritesNs)] 67 | fn get(key: &str, data_type: &str) -> Promise; 68 | 69 | #[wasm_bindgen(static_method_of = FavoritesNs)] 70 | fn put(key: &str, val: &str) -> Promise; 71 | 72 | #[wasm_bindgen(static_method_of = FavoritesNs)] 73 | fn delete(key: &str) -> Promise; 74 | } 75 | 76 | #[wasm_bindgen] 77 | extern "C" { 78 | fn fetch(req: &Request) -> Promise; 79 | // TODO 80 | fn generate_random_str() -> String; 81 | } 82 | 83 | // As of writing, web-sys does not support creating Response objects, so 84 | // we define our own wrapper here 85 | #[wasm_bindgen] 86 | extern "C" { 87 | type Response; 88 | 89 | #[wasm_bindgen(constructor)] 90 | fn new(body: &str, init: ResponseInit) -> Response; 91 | } 92 | 93 | #[derive(Serialize, Deserialize, Debug)] 94 | struct Goat { 95 | // properties from database 96 | id: GoatId, 97 | name: String, 98 | image: String, 99 | #[serde(rename = "imageSmall")] 100 | image_small: String, 101 | } 102 | 103 | static BASE_LAYOUT_TEMPLATE: &'static str = "BASE_LAYOUT"; 104 | static ERROR_PAGE_TEMPLATE: &'static str = "ERROR_PAGE"; 105 | static FAVORITES_PAGE_TEMPLATE: &'static str = "FAVORITES_PAGE"; 106 | static GOAT_LIST_PARTIAL_TEMPLATE: &'static str = "GOAT_LIST_PARTIAL"; 107 | static HOME_PAGE_TEMPLATE: &'static str = "HOME_PAGE"; 108 | static USER_ID_COOKIE: &'static str = "user_id"; 109 | static DEFAULT_TITLE: &'static str = "The Best Goats"; 110 | 111 | lazy_static! { 112 | static ref HBARS: Handlebars = { 113 | let mut reg = Handlebars::new(); 114 | 115 | assert!( 116 | reg.register_template_string(BASE_LAYOUT_TEMPLATE, templates::base::BASE_LAYOUT) 117 | .is_ok() 118 | ); 119 | assert!( 120 | reg.register_template_string(ERROR_PAGE_TEMPLATE, templates::error::ERROR_PAGE) 121 | .is_ok() 122 | ); 123 | assert!( 124 | reg.register_template_string( 125 | FAVORITES_PAGE_TEMPLATE, 126 | templates::favorites::FAVORITES_PAGE 127 | ) 128 | .is_ok() 129 | ); 130 | assert!( 131 | reg.register_template_string( 132 | GOAT_LIST_PARTIAL_TEMPLATE, 133 | templates::goat_list::GOAT_LIST_PARTIAL 134 | ) 135 | .is_ok() 136 | ); 137 | assert!( 138 | reg.register_template_string(HOME_PAGE_TEMPLATE, templates::home::HOME_PAGE) 139 | .is_ok() 140 | ); 141 | 142 | reg 143 | }; 144 | } 145 | 146 | trait ToJsResult { 147 | fn ok_or_js_err(self) -> Result; 148 | } 149 | 150 | trait ToJsResultWithMsg { 151 | fn ok_or_js_err_with_msg(self, msg: &str) -> Result; 152 | } 153 | 154 | impl ToJsResult for Option { 155 | fn ok_or_js_err(self) -> Result { 156 | match self { 157 | Some(v) => Ok(v), 158 | None => Err(JsValue::from(Error::new("expected Some but found None"))), 159 | } 160 | } 161 | } 162 | 163 | impl ToJsResultWithMsg for Option { 164 | fn ok_or_js_err_with_msg(self, msg: &str) -> Result { 165 | match self { 166 | Some(v) => Ok(v), 167 | None => Err(JsValue::from(Error::new(msg))), 168 | } 169 | } 170 | } 171 | 172 | impl ToJsResult for Result 173 | where 174 | E: Display, 175 | { 176 | fn ok_or_js_err(self) -> Result { 177 | match self { 178 | Ok(v) => Ok(v), 179 | Err(e) => Err(JsValue::from(Error::new(&e.to_string()))), 180 | } 181 | } 182 | } 183 | 184 | impl ToJsResultWithMsg for Result { 185 | fn ok_or_js_err_with_msg(self, msg: &str) -> Result { 186 | match self { 187 | Ok(v) => Ok(v), 188 | Err(_e) => Err(JsValue::from(Error::new(msg))), 189 | } 190 | } 191 | } 192 | 193 | fn get_user_id(req: &Request) -> Option { 194 | let headers = req.headers(); 195 | let cookie_header = match headers.get("cookie") { 196 | Ok(Some(v)) => v, 197 | _ => return None, 198 | }; 199 | for cookie_str in cookie_header.split(';').map(|s| s.trim()) { 200 | if let Ok(c) = Cookie::parse(cookie_str) { 201 | if c.name() == USER_ID_COOKIE { 202 | return Some(c.value().to_owned()); 203 | } 204 | } 205 | } 206 | None 207 | } 208 | 209 | fn wait_until(event: &FetchEvent, promise: &Promise) { 210 | // the waitUntil() method is not standard on the FetchEvent so it's not 211 | // part of the wasm-bindgen bindings 212 | let event_val = JsValue::from(event); 213 | let method_key = JsValue::from("waitUntil"); 214 | let method: Function = Reflect::get(&event_val, &method_key) 215 | .unwrap() 216 | .dyn_into() 217 | .unwrap(); 218 | method.call1(&event_val, &JsValue::from(promise)).unwrap(); 219 | } 220 | 221 | #[derive(Serialize)] 222 | struct GoatListItem { 223 | id: GoatId, 224 | name: String, 225 | image: String, 226 | image_small: String, 227 | is_favorite: bool, 228 | } 229 | 230 | fn get_goat_list_items(goats: Vec, favorites: &Vec) -> Vec { 231 | goats 232 | .into_iter() 233 | .map(|goat| { 234 | let is_favorite = favorites.contains(&goat.id); 235 | GoatListItem { 236 | id: goat.id, 237 | name: goat.name, 238 | image: goat.image, 239 | image_small: goat.image_small, 240 | is_favorite, 241 | } 242 | }) 243 | .collect() 244 | } 245 | 246 | fn get_featured_goats() -> Promise { 247 | GoatsNs::get("featured", "json") 248 | } 249 | 250 | fn get_favorites_from_user_id(user_id: &Option) -> JsFuture { 251 | match user_id { 252 | Some(sid) => JsFuture::from(FavoritesNs::get(&sid, "json")), 253 | None => JsFuture::from(Promise::resolve(&JsValue::from(Array::new()))), 254 | } 255 | } 256 | 257 | fn generate_error_response(status: StatusCode, msg: Option<&str>) -> Result { 258 | #[derive(Serialize)] 259 | struct Data { 260 | title: String, 261 | parent: &'static str, 262 | show_favorites: bool, 263 | error_message: String, 264 | } 265 | let status_error_msg = format!( 266 | "{} {}", 267 | status.as_u16(), 268 | status.canonical_reason().unwrap_or("Unknown Error") 269 | ); 270 | let error_message = match msg { 271 | Some(v) => v.to_owned(), 272 | None => status_error_msg.to_owned(), 273 | }; 274 | let data = Data { 275 | title: format!("{} - {}", &status_error_msg, DEFAULT_TITLE), 276 | parent: BASE_LAYOUT_TEMPLATE, 277 | show_favorites: false, 278 | error_message, 279 | }; 280 | 281 | let body = HBARS.render(ERROR_PAGE_TEMPLATE, &data).ok_or_js_err()?; 282 | 283 | let headers = Headers::new()?; 284 | headers.append("content-type", "text/html")?; 285 | let resp = generate_response(&body, status.as_u16(), &headers); 286 | Ok(JsValue::from(resp)) 287 | } 288 | 289 | fn generate_redirect_headers(url: &str) -> Result { 290 | let headers = Headers::new()?; 291 | headers.set("location", url)?; 292 | Ok(headers) 293 | } 294 | 295 | fn generate_response(body: &str, status: u16, headers: &Headers) -> Response { 296 | let mut init = ResponseInit::new(); 297 | init.status(status); 298 | init.headers(&JsValue::from(headers)); 299 | Response::new(body, init) 300 | } 301 | 302 | // Returns the referrer URL if there is one, otherwise 303 | // returns the URL of the request 304 | fn get_referrer_or_orig_url(req: &Request) -> String { 305 | let req_headers = req.headers(); 306 | match req_headers.get("referer") { 307 | Ok(Some(v)) => v, 308 | _ => req.url(), 309 | } 310 | } 311 | 312 | fn render_home(req: &Request) -> Promise { 313 | let user_id = get_user_id(req); 314 | let favorites_future = get_favorites_from_user_id(&user_id); 315 | let all_goats_future = JsFuture::from(get_featured_goats()); 316 | 317 | let f = JsFuture::join(favorites_future, all_goats_future).then(|tuple_result| { 318 | let (favorites_value, all_goats_value) = tuple_result?; 319 | let favorites: Vec = favorites_value.into_serde().ok_or_js_err()?; 320 | let all_goats: Vec = all_goats_value.into_serde().ok_or_js_err()?; 321 | 322 | #[derive(Serialize)] 323 | struct Data { 324 | title: &'static str, 325 | parent: &'static str, 326 | goat_list_template: &'static str, 327 | show_favorites: bool, 328 | fav_count: usize, 329 | goats: Vec, 330 | } 331 | let data = Data { 332 | title: DEFAULT_TITLE, 333 | parent: BASE_LAYOUT_TEMPLATE, 334 | goat_list_template: GOAT_LIST_PARTIAL_TEMPLATE, 335 | show_favorites: true, 336 | fav_count: favorites.len(), 337 | goats: get_goat_list_items(all_goats, &favorites), 338 | }; 339 | let body = HBARS.render(HOME_PAGE_TEMPLATE, &data).ok_or_js_err()?; 340 | 341 | let headers = Headers::new()?; 342 | headers.append("content-type", "text/html")?; 343 | let resp = generate_response(&body, 200, &headers); 344 | 345 | Ok(JsValue::from(resp)) 346 | }); 347 | future_to_promise(f) 348 | } 349 | 350 | fn render_favorites(req: &Request) -> Promise { 351 | let user_id = get_user_id(req); 352 | let favorites_future = get_favorites_from_user_id(&user_id); 353 | let all_goats_future = JsFuture::from(get_featured_goats()); 354 | 355 | let f = JsFuture::join(favorites_future, all_goats_future).then(|tuple_result| { 356 | let (favorites_value, all_goats_value) = tuple_result?; 357 | let favorites: Vec = favorites_value.into_serde().ok_or_js_err()?; 358 | let all_goats: Vec = all_goats_value.into_serde().ok_or_js_err()?; 359 | 360 | let favorite_goats = all_goats 361 | .into_iter() 362 | .filter(|goat| favorites.contains(&goat.id)) 363 | .collect(); 364 | 365 | #[derive(Serialize)] 366 | struct Data { 367 | title: String, 368 | parent: &'static str, 369 | goat_list_template: &'static str, 370 | show_favorites: bool, 371 | fav_count: usize, 372 | has_favorites: bool, 373 | goats: Vec, 374 | } 375 | let data = Data { 376 | title: format!("{} - {}", "Favorites", DEFAULT_TITLE), 377 | parent: BASE_LAYOUT_TEMPLATE, 378 | goat_list_template: GOAT_LIST_PARTIAL_TEMPLATE, 379 | show_favorites: true, 380 | fav_count: favorites.len(), 381 | has_favorites: favorites.len() > 0, 382 | goats: get_goat_list_items(favorite_goats, &favorites), 383 | }; 384 | let body = HBARS 385 | .render(FAVORITES_PAGE_TEMPLATE, &data) 386 | .ok_or_js_err()?; 387 | 388 | let headers = Headers::new()?; 389 | headers.append("content-type", "text/html")?; 390 | let headers = Headers::new()?; 391 | headers.append("content-type", "text/html")?; 392 | let resp = generate_response(&body, 200, &headers); 393 | 394 | Ok(JsValue::from(resp)) 395 | }); 396 | future_to_promise(f) 397 | } 398 | 399 | fn proxy_image(path: &str) -> Promise { 400 | let url = "https://storage.googleapis.com/best_goats".to_owned() + path; 401 | let request = match Request::new_with_str(&url) { 402 | Ok(v) => v, 403 | Err(e) => return Promise::reject(&e), 404 | }; 405 | 406 | fetch(&request) 407 | } 408 | 409 | fn render_error(status: StatusCode) -> Promise { 410 | match generate_error_response(status, None) { 411 | Ok(v) => Promise::resolve(&v), 412 | Err(e) => Promise::reject(&e), 413 | } 414 | } 415 | 416 | enum FavoritesModification { 417 | AddFavorite, 418 | RemoveFavorite, 419 | } 420 | 421 | fn modify_favorites(event: FetchEvent, modification: FavoritesModification) -> Promise { 422 | let req = &event.request(); 423 | let orig_user_id = get_user_id(req); 424 | let redirect_url = get_referrer_or_orig_url(req); 425 | let form_data_future = match req 426 | .form_data() 427 | .ok_or_js_err_with_msg("failed to get form_data") 428 | { 429 | Ok(v) => JsFuture::from(v), 430 | Err(e) => JsFuture::from(Promise::reject(&e)), 431 | }; 432 | let favorites_future = get_favorites_from_user_id(&orig_user_id); 433 | 434 | let f = JsFuture::join(form_data_future, favorites_future).then(move |tuple_result| { 435 | let (form_data_value, favorites_value) = tuple_result?; 436 | let form_data: FormData = form_data_value.dyn_into()?; 437 | let mut favorites: Vec = favorites_value.into_serde().ok_or_js_err()?; 438 | let goat_id_str: String = match form_data.get("id").as_string() { 439 | Some(v) => v, 440 | None => { 441 | return generate_error_response( 442 | StatusCode::BAD_REQUEST, 443 | Some("Missing id parameter"), 444 | ); 445 | } 446 | }; 447 | let goat_id: GoatId = match goat_id_str.parse() { 448 | Ok(v) => v, 449 | Err(_e) => { 450 | return generate_error_response( 451 | StatusCode::BAD_REQUEST, 452 | Some("Invalid id parameter"), 453 | ); 454 | } 455 | }; 456 | 457 | let modified = match modification { 458 | FavoritesModification::AddFavorite => { 459 | if !favorites.contains(&goat_id) { 460 | favorites.insert(0, goat_id); 461 | true 462 | } else { 463 | false 464 | } 465 | } 466 | FavoritesModification::RemoveFavorite => { 467 | if let Some(idx) = favorites.iter().position(|x| *x == goat_id) { 468 | favorites.remove(idx); 469 | true 470 | } else { 471 | false 472 | } 473 | } 474 | }; 475 | 476 | if !modified { 477 | let headers = generate_redirect_headers(&redirect_url)?; 478 | let resp = generate_response("", 302, &headers); 479 | return Ok(JsValue::from(&resp)); 480 | } else { 481 | let new_user_id = generate_random_str(); 482 | let favorites_json = serde_json::to_string(&favorites).ok_or_js_err()?; 483 | let update_favorites_promise = FavoritesNs::put(&new_user_id, &favorites_json); 484 | let future = 485 | JsFuture::from(update_favorites_promise).then(move |result| match result { 486 | Ok(_v) => { 487 | let cookie = Cookie::build(USER_ID_COOKIE, new_user_id) 488 | .http_only(true) 489 | .secure(true) 490 | .max_age(Duration::days(365 * 20)) 491 | .finish(); 492 | let headers = generate_redirect_headers(&redirect_url)?; 493 | headers.set("set-cookie", &cookie.to_string())?; 494 | 495 | // Delete the old user_id favorites from KV 496 | if let Some(uid) = orig_user_id { 497 | let delete_old_favorites_promise = FavoritesNs::delete(&uid); 498 | wait_until(&event, &delete_old_favorites_promise); 499 | } 500 | 501 | Ok(JsValue::from(&generate_response("", 302, &headers))) 502 | } 503 | Err(_e) => generate_error_response( 504 | StatusCode::INTERNAL_SERVER_ERROR, 505 | Some("Error updating favorites"), 506 | ), 507 | }); 508 | 509 | let promise = future_to_promise(future); 510 | return Ok(JsValue::from(&promise)); 511 | } 512 | }); 513 | 514 | future_to_promise(f) 515 | } 516 | 517 | #[wasm_bindgen] 518 | pub fn main(event: FetchEvent) -> Promise { 519 | let req = &event.request(); 520 | let url = match Url::parse(&req.url()).ok_or_js_err() { 521 | Ok(v) => v, 522 | Err(e) => return Promise::reject(&e), 523 | }; 524 | let path = url.path().to_lowercase(); 525 | let method = req.method().to_lowercase(); 526 | 527 | match path.split("/").nth(1) { 528 | Some("") => match method.as_ref() { 529 | "get" => render_home(req), 530 | _ => render_error(StatusCode::METHOD_NOT_ALLOWED), 531 | }, 532 | Some("favorites") => match method.as_ref() { 533 | "get" => render_favorites(req), 534 | _ => render_error(StatusCode::METHOD_NOT_ALLOWED), 535 | }, 536 | Some("add-favorite") => match method.as_ref() { 537 | "post" => modify_favorites(event, FavoritesModification::AddFavorite), 538 | _ => render_error(StatusCode::METHOD_NOT_ALLOWED), 539 | }, 540 | Some("remove-favorite") => match method.as_ref() { 541 | "post" => modify_favorites(event, FavoritesModification::RemoveFavorite), 542 | _ => render_error(StatusCode::METHOD_NOT_ALLOWED), 543 | }, 544 | Some("images") => match method.as_ref() { 545 | "get" => proxy_image(&path), 546 | _ => render_error(StatusCode::METHOD_NOT_ALLOWED), 547 | }, 548 | _ => render_error(StatusCode::NOT_FOUND), 549 | } 550 | } 551 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | const wasm = (() => { 2 | let wasm; 3 | 4 | const __wbg_get_2ce757bbb9988ab8_target = 5 | GoatsNs.get.bind(GoatsNs) || 6 | function() { 7 | throw new Error(`wasm-bindgen: GoatsNs.get.bind(GoatsNs) does not exist`); 8 | }; 9 | 10 | let cachedTextDecoder = new TextDecoder("utf-8"); 11 | 12 | let cachegetUint8Memory = null; 13 | function getUint8Memory() { 14 | if ( 15 | cachegetUint8Memory === null || 16 | cachegetUint8Memory.buffer !== wasm.memory.buffer 17 | ) { 18 | cachegetUint8Memory = new Uint8Array(wasm.memory.buffer); 19 | } 20 | return cachegetUint8Memory; 21 | } 22 | 23 | function getStringFromWasm(ptr, len) { 24 | return cachedTextDecoder.decode(getUint8Memory().subarray(ptr, ptr + len)); 25 | } 26 | 27 | const slab = [ 28 | { obj: undefined }, 29 | { obj: null }, 30 | { obj: true }, 31 | { obj: false } 32 | ]; 33 | 34 | let slab_next = slab.length; 35 | 36 | function addHeapObject(obj) { 37 | if (slab_next === slab.length) slab.push(slab.length + 1); 38 | const idx = slab_next; 39 | const next = slab[idx]; 40 | 41 | slab_next = next; 42 | 43 | slab[idx] = { obj, cnt: 1 }; 44 | return idx << 1; 45 | } 46 | 47 | function __wbg_get_2ce757bbb9988ab8(arg0, arg1, arg2, arg3) { 48 | let varg0 = getStringFromWasm(arg0, arg1); 49 | let varg2 = getStringFromWasm(arg2, arg3); 50 | return addHeapObject(__wbg_get_2ce757bbb9988ab8_target(varg0, varg2)); 51 | } 52 | 53 | const __wbg_get_8afbefd3accc7077_target = 54 | FavoritesNs.get.bind(FavoritesNs) || 55 | function() { 56 | throw new Error( 57 | `wasm-bindgen: FavoritesNs.get.bind(FavoritesNs) does not exist` 58 | ); 59 | }; 60 | 61 | function __wbg_get_8afbefd3accc7077(arg0, arg1, arg2, arg3) { 62 | let varg0 = getStringFromWasm(arg0, arg1); 63 | let varg2 = getStringFromWasm(arg2, arg3); 64 | return addHeapObject(__wbg_get_8afbefd3accc7077_target(varg0, varg2)); 65 | } 66 | 67 | const __wbg_put_4269264a4947ce72_target = 68 | FavoritesNs.put.bind(FavoritesNs) || 69 | function() { 70 | throw new Error( 71 | `wasm-bindgen: FavoritesNs.put.bind(FavoritesNs) does not exist` 72 | ); 73 | }; 74 | 75 | function __wbg_put_4269264a4947ce72(arg0, arg1, arg2, arg3) { 76 | let varg0 = getStringFromWasm(arg0, arg1); 77 | let varg2 = getStringFromWasm(arg2, arg3); 78 | return addHeapObject(__wbg_put_4269264a4947ce72_target(varg0, varg2)); 79 | } 80 | 81 | const __wbg_delete_42b051788bc6bc86_target = 82 | FavoritesNs.delete.bind(FavoritesNs) || 83 | function() { 84 | throw new Error( 85 | `wasm-bindgen: FavoritesNs.delete.bind(FavoritesNs) does not exist` 86 | ); 87 | }; 88 | 89 | function __wbg_delete_42b051788bc6bc86(arg0, arg1) { 90 | let varg0 = getStringFromWasm(arg0, arg1); 91 | return addHeapObject(__wbg_delete_42b051788bc6bc86_target(varg0)); 92 | } 93 | 94 | const stack = []; 95 | 96 | function getObject(idx) { 97 | if ((idx & 1) === 1) { 98 | return stack[idx >> 1]; 99 | } else { 100 | const val = slab[idx >> 1]; 101 | 102 | return val.obj; 103 | } 104 | } 105 | 106 | function __wbg_fetch_3c8f4980be83ba4a(arg0) { 107 | return addHeapObject(fetch(getObject(arg0))); 108 | } 109 | 110 | let cachedTextEncoder = new TextEncoder("utf-8"); 111 | 112 | function passStringToWasm(arg) { 113 | const buf = cachedTextEncoder.encode(arg); 114 | const ptr = wasm.__wbindgen_malloc(buf.length); 115 | getUint8Memory().set(buf, ptr); 116 | return [ptr, buf.length]; 117 | } 118 | 119 | let cachegetUint32Memory = null; 120 | function getUint32Memory() { 121 | if ( 122 | cachegetUint32Memory === null || 123 | cachegetUint32Memory.buffer !== wasm.memory.buffer 124 | ) { 125 | cachegetUint32Memory = new Uint32Array(wasm.memory.buffer); 126 | } 127 | return cachegetUint32Memory; 128 | } 129 | 130 | function __wbg_generaterandomstr_f4ba49766a789ec2(ret) { 131 | const [retptr, retlen] = passStringToWasm(generate_random_str()); 132 | const mem = getUint32Memory(); 133 | mem[ret / 4] = retptr; 134 | mem[ret / 4 + 1] = retlen; 135 | } 136 | 137 | function dropRef(idx) { 138 | idx = idx >> 1; 139 | if (idx < 4) return; 140 | let obj = slab[idx]; 141 | 142 | obj.cnt -= 1; 143 | if (obj.cnt > 0) return; 144 | 145 | // If we hit 0 then free up our space in the slab 146 | slab[idx] = slab_next; 147 | slab_next = idx; 148 | } 149 | 150 | function takeObject(idx) { 151 | const ret = getObject(idx); 152 | dropRef(idx); 153 | return ret; 154 | } 155 | 156 | function __wbg_new_278751770e71d4dc(arg0, arg1, arg2) { 157 | let varg0 = getStringFromWasm(arg0, arg1); 158 | return addHeapObject(new Response(varg0, takeObject(arg2))); 159 | } 160 | /** 161 | * @param {any} arg0 162 | * @returns {any} 163 | */ 164 | function main(arg0) { 165 | return takeObject(wasm.main(addHeapObject(arg0))); 166 | } 167 | 168 | function GetOwnOrInheritedPropertyDescriptor(obj, id) { 169 | while (obj) { 170 | let desc = Object.getOwnPropertyDescriptor(obj, id); 171 | if (desc) return desc; 172 | obj = Object.getPrototypeOf(obj); 173 | } 174 | throw new Error(`descriptor for id='${id}' not found`); 175 | } 176 | 177 | const __widl_f_request_FetchEvent_target = function() { 178 | return this.request; 179 | }; 180 | 181 | function __widl_f_request_FetchEvent(arg0) { 182 | return addHeapObject( 183 | __widl_f_request_FetchEvent_target.call(getObject(arg0)) 184 | ); 185 | } 186 | 187 | function __widl_instanceof_FormData(idx) { 188 | return getObject(idx) instanceof FormData ? 1 : 0; 189 | } 190 | 191 | const __widl_f_get_FormData_target = 192 | FormData.prototype.get || 193 | function() { 194 | throw new Error(`wasm-bindgen: FormData.prototype.get does not exist`); 195 | }; 196 | 197 | function __widl_f_get_FormData(arg0, arg1, arg2) { 198 | let varg1 = getStringFromWasm(arg1, arg2); 199 | return addHeapObject( 200 | __widl_f_get_FormData_target.call(getObject(arg0), varg1) 201 | ); 202 | } 203 | 204 | function __widl_f_new_Headers(exnptr) { 205 | try { 206 | return addHeapObject(new Headers()); 207 | } catch (e) { 208 | const view = getUint32Memory(); 209 | view[exnptr / 4] = 1; 210 | view[exnptr / 4 + 1] = addHeapObject(e); 211 | } 212 | } 213 | 214 | const __widl_f_append_Headers_target = 215 | Headers.prototype.append || 216 | function() { 217 | throw new Error(`wasm-bindgen: Headers.prototype.append does not exist`); 218 | }; 219 | 220 | function __widl_f_append_Headers(arg0, arg1, arg2, arg3, arg4, exnptr) { 221 | let varg1 = getStringFromWasm(arg1, arg2); 222 | let varg3 = getStringFromWasm(arg3, arg4); 223 | try { 224 | __widl_f_append_Headers_target.call(getObject(arg0), varg1, varg3); 225 | } catch (e) { 226 | const view = getUint32Memory(); 227 | view[exnptr / 4] = 1; 228 | view[exnptr / 4 + 1] = addHeapObject(e); 229 | } 230 | } 231 | 232 | const __widl_f_get_Headers_target = 233 | Headers.prototype.get || 234 | function() { 235 | throw new Error(`wasm-bindgen: Headers.prototype.get does not exist`); 236 | }; 237 | 238 | function isLikeNone(x) { 239 | return x === undefined || x === null; 240 | } 241 | 242 | function __widl_f_get_Headers(ret, arg0, arg1, arg2, exnptr) { 243 | let varg1 = getStringFromWasm(arg1, arg2); 244 | try { 245 | const val = __widl_f_get_Headers_target.call(getObject(arg0), varg1); 246 | const [retptr, retlen] = isLikeNone(val) ? [0, 0] : passStringToWasm(val); 247 | const mem = getUint32Memory(); 248 | mem[ret / 4] = retptr; 249 | mem[ret / 4 + 1] = retlen; 250 | } catch (e) { 251 | const view = getUint32Memory(); 252 | view[exnptr / 4] = 1; 253 | view[exnptr / 4 + 1] = addHeapObject(e); 254 | } 255 | } 256 | 257 | const __widl_f_set_Headers_target = 258 | Headers.prototype.set || 259 | function() { 260 | throw new Error(`wasm-bindgen: Headers.prototype.set does not exist`); 261 | }; 262 | 263 | function __widl_f_set_Headers(arg0, arg1, arg2, arg3, arg4, exnptr) { 264 | let varg1 = getStringFromWasm(arg1, arg2); 265 | let varg3 = getStringFromWasm(arg3, arg4); 266 | try { 267 | __widl_f_set_Headers_target.call(getObject(arg0), varg1, varg3); 268 | } catch (e) { 269 | const view = getUint32Memory(); 270 | view[exnptr / 4] = 1; 271 | view[exnptr / 4 + 1] = addHeapObject(e); 272 | } 273 | } 274 | 275 | function __widl_f_new_with_str_Request(arg0, arg1, exnptr) { 276 | let varg0 = getStringFromWasm(arg0, arg1); 277 | try { 278 | return addHeapObject(new Request(varg0)); 279 | } catch (e) { 280 | const view = getUint32Memory(); 281 | view[exnptr / 4] = 1; 282 | view[exnptr / 4 + 1] = addHeapObject(e); 283 | } 284 | } 285 | 286 | const __widl_f_method_Request_target = function() { 287 | return this.method; 288 | }; 289 | 290 | function __widl_f_method_Request(ret, arg0) { 291 | const [retptr, retlen] = passStringToWasm( 292 | __widl_f_method_Request_target.call(getObject(arg0)) 293 | ); 294 | const mem = getUint32Memory(); 295 | mem[ret / 4] = retptr; 296 | mem[ret / 4 + 1] = retlen; 297 | } 298 | 299 | const __widl_f_url_Request_target = function() { 300 | return this.url; 301 | }; 302 | 303 | function __widl_f_url_Request(ret, arg0) { 304 | const [retptr, retlen] = passStringToWasm( 305 | __widl_f_url_Request_target.call(getObject(arg0)) 306 | ); 307 | const mem = getUint32Memory(); 308 | mem[ret / 4] = retptr; 309 | mem[ret / 4 + 1] = retlen; 310 | } 311 | 312 | const __widl_f_headers_Request_target = function() { 313 | return this.headers; 314 | }; 315 | 316 | function __widl_f_headers_Request(arg0) { 317 | return addHeapObject(__widl_f_headers_Request_target.call(getObject(arg0))); 318 | } 319 | 320 | const __widl_f_form_data_Request_target = 321 | Request.prototype.formData || 322 | function() { 323 | throw new Error( 324 | `wasm-bindgen: Request.prototype.formData does not exist` 325 | ); 326 | }; 327 | 328 | function __widl_f_form_data_Request(arg0, exnptr) { 329 | try { 330 | return addHeapObject( 331 | __widl_f_form_data_Request_target.call(getObject(arg0)) 332 | ); 333 | } catch (e) { 334 | const view = getUint32Memory(); 335 | view[exnptr / 4] = 1; 336 | view[exnptr / 4 + 1] = addHeapObject(e); 337 | } 338 | } 339 | 340 | function __wbg_new_7a259c7860f1b5c4() { 341 | return addHeapObject(new Array()); 342 | } 343 | 344 | function __wbg_new_d85589be79b6c4df(arg0, arg1) { 345 | let varg0 = getStringFromWasm(arg0, arg1); 346 | return addHeapObject(new Error(varg0)); 347 | } 348 | 349 | function __wbg_instanceof_Function_220b2df285bee4ce(idx) { 350 | return getObject(idx) instanceof Function ? 1 : 0; 351 | } 352 | 353 | const __wbg_call_eeece939fc9a62db_target = 354 | Function.prototype.call || 355 | function() { 356 | throw new Error(`wasm-bindgen: Function.prototype.call does not exist`); 357 | }; 358 | 359 | function __wbg_call_eeece939fc9a62db(arg0, arg1, arg2, exnptr) { 360 | try { 361 | return addHeapObject( 362 | __wbg_call_eeece939fc9a62db_target.call( 363 | getObject(arg0), 364 | getObject(arg1), 365 | getObject(arg2) 366 | ) 367 | ); 368 | } catch (e) { 369 | const view = getUint32Memory(); 370 | view[exnptr / 4] = 1; 371 | view[exnptr / 4 + 1] = addHeapObject(e); 372 | } 373 | } 374 | 375 | function __wbg_new_6b7f4334f7b728fd() { 376 | return addHeapObject(new Object()); 377 | } 378 | 379 | const __wbg_get_b5fa2669cbf91d6f_target = 380 | Reflect.get.bind(Reflect) || 381 | function() { 382 | throw new Error(`wasm-bindgen: Reflect.get.bind(Reflect) does not exist`); 383 | }; 384 | 385 | function __wbg_get_b5fa2669cbf91d6f(arg0, arg1, exnptr) { 386 | try { 387 | return addHeapObject( 388 | __wbg_get_b5fa2669cbf91d6f_target(getObject(arg0), getObject(arg1)) 389 | ); 390 | } catch (e) { 391 | const view = getUint32Memory(); 392 | view[exnptr / 4] = 1; 393 | view[exnptr / 4 + 1] = addHeapObject(e); 394 | } 395 | } 396 | 397 | const __wbg_set_6f4fee47694f666d_target = 398 | Reflect.set.bind(Reflect) || 399 | function() { 400 | throw new Error(`wasm-bindgen: Reflect.set.bind(Reflect) does not exist`); 401 | }; 402 | 403 | function __wbg_set_6f4fee47694f666d(arg0, arg1, arg2, exnptr) { 404 | try { 405 | return __wbg_set_6f4fee47694f666d_target( 406 | getObject(arg0), 407 | getObject(arg1), 408 | getObject(arg2) 409 | ) 410 | ? 1 411 | : 0; 412 | } catch (e) { 413 | const view = getUint32Memory(); 414 | view[exnptr / 4] = 1; 415 | view[exnptr / 4 + 1] = addHeapObject(e); 416 | } 417 | } 418 | 419 | let cachedGlobalArgumentPtr = null; 420 | function globalArgumentPtr() { 421 | if (cachedGlobalArgumentPtr === null) { 422 | cachedGlobalArgumentPtr = wasm.__wbindgen_global_argument_ptr(); 423 | } 424 | return cachedGlobalArgumentPtr; 425 | } 426 | 427 | function getGlobalArgument(arg) { 428 | const idx = globalArgumentPtr() / 4 + arg; 429 | return getUint32Memory()[idx]; 430 | } 431 | 432 | function __wbg_new_ea60e716adf807fe(arg0) { 433 | let cbarg0 = function(arg0, arg1) { 434 | let a = this.a; 435 | this.a = 0; 436 | try { 437 | return this.f(a, this.b, addHeapObject(arg0), addHeapObject(arg1)); 438 | } finally { 439 | this.a = a; 440 | } 441 | }; 442 | cbarg0.f = wasm.__wbg_function_table.get(arg0); 443 | cbarg0.a = getGlobalArgument(0); 444 | cbarg0.b = getGlobalArgument(0 + 1); 445 | try { 446 | return addHeapObject(new Promise(cbarg0.bind(cbarg0))); 447 | } finally { 448 | cbarg0.a = cbarg0.b = 0; 449 | } 450 | } 451 | 452 | const __wbg_reject_aa4c9a50081bb8a9_target = 453 | Promise.reject.bind(Promise) || 454 | function() { 455 | throw new Error( 456 | `wasm-bindgen: Promise.reject.bind(Promise) does not exist` 457 | ); 458 | }; 459 | 460 | function __wbg_reject_aa4c9a50081bb8a9(arg0) { 461 | return addHeapObject(__wbg_reject_aa4c9a50081bb8a9_target(getObject(arg0))); 462 | } 463 | 464 | const __wbg_resolve_a96e53ca1abfddb3_target = 465 | Promise.resolve.bind(Promise) || 466 | function() { 467 | throw new Error( 468 | `wasm-bindgen: Promise.resolve.bind(Promise) does not exist` 469 | ); 470 | }; 471 | 472 | function __wbg_resolve_a96e53ca1abfddb3(arg0) { 473 | return addHeapObject( 474 | __wbg_resolve_a96e53ca1abfddb3_target(getObject(arg0)) 475 | ); 476 | } 477 | 478 | const __wbg_then_074a42f44879a676_target = 479 | Promise.prototype.then || 480 | function() { 481 | throw new Error(`wasm-bindgen: Promise.prototype.then does not exist`); 482 | }; 483 | 484 | function __wbg_then_074a42f44879a676(arg0, arg1, arg2) { 485 | return addHeapObject( 486 | __wbg_then_074a42f44879a676_target.call( 487 | getObject(arg0), 488 | getObject(arg1), 489 | getObject(arg2) 490 | ) 491 | ); 492 | } 493 | 494 | function __wbindgen_object_clone_ref(idx) { 495 | // If this object is on the stack promote it to the heap. 496 | if ((idx & 1) === 1) return addHeapObject(getObject(idx)); 497 | 498 | // Otherwise if the object is on the heap just bump the 499 | // refcount and move on 500 | const val = slab[idx >> 1]; 501 | val.cnt += 1; 502 | return idx; 503 | } 504 | 505 | function __wbindgen_object_drop_ref(i) { 506 | dropRef(i); 507 | } 508 | 509 | function __wbindgen_string_new(p, l) { 510 | return addHeapObject(getStringFromWasm(p, l)); 511 | } 512 | 513 | function __wbindgen_number_new(i) { 514 | return addHeapObject(i); 515 | } 516 | 517 | function __wbindgen_number_get(n, invalid) { 518 | let obj = getObject(n); 519 | if (typeof obj === "number") return obj; 520 | getUint8Memory()[invalid] = 1; 521 | return 0; 522 | } 523 | 524 | function __wbindgen_is_null(idx) { 525 | return getObject(idx) === null ? 1 : 0; 526 | } 527 | 528 | function __wbindgen_is_undefined(idx) { 529 | return getObject(idx) === undefined ? 1 : 0; 530 | } 531 | 532 | function __wbindgen_boolean_get(i) { 533 | let v = getObject(i); 534 | if (typeof v === "boolean") { 535 | return v ? 1 : 0; 536 | } else { 537 | return 2; 538 | } 539 | } 540 | 541 | function __wbindgen_is_symbol(i) { 542 | return typeof getObject(i) === "symbol" ? 1 : 0; 543 | } 544 | 545 | function __wbindgen_string_get(i, len_ptr) { 546 | let obj = getObject(i); 547 | if (typeof obj !== "string") return 0; 548 | const [ptr, len] = passStringToWasm(obj); 549 | getUint32Memory()[len_ptr / 4] = len; 550 | return ptr; 551 | } 552 | 553 | function __wbindgen_cb_drop(i) { 554 | const obj = getObject(i).original; 555 | dropRef(i); 556 | if (obj.cnt-- == 1) { 557 | obj.a = 0; 558 | return 1; 559 | } 560 | return 0; 561 | } 562 | 563 | function __wbindgen_json_serialize(idx, ptrptr) { 564 | const [ptr, len] = passStringToWasm(JSON.stringify(getObject(idx))); 565 | getUint32Memory()[ptrptr / 4] = ptr; 566 | return len; 567 | } 568 | 569 | function __wbindgen_closure_wrapper322(a, b, fi, di, _ignored) { 570 | const f = wasm.__wbg_function_table.get(fi); 571 | const d = wasm.__wbg_function_table.get(di); 572 | const cb = function(arg0) { 573 | this.cnt++; 574 | let a = this.a; 575 | this.a = 0; 576 | try { 577 | return f(a, b, addHeapObject(arg0)); 578 | } finally { 579 | this.a = a; 580 | if (this.cnt-- == 1) d(this.a, b); 581 | } 582 | }; 583 | cb.a = a; 584 | cb.cnt = 1; 585 | let real = cb.bind(cb); 586 | real.original = cb; 587 | return addHeapObject(real); 588 | } 589 | 590 | function __wbindgen_throw(ptr, len) { 591 | throw new Error(getStringFromWasm(ptr, len)); 592 | } 593 | 594 | const importObject = { 595 | "./the_best_goats": { 596 | __wbindgen_object_drop_ref, 597 | __wbindgen_closure_wrapper322, 598 | __wbg_get_2ce757bbb9988ab8, 599 | __wbg_get_8afbefd3accc7077, 600 | __wbg_new_278751770e71d4dc, 601 | __wbg_put_4269264a4947ce72, 602 | __wbg_delete_42b051788bc6bc86, 603 | __wbg_generaterandomstr_f4ba49766a789ec2, 604 | __wbg_fetch_3c8f4980be83ba4a, 605 | __wbindgen_json_serialize, 606 | __widl_f_request_FetchEvent, 607 | __widl_instanceof_FormData, 608 | __widl_f_get_FormData, 609 | __widl_f_new_Headers, 610 | __widl_f_append_Headers, 611 | __widl_f_get_Headers, 612 | __widl_f_set_Headers, 613 | __widl_f_new_with_str_Request, 614 | __widl_f_method_Request, 615 | __widl_f_url_Request, 616 | __widl_f_headers_Request, 617 | __widl_f_form_data_Request, 618 | __wbindgen_string_new, 619 | __wbindgen_cb_drop, 620 | __wbg_get_b5fa2669cbf91d6f, 621 | __wbg_new_7a259c7860f1b5c4, 622 | __wbg_new_d85589be79b6c4df, 623 | __wbg_instanceof_Function_220b2df285bee4ce, 624 | __wbg_call_eeece939fc9a62db, 625 | __wbg_new_6b7f4334f7b728fd, 626 | __wbg_set_6f4fee47694f666d, 627 | __wbg_new_ea60e716adf807fe, 628 | __wbg_reject_aa4c9a50081bb8a9, 629 | __wbg_resolve_a96e53ca1abfddb3, 630 | __wbg_then_074a42f44879a676, 631 | __wbindgen_number_get, 632 | __wbindgen_string_get, 633 | __wbindgen_boolean_get, 634 | __wbindgen_object_clone_ref, 635 | __wbindgen_is_null, 636 | __wbindgen_is_undefined, 637 | __wbindgen_is_symbol, 638 | __wbindgen_throw, 639 | __wbindgen_number_new 640 | } 641 | }; 642 | 643 | const inst = new WebAssembly.Instance(WASM, importObject); 644 | wasm = inst.exports; 645 | 646 | return { main }; 647 | })(); 648 | 649 | // async function fetchGoats() { 650 | // const names = [ 651 | // "Bailey", 652 | // "Bella", 653 | // "Max", 654 | // "Lucy", 655 | // "Charlie", 656 | // "Molly", 657 | // "Buddy", 658 | // "Daisy", 659 | // "Rocky", 660 | // "Maggie", 661 | // "Jake", 662 | // "Sophie", 663 | // "Jack", 664 | // "Sadie", 665 | // "Toby", 666 | // "Chloe", 667 | // "Cody", 668 | // "Bailey", 669 | // "Buster", 670 | // "Lola", 671 | // "Duke", 672 | // "Zoe", 673 | // "Cooper", 674 | // "Abby", 675 | // "Riley", 676 | // "Ginger", 677 | // "Harley", 678 | // "Roxy", 679 | // "Bear", 680 | // "Gracie", 681 | // "Tucker", 682 | // "Coco", 683 | // "Murphy", 684 | // "Sasha", 685 | // "Lucky", 686 | // "Lily", 687 | // "Oliver", 688 | // "Angel", 689 | // "Sam", 690 | // "Princess", 691 | // "Oscar", 692 | // "Emma", 693 | // "Teddy", 694 | // "Annie" 695 | // ]; 696 | 697 | // return names.map((name, i) => { 698 | // const id = i + 1; 699 | // const imageId = String(id).padStart(4, "0"); 700 | // return { 701 | // id, 702 | // name: name, 703 | // image: `/images/goat-${imageId}.jpg`, 704 | // imageSmall: `/images/goat-${imageId}-small.jpg` 705 | // }; 706 | // }); 707 | // } 708 | 709 | // async function fetchFavorites(userId) { 710 | // return [3, 6]; 711 | // } 712 | 713 | // async function saveFavorites(userId) {} 714 | 715 | // async function deleteFavorites(userId) {} 716 | 717 | function generate_random_str() { 718 | function getRandomSymbol(symbol) { 719 | let array; 720 | 721 | if (symbol === "y") { 722 | array = ["8", "9", "a", "b"]; 723 | return array[Math.floor(Math.random() * array.length)]; 724 | } 725 | 726 | array = new Uint8Array(1); 727 | crypto.getRandomValues(array); 728 | return (array[0] % 16).toString(16); 729 | } 730 | 731 | return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace( 732 | /[xy]/g, 733 | getRandomSymbol 734 | ); 735 | } 736 | 737 | addEventListener("fetch", event => { 738 | event.respondWith(handleRequest(event)); 739 | }); 740 | 741 | async function handleRequest(event) { 742 | try { 743 | return await wasm.main(event); 744 | } catch (e) { 745 | return new Response(e.stack || e.message || e || "unknown error", { 746 | status: 500 747 | }); 748 | } 749 | } 750 | --------------------------------------------------------------------------------