diff --git a/docs/docs/04-standard-library/04-util/api-reference.md b/docs/docs/04-standard-library/04-util/api-reference.md
index 6e187d19f4f..d5fb095130e 100644
--- a/docs/docs/04-standard-library/04-util/api-reference.md
+++ b/docs/docs/04-standard-library/04-util/api-reference.md
@@ -23,6 +23,8 @@ Utility functions.
| **Name** | **Description** |
| --- | --- |
+| base64Decode
| Converts a string from base64 to UTF-8. |
+| base64Encode
| Converts a string from UTF-8 to base64. |
| env
| Returns the value of an environment variable. |
| nanoid
| Generates a unique ID using the nanoid library. |
| sha256
| Computes the SHA256 hash of the given data. |
@@ -33,6 +35,56 @@ Utility functions.
---
+##### `base64Decode`
+
+```wing
+bring util;
+
+util.base64Decode(stringToDecode: str, url?: bool);
+```
+
+Converts a string from base64 to UTF-8.
+
+###### `stringToDecode`Required
+
+- *Type:* str
+
+base64 string to decode.
+
+---
+
+###### `url`Optional
+
+- *Type:* bool
+
+If `true`, the source is expected to be a URL-safe base64 string.
+
+---
+
+##### `base64Encode`
+
+```wing
+bring util;
+
+util.base64Encode(stringToEncode: str, url?: bool);
+```
+
+Converts a string from UTF-8 to base64.
+
+###### `stringToEncode`Required
+
+- *Type:* str
+
+---
+
+###### `url`Optional
+
+- *Type:* bool
+
+If `true`, a URL-safe base64 is returned.
+
+---
+
##### `env`
```wing
diff --git a/examples/tests/sdk_tests/util/base64.w b/examples/tests/sdk_tests/util/base64.w
new file mode 100644
index 00000000000..f0171adbc37
--- /dev/null
+++ b/examples/tests/sdk_tests/util/base64.w
@@ -0,0 +1,25 @@
+bring util;
+
+let string = "https://www.winglang.io/docs";
+let base64Encode = util.base64Encode(string);
+let base64urlEncode = util.base64Encode(string, true);
+let base64Decode = util.base64Decode("aHR0cHM6Ly93d3cud2luZ2xhbmcuaW8vZG9jcw==");
+let base64urlDecode = util.base64Decode("aHR0cHM6Ly93d3cud2luZ2xhbmcuaW8vZG9jcw", true);
+
+assert(base64Encode == "aHR0cHM6Ly93d3cud2luZ2xhbmcuaW8vZG9jcw==");
+assert(base64urlEncode == "aHR0cHM6Ly93d3cud2luZ2xhbmcuaW8vZG9jcw");
+assert(base64Decode == string);
+assert(base64urlDecode == string);
+
+test "inflight base64" {
+ let string = "https://www.winglang.io/docs";
+ let base64Encode = util.base64Encode(string);
+ let base64urlEncode = util.base64Encode(string, true);
+ let base64Decode = util.base64Decode("aHR0cHM6Ly93d3cud2luZ2xhbmcuaW8vZG9jcw==");
+ let base64urlDecode = util.base64Decode("aHR0cHM6Ly93d3cud2luZ2xhbmcuaW8vZG9jcw", true);
+
+ assert(base64Encode == "aHR0cHM6Ly93d3cud2luZ2xhbmcuaW8vZG9jcw==");
+ assert(base64urlEncode == "aHR0cHM6Ly93d3cud2luZ2xhbmcuaW8vZG9jcw");
+ assert(base64Decode == string);
+ assert(base64urlDecode == string);
+}
\ No newline at end of file
diff --git a/libs/wingc/src/lsp/snapshots/completions/util_static_methods.snap b/libs/wingc/src/lsp/snapshots/completions/util_static_methods.snap
index f9eb8e1722c..64dd9ba3511 100644
--- a/libs/wingc/src/lsp/snapshots/completions/util_static_methods.snap
+++ b/libs/wingc/src/lsp/snapshots/completions/util_static_methods.snap
@@ -1,6 +1,30 @@
---
source: libs/wingc/src/lsp/completions.rs
---
+- label: base64Decode
+ kind: 2
+ detail: "(stringToDecode: str, url: bool?): str"
+ documentation:
+ kind: markdown
+ value: "```wing\nstatic base64Decode: (stringToDecode: str, url: bool?): str\n```\n---\nConverts a string from base64 to UTF-8.\n\n\n### Returns\nThe UTF-8 string."
+ sortText: ff|base64Decode
+ insertText: base64Decode($0)
+ insertTextFormat: 2
+ command:
+ title: triggerParameterHints
+ command: editor.action.triggerParameterHints
+- label: base64Encode
+ kind: 2
+ detail: "(stringToEncode: str, url: bool?): str"
+ documentation:
+ kind: markdown
+ value: "```wing\nstatic base64Encode: (stringToEncode: str, url: bool?): str\n```\n---\nConverts a string from UTF-8 to base64.\n\n\n### Returns\nThe base64 string."
+ sortText: ff|base64Encode
+ insertText: base64Encode($0)
+ insertTextFormat: 2
+ command:
+ title: triggerParameterHints
+ command: editor.action.triggerParameterHints
- label: env
kind: 2
detail: "(name: str): str"
diff --git a/libs/wingsdk/src/util/util.ts b/libs/wingsdk/src/util/util.ts
index d159eaffebf..70aabf9bc54 100644
--- a/libs/wingsdk/src/util/util.ts
+++ b/libs/wingsdk/src/util/util.ts
@@ -78,6 +78,28 @@ export class Util {
return process.env[name];
}
+ /**
+ * Converts a string from UTF-8 to base64.
+ * @param name The name of the UTF-8 string to encode.
+ * @param url If `true`, a URL-safe base64 is returned.
+ * @returns The base64 string.
+ */
+ public static base64Encode(stringToEncode: string, url?: boolean): string {
+ return Buffer.from(stringToEncode).toString(url ? "base64url" : "base64");
+ }
+
+ /**
+ * Converts a string from base64 to UTF-8.
+ * @param stringToDecode base64 string to decode.
+ * @param url If `true`, the source is expected to be a URL-safe base64 string
+ * @returns The UTF-8 string.
+ */
+ public static base64Decode(stringToDecode: string, url?: boolean): string {
+ return Buffer.from(stringToDecode, url ? "base64url" : "base64").toString(
+ "utf8"
+ );
+ }
+
/**
* Suspends execution for a given duration.
* @param delay The time to suspend execution.
diff --git a/tools/hangar/__snapshots__/test_corpus/sdk_tests/util/base64.w_compile_tf-aws.md b/tools/hangar/__snapshots__/test_corpus/sdk_tests/util/base64.w_compile_tf-aws.md
new file mode 100644
index 00000000000..1569a00007c
--- /dev/null
+++ b/tools/hangar/__snapshots__/test_corpus/sdk_tests/util/base64.w_compile_tf-aws.md
@@ -0,0 +1,201 @@
+# [base64.w](../../../../../../examples/tests/sdk_tests/util/base64.w) | compile | tf-aws
+
+## inflight.$Closure1.js
+```js
+module.exports = function({ $util_Util }) {
+ class $Closure1 {
+ constructor({ }) {
+ const $obj = (...args) => this.handle(...args);
+ Object.setPrototypeOf($obj, this);
+ return $obj;
+ }
+ async handle() {
+ const string = "https://www.winglang.io/docs";
+ const base64Encode = (await $util_Util.base64Encode(string));
+ const base64urlEncode = (await $util_Util.base64Encode(string,true));
+ const base64Decode = (await $util_Util.base64Decode("aHR0cHM6Ly93d3cud2luZ2xhbmcuaW8vZG9jcw=="));
+ const base64urlDecode = (await $util_Util.base64Decode("aHR0cHM6Ly93d3cud2luZ2xhbmcuaW8vZG9jcw",true));
+ {((cond) => {if (!cond) throw new Error("assertion failed: base64Encode == \"aHR0cHM6Ly93d3cud2luZ2xhbmcuaW8vZG9jcw==\"")})((base64Encode === "aHR0cHM6Ly93d3cud2luZ2xhbmcuaW8vZG9jcw=="))};
+ {((cond) => {if (!cond) throw new Error("assertion failed: base64urlEncode == \"aHR0cHM6Ly93d3cud2luZ2xhbmcuaW8vZG9jcw\"")})((base64urlEncode === "aHR0cHM6Ly93d3cud2luZ2xhbmcuaW8vZG9jcw"))};
+ {((cond) => {if (!cond) throw new Error("assertion failed: base64Decode == string")})((base64Decode === string))};
+ {((cond) => {if (!cond) throw new Error("assertion failed: base64urlDecode == string")})((base64urlDecode === string))};
+ }
+ }
+ return $Closure1;
+}
+
+```
+
+## main.tf.json
+```json
+{
+ "//": {
+ "metadata": {
+ "backend": "local",
+ "stackName": "root",
+ "version": "0.17.0"
+ },
+ "outputs": {
+ "root": {
+ "Default": {
+ "cloud.TestRunner": {
+ "TestFunctionArns": "WING_TEST_RUNNER_FUNCTION_ARNS"
+ }
+ }
+ }
+ }
+ },
+ "output": {
+ "WING_TEST_RUNNER_FUNCTION_ARNS": {
+ "value": "[[\"root/Default/Default/test:inflight base64\",\"${aws_lambda_function.testinflightbase64_Handler_31E9772F.arn}\"]]"
+ }
+ },
+ "provider": {
+ "aws": [
+ {}
+ ]
+ },
+ "resource": {
+ "aws_iam_role": {
+ "testinflightbase64_Handler_IamRole_49F68A60": {
+ "//": {
+ "metadata": {
+ "path": "root/Default/Default/test:inflight base64/Handler/IamRole",
+ "uniqueId": "testinflightbase64_Handler_IamRole_49F68A60"
+ }
+ },
+ "assume_role_policy": "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Action\":\"sts:AssumeRole\",\"Principal\":{\"Service\":\"lambda.amazonaws.com\"},\"Effect\":\"Allow\"}]}"
+ }
+ },
+ "aws_iam_role_policy": {
+ "testinflightbase64_Handler_IamRolePolicy_031C1061": {
+ "//": {
+ "metadata": {
+ "path": "root/Default/Default/test:inflight base64/Handler/IamRolePolicy",
+ "uniqueId": "testinflightbase64_Handler_IamRolePolicy_031C1061"
+ }
+ },
+ "policy": "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Effect\":\"Allow\",\"Action\":\"none:null\",\"Resource\":\"*\"}]}",
+ "role": "${aws_iam_role.testinflightbase64_Handler_IamRole_49F68A60.name}"
+ }
+ },
+ "aws_iam_role_policy_attachment": {
+ "testinflightbase64_Handler_IamRolePolicyAttachment_FA451656": {
+ "//": {
+ "metadata": {
+ "path": "root/Default/Default/test:inflight base64/Handler/IamRolePolicyAttachment",
+ "uniqueId": "testinflightbase64_Handler_IamRolePolicyAttachment_FA451656"
+ }
+ },
+ "policy_arn": "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole",
+ "role": "${aws_iam_role.testinflightbase64_Handler_IamRole_49F68A60.name}"
+ }
+ },
+ "aws_lambda_function": {
+ "testinflightbase64_Handler_31E9772F": {
+ "//": {
+ "metadata": {
+ "path": "root/Default/Default/test:inflight base64/Handler/Default",
+ "uniqueId": "testinflightbase64_Handler_31E9772F"
+ }
+ },
+ "environment": {
+ "variables": {
+ "WING_FUNCTION_NAME": "Handler-c853d8cf",
+ "WING_TARGET": "tf-aws"
+ }
+ },
+ "function_name": "Handler-c853d8cf",
+ "handler": "index.handler",
+ "publish": true,
+ "role": "${aws_iam_role.testinflightbase64_Handler_IamRole_49F68A60.arn}",
+ "runtime": "nodejs18.x",
+ "s3_bucket": "${aws_s3_bucket.Code.bucket}",
+ "s3_key": "${aws_s3_object.testinflightbase64_Handler_S3Object_C9A792F2.key}",
+ "timeout": 30,
+ "vpc_config": {
+ "security_group_ids": [],
+ "subnet_ids": []
+ }
+ }
+ },
+ "aws_s3_bucket": {
+ "Code": {
+ "//": {
+ "metadata": {
+ "path": "root/Default/Code",
+ "uniqueId": "Code"
+ }
+ },
+ "bucket_prefix": "code-c84a50b1-"
+ }
+ },
+ "aws_s3_object": {
+ "testinflightbase64_Handler_S3Object_C9A792F2": {
+ "//": {
+ "metadata": {
+ "path": "root/Default/Default/test:inflight base64/Handler/S3Object",
+ "uniqueId": "testinflightbase64_Handler_S3Object_C9A792F2"
+ }
+ },
+ "bucket": "${aws_s3_bucket.Code.bucket}",
+ "key": "",
+ "source": ""
+ }
+ }
+ }
+}
+```
+
+## preflight.js
+```js
+const $stdlib = require('@winglang/sdk');
+const $outdir = process.env.WING_SYNTH_DIR ?? ".";
+const std = $stdlib.std;
+const $wing_is_test = process.env.WING_IS_TEST === "true";
+const util = require('@winglang/sdk').util;
+class $Root extends $stdlib.std.Resource {
+ constructor(scope, id) {
+ super(scope, id);
+ class $Closure1 extends $stdlib.std.Resource {
+ constructor(scope, id, ) {
+ super(scope, id);
+ this.display.hidden = true;
+ this._addInflightOps("handle", "$inflight_init");
+ }
+ static _toInflightType(context) {
+ return $stdlib.core.NodeJsCode.fromInline(`
+ require("./inflight.$Closure1.js")({
+ $util_Util: ${context._lift(util.Util)},
+ })
+ `);
+ }
+ _toInflight() {
+ return $stdlib.core.NodeJsCode.fromInline(`
+ (await (async () => {
+ const $Closure1Client = ${$Closure1._toInflightType(this).text};
+ const client = new $Closure1Client({
+ });
+ if (client.$inflight_init) { await client.$inflight_init(); }
+ return client;
+ })())
+ `);
+ }
+ }
+ const string = "https://www.winglang.io/docs";
+ const base64Encode = (util.Util.base64Encode(string));
+ const base64urlEncode = (util.Util.base64Encode(string,true));
+ const base64Decode = (util.Util.base64Decode("aHR0cHM6Ly93d3cud2luZ2xhbmcuaW8vZG9jcw=="));
+ const base64urlDecode = (util.Util.base64Decode("aHR0cHM6Ly93d3cud2luZ2xhbmcuaW8vZG9jcw",true));
+ {((cond) => {if (!cond) throw new Error("assertion failed: base64Encode == \"aHR0cHM6Ly93d3cud2luZ2xhbmcuaW8vZG9jcw==\"")})((base64Encode === "aHR0cHM6Ly93d3cud2luZ2xhbmcuaW8vZG9jcw=="))};
+ {((cond) => {if (!cond) throw new Error("assertion failed: base64urlEncode == \"aHR0cHM6Ly93d3cud2luZ2xhbmcuaW8vZG9jcw\"")})((base64urlEncode === "aHR0cHM6Ly93d3cud2luZ2xhbmcuaW8vZG9jcw"))};
+ {((cond) => {if (!cond) throw new Error("assertion failed: base64Decode == string")})((base64Decode === string))};
+ {((cond) => {if (!cond) throw new Error("assertion failed: base64urlDecode == string")})((base64urlDecode === string))};
+ this.node.root.new("@winglang/sdk.std.Test",std.Test,this,"test:inflight base64",new $Closure1(this,"$Closure1"));
+ }
+}
+const $App = $stdlib.core.App.for(process.env.WING_TARGET);
+new $App({ outdir: $outdir, name: "base64", rootConstruct: $Root, plugins: $plugins, isTestEnvironment: $wing_is_test }).synth();
+
+```
+
diff --git a/tools/hangar/__snapshots__/test_corpus/sdk_tests/util/base64.w_test_sim.md b/tools/hangar/__snapshots__/test_corpus/sdk_tests/util/base64.w_test_sim.md
new file mode 100644
index 00000000000..bd9c04529a4
--- /dev/null
+++ b/tools/hangar/__snapshots__/test_corpus/sdk_tests/util/base64.w_test_sim.md
@@ -0,0 +1,12 @@
+# [base64.w](../../../../../../examples/tests/sdk_tests/util/base64.w) | test | sim
+
+## stdout.log
+```log
+pass ─ base64.wsim » root/env0/test:inflight base64
+
+
+Tests 1 passed (1)
+Test Files 1 passed (1)
+Duration
+```
+