Repository: cloudflare/wrangler (rust)

Issue:
Allow file bindings as text_blob 
### 💡 Feature request

<!-- 
  Please fill out each section below before submitting your 💡 feature request.

  Before opening a new issue, please search for existing issues: https://github.com/cloudflare/wrangler/issues

  Thanks! -->

#### Overview and problem statement

We have a worker that does a HTML transformation that adds a new JavaScript script tag, called client code. We want to store the client code along the server-side transformation.

The server-side and client-side codes need to keep sync, even during deployements, so I think KV wouldn't be a good fit.

Alternatively, we can use webpack to inline the client-side code into the worker. But allowing files as text_blob seems useful (a customer requested to inline images, we can use the same technique). 

#### Basic example

worker.js:
```js
addEventListener('fetch', event => {
  event.respondWith(handleRequest(event.request))
})

async function handleRequest(request) {
  return new Response(CLIENT_SIDE_CODE);
}
```

metadata.json:
```json
{
  "body_part": "foo",
  "bindings": [
    {
      "name": "CLIENT_SIDE_CODE",
      "type": "text_blob",
      "part": "CLIENT_SIDE_CODE"
    },
  ]
}
```

Then the multipart from needs to include the `CLIENT_SIDE_CODE` part. Note that the size limit needs to take metadata.json into account too.

Pull request description:
Add support for text blob bindings
closes #483 

I need to write docs, and confirm I got the env inheritance stuff right (I copied how it works for vars, need to ensure that is correct.)

But my baby test passes :)

I probably need to ensure size limits are still taken into account here as well.

changelog/docs:

Wrangler now supports text blobs! text blobs are values to use in your workers, but read from a file instead of a string in your TOML.

Usage:

text_blobs = { FOO = "path/to/foo.txt", BAR = "path/to/bar.txt" }

Patch:
diff --git a/src/commands/kv/mod.rs b/src/commands/kv/mod.rs
index c6e2bcc3..0e533fd5 100644
--- a/src/commands/kv/mod.rs
+++ b/src/commands/kv/mod.rs
@@ -124,6 +124,7 @@ mod tests {
             webpack_config: None,
             site: None,
             vars: None,
+            text_blobs: None,
         };
         assert!(kv::get_namespace_id(&target_with_dup_kv_bindings, "").is_err());
     }
diff --git a/src/fixtures/wrangler_toml.rs b/src/fixtures/wrangler_toml.rs
index 0a2a48c9..b87bf363 100644
--- a/src/fixtures/wrangler_toml.rs
+++ b/src/fixtures/wrangler_toml.rs
@@ -44,6 +44,7 @@ pub struct EnvConfig {
     #[serde(alias = "kv-namespaces")]
     pub kv_namespaces: Option<Vec<KvConfig>>,
     pub vars: Option<HashMap<&'static str, &'static str>>,
+    pub text_blobs: Option<HashMap<&'static str, &'static str>>,
     pub triggers: Option<Triggers>,
 }
 
@@ -104,6 +105,7 @@ pub struct WranglerToml {
     pub kv_namespaces: Option<Vec<KvConfig>>,
     pub site: Option<SiteConfig>,
     pub vars: Option<HashMap<&'static str, &'static str>>,
+    pub text_blobs: Option<HashMap<&'static str, &'static str>>,
     pub triggers: Option<Triggers>,
 }
 
diff --git a/src/settings/toml/environment.rs b/src/settings/toml/environment.rs
index 8703579a..1ae42621 100644
--- a/src/settings/toml/environment.rs
+++ b/src/settings/toml/environment.rs
@@ -1,4 +1,5 @@
 use std::collections::HashMap;
+use std::path::PathBuf;
 
 use serde::{Deserialize, Serialize};
 use serde_with::rust::string_empty_as_none;
@@ -25,6 +26,7 @@ pub struct Environment {
     #[serde(alias = "kv-namespaces")]
     pub kv_namespaces: Option<Vec<ConfigKvNamespace>>,
     pub vars: Option<HashMap<String, String>>,
+    pub text_blobs: Option<HashMap<String, PathBuf>>,
     pub triggers: Option<Triggers>,
 }
 
diff --git a/src/settings/toml/manifest.rs b/src/settings/toml/manifest.rs
index ebc7a935..fd48508c 100644
--- a/src/settings/toml/manifest.rs
+++ b/src/settings/toml/manifest.rs
@@ -49,6 +49,7 @@ pub struct Manifest {
     pub kv_namespaces: Option<Vec<ConfigKvNamespace>>,
     pub env: Option<HashMap<String, Environment>>,
     pub vars: Option<HashMap<String, String>>,
+    pub text_blobs: Option<HashMap<String, PathBuf>>,
     pub triggers: Option<Triggers>,
 }
 
@@ -295,6 +296,7 @@ impl Manifest {
             kv_namespaces: get_namespaces(self.kv_namespaces.clone(), preview)?, // Not inherited
             site: self.site.clone(), // Inherited
             vars: self.vars.clone(), // Not inherited
+            text_blobs: self.text_blobs.clone(), // Inherited
         };
 
         let environment = self.get_environment(environment_name)?;
diff --git a/src/settings/toml/target.rs b/src/settings/toml/target.rs
index 7823f23d..a6836524 100644
--- a/src/settings/toml/target.rs
+++ b/src/settings/toml/target.rs
@@ -16,6 +16,7 @@ pub struct Target {
     pub webpack_config: Option<String>,
     pub site: Option<Site>,
     pub vars: Option<HashMap<String, String>>,
+    pub text_blobs: Option<HashMap<String, PathBuf>>,
 }
 
 impl Target {
diff --git a/src/sites/mod.rs b/src/sites/mod.rs
index d2c0aed7..2b4e7497 100644
--- a/src/sites/mod.rs
+++ b/src/sites/mod.rs
@@ -320,6 +320,7 @@ mod tests {
             webpack_config: None,
             site: Some(site),
             vars: None,
+            text_blobs: None,
         }
     }
 
diff --git a/src/upload/form/mod.rs b/src/upload/form/mod.rs
index 09de2c0e..c2c1fe8e 100644
--- a/src/upload/form/mod.rs
+++ b/src/upload/form/mod.rs
@@ -33,6 +33,13 @@ pub fn build(
     let mut plain_texts: Vec<PlainText> = Vec::new();
     let mut wasm_modules: Vec<WasmModule> = Vec::new();
 
+    if let Some(blobs) = &target.text_blobs {
+        for (key, blob_path) in blobs.iter() {
+            let blob = fs::read_to_string(blob_path)?;
+            text_blobs.push(TextBlob::new(blob, key.clone())?);
+        }
+    }
+
     if let Some(vars) = &target.vars {
         for (key, value) in vars.iter() {
             plain_texts.push(PlainText::new(key.clone(), value.clone())?)


Test patch:
diff --git a/tests/preview.rs b/tests/preview.rs
index f9e9f843..565c10e4 100644
--- a/tests/preview.rs
+++ b/tests/preview.rs
@@ -227,6 +227,35 @@ fn it_previews_with_config_text() {
     preview_succeeds_with(&fixture, None, test_value);
 }
 
+#[test]
+fn it_previews_with_text_blob() {
+    let fixture = Fixture::new();
+    fixture.create_file(
+        "index.js",
+        r#"
+        addEventListener('fetch', event => {
+            event.respondWith(handleRequest(event.request))
+        })
+
+        async function handleRequest(request) {
+            return new Response(BLOB)
+        }
+    "#,
+    );
+    fixture.create_default_package_json();
+
+    let test_value: &'static str = "sdhftiuyrtdhfjgpoopuyrdfjgkyitudrhf";
+    fixture.create_file("blob.txt", test_value);
+
+    let mut wrangler_toml = WranglerToml::javascript("test-preview-with-config");
+    let mut blobs: HashMap<&'static str, &'static str> = HashMap::new();
+    blobs.insert("BLOB", "blob.txt");
+    wrangler_toml.text_blobs = Some(blobs);
+    fixture.create_wrangler_toml(wrangler_toml);
+
+    preview_succeeds_with(&fixture, None, test_value);
+}
+
 fn preview_succeeds_with(fixture: &Fixture, env: Option<&str>, expected: &str) {
     env::remove_var("CF_ACCOUNT_ID");
     env::remove_var("CF_ZONE_ID");