<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Charla Bigelow</title>
    <description>The latest articles on DEV Community by Charla Bigelow (@conspiracytheory13).</description>
    <link>https://dev.to/conspiracytheory13</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F865556%2F6c5543b4-2e10-47fd-948c-4d851b96b7a3.png</url>
      <title>DEV Community: Charla Bigelow</title>
      <link>https://dev.to/conspiracytheory13</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/conspiracytheory13"/>
    <language>en</language>
    <item>
      <title>How to Create an Automated SMS Notification System</title>
      <dc:creator>Charla Bigelow</dc:creator>
      <pubDate>Tue, 18 Apr 2023 06:40:00 +0000</pubDate>
      <link>https://dev.to/courier/how-to-create-an-automated-sms-notification-system-17oj</link>
      <guid>https://dev.to/courier/how-to-create-an-automated-sms-notification-system-17oj</guid>
      <description>&lt;h1&gt;
  
  
  Introduction
&lt;/h1&gt;

&lt;p&gt;All apps need a solid, automated notifications system. Whether it’s reminders, updates, or alerts, some notifications can be urgent and time-sensitive, in which case SMS is the most effective communication channel.&lt;/p&gt;

&lt;p&gt;Let’s take a moment to talk about what that means for you as a developer. You need to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;store data on how each individual user is interacting with your app,&lt;/li&gt;
&lt;li&gt;design personalized messages that inform your users based on different activities,&lt;/li&gt;
&lt;li&gt;respect your user’s communication preferences to avoid notification fatigue,&lt;/li&gt;
&lt;li&gt;and view logs in a centralized location for efficient debugging.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Realistically, building this feature from scratch can be time-consuming and complicated. You risk wasting time on simple tasks like integrating with SMS providers, managing message delivery, and scaling your system to handle increased message volume.&lt;/p&gt;

&lt;p&gt;To simplify this process, we will use Courier, which provides a layer of abstraction over multiple SMS providers, making integration easier. It includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;CDP integrations to send automated messages based on user activity&lt;/li&gt;
&lt;li&gt;message delivery tracking and retry mechanisms, which can help ensure reliable message delivery&lt;/li&gt;
&lt;li&gt;scalable architecture, which can handle increased message volume as your application grows.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This tutorial will cover everything you need to know to set up an effective and efficient automated SMS notification system using Courier. Let’s begin.&lt;/p&gt;

&lt;h1&gt;
  
  
  Send with SMS
&lt;/h1&gt;

&lt;p&gt;The following is a simple API call that sends SMS messages with Courier, adapted from the &lt;a href="https://www.courier.com/docs/guides/getting-started/nodejs/" rel="noopener noreferrer"&gt;Node.js quick start&lt;/a&gt;: &lt;/p&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;p&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;CourierClient&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@trycourier/courier&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;courier&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;CourierClient&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;authorizationToken&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;&amp;lt;auth_token&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;sendSMS&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;br&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;requestId&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;courier&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;br&gt;
        &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;br&gt;
            &lt;span class="na"&gt;to&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;br&gt;
                &lt;span class="na"&gt;phone_number&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;123-456-7890&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;br&gt;
            &lt;span class="p"&gt;},&lt;/span&gt;&lt;br&gt;
            &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;br&gt;
                &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Welcome!&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;br&gt;
                &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Thanks for signing up, {{name}}&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;br&gt;
            &lt;span class="p"&gt;},&lt;/span&gt;&lt;br&gt;
            &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;br&gt;
                &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Peter Parker&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;br&gt;
            &lt;span class="p"&gt;},&lt;/span&gt;&lt;br&gt;
            &lt;span class="na"&gt;routing&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;br&gt;
                &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;single&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;br&gt;
                &lt;span class="na"&gt;channels&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;sms&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;br&gt;
            &lt;span class="p"&gt;},&lt;/span&gt;&lt;br&gt;
            &lt;span class="p"&gt;},&lt;/span&gt;&lt;br&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;&lt;br&gt;
&lt;span class="p"&gt;}&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;&lt;span class="nf"&gt;sendSMS&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;/p&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
&lt;br&gt;
  &lt;br&gt;
  &lt;br&gt;
  Integrate SMS API&lt;br&gt;
&lt;/h2&gt;

&lt;p&gt;You can use Twilio or &lt;a href="https://www.courier.com/docs/guides/providers/sms/" rel="noopener noreferrer"&gt;any of the following providers&lt;/a&gt; based on your preference.&lt;/p&gt;

&lt;p&gt;** Step 1: &lt;a href="https://www.twilio.com/try-twilio" rel="noopener noreferrer"&gt;Sign up for Twilio&lt;/a&gt;**&lt;/p&gt;

&lt;p&gt;** Step 2: Set up your preferred SMS API in &lt;a href="https://app.courier.com/channels" rel="noopener noreferrer"&gt;channels&lt;/a&gt;**&lt;/p&gt;

&lt;p&gt;Each Courier Integration needs specific information based on the Integration provider's requirements (check the &lt;strong&gt;Provider Documentation&lt;/strong&gt; section on the left sidebar for each Integration's needs).&lt;/p&gt;

&lt;p&gt;For Twilio, you will need to provide an &lt;strong&gt;&lt;a href="https://console.twilio.com/" rel="noopener noreferrer"&gt;Account SID&lt;/a&gt;&lt;/strong&gt;, an &lt;strong&gt;&lt;a href="https://console.twilio.com/" rel="noopener noreferrer"&gt;Auth Token&lt;/a&gt;&lt;/strong&gt;, and a &lt;strong&gt;&lt;a href="https://www.twilio.com/docs/messaging/guides/best-practices-at-scale" rel="noopener noreferrer"&gt;Messaging Service SID&lt;/a&gt;&lt;/strong&gt;, which you will get directly from Twilio. If you don’t have a Messaging Service SID from Twilio, you can add a &lt;strong&gt;From Number&lt;/strong&gt; instead.&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/z7iqk1q8njt4/y0Djn4VAYJTNo2WFFN0Wi/dc791a6c406960a0c416bf1b03d17899/image-1.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/z7iqk1q8njt4/y0Djn4VAYJTNo2WFFN0Wi/dc791a6c406960a0c416bf1b03d17899/image-1.png" alt="Add Twilio channel"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After you sign in to Twilio, click the house icon on the sidebar to go to the &lt;strong&gt;Dashboard&lt;/strong&gt;. On this page, you'll see a section called &lt;strong&gt;Project Info&lt;/strong&gt;, which shows your &lt;strong&gt;Account SID&lt;/strong&gt; and &lt;strong&gt;Auth Token&lt;/strong&gt;. Copy these and paste them into the matching fields on Courier's Twilio Integration page.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/.%2Fimages%2Fimage-2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/.%2Fimages%2Fimage-2.png"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="//images.ctfassets.net/z7iqk1q8njt4/2IDv94v66vmxo2AxU1ocmT/3f6083af3496e761c77c9d1f18ad35e0/image-2.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/z7iqk1q8njt4/2IDv94v66vmxo2AxU1ocmT/3f6083af3496e761c77c9d1f18ad35e0/image-2.png" alt="Twilio account info"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can find more information on how to get a phone number from Twilio and how to set up a Messaging Service SID in our &lt;a href="https://www.courier.com/docs/guides/providers/sms/twilio/" rel="noopener noreferrer"&gt;Twilio documentation&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Create Your Notification(s)
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Step 1: Create a new template&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Go to the Courier Designer and click on the &lt;strong&gt;Create Notification&lt;/strong&gt; button to create a new notification template. You will need to give your notification a name (otherwise it will default to “Untitled Notification”. You can choose to use AI to generate content based on the name you chose.&lt;/p&gt;

&lt;p&gt;Leave the Subscription Topic blank for now.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 2: Select your preferred channel and integration&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In this case, you will use the SMS channel and Twilio integration.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 3: Customize your message and publish changes when complete&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;** Step 4: Add links to the Preference Center**&lt;/p&gt;

&lt;p&gt;You’ll set up your Preference Center later in this tutorial, but you can add links to the Preference Center here by including &lt;code&gt;({$.urls.preferences})&lt;/code&gt; in the message or hyperlinking text with &lt;code&gt;{$.urls.preferences}&lt;/code&gt;. &lt;a href="https://www.courier.com/docs/courier-preferences/preference-center/hosted-page/#adding-preferences-link-to-notification-text" rel="noopener noreferrer"&gt;Learn more.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Optional Step 4: Test your event&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;To confirm that your message is appearing the way you’d expect it to, go to the Preview tab and create a new test event. Replace the value of &lt;code&gt;phone_number&lt;/code&gt; in the JSON with a number you would like to receive the test SMS at. Click create and proceed to the Send tab. Click Send Test to receive the test message. Click on the Design tab to revise the message until you are satisfied with the way it looks.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.courier.com/docs/guides/tutorials/how-to-design-a-notification-template/" rel="noopener noreferrer"&gt;Learn more about designing templates.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 5: Repeat this process to create all notifications you need to send via SMS&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Create an Automation
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Step 1: &lt;a href="https://app.courier.com/automations" rel="noopener noreferrer"&gt;Create a new Automation&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 2: Drag and drop your preferred trigger&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If you have integrated a CDP in Courier and want to send notifications based on events being tracked within your application, you can choose any of them as the trigger (Segment or Rudderstack).&lt;/p&gt;

&lt;p&gt;This will require you to have the integration setup within the CDPs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://segment.com/docs/connections/destinations/catalog/courier/" rel="noopener noreferrer"&gt;Send Segment events to Courier&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.rudderstack.com/docs/destinations/streaming-destinations/courier/" rel="noopener noreferrer"&gt;Send Rudderstack events to Courier&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Step 3: Drag and drop a Send step&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Connect the notification template based on which message you want to send with this trigger.&lt;/p&gt;

&lt;p&gt;Edit the recipient information:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;add a &lt;code&gt;phone_number&lt;/code&gt; field to send to a specific number,&lt;/li&gt;
&lt;li&gt;add a &lt;code&gt;user_id&lt;/code&gt; to send to a specific contact within your [Courier Users database](&lt;a href="https://app.courier.com/users0" rel="noopener noreferrer"&gt;https://app.courier.com/users0&lt;/a&gt;,&lt;/li&gt;
&lt;li&gt;or add a &lt;code&gt;list_id&lt;/code&gt; to send to a list of users (another way to do this is to use the Send to List step)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Step 4: Publish the Automation template&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.courier.com/docs/automations/designer/" rel="noopener noreferrer"&gt;Learn more about designing Automation templates&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Set up user preferences
&lt;/h2&gt;

&lt;p&gt;To allow your users to decide how they receive messages, you can use Subscription Topics to categorize your notifications and give users the ability to opt in/out of these Topics.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1: Create Subscription Topics&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Head over to the &lt;a href="https://app.courier.com/designer/preferences" rel="noopener noreferrer"&gt;Preferences Designer&lt;/a&gt; and add your Topics. Some examples are “Marketing Messages,” “Billing Alerts,” and “Critical Messages.” You can edit the Default State of each Topic to decide whether users can opt in/out or if they are required to receive these notifications.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 2: Link the Notification Templates&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Edit your Topics to link the Templates you created earlier.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Optional Step 3: Enable Channel Delivery Customization&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;You can also edit the Section settings (a Section groups multiple topics together) and enable Channel Delivery Customization so users can decide which channel they receive messages on. This means that if a user opts out of “SMS” notifications for a Section, they will not receive any SMS notifications within the Topics of that Section.&lt;/p&gt;

&lt;p&gt;Learn more about the &lt;a href="https://www.courier.com/docs/courier-preferences/preference-center/designer/" rel="noopener noreferrer"&gt;Preferences Designer&lt;/a&gt;.&lt;/p&gt;

&lt;h1&gt;
  
  
  Best Practices for SMS Notifications
&lt;/h1&gt;

&lt;p&gt;The ease and convenience of SMS have made it an effective and popular method for delivering messages to customers, employees, and stakeholders. However, best practices must be followed to ensure the effectiveness of SMS notifications. Here are some of the best practices for SMS notifications:&lt;/p&gt;

&lt;h2&gt;
  
  
  A. Timing and frequency of SMS notifications
&lt;/h2&gt;

&lt;p&gt;SMS notifications require precise timing and frequency to be effective. The notifications should be sent at the optimal time for the recipient to read and respond to the message. For example, a retail store may send SMS notifications to customers with promotions and discounts during the holiday season or a special sale event. Similarly, a healthcare provider may send reminders to patients for upcoming appointments or medication refills. It is also important to avoid overloading the recipient with too many messages. Finding the right balance between frequency and relevance is crucial to ensure that SMS notifications are well-received and effective.&lt;/p&gt;

&lt;h2&gt;
  
  
  B. Personalizing the SMS notifications for each recipient
&lt;/h2&gt;

&lt;p&gt;Personalization is a key factor in delivering effective SMS notifications. By personalizing the message with the recipient's name, location, or other relevant information, it becomes more engaging and meaningful. Personalized SMS notifications can also help to build stronger relationships between the sender and recipient. For example, a fitness center may send SMS notifications to members with personalized workout plans based on their fitness goals and progress. Similarly, a bank may send personalized SMS notifications to customers with account balances and transaction details.&lt;/p&gt;

&lt;h2&gt;
  
  
  C. Measuring the success of SMS notifications with analytics
&lt;/h2&gt;

&lt;p&gt;Analytics can provide valuable insights into how the recipient is engaging with the message. For example, open rates, click-through rates, and conversion rates can indicate the effectiveness of the SMS notification. By analyzing these metrics, the sender can identify areas for improvement and make data-driven decisions. Analytics can also help to refine the timing and frequency of SMS notifications.&lt;/p&gt;

&lt;h2&gt;
  
  
  D. Improving the SMS notification strategy based on recipient engagement
&lt;/h2&gt;

&lt;p&gt;Finally, we can continuously improve the SMS notification strategy based on recipient engagement. By soliciting feedback from recipients, the sender can better understand their preferences and needs. For example, a survey or feedback form can be included in the SMS notification to gather feedback from the recipient. Based on this feedback, the sender can adjust the timing, frequency, and content of the SMS notifications to meet the recipient's needs better.&lt;/p&gt;

&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;Now you have a completely automated SMS notification system that respects your user’s preferences. Creating an automated SMS notification system with Courier and Twilio is a powerful way to enhance customer communication. Courier's advanced features, like multi-channel support, pre-built templates, and detailed analytics, make it a great tool for managing all your notifications in one place. Twilio's SMS API is easy to use and provides a wide range of features, like two-factor authentication and automated responses, to help you create an efficient and secure notification system. Combining these two powerful tools allows you to create a notification system that meets your business needs and helps you stay connected with your customers.&lt;/p&gt;

</description>
      <category>mobile</category>
      <category>tutorial</category>
      <category>twilio</category>
    </item>
    <item>
      <title>New Datadog integration for Courier notification logs and metrics</title>
      <dc:creator>Charla Bigelow</dc:creator>
      <pubDate>Wed, 12 Apr 2023 16:00:00 +0000</pubDate>
      <link>https://dev.to/courier/new-datadog-integration-for-courier-notification-logs-and-metrics-18k1</link>
      <guid>https://dev.to/courier/new-datadog-integration-for-courier-notification-logs-and-metrics-18k1</guid>
      <description>&lt;p&gt;The ability to unify all notification metrics and logs across channels and providers into an easy-to-use dashboard is a core advantage of Courier’s notification infrastructure. &lt;/p&gt;

&lt;p&gt;However, with product notifications so critical to the entire application experience, it’s important to connect that data back to central cloud observability platforms that look across the entire stack. This gives platform engineering teams a complete understanding of application health and lets them quickly troubleshoot even the most complex issues.&lt;/p&gt;

&lt;p&gt;Today, we’re incredibly excited to announce our first observability integration with Datadog.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.datadoghq.com/"&gt;Datadog&lt;/a&gt; is a tool for collecting metrics and other data from applications and viewing them in a centralized place. Datadog’s dashboards and monitoring tools build a more accurate picture of the health of your application and how it changes over time.&lt;/p&gt;

&lt;p&gt;Our integration with Datadog is easy to spin up when you use our “dashboard starter kit.” This consists of some pre-configured JSON to set up a simple dashboard in Datadog for your Courier data.&lt;/p&gt;

&lt;p&gt;Our Datadog integration is currently only available on Courier’s Business plan, much like our other production-focused features such as &lt;a href="https://www.courier.com/docs/guides/business/analytics/#template-analytics-notification-metrics"&gt;template analytics&lt;/a&gt;, &lt;a href="https://www.courier.com/docs/guides/business/okta-integration/"&gt;Okta integration for SSO&lt;/a&gt;, and &lt;a href="https://www.courier.com/docs/courier-preferences/preference-center/introduction/"&gt;advanced user preferences&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The fastest way to pull your notification data into Datadog
&lt;/h2&gt;

&lt;p&gt;Courier's &lt;a href="https://www.courier.com/docs/notification-data/message-logs/"&gt;message logs&lt;/a&gt; and analytics dashboard are optimized for helping product teams understand everything about the messaging use case — the status of notifications, recipients, lists, automations, and more. It brings all this data together across channels and providers in one place and provides the fastest way to learn about your notifications and troubleshoot notifications-related issues. &lt;/p&gt;

&lt;p&gt;However, many organizations need to understand how notifications interact with the rest of the tech stack or would prefer to view and store all logs and metrics data in one place. &lt;/p&gt;

&lt;p&gt;Until now, the only way to pull this data outside of Courier was to build your own bespoke solution, by listening to Courier’s outbound webhooks and writing custom code. But now the Datadog integration enables sending information about health and performance directly to your own monitoring system, which is much more convenient.&lt;/p&gt;

&lt;h2&gt;
  
  
  Use cases
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Notification metrics and logs
&lt;/h3&gt;

&lt;p&gt;The most obvious use cases involve sending data about the health and delivery status of your notifications. This could include metrics such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Percentage of messages that are successfully sent, delivered, opened, and clicked.&lt;/li&gt;
&lt;li&gt;Delivery errors&lt;/li&gt;
&lt;li&gt;Percentage of customers who are unsubscribing from your messages&lt;/li&gt;
&lt;li&gt;Any metrics that measure outliers — for example, a metric that checks if you are sending either more or fewer messages than you should be.&lt;/li&gt;
&lt;li&gt;Number of people who opened your emails over time.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/z7iqk1q8njt4/3lM8WS2Gycadc9PLXOQPHi/5e2a92ec8a1bda6dedc4a55136c8bdbb/datadog-courier-integration.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/z7iqk1q8njt4/3lM8WS2Gycadc9PLXOQPHi/5e2a92ec8a1bda6dedc4a55136c8bdbb/datadog-courier-integration.png" alt="datadog-courier-integration"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;(An example Datadog dashboard showing key notification health metrics)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/z7iqk1q8njt4/7kIbn1d7NZu1aN43JrG7HD/4fce7f34bba7d074e13a3a52e1bf3fd5/click_and_open_rate_email.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/z7iqk1q8njt4/7kIbn1d7NZu1aN43JrG7HD/4fce7f34bba7d074e13a3a52e1bf3fd5/click_and_open_rate_email.png" alt="Datadog click and open rate tracking"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;(A Datadog graph showing the number of emails that are being sent over the course of the day — as well as how many emails are being opened and how many people are clicking on email links. There are options to compare these metrics to other time periods such as the previous day, week, or quarter.)&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Feature-specific metrics and logs
&lt;/h3&gt;

&lt;p&gt;The integration also supports data from Courier platform features. This can provide even more color on how notifications support your business or operational goals. For example, If you use &lt;a href="https://www.courier.com/blog/automations-notification-workflow-designer/"&gt;Courier Automations&lt;/a&gt; to build smart notification workflows, you may want to monitor how many times each automation has been invoked. Or, if you use Courier’s &lt;a href="https://www.courier.com/blog/app-notifications-inbox/"&gt;in-app notification Inbox&lt;/a&gt;, you might choose to monitor the number of connections to your Inbox and compare this to the number of active users on your site. A significant difference could indicate a problem that needs further investigation.For example, you might have released a change that broke your Inbox implementation through changes to an authentication method or a key.&lt;/p&gt;

&lt;h2&gt;
  
  
  What data can be sent to Datadog from Courier?
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Metrics
&lt;/h3&gt;

&lt;p&gt;Courier metric names will have a &lt;code&gt;courier.*&lt;/code&gt; prefix — for example, &lt;code&gt;courier.notification.sent&lt;/code&gt;. A full list of all the Courier metrics that are available for you to send to Datadog is provided &lt;a href="https://www.courier.com/docs/guides/business/observability/"&gt;here&lt;/a&gt;. These metrics will be sent with additional information, such as success or failure codes, and tags (including whether the metric is for your production or staging environment).&lt;/p&gt;

&lt;p&gt;Once the metrics are being sent to Datadog, you can set up corresponding alerts in Datadog. For example, you could set up an alert that emails your engineers when the  number of notification delivery errors reach a certain predefined threshold. &lt;/p&gt;

&lt;h3&gt;
  
  
  Logs
&lt;/h3&gt;

&lt;p&gt;Courier always had the ability to &lt;a href="https://help.courier.com/en/articles/4364860-using-the-data-logs"&gt;record logs&lt;/a&gt; about notifications, recipients, lists, and automations. Now these can be sent to Datadog in the same way that metrics are sent. &lt;/p&gt;

&lt;p&gt;Courier’s logs provide details about delivery failures, as well as other information that can be relevant when debugging. With these logs now in Datadog, you’ll now be able to easily correlate the logs to your metrics, helping you debug any issues.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting up the Datadog integration
&lt;/h2&gt;

&lt;p&gt;To set up this integration all you need is your Datadog API key and URL. From there, simply follow the &lt;a href="https://www.courier.com/docs/guides/business/observability/#datadog"&gt;setup guide&lt;/a&gt;, which will cover the steps to connect Courier with Datadog and how to create a Courier-specific dashboard in Datadog. This is where you’ll find our “dashboard starter kit,” a JSON file that has everything you need to  set up a Courier dashboard, including commonly used Courier metrics. You can easily customize the JSON to add or remove different metrics according to your needs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Next steps
&lt;/h2&gt;

&lt;p&gt;If you’re already on Courier’s Business plan, you can start sending metrics and logs to Datadog right away. Otherwise, you can &lt;a href="https://www.courier.com/request-demo/?utm_source=datadog_launch&amp;amp;utm_medium=blog&amp;amp;utm_campaign=product_news"&gt;request a demo&lt;/a&gt;, and we’ll walk you through this integration in more detail, along with the other features in the Business plan to see if it’s right for your use case. &lt;/p&gt;

&lt;p&gt;The ability to access your metrics and logs in an observability platform like Datadog allows you to set up a dashboard that shows the data that’s most important to you. You can also create graphs that enable you to see at a glance if something’s not right, or even set up alerts based on message delivery failures. Whatever size your business is, some level of monitoring is necessary if you want to have control over your application.&lt;/p&gt;

&lt;p&gt;If you’re a Datadog user who is interested in trying Courier’s full-featured notifications infrastructure, you can &lt;a href="https://app.courier.com/signup"&gt;try it&lt;/a&gt; for free.&lt;/p&gt;

</description>
      <category>webdev</category>
    </item>
    <item>
      <title>Streamline your workflow with CourierJS: Our new client-side SDK</title>
      <dc:creator>Charla Bigelow</dc:creator>
      <pubDate>Mon, 10 Apr 2023 16:03:00 +0000</pubDate>
      <link>https://dev.to/courier/streamline-your-workflow-with-courierjs-our-new-client-side-sdk-3fkk</link>
      <guid>https://dev.to/courier/streamline-your-workflow-with-courierjs-our-new-client-side-sdk-3fkk</guid>
      <description>&lt;p&gt;We are thrilled to announce the release of our first client-side SDK for Courier, written for JavaScript. This new addition expands our existing SDK offerings, which include Java, Python, Node, Go, Ruby, and PHP, and makes it even easier for developers to integrate Courier into their projects. By enabling direct calls to Courier within your JavaScript code, you can eliminate the need to use a back-end service as an extra layer between your front end and the Courier API, saving valuable time and resources.&lt;/p&gt;

&lt;p&gt;With the initial release, we're introducing three new API calls: the &lt;code&gt;identify&lt;/code&gt; call for tying a user to actions they perform, such as logging in or updating their profile, and the &lt;code&gt;subscribe&lt;/code&gt; and &lt;code&gt;unsubscribe&lt;/code&gt; calls for subscribing and unsubscribing to lists. By simplifying these key interactions, CourierJS enhances the overall experience of using Courier. &lt;/p&gt;

&lt;h2&gt;
  
  
  Client-side vs. Server-side SDKs
&lt;/h2&gt;

&lt;p&gt;You may still be wondering: why introduce a client-side JavaScript SDK for Courier when we already have SDKs for many other languages? Let's dive into the differences between client-side and server-side SDKs, which will help you determine the right type of SDK for your specific needs.&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/z7iqk1q8njt4/5rsBb6Xcz2QAvTGR3HTIx7/523b5e34169adb335970d98209b8b492/Client-side_SDK_benefits.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/z7iqk1q8njt4/5rsBb6Xcz2QAvTGR3HTIx7/523b5e34169adb335970d98209b8b492/Client-side_SDK_benefits.png" alt="Client-side SDK benefits"&gt;&lt;/a&gt;&lt;br&gt;
(&lt;em&gt;Comparing integration flows: with and without Courier client-side SDK&lt;/em&gt;)&lt;/p&gt;

&lt;p&gt;Client-side SDKs are designed to provide a way for you to interact with an API within your application's front-end code. Server-side SDKs, in contrast, interact with your back-end code, making them more suitable for processing and handling data that doesn't directly involve user interactions on the front end. By offering a client-side JavaScript SDK, we're providing developers with an alternative to server-side SDKs that is more suitable when you need to integrate Courier's functionality directly into your web application's front end. When trying to call the API from your front end, having an extra layer of back-end code might not always be the most suitable option for certain use cases. With CourierJS, we provide an alternative that streamlines the process and caters to a broader range of scenarios, enhancing efficiency and helping you deliver valuable applications more quickly.&lt;/p&gt;

&lt;p&gt;We have taken inspiration from popular customer data platforms like Segment and RudderStack, both of which provide an &lt;code&gt;identify&lt;/code&gt; call. In addition to an &lt;code&gt;identify&lt;/code&gt; call, which ties a user action to a particular user, our initial release also includes support for subscribing and unsubscribing to lists, allowing users to manage their communication preferences with ease.&lt;/p&gt;

&lt;p&gt;The JavaScript SDK has been designed to be both intuitive and efficient, working exactly how you'd expect. Instead of issuing multiple commands to update specific fields, you can now easily update multiple fields in a single operation. For example, if you have a user with an ID, name, email address, physical address, and phone number, and you just want to update the email address and phone number, you can pass in just those values. The SDK also automatically merges new data with pre-existing data, overwriting only the specified fields. This functionality ensures a smooth integration with your front-end code and a more seamless user experience.&lt;/p&gt;
&lt;h2&gt;
  
  
  Discover the advantages of CourierJS
&lt;/h2&gt;

&lt;p&gt;Previously, developers using Courier only had access to server-side SDKs. These SDKs required the front-end code to communicate with the back-end services to utilize Courier's functionality, often adding an unnecessary extra layer. In some cases, you even had to create an entire back-end service just to handle the Courier SDK.&lt;/p&gt;

&lt;p&gt;One of the primary challenges faced when using server-side SDKs in the context of Courier arises when you need to send data from the front end to Courier, which previously required going through the back end. This process can be manageable, but it can also be tedious for developers who want a more streamlined solution. To address this, we’ve developed the client-side SDK, making it easier for those who want to directly integrate Courier's functionality into their web application's front end, bypassing the need to pass data through the back end first.&lt;/p&gt;

&lt;p&gt;By introducing the new Courier client-side SDK, we aim to simplify this process and reduce the workload for engineers. This not only saves time but also accelerates the implementation of new features, making your development process more efficient and agile.&lt;/p&gt;
&lt;h2&gt;
  
  
  Ready to get started? Using CourierJS is a breeze
&lt;/h2&gt;

&lt;p&gt;Now that you've discovered the advantages of the Courier client-side SDK, it's time to dive into how you can seamlessly integrate it into your project. To begin, all you need to do is install the JavaScript package and start using the provided function calls in your code. For a comprehensive step-by-step guide on setting up the Courier client-side SDK, you can refer to our documentation &lt;a href="https://github.com/trycourier/courier-js#getting-started"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  Exploring the new API calls in Courier's client-side SDK
&lt;/h2&gt;

&lt;p&gt;Now that you're all set up, let's take a closer look at the new API calls introduced in the SDK.&lt;/p&gt;
&lt;h3&gt;
  
  
  The &lt;code&gt;identify&lt;/code&gt; call
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;identify&lt;/code&gt; call allows you to create and update user profiles with ease. Here's an example of how the &lt;code&gt;identify&lt;/code&gt; call can be used in your code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;courierSDK&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;identify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;user123&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;user123@example.com&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;firstName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;John&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;lastName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Doe&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this example, the &lt;code&gt;identify&lt;/code&gt; call creates or updates a user profile with the specified user ID, email, first name, and last name. The simplicity of the call ensures that you can quickly and easily update user profiles as needed, making it an invaluable tool in your communication management toolkit.&lt;/p&gt;

&lt;h3&gt;
  
  
  Subscribing and unsubscribing from lists
&lt;/h3&gt;

&lt;p&gt;A list in Courier is a way to group recipient profiles, allowing you to send a message to all subscribed recipients with a single call. This feature is particularly useful for managing newsletters, product updates, and other types of mass communication.&lt;/p&gt;

&lt;p&gt;Managing user subscriptions is easy with the new &lt;code&gt;subscribe&lt;/code&gt; and &lt;code&gt;unsubscribe&lt;/code&gt; API calls. These calls make it straightforward to maintain up-to-date lists of subscribers, due to not needing to call a back-end service. Here are examples of how to use the &lt;code&gt;subscribe&lt;/code&gt; and &lt;code&gt;unsubscribe&lt;/code&gt; calls in your code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Subscribe a user to a list&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;courierSDK&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;subscribe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;user123&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;my-newsletter-list&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Unsubscribe a user from a list&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;courierSDK&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;unsubscribe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;user123&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;my-newsletter-list&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see from the comments in these examples, the &lt;code&gt;subscribe&lt;/code&gt; call adds a user to a specified list, while the &lt;code&gt;unsubscribe&lt;/code&gt; call removes them from the list. &lt;/p&gt;

&lt;p&gt;By incorporating these new API calls into your project, you can further streamline your development process.&lt;/p&gt;

&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;Throughout this article, we've unveiled the power of the new CourierJS client-side SDK and explored its features, including the &lt;code&gt;identify&lt;/code&gt; call and &lt;code&gt;subscribe&lt;/code&gt; and &lt;code&gt;unsubscribe&lt;/code&gt; calls. By using the CourierJS client-side SDK, you can reduce the workload involved in your development process and improve the overall efficiency of your projects.&lt;/p&gt;

&lt;p&gt;As you’ve seen, the SDK saves you time and effort by eliminating the need for complex back-end services and enabling direct communication between the front end and Courier. Its intuitive functionality, ease of use, and powerful capabilities make it a valuable addition to your development toolkit.&lt;/p&gt;

&lt;p&gt;We encourage you to try out the new &lt;a href="https://github.com/trycourier/courier-js"&gt;CourierJS client-side SDK&lt;/a&gt; and experience the benefits it offers firsthand. By adopting this innovative tool, you can enhance your product-triggered communication management and unlock the full potential of the Courier ecosystem.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>api</category>
    </item>
    <item>
      <title>Introducing the new and improved Automations designer</title>
      <dc:creator>Charla Bigelow</dc:creator>
      <pubDate>Mon, 03 Apr 2023 15:31:00 +0000</pubDate>
      <link>https://dev.to/courier/introducing-the-new-and-improved-automations-designer-fbd</link>
      <guid>https://dev.to/courier/introducing-the-new-and-improved-automations-designer-fbd</guid>
      <description>&lt;p&gt;&lt;a href="https://www.courier.com/blog/announcing-courier-automations/"&gt;Courier Automations&lt;/a&gt; lets you build smart notification workflows. With Automations, notifications can be scheduled, driven by events, canceled, and more.&lt;/p&gt;

&lt;p&gt;We’ve just completed a big redesign of the automations UI — before this overhaul, notifications were restricted to linear workflows. This made it difficult to build more advanced automations, however the new automations UI, based on &lt;a href="https://reactflow.dev/"&gt;React Flow&lt;/a&gt;, allows you to build more sophisticated workflows, including those requiring branching logic.&lt;/p&gt;

&lt;p&gt;In this article, we explain the differences in functionality between the old and new UI, how the new UI helps developers as well as product managers, and how the migration will affect you if you’re already using the automations designer. For those who are new to Courier automations, we explain the background of what automations are for and how to use them. We also cover how to use the new UI, while providing some useful real-world examples to help you get started.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is Courier?
&lt;/h2&gt;

&lt;p&gt;Courier serves as a product-oriented notification platform, and is well-suited for the post-marketing phase of a business; allowing you to streamline communication with users across your websites and/or applications. Its unified API accommodates multi-channel delivery — including email, SMS, browser notifications, push notifications, and direct messages through platforms such as Slack or Microsoft Teams. The power of the Courier app is that it allows you to design versatile message templates that can be easily repurposed across each messaging channel.&lt;/p&gt;

&lt;p&gt;You can pull data from other systems into Courier, to populate variables within your message templates or when building your application logic in your automations. This data can come from a variety of sources — for example, from customer data platforms (CDPs) like Segment or RudderStack, or from your own site.&lt;/p&gt;

&lt;h2&gt;
  
  
  What are Courier automations?
&lt;/h2&gt;

&lt;p&gt;Courier automations enable you to craft intricate notification delivery workflows while seamlessly integrating your custom application logic into the Courier notification ecosystem. Automations can be controlled either through an API or through the automations designer inside the Courier app. The automations designer allows you to design automations either by using a visual builder or by directly editing JSON. It’s the fastest way to build workflows. &lt;/p&gt;

&lt;p&gt;The latest update to the automations designer includes a big update to the visual builder. Where previously your workflows were limited, or were made unnecessarily complex by the linear nature of the visual builder, you can now use the designer to design complex workflows in a simple and intuitive way.&lt;/p&gt;

&lt;p&gt;For example, if you want to send different welcome emails to different groups of people based on user attributes, it was difficult to handle this level of complexity in the old UI.&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/z7iqk1q8njt4/5fe4Xoo5ga0cRVS6zep1il/278908b468abe57fd0ab68593cbb59dc/segmentacctcreated-old2.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/z7iqk1q8njt4/5fe4Xoo5ga0cRVS6zep1il/278908b468abe57fd0ab68593cbb59dc/segmentacctcreated-old2.png" alt="Automation v1 UI Segment account created"&gt;&lt;/a&gt;&lt;br&gt;
(&lt;em&gt;The old UI consists of a linear series of steps. In this example, a Segment event, “Account Created” triggers a 10-minute delay before sending a welcome email.&lt;/em&gt;)&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/z7iqk1q8njt4/41UUw7FkgalcgFyFtU4AAm/64f52b56267bcf210adc20580784da91/segmentacctcreated3.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/z7iqk1q8njt4/41UUw7FkgalcgFyFtU4AAm/64f52b56267bcf210adc20580784da91/segmentacctcreated3.png" alt="Automations v2 UI Segment account created"&gt;&lt;/a&gt;&lt;br&gt;
(&lt;em&gt;The new automations designer has drag-and-drop elements that can help you create non-linear workflows. In this example, the Segment event, “Account Created” triggers a 10-minute delay and then checks whether the user’s job role contains “engineer.” If they are an engineer, they will be sent the engineer-specific welcome email, and if not, they will receive a generic welcome email.&lt;/em&gt;)&lt;/p&gt;

&lt;p&gt;The new automations designer was built using React Flow, a library for building node-based interactive UIs. You can build your messaging workflows by dragging and dropping different elements such as triggers and actions onto a canvas. You can then connect them together in a sequential order, or use branching logic if required to build a more complex workflow.&lt;/p&gt;

&lt;p&gt;The example above shows a workflow for waiting until a user has signed up to send them a message. If they’re online, you can just send a browser notification; otherwise you might want to send an email.&lt;/p&gt;

&lt;h3&gt;
  
  
  The elements of a Courier automation
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Triggers
&lt;/h4&gt;

&lt;p&gt;Triggers are events that cause a new automation run to be started. They define the conditions that must occur before an action can happen. A simple example of a trigger is a scheduler that waits until a particular timestamp is reached before an action can happen (such as sending a notification).  Data pulled from your CDP can act as a trigger, too — for example, a new user signing up to your system.&lt;/p&gt;

&lt;p&gt;You can also make use of data from &lt;a href="https://updates.courier.com/announcements/audiences"&gt;Courier Audiences&lt;/a&gt; to design your triggers. An Audience is a dynamic list of your users. When someone joins or leaves an Audience, this event could be used as a trigger to send a notification. An example of this could involve an Audience of all users who are software engineers. If a user who previously worked in tech support becomes a software engineer, as soon as they update their profile they become part of the “software engineer” audience  — which will cause your trigger to invoke an action such as sending a message.&lt;/p&gt;

&lt;h4&gt;
  
  
  Actions
&lt;/h4&gt;

&lt;p&gt;An action is an activity that happens following a trigger. The most common action in Courier is the sending of a message, either to an individual or group. However, actions can also include fetching data from another API, updating a user profile, or adding a delay to your workflow – for example, waiting for 24 hours after a user signed up to remind them to fill in their profile.&lt;/p&gt;

&lt;h4&gt;
  
  
  Branching logic
&lt;/h4&gt;

&lt;p&gt;Branching logic is a new feature in the latest automations designer that allows you to design more complex workflows by dragging and dropping &lt;code&gt;if&lt;/code&gt; statement blocks into your workflow. These are accessed in the menu on the left, under “Control Flow.” An &lt;code&gt;if&lt;/code&gt; statement block has nodes next to its &lt;code&gt;true&lt;/code&gt; and &lt;code&gt;false&lt;/code&gt; conditions, so you can draw lines that link these different conditions to the next steps in your workflow. As this doesn’t involve writing code, non-developers with a decent understanding of the data involved are often able to design their own notification workflows.&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/z7iqk1q8njt4/2054asBqXWfi2Ctwa7umPg/07d34f550ca8ce314e1e413c30e339e8/Screenshot_from_2023-03-15_18-20-49.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/z7iqk1q8njt4/2054asBqXWfi2Ctwa7umPg/07d34f550ca8ce314e1e413c30e339e8/Screenshot_from_2023-03-15_18-20-49.png" alt="Automations v2 UI if/else conditional branching"&gt;&lt;/a&gt;&lt;br&gt;
(&lt;em&gt;Once you’ve dragged an &lt;code&gt;if&lt;/code&gt; statement box onto the canvas, you can connect it to different parts of your workflow by drawing lines from the &lt;code&gt;true&lt;/code&gt; or &lt;code&gt;false&lt;/code&gt; nodes to other elements within your automations template.&lt;/em&gt;)&lt;/p&gt;

&lt;h2&gt;
  
  
  Why the new version of the Courier automations designer helps you
&lt;/h2&gt;

&lt;p&gt;Until now, if you were using automations for complex, non-linear workflows, there was a good chance you had to manage this logic yourself using our API, which may even have required a separate back-end service just for dealing with this. And, due to ever-changing requirements, you may have needed to regularly update your code in small but necessary ways. &lt;/p&gt;

&lt;p&gt;This can now be done in the UI, leading to much faster turnaround for small workflow changes. To achieve the fastest turnaround, it’s worth empowering product managers or designers to make their own changes to the logic when needed, so they don’t have to wait for a developer to fit small changes around feature work. As long as they have some knowledge of the underlying data, this can be accomplished with minimal training, as the interface is very intuitive. Allowing your colleagues to create or edit automations has the added benefit of freeing up your time to do more feature work instead of having to focus on small updates to application logic.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to use the automations designer
&lt;/h2&gt;

&lt;p&gt;You can follow our &lt;a href="https://www.courier.com/docs/automations/designer/"&gt;documentation&lt;/a&gt; on how to create a basic automation using the new automations designer UI. After creating your automation template, you’ll need to publish it before it can be invoked. The standard way for an automation to be invoked is automatically — when its trigger condition is met. However, it is still possible to invoke an automation by using our &lt;a href="https://www.courier.com/docs/reference/automation/invoke-template/"&gt;automations API&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you need to use variables within your automations — for example, if you want to send your message to varying recipients, or if you want your message template to vary according to certain conditions — you can use our &lt;a href="https://www.courier.com/docs/automations/dynamic/"&gt;refs variable&lt;/a&gt;, which stores dynamic data within Courier.&lt;/p&gt;

&lt;h2&gt;
  
  
  The automations API
&lt;/h2&gt;

&lt;p&gt;If you prefer to &lt;a href="https://www.courier.com/docs/reference/automation/"&gt;use the API directly&lt;/a&gt;, or use an SDK, that’s still an option. You can use these to either invoke automations or get the status of automations. For existing users of the API, you can be reassured that we’ve made no changes to the API — our latest automations release applies to the UI only.&lt;/p&gt;

&lt;h2&gt;
  
  
  Details for existing users of the automations designer
&lt;/h2&gt;

&lt;p&gt;For existing users of the automations designer, we’ll now cover the main points that are likely to be of interest: the main differences between the old and new UIs, and what you need to do to migrate to the new version of automations.&lt;/p&gt;

&lt;h3&gt;
  
  
  Differences between the old and new automations designer UIs
&lt;/h3&gt;

&lt;p&gt;The main difference is that the new automations designer UI is more expansive: you are no longer forced to follow a linear workflow, and can take advantage of branching logic. In addition to this, we have added some improvements to make the UI easier to use. For example, running an automation on a repeat schedule used to require users to learn &lt;a href="https://en.wikipedia.org/wiki/Cron"&gt;Cron&lt;/a&gt; expressions — something that even most engineers have a tough time remembering. Now, with the new “Schedule” trigger, scheduling an automation is as easy as scheduling an appointment in your favorite Calendar app. Changes like these have made the UI more user-friendly, meaning that letting non-developers set up their own workflows is finally an option.&lt;/p&gt;

&lt;h3&gt;
  
  
  What you need to do to migrate to the new version of automations
&lt;/h3&gt;

&lt;p&gt;You don’t need to do anything! We have already rolled out the new changes, so no work is required from your end. &lt;/p&gt;

&lt;p&gt;If you have any existing automations templates that you’ve designed in the past, these will not change. They will appear in exactly the same way using the old UI. However, any new automations templates that you create will now appear using the new UI.&lt;/p&gt;

&lt;h2&gt;
  
  
  Get started
&lt;/h2&gt;

&lt;p&gt;You can get started creating automations with the new Designer &lt;a href="https://app.courier.com/automations"&gt;directly in the Courier UI&lt;/a&gt;. Setting up smart notifications in the new UI is fast and intuitive — just follow our &lt;a href="https://www.courier.com/docs/automations/"&gt;documentation&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>automation</category>
      <category>notifications</category>
    </item>
    <item>
      <title>Simplifying notifications with the Courier iOS SDK</title>
      <dc:creator>Charla Bigelow</dc:creator>
      <pubDate>Thu, 23 Mar 2023 17:16:58 +0000</pubDate>
      <link>https://dev.to/courier/simplifying-notifications-with-the-courier-ios-sdk-21ah</link>
      <guid>https://dev.to/courier/simplifying-notifications-with-the-courier-ios-sdk-21ah</guid>
      <description>&lt;p&gt;Push notifications are a valuable tool for keeping users informed and increasing their engagement with your app. You can use push notifications to alert users about promotions, new content, or any other important updates. While push notifications are a powerful tool, setting up push notifications in iOS can be a daunting task that requires a significant amount of effort and time. Fortunately, the Courier iOS Mobile Notifications Software Development Kit (SDK) simplifies this process.&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/z7iqk1q8njt4/3rUcQDOobYWo1ZWJPFVwnH/d687bb8df008f348d2f18e38f38b091d/Simplifying_Notifications_with_the_Courier_iOS_SDK.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/z7iqk1q8njt4/3rUcQDOobYWo1ZWJPFVwnH/d687bb8df008f348d2f18e38f38b091d/Simplifying_Notifications_with_the_Courier_iOS_SDK.png" alt="iOS push notifications with Courier"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The Courier iOS SDK can help you to implement push notifications in iOS apps more easily thanks to some useful tools. The benefits of using the SDK include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Simplified setup process&lt;/strong&gt;. The Courier iOS SDK streamlines the process, making it easy to get set up and start sending push notifications. With some easy-to-follow setup steps and a few lines of code, you can quickly integrate the SDK into your apps.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Effective management of tokens and user state&lt;/strong&gt;. When implementing push notifications, you would normally need to handle token management manually, which involves generating and storing unique identifiers for each device that can receive notifications. This can be complex and time-consuming. The Courier iOS SDK provides tools that enable you to manage tokens and user state effectively.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sending test notifications&lt;/strong&gt;. Before sending push notifications to all users, it's important to test that they're working as intended. With the Courier iOS SDK, you can send test notifications to check that your notifications are working as intended before they go live. This can save time and prevent problems later.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Token management
&lt;/h2&gt;

&lt;p&gt;Managing push notification tokens is a complex task. Let’s talk a bit about how they work, to help us better understand the benefits of the Courier SDK.&lt;/p&gt;

&lt;p&gt;Tokens are unique identifiers that are used to identify a specific device for push notifications. They are generated and managed by the push notification service, which is the Apple Push Notification Service (APNS) for iOS. These tokens need to be securely stored on the back end, associated with the relevant user or device, and regularly updated as they can change or expire. Additionally, token management includes handling token deletion when a user unsubscribes from push notifications or uninstalls your app.&lt;/p&gt;

&lt;p&gt;Setting up and managing your own token management infrastructure can be a time-consuming and challenging task, requiring collaboration between front-end, mobile, and back-end developers. However, with the Courier iOS SDK, token management is handled automatically. The SDK automatically takes push notification tokens, stores them in Courier, and invalidates tokens that expire, so you can focus on building your product without having to learn the complex logistics of token management.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting started with Courier's iOS SDK
&lt;/h2&gt;

&lt;p&gt;Before you can use the &lt;a href="https://github.com/trycourier/courier-ios"&gt;Courier iOS SDK&lt;/a&gt;, you'll need a few things:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://developer.apple.com/xcode/"&gt;Xcode&lt;/a&gt;&lt;/strong&gt;. You'll need to have Apple's integrated development environment (IDE), Xcode, installed on your computer. &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;An Apple developer account&lt;/strong&gt;. To enable push notifications in your app, you'll need a valid Apple developer account.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A physical iOS device&lt;/strong&gt;. You'll need an iOS device to test your push notifications.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can find a full and detailed set-up guide in the iOS docs on the &lt;a href="https://github.com/trycourier/courier-ios#installation"&gt;Courier GitHub repository&lt;/a&gt;. &lt;/p&gt;

&lt;h2&gt;
  
  
  Push providers
&lt;/h2&gt;

&lt;p&gt;Before we look at sending a test push notification, it’s worth noting that to enable push notification support you will need to configure a push provider. A push provider is a service that handles the delivery of push notifications to targeted devices, ensuring reliable and efficient message transmission.&lt;/p&gt;

&lt;p&gt;Take a look at our &lt;a href="https://github.com/trycourier/courier-ios#3-configure-push-provider"&gt;GitHub&lt;/a&gt; page for more information. There you will find links to APNS (Apple Push Notification Service) and FCM (Firebase Cloud Messaging). Follow the links there to see more detailed information.&lt;/p&gt;

&lt;h2&gt;
  
  
  Enabling push notification capability
&lt;/h2&gt;

&lt;p&gt;Another vital step is to enable push notification capability in your iOS app. Here's how to do that.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create a new iOS app ID and a provisioning profile in the Apple Developer portal.

&lt;ul&gt;
&lt;li&gt;Log in to the Apple Developer portal and create a new app ID for your app.&lt;/li&gt;
&lt;li&gt;Create a new provisioning profile for your app, which will allow it to access push notification services.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;In Xcode, open your app project and select the project file from the project navigator.&lt;/li&gt;
&lt;li&gt;Select your app target and click on the "Capabilities" tab.&lt;/li&gt;
&lt;li&gt;Turn on the "Push Notifications" capability.&lt;/li&gt;
&lt;li&gt;In the "Signing &amp;amp; Capabilities" tab, select the team for your app and choose the provisioning profile you created in step 1.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Once this is done, you’ll have enabled push notification capability in your iOS app and can start sending push notifications to your users through the Courier iOS SDK.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting up a notification service extension
&lt;/h2&gt;

&lt;p&gt;So that you can better track your notifications, we recommend that you create a notification service extension in your iOS app. &lt;/p&gt;

&lt;p&gt;A notification service extension is a type of iOS app extension that allows developers to track the delivery status of notifications and perform additional actions, such as modifying the content of a notification. &lt;/p&gt;

&lt;p&gt;Setting up this extension allows Courier to accurately tell when your user receives a push notification when they are not using the app.&lt;/p&gt;

&lt;p&gt;You can find a full setup guide &lt;a href="https://github.com/trycourier/courier-ios#add-the-notification-service-extension-recommended"&gt;here&lt;/a&gt;, on the Courier iOS SDK GitHub page. It won’t take long, and then Courier will be able to track delivery status in any state your app could be in.&lt;/p&gt;

&lt;h2&gt;
  
  
  Push notification permissions
&lt;/h2&gt;

&lt;p&gt;The Courier iOS SDK needs to have push notification permissions granted by your users before it can access the push notification token. That’s where the &lt;code&gt;requestNotificationPermission()&lt;/code&gt; function comes in.&lt;br&gt;
Here's how it works:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;Courier&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;shared&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;requestNotificationPermission&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; 
&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="c1"&gt;// Send some notification }&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;requestNotificationPermission()&lt;/code&gt; function returns an authorization status that indicates whether the user has granted or denied permissions for push notifications.&lt;/p&gt;

&lt;p&gt;You can read more about authorization status &lt;a href="https://developer.apple.com/documentation/usernotifications/unauthorizationstatus"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Once your user has given access to receive notifications, a push notification device token is created by iOS and automatically synced to Courier.&lt;/p&gt;

&lt;h2&gt;
  
  
  Managing user state
&lt;/h2&gt;

&lt;p&gt;Now that you've set up your notification service extension and handled permissions, it's time to focus on delivering a great user notification experience. One key part of this is keeping track of user state — the status of a user in relation to your app. This allows you to send users appropriate notifications based on whether they’re logged in or out. For instance, if a user is logged out, it wouldn't make sense to send them a notification about an activity that requires them to be logged in. &lt;/p&gt;

&lt;p&gt;The Courier iOS SDK provides tools that help developers manage user state effectively and easily. By using the &lt;code&gt;signIn()&lt;/code&gt; function provided by the SDK, developers can ensure that the user's push notification tokens and their state are kept in sync. Here's how to use the function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;Courier&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;shared&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;signIn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;accessToken&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;accessToken&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;accessToken&lt;/code&gt; can either be a Courier Auth Key used during development or a JWT (JSON Web Token) that should be used in a production app. You can create an access token by calling the Courier API as shown in the GitHub &lt;a href="https://github.com/trycourier/courier-ios#going-to-production"&gt;documentation&lt;/a&gt;. When the user signs out, you can use the &lt;code&gt;signOut()&lt;/code&gt; function to clear their state.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;signIn()&lt;/code&gt; function saves the &lt;code&gt;accessToken&lt;/code&gt; and &lt;code&gt;userId&lt;/code&gt; to the native-level local storage, ensuring that the user's state persists between app sessions. This ensures that the user's push notification tokens are kept up to date.&lt;br&gt;
By following these steps, you automatically sync push notification tokens and user state to Courier. This lets you ensure that push notifications are delivered at the right time and in the appropriate context for a better user experience.&lt;/p&gt;
&lt;h2&gt;
  
  
  Handling notification delivery
&lt;/h2&gt;

&lt;p&gt;We've made it easy for you to monitor the delivery status of your notifications using the Courier iOS SDK via the &lt;code&gt;onPushNotificationDelivered()&lt;/code&gt; function. &lt;/p&gt;

&lt;p&gt;When a push notification is delivered to a user's device, &lt;code&gt;onPushNotificationDelivered()&lt;/code&gt; is triggered automatically by the SDK if the app is in foreground. Here is an example implementation of the function in the &lt;code&gt;AppDelegate&lt;/code&gt; class (the &lt;code&gt;AppDelegate&lt;/code&gt; class is a key part of every iOS app and handles system-level events, including push notifications):&lt;/p&gt;

&lt;p&gt;Courier analytics will track the delivery of this notification for you automatically.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;AppDelegate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;CourierDelegate&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="nx"&gt;override&lt;/span&gt; &lt;span class="nx"&gt;func&lt;/span&gt; &lt;span class="nx"&gt;pushNotificationDeliveredInForeground&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;AnyHashable&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Any&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;UNNotificationPresentationOptions&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[.&lt;/span&gt;&lt;span class="nx"&gt;sound&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;list&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;banner&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;badge&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="c1"&gt;// Pass [] to hide any foreground presentation&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note that this function will not be triggered when the app is in the "killed", "not running", or “background” state.&lt;/p&gt;

&lt;h2&gt;
  
  
  Handling a notification click
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;pushNotificationClicked()&lt;/code&gt; function provided by the Courier iOS SDK can be used to handle user interaction with a notification inside your AppDelegate class. This function is called when the user clicks on a notification that was sent through Courier. Here's sample code for using the function:&lt;/p&gt;

&lt;p&gt;Courier analytics will track the clicking of this notification for you automatically.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;AppDelegate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;CourierDelegate&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;override&lt;/span&gt; &lt;span class="nx"&gt;func&lt;/span&gt; &lt;span class="nx"&gt;pushNotificationClicked&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;AnyHashable&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Any&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this example, the function simply prints out the content of the message payload. However, you can use the message to perform any number of actions in your app, such as opening a specific screen or executing a certain function. &lt;/p&gt;

&lt;h2&gt;
  
  
  Sending a test push
&lt;/h2&gt;

&lt;p&gt;Now that you’ve set up your push notification infrastructure and know how to manage user state, you can start sending messages. The SDK allows you to send push notifications directly to a user ID without having to juggle tokens or manage a back end.&lt;/p&gt;

&lt;p&gt;To send a test push notification, you can use the &lt;code&gt;sendPush()&lt;/code&gt; function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;messageId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;Courier&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;shared&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sendPush&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;authKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;a_courier_auth_key_that_should_only_be_used_for_testing&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;example_user&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Happy Holidays!&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Happy holidays from Courier.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;providers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[.&lt;/span&gt;&lt;span class="nx"&gt;apns&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fcm&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note that your user must have been given access to receive push notifications so that the push notification device tokens can be automatically synced to Courier and Courier can send a message to your user’s device. See “Push notification permissions” above for more details.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;With just a few lines of code, you can streamline your push notification management and focus on building your product, thanks to the Courier iOS SDK.&lt;/p&gt;

&lt;p&gt;By taking care of complex tasks such as token management and message delivery tracking, the SDK allows you to focus on building high-quality push notification experiences for your users. &lt;/p&gt;

&lt;p&gt;If you’re an iOS developer, make sure to give the Courier iOS SDK a try and see how it can simplify and enhance your push notification workflow. Courier also has other SDKs available for &lt;a href="https://github.com/trycourier/courier-android"&gt;Android&lt;/a&gt;, &lt;a href="https://github.com/trycourier/courier-react-native"&gt;React Native&lt;/a&gt;, and &lt;a href="https://github.com/trycourier/courier-flutter"&gt;Flutter&lt;/a&gt;. So if you're looking for a way to simplify your push notification management, check out &lt;a href="https://www.courier.com/"&gt;Courier&lt;/a&gt; today.&lt;/p&gt;

</description>
      <category>mobile</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Introducing Courier Preferences: The Fastest Way to Design the Best Preference Experience</title>
      <dc:creator>Charla Bigelow</dc:creator>
      <pubDate>Thu, 23 Feb 2023 16:32:17 +0000</pubDate>
      <link>https://dev.to/courier/introducing-courier-preferences-the-fastest-way-to-design-the-best-preference-experience-1503</link>
      <guid>https://dev.to/courier/introducing-courier-preferences-the-fastest-way-to-design-the-best-preference-experience-1503</guid>
      <description>&lt;p&gt;Notification preferences are important — especially if you’re building an application that sends dozens of notifications. Users want control over which kind of notifications they receive, and through what channels. That’s why it is essential to provide a well-designed preference experience that’s quick, intuitive, and guaranteed to respect your users’ chosen preferences.&lt;/p&gt;

&lt;p&gt;With Courier, you can build great notification infrastructure effortlessly. But until now, preferences were handled separately, requiring engineers to spend a significant amount of time on both the back-end logic and the UI where users could select which notifications they want to receive and how.&lt;/p&gt;

&lt;p&gt;As an engineer myself, I’ve had to build workflows like this several times in past projects &amp;amp; companies. But those days are over: we’re excited to announce general availability of Courier Preferences. With Courier Preferences, you can build a full notification Preference Center in no time. The clean interface, which comes with endless options for customization, lets users manage their preferences. Courier’s infrastructure will take care of the rest by integrating them into our core products that trigger sends.&lt;br&gt;
&lt;a href="//images.ctfassets.net/z7iqk1q8njt4/1zGTehzHW9Zhm9bF9mMJ54/7f165ef31524e36198b437e748075d4a/preferences_center.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/z7iqk1q8njt4/1zGTehzHW9Zhm9bF9mMJ54/7f165ef31524e36198b437e748075d4a/preferences_center.png" alt="Preferences Center"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;(Within the Preference Center, users can comfortably &lt;a href="https://www.courier.com/blog/pm-guide-to-preference-management/" rel="noopener noreferrer"&gt;opt in and out of notifications by topic and channel&lt;/a&gt;)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Whether you want to build a bespoke notification experience with the Preferences API, embed our pre-built UI components, or use the hosted preferences page that Courier provides out of the box for faster time to value, is entirely up to you.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"Courier turned something that takes months into days. We were able to unlock the ability for our users to opt in/out of notifications in a customized Preference Center within one day. It was easy."&lt;/em&gt;&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Guilherme Samora&lt;/strong&gt;, Senior Product Manager, &lt;a href="https://www.sastrify.com/" rel="noopener noreferrer"&gt;Sastrify&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In this post, we’ll look at how to implement the best preference experience for your users. We’ll talk about how Courier makes it easy to handle preference settings and logs, and how the new Preferences Designer makes preference management a walk in the park. But first, let’s briefly recap why it’s important to implement preferences for your users.&lt;/p&gt;

&lt;h2&gt;
  
  
  Notification preferences are a vital feature of any app
&lt;/h2&gt;

&lt;p&gt;Aside from their core functionality of alerting your users to activity in the app that requires their attention, notifications can be used to increase users’ awareness of your product’s features, for marketing and other purposes. &lt;/p&gt;

&lt;p&gt;But getting all these kinds of notifications from different organizations can quickly become annoying for users. To make sure they remain in control of their own notification experience, and to avoid them turning off all notifications from your app, it is vital that you implement notification preferences that are fine-grained and easy to use. And it’s just as important that you respect those preferences and only send your users the notifications they really want.&lt;/p&gt;

&lt;p&gt;Building notification preferences for your app from scratch is hard, especially if you use more than one channel to communicate with your users. It requires a significant amount of time and engineering resources — and don’t forget the need for future maintenance. By letting Courier host your preference infrastructure, you can instead use those resources to focus on building your core product. &lt;br&gt;
&lt;a href="//images.ctfassets.net/z7iqk1q8njt4/7F272P2uQSET33IeUXnNv5/ee57c441348aa4e771bd187d6b6fa6ea/preferences-infrastructure.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/z7iqk1q8njt4/7F272P2uQSET33IeUXnNv5/ee57c441348aa4e771bd187d6b6fa6ea/preferences-infrastructure.png" alt="Preferences Infrastructure"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here at Courier, we have spent quite some time thinking about what the best preference experience looks like. It needs to be clean, customizable, and easy to use for the end users. It also needs to offer sufficient transparency for compliance and record-keeping purposes on the back end. Since these requirements align across products, there is no need for every company to build their own. Instead, Courier Preferences lets you set up the notification experience that you want for your users in just a few hours.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to design a good preference experience
&lt;/h2&gt;

&lt;p&gt;You can now use the &lt;a href="https://www.courier.com/docs/courier-preferences/preference-center/introduction/#preferences-designer" rel="noopener noreferrer"&gt;Preferences Designer&lt;/a&gt; in the Courier web interface to design the best preference experience for your users. (As an alternative to the Preferences Designer, you can use the Preferences API for a more native experience within your app — more on that later.)&lt;/p&gt;

&lt;p&gt;Since most applications send out many different notifications, it can quickly become cumbersome for users to manually set preferences for every single option. For a better user experience, Courier groups notifications by &lt;a href="https://www.courier.com/docs/courier-preferences/#what-is-a-subscription-topic" rel="noopener noreferrer"&gt;subscription topics&lt;/a&gt; (formerly called “categories”). In addition, business tier Courier customers can bundle several topics by sections: as an example, you could set up two topics named “Feature launch” and “Product update” and file them both under a “Product” umbrella section, for even easier preference management.&lt;/p&gt;

&lt;p&gt;Turning notifications on and off is not all, however. Notifications can be sent via &lt;a href="https://www.courier.com/docs/getting-started/courier-concepts/#channels" rel="noopener noreferrer"&gt;different channels&lt;/a&gt; in Courier, such as email, SMS, and push notifications. Users can select which channels they prefer for each subscription topic. For instance, they might prefer to be notified via email about a new message or rating on a social media platform, but not about minor interactions such as likes. Conversely, they may want to activate multiple channels — like SMS, email, and Slack — if there’s an emergency, like a security breach.&lt;/p&gt;

&lt;p&gt;As a developer, you can turn certain notifications on and off by default for different topics and channels. You can also set certain indispensable notification topics as required. By marking a notification topic as required (for example, "Reset Password"), it prevents your users from disabling notifications for that topic, while reassuring them that they’ll still receive such critical notifications even if they disable everything else.&lt;br&gt;
&lt;a href="//images.ctfassets.net/z7iqk1q8njt4/4QZxztLxHb1sx0Lxagztxc/1f489f232c4e074cd88b941715ff3ff6/subscription_topics.gif" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/z7iqk1q8njt4/4QZxztLxHb1sx0Lxagztxc/1f489f232c4e074cd88b941715ff3ff6/subscription_topics.gif" alt="Subscription Topics"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Voilà: a user-facing Preference Center
&lt;/h3&gt;

&lt;p&gt;In the Preferences Designer, you can preview how your users will see the Preference Center in their browser. The page is designed to be easily navigable: preferences for notification topics and channels can be turned on and off with the click of a mouse. &lt;br&gt;
&lt;a href="//images.ctfassets.net/z7iqk1q8njt4/XEsGsLpOZrEmHfXynxBsx/34639f4228816802dd71a29635874a84/user-preferences.gif" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/z7iqk1q8njt4/XEsGsLpOZrEmHfXynxBsx/34639f4228816802dd71a29635874a84/user-preferences.gif" alt="Preferences Center"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;All of these features are available in our free tier. For more functionality, like multiple sections or the ability to remove the Courier watermark from the user-facing Preference Center, use our &lt;a href="https://www.courier.com/request-demo" rel="noopener noreferrer"&gt;Business plan&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Your preference UI, hosted by Courier
&lt;/h3&gt;

&lt;p&gt;You can now direct users to their personal Preference Center through a hyperlink in your notifications to them. In newer Courier workspaces, your notification templates will include this hyperlink by default. Otherwise, you can &lt;a href="https://www.courier.com/docs/courier-preferences/preference-center/hosted-page/#preference-center-link" rel="noopener noreferrer"&gt;add the link manually&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;By following the link, your users will be able to see and update their own notification preferences within the Preference Center. Note that this requires no login: by automatically encoding their &lt;strong&gt;user_id&lt;/strong&gt; in the Preference Center hyperlink, it will be personalized to that user, and their preferences will be saved in their profile in the back end. &lt;br&gt;
&lt;a href="//images.ctfassets.net/z7iqk1q8njt4/7qkwz5ZMzIFWRXjdGoYcHw/49c0ee1ca66838f98efce8e9ffe8325c/Preferences__feature_launch_post_2.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/z7iqk1q8njt4/7qkwz5ZMzIFWRXjdGoYcHw/49c0ee1ca66838f98efce8e9ffe8325c/Preferences__feature_launch_post_2.png" alt="Preferences architecture diagram"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And that’s it: the easiest way to create the best customized preference experience, with only a few clicks in the Courier Preferences Designer.    &lt;/p&gt;

&lt;h2&gt;
  
  
  Build your own interface via the Preferences API
&lt;/h2&gt;

&lt;p&gt;You don’t have to use the hosted Preference Center — you can build your own, or even integrate notification controls elsewhere in your application. Have a look at the &lt;a href="https://www.courier.com/docs/courier-preferences/user-preferences-api/" rel="noopener noreferrer"&gt;documentation for the Courier Preferences API&lt;/a&gt; to learn more. &lt;/p&gt;

&lt;p&gt;Finally, you can choose the middle ground and embed &lt;a href="https://www.courier.com/docs/courier-preferences/use-guides/embed-courier-preferences/" rel="noopener noreferrer"&gt;Courier’s prebuilt component&lt;/a&gt; in your own preferences UI. &lt;/p&gt;

&lt;h2&gt;
  
  
  A user-friendly unsubscribe experience
&lt;/h2&gt;

&lt;p&gt;Unsubscribing from unwanted notifications should be fast and easy — and you should make sure to respect your users’ preferences to avoid unnecessary frustration. Similar to the preferences hyperlink, you can easily &lt;a href="https://www.courier.com/docs/courier-preferences/use-guides/embed-courier-preferences/#unsubscribing-from-notifications" rel="noopener noreferrer"&gt;embed an unsubscribe link&lt;/a&gt; in your notification template. By clicking on it, users can unsubscribe from topics that they’re not interested in. &lt;/p&gt;

&lt;h2&gt;
  
  
  Keeping a digital paper trail of your users’ preference history
&lt;/h2&gt;

&lt;p&gt;Keeping &lt;a href="https://www.courier.com/docs/courier-preferences/logs/" rel="noopener noreferrer"&gt;logs of your users’ changes in preference settings&lt;/a&gt; is useful for debugging, compliance, and solving issues in customer support. Courier automatically logs all your users’ changes in notification preferences and lets you view them via the API or directly in the Courier web interface. &lt;/p&gt;

&lt;p&gt;Let’s say, for instance, that a user isn’t receiving your notifications, even though they were enabled in their settings. You can now simply check the logs to determine where the error occurred. Conversely, a user might complain that they’re still receiving unwanted notifications after they thought they turned them off. By looking at the preference logs, you can see whether they indeed turned notifications off, or if they accidentally enabled them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Get started with Courier Preferences today
&lt;/h2&gt;

&lt;p&gt;Being able to offer users a fine-grained and intuitive preference experience saves companies a lot of time and puts them further ahead on their roadmap. We’re excited to make preference management easy and smooth for businesses.&lt;/p&gt;

&lt;p&gt;To learn more and start designing the best preference experience for your audience, take a closer look at &lt;a href="https://www.courier.com/features/preferences/" rel="noopener noreferrer"&gt;Courier Preferences&lt;/a&gt; and the &lt;a href="https://www.courier.com/docs/courier-preferences/" rel="noopener noreferrer"&gt;docs&lt;/a&gt;. We’re sure your users will love it!&lt;/p&gt;

</description>
      <category>gratitude</category>
    </item>
    <item>
      <title>Tools and Techniques to Establish Your Data Team Early</title>
      <dc:creator>Charla Bigelow</dc:creator>
      <pubDate>Tue, 21 Feb 2023 17:14:11 +0000</pubDate>
      <link>https://dev.to/courier/tools-and-techniques-to-establish-your-data-team-early-3pfk</link>
      <guid>https://dev.to/courier/tools-and-techniques-to-establish-your-data-team-early-3pfk</guid>
      <description>&lt;p&gt;You can’t really invest in a data team too early. That’s something we learned as a small but growing team. We invested in a data team at an early stage, so we could establish product usage trends, create business insights, and identify areas to improve our product. &lt;/p&gt;

&lt;p&gt;If you’re an early-stage startup, start building your data strategy team and architecture as soon as you can. You don’t have to go big—a one-person data team can make a massive difference to your success as you grow.&lt;/p&gt;

&lt;h2&gt;
  
  
  Hire a Generalist First
&lt;/h2&gt;

&lt;p&gt;When it comes to launching your data team, you might be tempted to hire an expert from the largest brand name you possibly can. Our advice: find someone with early-stage startup experience and has worked with data at scale too.&lt;/p&gt;

&lt;p&gt;There are wonderful people with brilliant minds working on large enterprise data teams, but these large teams often have their employees specialize in specific parts of their data platform. That makes sense when you have 10 data engineers on a team or when you have a large team of machine learning data scientists working on pricing algorithms. But when you’ve only got one data role for the entire organization, you need someone who can do a little bit of everything.  &lt;/p&gt;

&lt;p&gt;It’s important to first hire an all-rounder who can create data insights, develop business hypotheses, and create a scalable data architecture from scratch. You need someone ready to start building the data team from the ground up, preferably someone who can hire out the rest of your team once you’re ready to do so.&lt;/p&gt;

&lt;p&gt;This first hire should be someone who has experience building (or at least working on) a data team at an early-stage startup. They need to be able to generate value quickly and have the business acumen to ask the right questions. They should be able to approach data in an 80/20 manner to deliver immediate results instead of diving into the hardest problems that you have.&lt;/p&gt;

&lt;p&gt;For the data engineering side, it’s common to hire a consultant or contractor to help set up your architecture. We decided to complement our first hire with a consultant. This way, we have the best of both worlds – an expert in data engineering part time, and a generalist who can maintain data architecture on a day to day basis. We set up strong foundations and also kept our knowledge in house.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Small Data Team Can Answer Big Questions
&lt;/h2&gt;

&lt;p&gt;Even if you can’t hire right away for all the roles you want on your data team long term, you can do a lot with only one or two of the right people. The key is thinking in terms of scalability and efficiency. What helps your data team do more with less? And how will this work when your team is bigger? &lt;/p&gt;

&lt;p&gt;Scalable analyses are extremely important with a small data team. By thinking ahead about how your processes and workflows will function as your team grows, you’ll save time and create valuable solutions for some of your employees’ most time-consuming issues. &lt;/p&gt;

&lt;p&gt;For example, we immediately realized that our sales team had no visibility into prospective customer product usage, and it was going to come up often. So we built our sales reps a workspace dash so they can pull the data themselves. Our data team’s job is to enable data-driven decision-making. When we saw an opportunity for a one-time data investment that could lead to hundreds of saved hours on the sales side, we acted on it.  &lt;/p&gt;

&lt;p&gt;A small data team should also be focused on preventing issues in the future. If you only have the headcount for a one-person data team at the moment, keep them focused on your biggest problems but also budget time to maintain and scale your data architecture to reduce tech debt.  &lt;/p&gt;

&lt;h2&gt;
  
  
  The Tools Our Data Team Uses
&lt;/h2&gt;

&lt;p&gt;From the beginning, our goal was to be forward-thinking with every decision. &lt;/p&gt;

&lt;p&gt;Like all startups, we wanted to generate immediate ROI. We use a centralized data warehouse that is our single source of truth – nothing hurts data teams more than having fragmented or untrustworthy data. We’re able to consolidate our data thanks to automations and integrations. This helps us provide valuable insights to the entire company with low lift.&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/z7iqk1q8njt4/3pFCwyZg6MxboLiVy7XSbg/595cfb4106487dcfd83df6160a93a6ec/data-tools.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/z7iqk1q8njt4/3pFCwyZg6MxboLiVy7XSbg/595cfb4106487dcfd83df6160a93a6ec/data-tools.png" alt="data-tools"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We pipe in all of our data using ELT tools like Segment (which is also our data collection tool), Fivetran, and Snowflake external stages.&lt;/p&gt;

&lt;p&gt;We use &lt;a href="https://docs.getdbt.com/" rel="noopener noreferrer"&gt;dbt&lt;/a&gt; to clean all of our raw data into a more usable format. For example, our dim_user table has ~20 tables powering it under the hood, that have a lot of critical info on our customers, such as how many notifications they send and what plan type they are on. By making this so readily available, our non-technical users can quickly draw business insights. We can quickly look at non-paying or self-serve customers that are sending a lot of notifications to identify business contract prospects. Our customer success team also built reports to look at the health of our business contract customers. We also use dbt to clean our data of sensitive information.&lt;/p&gt;

&lt;p&gt;We use Census to send our warehouse data back to places such as HubSpot and Courier. This allows our sales and marketing teams to access this information on the platform that they are using on a daily basis.&lt;/p&gt;

&lt;p&gt;Metabase is our BI tool. It allows us to create dashboards, write SQL queries, and provide a self-serve format for non-technical users. We’ve found that creating these kinds of self-serve data dashboards allows users to answer a lot of their own questions. They are quick to spin up without large investments upfront like teams that use Looker or Tableau. &lt;/p&gt;

&lt;p&gt;For example, we have a KPI dash that shows the top metrics across the company. We can also track how individual workspaces are performing and identify workspaces that we can target for our sales team. We have also brought data-driven decision-making to our product development by deep-diving into our customers’ product usage. One example is that we found that a lot of our users were creating their own Courier instance instead of joining their colleague’s existing Courier instance. Using this insight, our product team went back to the drawing board to create a page that surfaces Courier workspaces that are created by the same business email domain so you can easily request access. &lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/z7iqk1q8njt4/3YqJYHW2TZvNZKECRcxRLp/aa3270ad15f19508ccc4163f0eecbcba/data-courier-workspace.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/z7iqk1q8njt4/3YqJYHW2TZvNZKECRcxRLp/aa3270ad15f19508ccc4163f0eecbcba/data-courier-workspace.png" alt="data-courier-workspace"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We also use Propel to power our in-app analytics to provide visibility for our end users. One way that we use Propel is to deliver template analytics for our Business-tier customers. Propel allows our engineers to easily put GraphQL visualizations on top of our metrics from our warehouse.&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/z7iqk1q8njt4/21zTz6npIrfOc1bmnswwuq/428adf42da4af54d0389b0899c52f780/data-courier-analytics.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/z7iqk1q8njt4/21zTz6npIrfOc1bmnswwuq/428adf42da4af54d0389b0899c52f780/data-courier-analytics.png" alt="data-courier-analytics"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Our Data Team Is Evolving Again
&lt;/h2&gt;

&lt;p&gt;Now, we’re entering a period of growth, so naturally, we’re evolving our data approach again. Our current goal is to enable our data scientists to build subject matter expertise while still maintaining centralized data standards. &lt;/p&gt;

&lt;p&gt;Our current structure is centralized, with a senior analytics engineer reporting to the head of data. It works now since we’re still in the earlier stages of building out a data team. But when the team expands, we will be transitioning to a hybrid model.&lt;/p&gt;

&lt;p&gt;Data scientists will cover certain business areas (like the product team), but they’ll still report to the Head of Data. This will allow the data scientists to build subject matter expertise while still maintaining centralized data standards as we continue to grow. Because the data team will always be changing and growing along with the rest of the organization, you need standards that will scale with the business.&lt;/p&gt;

&lt;p&gt;If you’d like to learn more about how we approach data standardization, check out our blog post on &lt;a href="https://www.courier.com/blog/how-courier-became-hipaa-compliant/" rel="noopener noreferrer"&gt;how Courier became HIPAA compliant&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>gratitude</category>
    </item>
    <item>
      <title>How to Send Invoice and Add Payment Reminder in Next.js with Courier API</title>
      <dc:creator>Charla Bigelow</dc:creator>
      <pubDate>Wed, 15 Feb 2023 17:32:02 +0000</pubDate>
      <link>https://dev.to/courier/how-to-send-invoice-and-add-payment-reminder-in-nextjs-with-courier-api-3i0a</link>
      <guid>https://dev.to/courier/how-to-send-invoice-and-add-payment-reminder-in-nextjs-with-courier-api-3i0a</guid>
      <description>&lt;h2&gt;
  
  
  Background
&lt;/h2&gt;

&lt;p&gt;A lot of open-source invoice management apps are built with Laravel. As a Javascript developer, I wanted to build the “React Solution” for devs that are familiar with React and Javascript.&lt;/p&gt;

&lt;p&gt;A problem I found when building with services in Node.js is that there is no built-in mailer. So, I had to find a 3rd party service to do that for me. In this article, I will be integrating &lt;a href="https://www.courier.com/" rel="noopener noreferrer"&gt;Courier&lt;/a&gt; to send emails for this project &lt;a href="https://github.com/fazzaamiarso/invoys" rel="noopener noreferrer"&gt;https://github.com/fazzaamiarso/invoys&lt;/a&gt;.  &lt;/p&gt;

&lt;h2&gt;
  
  
  Pre-requisites
&lt;/h2&gt;

&lt;p&gt;As this article isn't your typical follow-along (more like "please sit tight and see how I do it"), it's not mandatory to be familiar with all technologies used. However, familiarity with Typescript and Next.js will be beneficial for quicker understanding.&lt;/p&gt;

&lt;p&gt;Technologies in this blog:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://www.typescriptlang.org/" rel="noopener noreferrer"&gt;Typescript&lt;/a&gt;: type-safety and auto-completion are the best, right? &lt;/li&gt;
&lt;li&gt;
&lt;a href="https://nextjs.org/" rel="noopener noreferrer"&gt;Next.js&lt;/a&gt;: a production-ready framework to build a full-stack app, even for beginners.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.prisma.io/" rel="noopener noreferrer"&gt;Prisma&lt;/a&gt;: a great ORM to work with databases. We use Prisma because of its type-safety and auto-completion, providing great developer experience with typescript added.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://trpc.io/" rel="noopener noreferrer"&gt;Trpc&lt;/a&gt;: enable us to easily build end-to-end type-safety between our Next.js client and server.&lt;/li&gt;
&lt;li&gt;Courier API: a great service/platform to handle our notifications, such as email, SMS, and much more.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can find the full &lt;a href="https://github.com/fazzaamiarso/invoys/tree/bc231301c92bb07692a3388bb50d76d61603a41f" rel="noopener noreferrer"&gt;source code here&lt;/a&gt; for reference.&lt;/p&gt;

&lt;h3&gt;
  
  
  Goals
&lt;/h3&gt;

&lt;p&gt;Before building the features, let's define our goals.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Send invoice link to client's email.&lt;/li&gt;
&lt;li&gt;Send a reminder a day before an invoice's due date.&lt;/li&gt;
&lt;li&gt;Cancel an invoice due date reminder when the invoice is already paid.&lt;/li&gt;
&lt;li&gt;Handling network errors.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Part 1: Setup Courier Platform
&lt;/h3&gt;

&lt;p&gt;Let's head over to the Courier Dashboard. By default, it's in a production environment. Since I want to test things out, I'm going to change to the test environment by clicking the dropdown in the top-right corner.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;We can copy all templates later to production or vice-versa.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Now, I will create a &lt;a href="https://www.courier.com/docs/getting-started/courier-concepts/#brands" rel="noopener noreferrer"&gt;&lt;strong&gt;brand&lt;/strong&gt;&lt;/a&gt; for my email notifications.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp3o9789qozxnh8clnz97.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp3o9789qozxnh8clnz97.gif" alt="go to brand" width="600" height="289"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I'm just going to add a logo (beware that the logo width is fixed to 140px) on the header and social links on the footer. The designer UI is pretty straightforward, so here is the final result. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.imgur.com%2FsFrofll.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.imgur.com%2FsFrofll.png" alt="brand template" width="800" height="383"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Don't forget to publish the changes.&lt;/p&gt;

&lt;h3&gt;
  
  
  Part 2: Send Invoice to Email
&lt;/h3&gt;

&lt;p&gt;Currently, the send email button on the UI is doing nothing.&lt;/p&gt;

&lt;p&gt;I'm going to create a &lt;code&gt;courier.ts&lt;/code&gt; file in &lt;code&gt;src/lib/&lt;/code&gt; to keep all Courier-related code. Also, I will use &lt;a href="https://github.com/trycourier/courier-node" rel="noopener noreferrer"&gt;courier node.js client library&lt;/a&gt; which already abstracted all Courier API endpoints to functions.&lt;/p&gt;

&lt;p&gt;Before I build the functionality, let's create the email notification design within Courier's Designer and set up the Gmail provider.&lt;/p&gt;

&lt;p&gt;On the email designer page, we will see that the created brand is already integrated. After that, let's design the template accordingly with the needed data. Here is the final result.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.imgur.com%2FqFhHvAP.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.imgur.com%2FqFhHvAP.png" alt="email template final" width="800" height="785"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.imgur.com%2FB6vKjpH.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.imgur.com%2FB6vKjpH.png" alt="action button" width="800" height="577"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Notice the value with &lt;code&gt;{}&lt;/code&gt; that becomes green, it means it's a variable that can be inserted dynamically. I also set the 'See Invoice' button (or action) with a variable.&lt;/p&gt;

&lt;p&gt;Before I can use the template, I need to create a &lt;em&gt;test event&lt;/em&gt; by clicking the preview tab. Then, it will show a prompt to name the event and set &lt;code&gt;data&lt;/code&gt; in JSON format. That data field is what will populate the value of the green &lt;code&gt;{}&lt;/code&gt; variables (the data can be set from code also). Since it's a test event, I will fill it with arbitrary values.&lt;/p&gt;

&lt;p&gt;Next, I will publish the template so I can use it. Then, go to send tab. It will show the necessary code to send the email programmatically and the &lt;code&gt;data&lt;/code&gt; will be populated with the previous &lt;em&gt;test event&lt;/em&gt; that I created. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.imgur.com%2FPMGcVQq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.imgur.com%2FPMGcVQq.png" alt="code snippet" width="800" height="743"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Backend
&lt;/h4&gt;

&lt;p&gt;I will copy the test &lt;code&gt;AUTH_TOKEN&lt;/code&gt; to the &lt;code&gt;.env&lt;/code&gt; file and copy the snippet to &lt;code&gt;src/lib/courier.ts&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;authToken&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;COURIER_AUTH_TOKEN&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// email to receive all sent notifications in DEVELOPMENT mode&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;testEmail&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;COURIER_TEST_EMAIL&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;INVOICE_TEMPLATE_ID&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;TEMPLATE_ID&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;courierClient&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;CourierClient&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;authorizationToken&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;authToken&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create a &lt;code&gt;sendInvoice&lt;/code&gt; function that will be responsible for sending an email. To send an email from the code, I use the &lt;code&gt;courierClient.send()&lt;/code&gt; function.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// src/lib/courier.ts&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sendInvoice&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="nx"&gt;customerName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;invoiceNumber&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;invoiceViewUrl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;emailTo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;productName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;dueDate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}:&lt;/span&gt; &lt;span class="nx"&gt;SendInvoice&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;recipientEmail&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;NODE_ENV&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;production&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;emailTo&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;testEmail&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;requestId&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;courierClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;to&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;recipientEmail&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;INVOICE_TEMPLATE_ID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="c1"&gt;// Data for courier template designer&lt;/span&gt;
        &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="nx"&gt;customerName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="nx"&gt;invoiceNumber&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="nx"&gt;invoiceViewUrl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="nx"&gt;productName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="nx"&gt;dueDate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;requestId&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Define types for the &lt;code&gt;sendInvoice&lt;/code&gt; function.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// src/lib/courier.ts&lt;/span&gt;

&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;SendInvoice&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;productName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;dueDate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;customerName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;invoiceNumber&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;invoiceViewUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;emailTo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now that I can send the email, I will call it in the &lt;code&gt;sendEmail&lt;/code&gt; trpc endpoint that resides in &lt;code&gt;src/server/trpc/router/invoice.ts&lt;/code&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Just remember that trpc endpoint is a Next.js API route. In this case, &lt;code&gt;sendEmail&lt;/code&gt; will be the same as calling the &lt;code&gt;/api/trpc/sendEmail&lt;/code&gt; route with &lt;code&gt;fetch&lt;/code&gt; under the hood. For more explanation &lt;a href="https://trpc.io/docs/quickstart" rel="noopener noreferrer"&gt;https://trpc.io/docs/quickstart&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// src/server/trpc/router/invoice.ts&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;sendInvoice&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@lib/courier&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;dayjs&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@lib/dayjs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// .....SOMEWHERE BELOW&lt;/span&gt;
  &lt;span class="nl"&gt;sendEmail&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;protectedProcedure&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;input&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;object&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;customerName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="na"&gt;invoiceNumber&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="na"&gt;invoiceViewUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="na"&gt;emailTo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="na"&gt;invoiceId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="na"&gt;productName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="na"&gt;dueDate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;date&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
      &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;mutation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;input&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;invoiceData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;dueDate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;dayjs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dueDate&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;D MMMM YYYY&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="p"&gt;};&lt;/span&gt;

      &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;sendInvoice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;invoiceData&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}),&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For those who are unfamiliar with trpc, what I did is the same as handling a &lt;code&gt;POST&lt;/code&gt; request. Let's break it down.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Trpc way of defining &lt;strong&gt;request input from client&lt;/strong&gt; by validating with Zod. Here I define all data that are needed for the &lt;code&gt;sendInvoice&lt;/code&gt; function.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;input&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;object&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;customerName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="na"&gt;invoiceNumber&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="na"&gt;invoiceViewUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="na"&gt;emailTo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="na"&gt;invoiceId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="na"&gt;productName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="na"&gt;dueDate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;date&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
      &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Define a &lt;code&gt;POST&lt;/code&gt; request handler (mutation).
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// input from before&lt;/span&gt;
 &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;mutation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;input&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;invoiceData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="c1"&gt;// format a date to string with a defined format. &lt;/span&gt;
        &lt;span class="na"&gt;dueDate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;dayjs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dueDate&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;D MMMM YYYY&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="c1"&gt;// ex.'2 January 2023'&lt;/span&gt;
      &lt;span class="p"&gt;};&lt;/span&gt;

      &lt;span class="c1"&gt;// send the email&lt;/span&gt;
      &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;sendInvoice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;invoiceData&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}),&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Frontend
&lt;/h4&gt;

&lt;p&gt;Now, I can start to add the functionality to the send email button. I'm going to use the &lt;code&gt;trpc.useMutation()&lt;/code&gt; function which is a thin wrapper of &lt;code&gt;tanstack-query's&lt;/code&gt;useMutation`.&lt;/p&gt;

&lt;p&gt;Let's add the mutation function. On successful response, I want to send a success toast on UI.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&lt;/code&gt;`tsx&lt;br&gt;
//src/pages/invoices/[invoiceId]/index.tsx&lt;br&gt;
import toast from 'react-hot-toast';&lt;/p&gt;

&lt;p&gt;const InvoiceDetail: NextPage = () =&amp;gt; {&lt;br&gt;
// calling the &lt;code&gt;sendEmail&lt;/code&gt; trpc endpoint with tanstack-query.&lt;br&gt;
  const sendEmailMutation = trpc.invoice.sendEmail.useMutation({&lt;br&gt;
    onSuccess() {&lt;br&gt;
      toast.success('Email sent!');&lt;br&gt;
    }&lt;br&gt;
  });&lt;/p&gt;

&lt;p&gt;}&lt;br&gt;
`&lt;code&gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;I can just use the function as an inline handler, but I want to create a new handler for the button.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&lt;/code&gt;`ts&lt;br&gt;
//src/pages/invoices/[invoiceId]/index.tsx&lt;/p&gt;

&lt;p&gt;// still inside the InvoiceDetail component&lt;br&gt;
 const sendInvoiceEmail = () =&amp;gt; {&lt;br&gt;
    const hostUrl = window.location.origin;&lt;/p&gt;

&lt;p&gt;// prevent a user from spamming when the API call is not done.&lt;br&gt;
    if (sendEmailMutation.isLoading) return;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// send input data to `sendEmail` trpc endpoint
sendEmailMutation.mutate({
  customerName: invoiceDetail.customer.name,
  invoiceNumber: `#${invoiceDetail.invoiceNumber}`,
  invoiceViewUrl: `${hostUrl}/invoices/${invoiceDetail.id}/preview`,
  emailTo: invoiceDetail.customer.email,
  invoiceId: invoiceDetail.id,
  dueDate: invoiceDetail.dueDate,
  productName: invoiceDetail.name,
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;};&lt;br&gt;
`&lt;code&gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Now I can attach the handler to the send email button.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&lt;/code&gt;`ts&lt;br&gt;
//src/pages/invoices/[invoiceId]/index.tsx&lt;/p&gt;

&lt;p&gt;
   variant="primary"&lt;br&gt;
   onClick={sendInvoiceEmail}&lt;br&gt;
   isLoading={sendEmailMutation.isLoading}&amp;gt;&lt;br&gt;
   Send to Email&lt;br&gt;
&lt;br&gt;
`&lt;code&gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Here's the working UI.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.imgur.com%2Fush8wdI.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.imgur.com%2Fush8wdI.gif" alt="working ui" width="600" height="343"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Part 3: Send Payment Reminder
&lt;/h3&gt;

&lt;p&gt;To schedule a reminder that will be sent a day before an invoice's due date, I'm going to use &lt;a href="https://www.courier.com/docs/automations/" rel="noopener noreferrer"&gt;Courier's Automation API&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;First, let's design the email template in Courier designer. As I already go through the process before, here is the final result.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.imgur.com%2F4l6shqK.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.imgur.com%2F4l6shqK.png" alt="payment reminder template" width="800" height="810"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Before adding the function, define the types for the parameter and refactor the types.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&lt;/code&gt;`ts&lt;br&gt;
// src/lib/courier&lt;/p&gt;

&lt;p&gt;interface CourierBaseData {&lt;br&gt;
  customerName: string;&lt;br&gt;
  invoiceNumber: string;&lt;br&gt;
  invoiceViewUrl: string;&lt;br&gt;
  emailTo: string;&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;interface SendInvoice extends CourierBaseData {&lt;br&gt;
  productName: string;&lt;br&gt;
  dueDate: string;&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;interface ScheduleReminder extends CourierBaseData {&lt;br&gt;
  scheduledDate: Date;&lt;br&gt;
  invoiceId: string;&lt;br&gt;
}&lt;br&gt;
`&lt;code&gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Now, I add the &lt;code&gt;scheduleReminder&lt;/code&gt; function to &lt;code&gt;src/lib/courier&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&lt;/code&gt;`ts&lt;br&gt;
//src/pages/invoices/[invoiceId]/index.tsx&lt;/p&gt;

&lt;p&gt;// check if the development environment is production&lt;br&gt;
const &lt;strong&gt;IS_PROD&lt;/strong&gt; = process.env.NODE_ENV === 'production';&lt;/p&gt;

&lt;p&gt;const PAYMENT_REMINDER_TEMPLATE_ID = '';&lt;/p&gt;

&lt;p&gt;export const scheduleReminder = async ({&lt;br&gt;
  scheduledDate,&lt;br&gt;
  emailTo,&lt;br&gt;
  invoiceViewUrl,&lt;br&gt;
  invoiceId,&lt;br&gt;
  customerName,&lt;br&gt;
  invoiceNumber,&lt;br&gt;
}: ScheduleReminder) =&amp;gt; {&lt;/p&gt;

&lt;p&gt;// delay until a day before due date in production, else 20 seconds after sent for development&lt;br&gt;
  const delayUntilDate = &lt;strong&gt;IS_PROD&lt;/strong&gt;&lt;br&gt;
    ? scheduledDate&lt;br&gt;
    : new Date(Date.now() + SECOND_TO_MS * 20);&lt;/p&gt;

&lt;p&gt;const recipientEmail = &lt;strong&gt;IS_PROD&lt;/strong&gt; ? emailTo : testEmail;&lt;/p&gt;

&lt;p&gt;// define the  automation steps programmatically&lt;br&gt;
   const { runId } = await courierClient.automations.invokeAdHocAutomation({&lt;br&gt;
     automation: {&lt;br&gt;
       steps: [&lt;br&gt;
         // 1. Set delay for the next steps until given date in ISO string&lt;br&gt;
          { action: 'delay', until: delayUntilDate.toISOString() },&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;      // 2. Send the email notification. Equivalent to `courierClient.send()`
      {
        action: 'send',
        message: {
          to: { email: recipientEmail },
          template: PAYMENT_REMINDER_TEMPLATE_ID,
          data: {
            invoiceViewUrl,
            customerName,
            invoiceNumber,
          },
        },
      },
    ],
  },
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;return runId;&lt;br&gt;
};&lt;br&gt;
`&lt;code&gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;To send the reminder, I will call &lt;code&gt;scheduleReminder&lt;/code&gt; after a successful &lt;code&gt;sendInvoice&lt;/code&gt; attempt. Let's modify the &lt;code&gt;sendEmail&lt;/code&gt; trpc endpoint.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&lt;/code&gt;`ts&lt;br&gt;
// src/server/trpc/router/invoice.ts&lt;/p&gt;

&lt;p&gt;sendEmail: protectedProcedure&lt;br&gt;
    .input(..) // omitted for brevity&lt;br&gt;
    .mutation(async ({ input }) =&amp;gt; {&lt;br&gt;
      // multiplier for converting day to milliseconds.&lt;br&gt;
      const DAY_TO_MS = 1000 * 60 * 60 * 24;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  // get a day before the due date
  const scheduledDate = new Date(input.dueDate.getTime() - DAY_TO_MS * 1);

  const invoiceData = {..}; //omitted for brevity

  await sendInvoice(invoiceData);

  //after the invoice is sent, schedule the reminder
  await scheduleReminder({
    ...invoiceData,
        scheduledDate,
  });
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;`&lt;code&gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Now if I try to send an invoice by email, I should get a reminder 20 seconds later since I'm in the development environment.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.imgur.com%2FMfwQ6F0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.imgur.com%2FMfwQ6F0.png" alt="with payment reminder" width="800" height="79"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Part 4: Cancel a reminder
&lt;/h3&gt;

&lt;p&gt;Finally, all the features are ready. However, I got a problem, what if a client had paid before the scheduled date for payment reminder? Currently, the reminder email will still be sent. That's not a great user experience and potentially a confused client. Thankfully, Courier has an automation cancellation feature.&lt;/p&gt;

&lt;p&gt;Let's add &lt;code&gt;cancelAutomationWorkflow&lt;/code&gt; function that can cancel any automation workflow in &lt;code&gt;src/lib/courier.ts&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&lt;/code&gt;`ts&lt;br&gt;
export const cancelAutomationWorkflow = async ({&lt;br&gt;
  cancelation_token,&lt;br&gt;
}: {&lt;br&gt;
  cancelation_token: string;&lt;br&gt;
}) =&amp;gt; {&lt;br&gt;
    const { runId } = await courierClient.automations.invokeAdHocAutomation({&lt;br&gt;
      automation: {&lt;br&gt;
        // define a cancel action, that sends a cancelation_token&lt;br&gt;
        steps: [{ action: 'cancel', cancelation_token }],&lt;br&gt;
      },&lt;br&gt;
    });&lt;/p&gt;

&lt;p&gt;return runId;&lt;br&gt;
};&lt;br&gt;
`&lt;code&gt;&lt;/code&gt; &lt;/p&gt;

&lt;p&gt;What is a cancelation_token? It's a unique token that can be set to an automation workflow, so it's cancelable by sending a &lt;code&gt;cancel&lt;/code&gt; action with a matching &lt;code&gt;cancelation_token&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;Add cancelation_token to &lt;code&gt;scheduleReminder&lt;/code&gt;, I use the invoice's Id as a token.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&lt;/code&gt;`ts&lt;br&gt;
// src/lib/courier.ts&lt;/p&gt;

&lt;p&gt;export const scheduleReminder = async (..) =&amp;gt; {&lt;br&gt;
  // ...omitted for brevity&lt;/p&gt;

&lt;p&gt;const { runId } = await courierClient.automations.invokeAdHocAutomation({&lt;br&gt;
    automation: {&lt;br&gt;
      // add cancelation token here&lt;br&gt;
      cancelation_token: &lt;code&gt;${invoiceId}-reminder&lt;/code&gt;,&lt;br&gt;
      steps: [&lt;br&gt;
        { action: 'delay', until: delayUntilDate.toISOString() },&lt;/p&gt;

&lt;p&gt;// ... omitted for brevity&lt;br&gt;
`&lt;code&gt;&lt;/code&gt; &lt;/p&gt;

&lt;p&gt;I will call &lt;code&gt;cancelAutomationWorkflow&lt;/code&gt; when an invoice's status is updated to &lt;code&gt;PAID&lt;/code&gt; in the &lt;code&gt;updateStatus&lt;/code&gt; trpc endpoint. &lt;/p&gt;

&lt;p&gt;&lt;code&gt;&lt;/code&gt;`ts&lt;br&gt;
// src/server/trpc/router/invoice.ts&lt;/p&gt;

&lt;p&gt;updateStatus: protectedProcedure&lt;br&gt;
    .input(..) // omitted for brevity&lt;br&gt;
    .mutation(async ({ ctx, input }) =&amp;gt; {&lt;br&gt;
      const { invoiceId, status } = input;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  // update an invoice's status in database
  const updatedInvoice = await ctx.prisma.invoice.update({
    where: { id: invoiceId },
    data: { status },
  });

  // cancel payment reminder automation workflow if the status is paid.
  if (updatedInvoice.status === 'PAID') {

    //call the cancel workflow to cancel the payment reminder for matching cancelation_token.
    await cancelAutomationWorkflow({
      cancelation_token: `${invoiceId}-reminder`,
    });
  }

  return updatedStatus;
}),
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;`&lt;code&gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Here is the working UI.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.imgur.com%2FjnLk4Tg.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.imgur.com%2FjnLk4Tg.gif" alt="cancel log" width="600" height="344"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Part 5: Error Handling
&lt;/h3&gt;

&lt;p&gt;An important note when doing network requests is there are possibilities of failed requests/errors. I want to handle the error by throwing it to the client, so it can be reflected in UI.&lt;/p&gt;

&lt;p&gt;On error, Courier API throws an error with &lt;code&gt;CourierHttpClientError&lt;/code&gt; type by default. I will also have all functions' return value in &lt;code&gt;src/lib/courier.ts&lt;/code&gt; consistent with the below format.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&lt;/code&gt;`ts&lt;br&gt;
// On Success&lt;br&gt;
type SuccessResponse = { data: any, error: null }&lt;/p&gt;

&lt;p&gt;// On Error&lt;br&gt;
type ErrorResponse = { data: any, error: string }&lt;br&gt;
`&lt;code&gt;&lt;/code&gt; &lt;/p&gt;

&lt;p&gt;Now, I can handle errors by adding a &lt;code&gt;try-catch&lt;/code&gt; block to all functions in &lt;code&gt;src/lib/courier.ts&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&lt;/code&gt;`ts&lt;br&gt;
try {&lt;br&gt;
  // ..function code&lt;/p&gt;

&lt;p&gt;// modified return example&lt;br&gt;
  return { data: runId, error: null };&lt;/p&gt;

&lt;p&gt;} catch (error) {&lt;br&gt;
  // make sure it's an error from Courier&lt;br&gt;
  if (error instanceof CourierHttpClientError) {&lt;br&gt;
      return { data: error.data, error: error.message };&lt;br&gt;
    } else {&lt;br&gt;
      return { data: null, error: "Something went wrong!" };&lt;br&gt;
    }&lt;br&gt;
}&lt;br&gt;
`&lt;code&gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Let's see a handling example on the &lt;code&gt;sendEmail&lt;/code&gt; trpc endpoint.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&lt;/code&gt;`ts&lt;br&gt;
// src/server/trpc/router/invoice.ts&lt;/p&gt;

&lt;p&gt;const { error: sendError } = await sendInvoice(..);&lt;br&gt;
  if (sendError) throw new TRPCClientError(sendError);&lt;/p&gt;

&lt;p&gt;const { error: scheduleError } = await scheduleReminder(..);&lt;br&gt;
  if (scheduleError) throw new TRPCClientError(scheduleError);&lt;br&gt;
`&lt;code&gt;&lt;/code&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Part 6: Go To Production
&lt;/h3&gt;

&lt;p&gt;Now that all templates are ready, I will copy all assets in the test environment to production. Here is an example.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.imgur.com%2FMr3bmuH.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.imgur.com%2FMr3bmuH.gif" alt="copy assets to production" width="600" height="343"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Finally, all the features are integrated with Courier. We've gone through a workflow of integrating Courier API to a Next.js application. Although it's in Next.js and trpc, the workflow will be pretty much the same with any other technology. I hope now you can integrate Courier into your application by yourself.&lt;/p&gt;

&lt;p&gt;Get started now: &lt;a href="https://app.courier.com/signup" rel="noopener noreferrer"&gt;https://app.courier.com/signup&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  About the Author
&lt;/h2&gt;

&lt;p&gt;I'm Fazza Razaq Amiarso, a full-stack web developer from Indonesia. I'm also an Open Source enthusiast. I love to share my knowledge and learning &lt;a href="https://fazzaamiarso.me/" rel="noopener noreferrer"&gt;on my blog&lt;/a&gt;. I occasionally help other developers on &lt;a href="https://www.frontendmentor.io/profile/fazzaamiarso" rel="noopener noreferrer"&gt;FrontendMentor&lt;/a&gt; in my free time.&lt;/p&gt;

&lt;p&gt;Connect with me on &lt;a href="https://www.linkedin.com/in/fazzaamiarso/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Quick Links
&lt;/h2&gt;

&lt;p&gt;🔗 &lt;a href="https://www.courier.com/docs/" rel="noopener noreferrer"&gt;Courier Docs&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;🔗 &lt;a href="https://github.com/fazzaamiarso/invoys" rel="noopener noreferrer"&gt;Contribute to Invoys&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;🔗 &lt;a href="https://fazzaamiarso.me/projects/invoys" rel="noopener noreferrer"&gt;Invoys Motivation&lt;/a&gt;&lt;/p&gt;

</description>
      <category>devto</category>
      <category>announcement</category>
      <category>webmonetization</category>
    </item>
    <item>
      <title>Develop a Motivational QOTD with Courier and GPT2</title>
      <dc:creator>Charla Bigelow</dc:creator>
      <pubDate>Mon, 13 Feb 2023 18:12:38 +0000</pubDate>
      <link>https://dev.to/courier/develop-a-motivational-qotd-with-courier-and-gpt2-3id3</link>
      <guid>https://dev.to/courier/develop-a-motivational-qotd-with-courier-and-gpt2-3id3</guid>
      <description>&lt;p&gt;Motivational quotes were quite the rage back in the day when MMS &amp;amp; email forwarding were popular. I remember my parents forwarding me at the start of every morning. Fast forward to today, if you are lucky, you are part of some forward group on your messaging app of choice (Whatsapp, Telegram, etc.).&lt;/p&gt;

&lt;p&gt;Inspired by the same idea, today we are going to build a service that sends our friends and family an AI generated motivational quote-of-the-day. Rather than hardcoding a list of motivational quotes, we are going to use a machine learning model to generate a quote on demand, so that we never run out of quotes to share!&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/z7iqk1q8njt4/1R04JX4FjNAccZFLrmr9SF/467f50f89c928e7665e81ee25fb69c19/qotd_pic_1.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/z7iqk1q8njt4/1R04JX4FjNAccZFLrmr9SF/467f50f89c928e7665e81ee25fb69c19/qotd_pic_1.png" alt="QOTD Final Project"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Instructions
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Part 1: Using AI to generate motivational quotes
&lt;/h3&gt;

&lt;h4&gt;
  
  
  OpenGPT2 and Language Models
&lt;/h4&gt;

&lt;p&gt;OpenAI GPT-2 model was proposed in Language Models are Unsupervised Multitask Learners by Alec Radford, Jeffrey Wu, Rewon Child, David Luan, Dario Amodei and Ilya Sutskever. It’s a causal transformer pre-trained using language modeling on a very large corpus of ~40 GB of text data.&lt;/p&gt;

&lt;p&gt;To simplify this, at a high level OpenAI GPT2 is a large language model that has been trained on massive amounts of data. This model can be used to predict the next token in a given sequence.&lt;/p&gt;

&lt;p&gt;If that sounds too complicated, don't worry, you don't need to know any Machine Learning or AI to follow along with this project. Libraries such as &lt;a href="https://huggingface.co"&gt;Hugging Face&lt;/a&gt; make using this model in our app very easy.&lt;/p&gt;

&lt;h4&gt;
  
  
  Hugging Face
&lt;/h4&gt;

&lt;p&gt;We'll use the &lt;a href="https://huggingface.co/"&gt;Hugging Face&lt;/a&gt; library to load and serve the ML model that will generate the quotes for us. Hugging Face makes it very easy to use transformer models (of which GPT2 is a type) in our projects without any knowledge of ML or AI. As mentioned earlier, GPT2 is a general purpose language model which means that it is good at predicting generic text given an input sequence. In our case, we need a model more suited for generating quotes. To do that, we have two options:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;We can fine-tune the GPT2 model by using our own text for which we'll need a good dataset of quotes.&lt;/li&gt;
&lt;li&gt;Or we can find an &lt;em&gt;already&lt;/em&gt; existing model which has been fine-tuned with some quotes.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Luckily, in our case there’s a fine-tuned model that has been trained on the 500k quotes dataset - &lt;a href="https://huggingface.co/nandinib1999/quote-generator"&gt;https://huggingface.co/nandinib1999/quote-generator&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With Hugging Face, using this model is as easy as as creating a tokenizer&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;transformers&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;AutoTokenizer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;AutoModelWithLMHead&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pipeline&lt;/span&gt;

&lt;span class="n"&gt;tokenizer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;AutoTokenizer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;from_pretrained&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"nandinib1999/quote-generator"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;then, constructing a model from the pretrained model&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;AutoModelWithLMHead&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;from_pretrained&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"nandinib1999/quote-generator"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and finally, constructing the generator which we can use to generate the quote&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;generator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pipeline&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"text-generation"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tokenizer&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;tokenizer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# use a starting prompt
&lt;/span&gt;&lt;span class="n"&gt;generator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Keep an open mind and"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="s"&gt;'generated_text'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;'Keep an open mind and a deep love for others'&lt;/span&gt;&lt;span class="p"&gt;}]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Building an API to serve the model
&lt;/h4&gt;

&lt;p&gt;Now that we have a way to generate quotes for us, we have to think about how we can use this in our app. There are multiple ways to go about building this.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Load the model everytime we want to run the script to send the script.&lt;/li&gt;
&lt;li&gt;Create an API or service that serves this GPT2 model to generate quotes for us on demand. &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;A key plus point of the second option is that once the model is loaded the API can respond to us quickly and can be used in other applications as well. FWIW, the first option is a totally valid approach as well.&lt;/p&gt;

&lt;p&gt;We can use &lt;a href="https://fastapi.tiangolo.com/"&gt;Fast API&lt;/a&gt; to build a quick serving API. Here's what that looks like&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;
&lt;span class="c1"&gt;# in file api.py
&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;pydantic&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;BaseModel&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;fastapi&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;FastAPI&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;HTTPException&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;transformers&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;AutoTokenizer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;AutoModelWithLMHead&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pipeline&lt;/span&gt;

&lt;span class="c1"&gt;## create the pipeline
&lt;/span&gt;&lt;span class="n"&gt;tokenizer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;AutoTokenizer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;from_pretrained&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"nandinib1999/quote-generator"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;AutoModelWithLMHead&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;from_pretrained&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"nandinib1999/quote-generator"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;generator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pipeline&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"text-generation"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tokenizer&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;tokenizer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;FastAPI&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;QuoteRequest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BaseModel&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;QuoteResponse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BaseModel&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;

&lt;span class="c1"&gt;### Serves the Model API to generate quote
&lt;/span&gt;&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/generate"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;response_model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;QuoteResponse&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;generate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;QuoteRequest&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;resp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;generator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="s"&gt;"generated_text"&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="n"&gt;HTTPException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;status_code&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;detail&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'Error in generation'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;QuoteResponse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="s"&gt;"generated_text"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's test it out&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;uvicorn api:app

INFO:     Started server process &lt;span class="o"&gt;[&lt;/span&gt;40767]
INFO:     Waiting &lt;span class="k"&gt;for &lt;/span&gt;application startup.
INFO:     Application startup complete.
INFO:     Uvicorn running on http://127.0.0.1:8000 &lt;span class="o"&gt;(&lt;/span&gt;Press CTRL+C to quit&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we can start sending requests to the &lt;code&gt;/generate&lt;/code&gt; endpoint that will generate a quote for us.&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/z7iqk1q8njt4/1TXDqA7VRfbwZRNDH76Kms/f6cce20ca8d4f5aaa9ecd313976488a3/qotd2.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/z7iqk1q8njt4/1TXDqA7VRfbwZRNDH76Kms/f6cce20ca8d4f5aaa9ecd313976488a3/qotd2.png" alt="QOTD Part 2"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Part 2: Building the Quote Generator
&lt;/h3&gt;

&lt;p&gt;Now that we have a way to generate quotes on demand, we can stop here and start working on sending this via &lt;a href="http://courier.com/"&gt;Courier&lt;/a&gt;. But who are we kidding, no one reads text anymore! We can make this interesting by using a nice image and placing our quote on it to make it look like a poster.&lt;/p&gt;

&lt;h4&gt;
  
  
  Generate quote
&lt;/h4&gt;

&lt;p&gt;Given our API, we can now do the following to generate a quote&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;random&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;choice&lt;/span&gt;
&lt;span class="c1"&gt;# feel free to add more starting prompts for more variety
&lt;/span&gt;&lt;span class="n"&gt;canned_seeds&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"Always remember to"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Start today with"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"It is okay to"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;seed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;choice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;canned_seeds&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;resp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'http://127.0.0.1:8000/generate'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="s"&gt;"text"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;seed&lt;/span&gt;&lt;span class="p"&gt;}))&lt;/span&gt;
&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()[&lt;/span&gt;&lt;span class="s"&gt;"text"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Downloading the background image
&lt;/h4&gt;

&lt;p&gt;The first challenge is getting a beautiful background image for our quote. For that, we'll use the Unsplash API that provides a nice endpoint to return a random image matching a query. Opening &lt;a href="https://source.unsplash.com/random/800%C3%97800/?nature"&gt;https://source.unsplash.com/random/800×800/?nature&lt;/a&gt; in our browser returns a nice nature image. &lt;/p&gt;

&lt;p&gt;To keep things interesting, we can use different query terms such as stars, etc. Here's the how the code for downloading our background image looks like -&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;random&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;choice&lt;/span&gt;
&lt;span class="n"&gt;image_backgdrops&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;'nature'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;'stars'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;'mountains'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;'landscape'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;backdrop&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;choice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;image_backdrops&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"https://source.unsplash.com/random/800×800/?"&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;backdrop&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;stream&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;# write the output the img.png on our filesystem
&lt;/span&gt;&lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nb"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'img.png'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;'wb'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;out_file&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;shutil&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;copyfileobj&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;raw&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;out_file&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;del&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Creating the image with the quote
&lt;/h4&gt;

&lt;p&gt;Ok, now we have our background image and a quote which means we can work on assembling the final image that will be sent to the recipients. At a high level we want to place some text on an image but even this simple task can be challenging. For starters, there are a number of questions for us to answer&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;How will the text be placed on the image?&lt;/li&gt;
&lt;li&gt;What about wrapping the text?&lt;/li&gt;
&lt;li&gt;What color should the text be so that it is visible on the background image?&lt;/li&gt;
&lt;li&gt;How do we do this for images with varying widths and heights?&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The answers to some of these questions are more complicated than others. To keep it simple, we'll put the text in the center, and do some wrapping so that it looks good. Finally, we'll use a light color text for now. For all image manipulation, we'll use Python Image Library (PIL) to make this easy for us.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# use the image we downloaded in the above step
&lt;/span&gt;&lt;span class="n"&gt;img&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Image&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"img.png"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;width&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;height&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;img&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;
&lt;span class="n"&gt;image_editable&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ImageDraw&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Draw&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;img&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# wrap text
&lt;/span&gt;&lt;span class="n"&gt;lines&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;textwrap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;wrap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;width&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# get the line count and generate a starting offset on y-axis
&lt;/span&gt;&lt;span class="n"&gt;line_count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lines&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;y_offset&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;height&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;line_count&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;title_font&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getbbox&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lines&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;])[&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

&lt;span class="c1"&gt;# for each line of text, we generate a (x,y) to calculate the positioning
&lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;lines&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;line_w&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;line_h&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;title_font&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getbbox&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;width&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;line_w&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;
    &lt;span class="n"&gt;image_editable&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;y_offset&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;237&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;230&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;211&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;font&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;title_font&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;y_offset&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;line_h&lt;/span&gt;
&lt;span class="n"&gt;img&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"result.jpg"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"generated "&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;filename&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This generates the final image called &lt;code&gt;result.jpg&lt;/code&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Uploading the image
&lt;/h4&gt;

&lt;p&gt;For the penultimate step, we need to upload the image so that we can use that with Courier. In this case, I'm using Firebase Storage but you can feel free to use whatever you like.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;firebase_admin&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;firebase_admin&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;credentials&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;firebase_admin&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;storage&lt;/span&gt;

&lt;span class="n"&gt;cred&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;credentials&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Certificate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'serviceaccount.json'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;firebase_admin&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;initialize_app&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cred&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{...})&lt;/span&gt;

&lt;span class="n"&gt;bucket&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;storage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bucket&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;blob&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;bucket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;blob&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;blob&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;upload_from_filename&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;blob&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;make_public&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;blob&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;public_url&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 3: Integrating with Courier
&lt;/h2&gt;

&lt;p&gt;Finally, we have everything we need to start sending our awesome quotes to our friends and family. We can use Courier to create a good looking email template.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://app.courier.com/signup"&gt;Start by creating an account.&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Creating the template in Courier
&lt;/h4&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/z7iqk1q8njt4/NvMlJ4UVAaWZHzIOooG0P/879d8e96f866f09d7a5d0b582a79562a/qotd3.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/z7iqk1q8njt4/NvMlJ4UVAaWZHzIOooG0P/879d8e96f866f09d7a5d0b582a79562a/qotd3.png" alt="QOTD Courier Email Template Preview"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Sending the message
&lt;/h4&gt;

&lt;p&gt;Sending a message with Courier is as easy as it gets. While Courier has its own SDKs that can make integration easy, I prefer using their API endpoint to keep things simple. With my &lt;code&gt;AUTH_TOKEN&lt;/code&gt; and &lt;code&gt;TEMPLATE_ID&lt;/code&gt; in hand, we can use the following piece of code to send our image&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;requests&lt;/span&gt;

&lt;span class="n"&gt;headers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="s"&gt;"Accept"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"application/json"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;"Content-Type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"application/json"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;"Authorization"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"Bearer {}"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;'COURIER_AUTH_TOKEN'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="s"&gt;"to"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s"&gt;"email"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"COURIER_RECIPIENT"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="s"&gt;"data"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="s"&gt;"date"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;today&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="n"&gt;strftime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"%B %d, %Y"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="s"&gt;"img"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;image_url&lt;/span&gt; &lt;span class="c1"&gt;## this is image url we generated earlier
&lt;/span&gt;    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="s"&gt;"routing"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="s"&gt;"method"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"single"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s"&gt;"channels"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="s"&gt;"email"&lt;/span&gt;
        &lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="s"&gt;"template"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"COURIER_TEMPLATE"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"https://api.courier.com/send"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;"message"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The API key can be found in &lt;a href="https://app.courier.com/settings"&gt;Settings&lt;/a&gt; and the Template ID can be found in the &lt;a href="https://app.courier.com/designer"&gt;template design's&lt;/a&gt; settings. And that's it!&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusions
&lt;/h2&gt;

&lt;p&gt;This tutorial demonstrated how easy it is to get started with machine learning &amp;amp; Courier.&lt;/p&gt;

&lt;p&gt;If you want to go ahead and improve this project, here are some interesting ideas to try&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Better background image: Use a term from the generated quote to search for an image?&lt;/li&gt;
&lt;li&gt;Better background color for the text: Use better colors for the text. One cool idea is to use the complimentary color from the image's main color. You can use k-means clustering to find that out.&lt;/li&gt;
&lt;li&gt;Adding more channels : Extends this to messages on messaging clients and sms!&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  About the Author
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://prakhar.me"&gt;Prakhar&lt;/a&gt; is a senior software engineer at Google where he works on building developer tools. He's a passionate open-source developer and loves playing the guitar in his free time.&lt;/p&gt;

&lt;h2&gt;
  
  
  Quick Links
&lt;/h2&gt;

&lt;p&gt;🔗 &lt;a href="https://www.courier.com/docs/"&gt;Courier Docs&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;🔗 &lt;a href="//huggingface.co"&gt;Hugging Face&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;🔗 &lt;a href="https://fastapi.tiangolo.com/"&gt;Fast API&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;🔗 &lt;a href="https://unsplash.com/developers"&gt;Unsplash API&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>I Owned a Major Product Feature as a PM Intern</title>
      <dc:creator>Charla Bigelow</dc:creator>
      <pubDate>Fri, 09 Sep 2022 19:32:35 +0000</pubDate>
      <link>https://dev.to/courier/i-owned-a-major-product-feature-as-a-pm-intern-4j04</link>
      <guid>https://dev.to/courier/i-owned-a-major-product-feature-as-a-pm-intern-4j04</guid>
      <description>&lt;p&gt;&lt;em&gt;Preference management is an integral part of a great notification infrastructure, which makes it a very important piece of the puzzle here at Courier. This also means that there was a lot of learning and experience-building opportunity for the intern on the project, Denis Tatar. Denis’ internship was only a few months long, but he was able to make an enormous impact as the product manager for Courier’s Preferences feature.&lt;/em&gt; &lt;/p&gt;

&lt;p&gt;&lt;em&gt;We wanted to learn more about Denis’ experience, so we asked if he’d be willing to work on a writeup for us. Then what he came up with was so good, we thought it would be a good idea to share with all of you! This post will cover Denis’ workflow from planning to execution around the Preferences project as well as his experience working with the Courier team specifically. We’ll let Denis take it from here!&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What was the problem?
&lt;/h2&gt;

&lt;p&gt;The problem we experienced at Courier was that we’d offer Preferences (V3) to our customers, but the entire product feature’s strategy wasn’t fully fleshed out. I believe that V3 was a great stepping stone for V4, but fundamentally had flaws in certain parts of its strategy. Allowing only global opt-in/out is an example of this. Another problem was that V3 didn’t gain much traction, which I believe points back to the lack of a fully thought out strategy. In May, Preferences (V3) would only allow users to opt-in and out of their preferences of choice globally. V3 of Preferences only allowed our customers (and their customers) to opt-in/out of notifications, and customers globally could also decide if certain notifications were required or not. V3 helped give our customers the ability to start playing around with the idea of having preferences. However, globally opt/in out is a problem because it constrains end users’ notifications options. It was either all or nothing, no in-between. Similar in theory to the Pokemon saying, “Catch one, catch them all.” A problem with this, though, is inbox flooding. Global opt-in would allow for the concept of the end users’ inboxes becoming flooded with every product notification which is problematic because it can easily drive users away from your product. &lt;/p&gt;

&lt;p&gt;Global opt-in/out was quickly resolved by allowing users to select what content they wanted to be notified of and the channels they’d prefer. This overall trend in the market is used by thousands of companies (B2B and B2C products), especially in the technology space.&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/z7iqk1q8njt4/4wL7Y4vOcZxXWQHf40diKM/95442b858fa2c6def0f2d46add560a54/image_for_internship_blog_post.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/z7iqk1q8njt4/4wL7Y4vOcZxXWQHf40diKM/95442b858fa2c6def0f2d46add560a54/image_for_internship_blog_post.png" alt="Product Preferences Screenshot"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Visibility became a serious issue with V3. End users never had the option to select what type of content and channel they wanted. Users would be asked if they were interested in receiving notifications in general. Not offering options is problematic as it constrains visibility on what you’ll receive through notifications. End users also don’t like surprises when it comes to product notifications. Having clarity is key because it enables end users to have control over their notifications.&lt;/p&gt;

&lt;p&gt;With V4, we solved the visibility problem, which was the main “fail” from V3. Our customers and their end users now had the ability to control the content they received and the channels they would prefer.&lt;/p&gt;

&lt;p&gt;V4 was a good way to strategically notify end users. &lt;/p&gt;

&lt;h2&gt;
  
  
  Plan
&lt;/h2&gt;

&lt;p&gt;There were three phases we followed as a team when building Preferences. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Research&lt;/strong&gt;&lt;br&gt;
In phase one, I spent a decent amount of time looking at what is currently available in the market in terms of preference centers. I would constantly ask myself the following two questions:&lt;/p&gt;

&lt;p&gt;“How does [insert company name] manage notifications? What does their preference center look like?”&lt;/p&gt;

&lt;p&gt;These questions were key. They helped me dive into necessary research which helped me pick up on many important market trends. Out of all the trends found though, there was one that stood out the most. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Trend&lt;/strong&gt;: Channel &amp;amp; content selection through a toggle/grid style layout for preference centers is a must have.&lt;/p&gt;

&lt;p&gt;This trend became the backbone to our summer project. It was important to offer users preferences options. &lt;/p&gt;

&lt;p&gt;During this phase, I read many case studies. I was surprised to see how many existed on this topic. Each one helped enlighten me with solutions for the problem we were tackling. I learned through other people’s mistakes, absorbing the valuable lessons. &lt;/p&gt;

&lt;p&gt;Talking to customers was extremely helpful in this phase. I was able to hop on numerous calls with our customers and hear their perspectives on this problem. I would ask questions and show examples of what we had in mind for a solution. This helped tailor a POC. The better we understand our customers’ perspective, the easier it is to start designing something that they would use. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Designing a POC&lt;/strong&gt;&lt;br&gt;
I really enjoyed this phase. Watching research become an actual POC was incredible, and so fun. We took all the feedback I had gathered, then created several iterations of designs.&lt;/p&gt;

&lt;p&gt;Once we created a POC, it was essential to go back to our customers and ensure that it was something they envisioned for Preferences. Our customers were also lovely people to work with, always there to provide their much-needed feedback.&lt;/p&gt;

&lt;p&gt;Often, I would have a decent amount of brainstorming sessions with Ian. We’d look at our research and what our customers had to say, and combine these sources into a POC. It was an insightful process to be a part of.&lt;/p&gt;

&lt;p&gt;This phase lasted several weeks until we finally landed on a POC that satisfied all of the customers we had spoken to since May 2022.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Building an MVP&lt;/strong&gt;&lt;br&gt;
Once we had a POC and fully completed designs, we began building our product feature. This phase taught me two vital lessons…&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Being organized is extremely important.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;and...&lt;/p&gt;

&lt;p&gt;&lt;em&gt;As a product manager, you need to stay one week ahead of your team.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;While working at Courier and delegating tasks, I noticed that the more organized you were, the easier it was to work with others. My organizational skills would impact how clearly our designers and engineers understood our goals and individually delegated tasks. The more organized we were, the faster and more efficient we worked. Outlining the tasks our team needed to complete, and vocalizing why these tasks were necessary helped a lot. It brought clarity to everyone, including myself. &lt;/p&gt;

&lt;p&gt;The second lesson here was given to me by our CEO,Troy. He taught me how important it was to think about the current and following week. Troy brought this up because, as a product manager, you want to keep your team informed on what projects and tasks are in the pipeline.. A product manager that isn’t in this mindset can quickly become a hindrance to their team. Taking on this mindset brought clarity and made me less stressed about our project. I would spend time planning each week,and estimating where the team should be at the end of each week (cycle). &lt;/p&gt;

&lt;p&gt;These two lessons helped when we were building an MVP. We progressed through each week as a team, with each week’s tasks outlined, along with the weekly goals we’d needed to achieve. Working with the team at Courier was amazing, everyone was  understanding and absolutely brilliant. What I loved the most was that everyone had an opinion. Engineers and designers would ask if specific tasks were priorities before building them, which allowed us to build the MVP and get it up and running quickly. These questions helped guide priorities and decision-making. &lt;/p&gt;

&lt;h2&gt;
  
  
  Execution: What I did exactly
&lt;/h2&gt;

&lt;p&gt;Throughout this internship, every day was different. My week would involve speaking to customers, delegating tasks to our engineers and designers, and taking time to think ahead, two to three weeks ahead of the current week. I also worked closely with both engineers and designers, ensuring great synergy between both teams. &lt;/p&gt;

&lt;p&gt;There were four core responsibilities that I embodied throughout my product manager internship at Courier:&lt;/p&gt;

&lt;p&gt;Being the internal expert on what the market (customers &amp;amp; prospects) want within my realm of focus.&lt;/p&gt;

&lt;p&gt;Being the internal expert on what my product feature already provides (and thus the delta to the above core responsibility).&lt;br&gt;
The ability to prioritize the path that gets Courier from our current state to our desired state.&lt;/p&gt;

&lt;p&gt;The ability to educate the rest of the company on the current product, the product being built, and our ultimate product vision so that everyone can be on the same page without being experts. &lt;br&gt;
As the &lt;strong&gt;glue&lt;/strong&gt; that ensures the team is working together on the right thing for the right reasons, their success is ultimately limited by my effectiveness in one and two. I cannot accurately educate teammates on what I don’t profoundly understand myself. &lt;/p&gt;

&lt;p&gt;Troy was a fantastic mentor throughout my entire time at Courier. He brought these four core responsibilities to my attention, which became an integral part of my internship. What resonated with me the most was being the glue between all teams. At first, I looked at this concept and was intimidated by it. But, I had a blast easing myself into becoming this glue-like figure between teams. It definitely wasn’t something easy to pull off, but I enjoyed the challenge. I managed to make a lot of great friends along the way too!&lt;/p&gt;

&lt;h2&gt;
  
  
  What trade offs did I experience?
&lt;/h2&gt;

&lt;p&gt;There were many trade-offs throughout my entire internship. The main ones I experienced were related to the following concept “make it work -&amp;gt; make it good -&amp;gt; make it fast,” which I often heard throughout my internship. There were many times when small features that could make our UI/UX stand out would actually slow down the team. &lt;/p&gt;

&lt;p&gt;Trade-offs like these were necessary. They had me questioning whether we focus on the nitty gritty details, or do we put the feature aside and focus on making a product that functions  and gets the job done? The latter part of that question was often chosen as our strategy. It’s tough to make these calls because you know the team can develop an outstanding product build with phenomenal UI/UX components, but our build must accomplish what it’s meant to do in the first place. &lt;/p&gt;

&lt;p&gt;The main goal was to release our MVP, ensure we have customer feedback, improve any core features, and then focus on minor priorities that affect the UI/UX of this product. &lt;/p&gt;

&lt;h2&gt;
  
  
  What was it like working with the team?
&lt;/h2&gt;

&lt;p&gt;I had a blast working with the Lifecycle team on Preferences. Everyone was so kind, and offered fantastic feedback. I’ve mentioned this numerous times around the office, and on calls with coworkers. Working at Courier didn’t feel like work; it felt like I was working on a side project with close friends. I genuinely think this is why I was able to produce the work I did at Courier, because of the environment. Courier is easy-going, extremely friendly, and results-driven.&lt;/p&gt;

&lt;p&gt;Another thing I really loved about the team was that they always thought of me. If there were tasks that engineers or designers would do that was considered a ‘norm’ for them, they’d ask me if I’d like to give it a shot to see what it’s like. I have three examples of this. &lt;/p&gt;

&lt;p&gt;When designing a specific tool tip for a part of the Preferences project, Ian (Senior Designer) during one of our calls, asked if I wanted a shot at designing a tool tip. This involved working with Frames in Figma, a concept I never worked with. I really enjoyed creating that tool tip. It made me feel included.&lt;/p&gt;

&lt;p&gt;Every week the team would have our team Lifecycle planning meetings. We’d speak about tickets, and write them up. It was early on in my internship when Suhas (Senior Engineer) asked me if I wanted to try writing up several tickets on Linear. I was aware that this was something most PMs would normally do, but I had a lot of respect for Suhas for bringing this up, and was willing to teach me how to properly create tickets in Linear. &lt;br&gt;
Once the team had something solidified for Preferences, I began to ask around internally to learn if there were any companies that would be interested in hearing about my project. Nathan (Account Executive), reached out to me to let me know about a customer interested in this product feature. Nathan encouraged me a TON when it came to communicating with this customer. I was able to step outside of my comfort zone because of Nathan’s encouragement. I’ve never gotten to speak to a customer before at any of my previous internship experiences, and I learned a lot about customers and the way they perceive Courier. &lt;/p&gt;

&lt;p&gt;Note that these are just three examples from the many opportunities like this throughout my internship. I do want to give a big shoutout to Ian, Suhas, and Nathan. I appreciate you guys giving me opportunities to learn and grow, it means the world to me. &lt;/p&gt;

&lt;h2&gt;
  
  
  How did I work with designers?
&lt;/h2&gt;

&lt;p&gt;I enjoyed working with Ian (Senior Designer). We’d have impromptu meetings through Slack’s ‘Huddle’ feature, (which was internally renamed to &lt;strong&gt;‘Huggle’&lt;/strong&gt; because of a typo I once made that stuck). These meetings were really great for brainstorming and product design. Sometimes, Ian and I would have calls later at night because it was easier to come up with more creative ideas.. We’d have one to two hour sessions, and we’d come up with some really awesome UI/UX for Preferences. This is something I’ll never forget. &lt;/p&gt;

&lt;p&gt;When working with Ian, I would try to make it as easy as possible. I would create a to-do list through Google docs or Slack, so both Ian and I would be aware of what needed to be completed every week. &lt;/p&gt;

&lt;p&gt;During our calls, Ian and I would also talk about a lot of different topics not always directly related to work, which was great. This really helped build our friendship, making it so much easier to collaborate and come up with a clean UI/UX for Preferences. I often do a decent amount of product design outside of work hours, and Ian would always give me phenomenal advice on product design and on Figma!&lt;/p&gt;

&lt;h2&gt;
  
  
  How did I work with engineers?
&lt;/h2&gt;

&lt;p&gt;I briefly touched on this, but when working with engineers, I would help create tickets, and organize what each week would look like. I helped our engineers whenever they were confused about anything related to Preferences, and would explain how I came up with specific names, why Ian and I would design certain parts of our product, and the goals behind these designs. Many of these answers came from my extensive research process with customers that were interested in Preferences. &lt;/p&gt;

&lt;p&gt;While working with engineers, I learned a lot on how to properly break down tickets (eipcs) within Linear. I thought I had a firm grasp before working at Courier, but I learned a lot more after working with the Lifecycle team. The key to breaking down tickets is to be able to think about every single detail alongside engineers. Being in a small group while doing this was super important, because each individual helped bring up a very important perspective on how to break down tickets. &lt;/p&gt;

&lt;p&gt;During my internship, Seth (Chief Technology Officer), Suhas (Senior Software Engineer), Christian (Software Engineer) encouraged me to speak to the Developer Experience team at Courier and show them our project. The main goal behind this was to test to see if the UI/UX of Preferences was intuitive, if the design made sense, and if the Developer Experience team understood the goal behind this product feature. I prepared a slide deck explaining our project in its entirety. This was a great experience because it helped me prepare on how to &lt;strong&gt;sell Preferences to our customers&lt;/strong&gt;. Giving insightful presentations about products and &lt;strong&gt;“selling” your product to customers is such an important and overlooked skill for product managers&lt;/strong&gt;. Speaking to folks internally helped me practice and refine this skill, as well.. &lt;/p&gt;

&lt;h2&gt;
  
  
  How did I solve the problem?
&lt;/h2&gt;

&lt;p&gt;Before V4 Preferences, customers weren’t given the option of choosing what content they’d like to hear about, and how they’d like to hear about it. With V4 Preferences, users now can voice their opinions on both of these matters. This new product feature makes it possible for customers to choose what they like, and want to hear about. Specifically through their channels preference.&lt;/p&gt;

&lt;p&gt;Gone are the days of inbox cluttering and customers becoming aggravated with the amount of notifications they receive. Preferences minimizes the frustration that end users experience with their notifications. &lt;/p&gt;

&lt;p&gt;&lt;em&gt;Want to apply for a role at Courier? Check out our &lt;a href="https://www.courier.com/careers/"&gt;careers page&lt;/a&gt;. To check out what Denis helped build in action, request a demo &lt;a href="https://www.courier.com/request-demo/"&gt;here&lt;/a&gt; and ask to see the Preferences feature.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>design</category>
      <category>productivity</category>
      <category>internship</category>
    </item>
    <item>
      <title>Build a WebAssembly Language for Fun and Profit: Code Generation</title>
      <dc:creator>Charla Bigelow</dc:creator>
      <pubDate>Fri, 02 Sep 2022 15:54:06 +0000</pubDate>
      <link>https://dev.to/courier/build-a-webassembly-language-for-fun-and-profit-code-generation-1mop</link>
      <guid>https://dev.to/courier/build-a-webassembly-language-for-fun-and-profit-code-generation-1mop</guid>
      <description>&lt;p&gt;&lt;em&gt;by Andrew Youngwerth&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The final phase of our compiler is code generation. This phase takes the AST and converts it&lt;br&gt;
to a set of executable instructions. In our case, WebAssembly. To accomplish this, we are going&lt;br&gt;
to use a popular WebAssembly compiler toolchain called &lt;a href="https://github.com/WebAssembly/binaryen"&gt;binaryen&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Binaryen does most of the heavy compiling tasks for us. Tricky optimizations like dead-code removal, code folding, and more are all handled out of the box. Thanks to binaryen, we can make a powerful language with very little code.&lt;/p&gt;

&lt;p&gt;This is the third article in the Build a programming language series. If you’re just starting out, start with the first post &lt;a href="https://www.courier.com/blog/build-a-webassembly-language-lexing/"&gt;here&lt;/a&gt; before continuing on.&lt;/p&gt;
&lt;h3&gt;
  
  
  The Compile function
&lt;/h3&gt;

&lt;p&gt;One of the trickiest tasks in code generation is what to call the code generation function. For this article, I have opted to call the main function &lt;code&gt;compile&lt;/code&gt;. The compile function will take our root AST (a &lt;code&gt;BlockNode&lt;/code&gt;) and return a new &lt;code&gt;binaryen.Module&lt;/code&gt;. The &lt;code&gt;binaryen.Module&lt;/code&gt; exposes functions that can optimize and emit wasm in various formats.&lt;/p&gt;

&lt;p&gt;Here's the outline:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// src/compiler.mts&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;binaryen&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;binaryen&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;AstNode&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;BlockNode&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;IdentifierNode&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./types/index.mjs&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;compile&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;block&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;BlockNode&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;binaryen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Module&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Creates a new binaryen module that our helper functions will fill in&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;mod&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;binaryen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Module&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="c1"&gt;// The function map is used to track all the functions and their types. More on this later&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;functionMap&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;generateFunctionMap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;block&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// This function registers all the standard library functions we'll include with our language.&lt;/span&gt;
  &lt;span class="c1"&gt;// This includes functions like add, subtract, etc.&lt;/span&gt;
  &lt;span class="nx"&gt;registerStandardFunctions&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;mod&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;functionMap&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// This is where the magic happens. Because `BlockNode` is an expression, this&lt;/span&gt;
  &lt;span class="c1"&gt;// function can recursively compile every instruction in a wispy program file&lt;/span&gt;
  &lt;span class="nx"&gt;compileExpression&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;expression&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;block&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;mod&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;functionMap&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;parameters&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="c1"&gt;// Finally, we return the binaryen module&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;mod&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Generating A Function Map
&lt;/h2&gt;

&lt;p&gt;Next we define the &lt;code&gt;generateFunctionMap&lt;/code&gt; function. This function crawls the entire expression&lt;br&gt;
tree to find and register function definitions. Its important we do this before actually&lt;br&gt;
compiling the functions as some functions may call other functions before they've been defined.&lt;/p&gt;

&lt;p&gt;The return type of &lt;code&gt;generateFunctionMap&lt;/code&gt; is a map where the key is the function name and the&lt;br&gt;
value is an object containing all the important information about the function the compiler&lt;br&gt;
needs to know about. For now, all we need is &lt;code&gt;returnType&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Here’s the return type definition:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// src/compiler.mts&lt;/span&gt;
&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;FunctionMap&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Map&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;returnType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now that we have defined our return type, we can define the actual function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// src/compiler.mts&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;generateFunctionMap&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;block&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;BlockNode&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;FunctionMap&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Preview the first node (i.e. expression) of the block&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;firstNode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;block&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;expressions&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

  &lt;span class="c1"&gt;// If the first node is an identifier and the identifier is "fn", then we know this block represents a function definition.&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;isNodeType&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;firstNode&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;identifier&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;firstNode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;identifier&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;fn&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Grab the function identifier / name, and it's return type. This is the second expression of&lt;/span&gt;
    &lt;span class="c1"&gt;// the function definition, a typed identifier.&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;identifier&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;returnType&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;getFunctionIdentifier&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;block&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// We'll define this next&lt;/span&gt;

    &lt;span class="c1"&gt;// Return the function map&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
      &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;identifier&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;returnType&lt;/span&gt; &lt;span class="p"&gt;}],&lt;/span&gt;

      &lt;span class="c1"&gt;// It's possible this function may contain function definitions inside of it. So we&lt;/span&gt;
      &lt;span class="c1"&gt;// We put all the remaining expressions of the function into a new block and scan it&lt;/span&gt;
      &lt;span class="c1"&gt;// then we merge the resulting map with this one.&lt;/span&gt;
      &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;generateFunctionMap&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;block&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;expressions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;block&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;expressions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}),&lt;/span&gt;
    &lt;span class="p"&gt;]);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// A block can contain multiple expressions. So, we must scan each one to see if it is a function&lt;/span&gt;
  &lt;span class="c1"&gt;// definition. The root `BlockNode` for instance, will almost always have multiple functions.&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;block&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;expressions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;reduce&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;expression&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Only block expressions can be functions&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;expression&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;block&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;([...&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;generateFunctionMap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;expression&lt;/span&gt;&lt;span class="p"&gt;)]);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// We can ignore all other expression&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Onto &lt;code&gt;getFunctionIdentifier&lt;/code&gt; we called earlier. This function is simple. It takes a &lt;code&gt;BlockNode&lt;/code&gt;, ensures that the second identifier is a &lt;code&gt;TypedIdentifierNode&lt;/code&gt;, and then returns the identifier&lt;br&gt;
and return type:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// src/compiler.mts&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;getFunctionIdentifier&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;block&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;BlockNode&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Grab the second expression&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;node&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;block&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;expressions&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

  &lt;span class="c1"&gt;// Ensure the expression is a typed identifier&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;isNodeType&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;typed-identifier&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Expected typed function name&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;identifier&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;identifier&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

    &lt;span class="c1"&gt;// We have to map the return type to a type binaryen understands.&lt;/span&gt;
    &lt;span class="na"&gt;returnType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;mapBinaryenType&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;typeIdentifier&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As noted in the &lt;code&gt;getFunctionIdentifier&lt;/code&gt; function, Binaryen doesn't understand what type&lt;br&gt;
the string &lt;code&gt;typeIdentifier&lt;/code&gt; is. To handle this we have to map our defined types to binaryen types. For now, we'll just support &lt;code&gt;i32&lt;/code&gt; and &lt;code&gt;f32&lt;/code&gt;. Thankfully binaryen uses the same nomenclature we do. So the map function is pretty simple:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// src/compiler.mts&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;mapBinaryenType&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;typeIdentifier&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;binaryen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Type&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;typeIdentifier&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;i32&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;binaryen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;i32&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;typeIdentifier&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;f32&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;binaryen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;f32&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Unsupported type &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;typeIdentifier&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;getFunctionIdentifier&lt;/code&gt; Made a call to a new function, &lt;code&gt;isNodeType&lt;/code&gt;. This function is&lt;br&gt;
essentially the same concept as the &lt;code&gt;isTokenType&lt;/code&gt; function we &lt;a href="https://www.courier.com/blog/build-a-webassembly-language-parsing/"&gt;defined in the parsing article&lt;/a&gt;, only for &lt;code&gt;ASTNode&lt;/code&gt; instead of &lt;code&gt;Token&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Here's the definition:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// src/compiler.mts&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;isNodeType&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nx"&gt;AstNode&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;type&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;unknown&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="kd"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;T&lt;/span&gt;
&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;item&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="nx"&gt;Extract&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;AstNode&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;T&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="c1"&gt;// Ensure the type exists&lt;/span&gt;
    &lt;span class="o"&gt;!!&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
    &lt;span class="c1"&gt;// Ensure the type is an object&lt;/span&gt;
    &lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;item&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;object&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
    &lt;span class="c1"&gt;// Cast the type as a record, so TypeScript doesn't get mad at us and then compare the&lt;/span&gt;
    &lt;span class="c1"&gt;// type field with the type parameter. If they are equal, we know the node is the&lt;/span&gt;
    &lt;span class="c1"&gt;// the type we were looking for.&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nb"&gt;Record&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;unknown&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;type&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With the mapper finished we can start generating some code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Compiling Expressions
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;compileExpression&lt;/code&gt; function is where we really start to make use of &lt;code&gt;binaryen&lt;/code&gt; to model&lt;br&gt;
the generated machine code. Because of the tree structure of an, &lt;em&gt;ahem&lt;/em&gt;, abstract syntax tree,&lt;br&gt;
&lt;code&gt;compileExpression&lt;/code&gt; is highly recursive. This is one of my favorite things about programming&lt;br&gt;
languages, their patterns tend to lend themselves to elegant recursive functions with high&lt;br&gt;
levels of code re-use.&lt;/p&gt;

&lt;p&gt;Let's start with defining the parameters of &lt;code&gt;compileExpression&lt;/code&gt;. We will need to pass the&lt;br&gt;
&lt;code&gt;binaryen.Module&lt;/code&gt; and the &lt;code&gt;functionMap&lt;/code&gt; we created earlier, the actual expression we are&lt;br&gt;
compiling, and any parameters of the function this expression may be a part of (if it's inside)&lt;br&gt;
of a function. When there are more than two parameters of a function it can be difficult&lt;br&gt;
to visually keep track of what is what. So I like to make it clear by grouping them together&lt;br&gt;
in an object. This enforces labeling the parameters on call and as a result, improves code&lt;br&gt;
readability.&lt;/p&gt;

&lt;p&gt;Here's the interface of that object:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// src/compiler.mts&lt;/span&gt;
&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;CompileExpressionOpts&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;expression&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;AstNode&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;mod&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;binaryen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Module&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;functionMap&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;FunctionMap&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// We defined this earlier&lt;/span&gt;
  &lt;span class="nl"&gt;parameters&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ParameterMap&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Defined below.&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// A map where the key is the parameter identifier and the value is the important information&lt;/span&gt;
&lt;span class="c1"&gt;// required by binaryen to fetch the parameter down the line&lt;/span&gt;
&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;ParameterMap&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Map&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;index&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now that we have the options for &lt;code&gt;compileExpression&lt;/code&gt; defined, we can define the actual function. &lt;code&gt;compileExpression&lt;/code&gt; takes &lt;code&gt;CompileExpressionOpts&lt;/code&gt; as a parameter and returns a &lt;code&gt;number&lt;/code&gt;. The job of this function is to take an expression and determine what type of expression it is, from there it can pass the expression to another compiler function that can handle that specific type of expression.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Why return a number? When we build an expression with &lt;code&gt;binaryen&lt;/code&gt; it returns a number as an identifier for that expression. This allows us to compile an expression ahead of time and then reference that expression later down the line.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Here's the definition:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// src/compiler.mts&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;compileExpression&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;CompileExpressionOpts&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Grab the expression and the binaryen module (mod) from the options.&lt;/span&gt;
  &lt;span class="c1"&gt;// The other fields are used by child function calls&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;expression&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;mod&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c1"&gt;// Map the expression node to it's corresponding specific compiler&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;isNodeType&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;expression&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;block&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;compileBlock&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;expression&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="c1"&gt;// Numbers are simple enough to compiler that we can just inline the compiler here.&lt;/span&gt;
  &lt;span class="c1"&gt;// They are represented as constants&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;isNodeType&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;expression&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;int&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;mod&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;i32&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;expression&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;isNodeType&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;expression&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;float&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;mod&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;f32&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;expression&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;isNodeType&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;expression&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;identifier&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;compileIdentifier&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;expression&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="c1"&gt;// Throw a helpful error message if we don't recognize the expression&lt;/span&gt;
  &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Unrecognized expression &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;expression&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;type&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's define the &lt;code&gt;compileBlock&lt;/code&gt; functions. Because this function is also compiling an expression,&lt;br&gt;
we can re-use the previously defined &lt;code&gt;CompileExpressionOpts&lt;/code&gt;, but we'll narrow the expression&lt;br&gt;
field to the &lt;code&gt;BlockNode&lt;/code&gt; type, since we know we are compiling a block by the time this function is called:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;CompileBlockOpts&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nx"&gt;CompileExpressionOpts&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;expression&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;BlockNode&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;compileBlock&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;CompileBlockOpts&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// We re-map the expression field to block here for clarity.&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;expression&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;block&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;mod&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c1"&gt;// When a block has multiple expressions and the first one is an identifier, that means&lt;/span&gt;
  &lt;span class="c1"&gt;// the block is actually a function call.&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;isNodeType&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;block&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;expressions&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;identifier&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;block&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;expressions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// If it is a function call, transfer responsibility to the `compileFunctionCall` function (defined next)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;compileFunctionCall&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// This is where the recursive beauty starts to show. Since every value of a block&lt;/span&gt;
  &lt;span class="c1"&gt;// is an expression, we can map each one back to the compileExpression function.&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;expressions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;block&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;expressions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;expression&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;compileExpression&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;expression&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="c1"&gt;// Now we generate the machine code by calling the block function of binaryen&lt;/span&gt;
  &lt;span class="c1"&gt;// This function takes a block name, an array of compiled expressions, and a block return type.&lt;/span&gt;
  &lt;span class="c1"&gt;// Named blocks are mostly useful for looping constructs like `for` and `while`. In this&lt;/span&gt;
  &lt;span class="c1"&gt;// case we can pass null as we're not compiling a loop construct. Additionally, we can&lt;/span&gt;
  &lt;span class="c1"&gt;// pass `auto` as the type since binaryen is smart enough to determine the return type&lt;/span&gt;
  &lt;span class="c1"&gt;// of blocks automatically.&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;mod&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;block&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;expressions&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;binaryen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;auto&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Note: If you're curious to see how looping works in binaryen / WebAssembly works, check out my blog &lt;a href="https://drew.ltd/blog/posts/2020-4-28.html"&gt;post on the subject here&lt;/a&gt;. Spoiler alert, its pretty weird.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The last simple expression we'll compile in this section is the identifier expression. If&lt;br&gt;
&lt;code&gt;compileExpression&lt;/code&gt; was passed a lone &lt;code&gt;IdentifierNode&lt;/code&gt; it means that the expression evaluates&lt;br&gt;
to the value of the identifier. In wispy, we don't have variables and function identifiers are&lt;br&gt;
caught before the could've been passed here. That means the only thing &lt;code&gt;IdentifierNode&lt;/code&gt; can&lt;br&gt;
resolve to is a parameter.&lt;/p&gt;

&lt;p&gt;Here's the definition:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;CompileIdentifierOpts&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nx"&gt;CompileExpressionOpts&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;expression&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;IdentifierNode&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;compileIdentifier&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;CompileIdentifierOpts&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// We remap expression to node to keep our lines a little shorter&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;expression&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;parameters&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;mod&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c1"&gt;// Since we know the identifier has to be a parameter, we look it up in our&lt;/span&gt;
  &lt;span class="c1"&gt;// parameter map. Don't worry, we'll define the parameter map in the next section&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;info&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;parameters&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;identifier&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;info&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Unrecognized identifier &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;identifier&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// Finally, we use the local.get instruction to return the parameter value.&lt;/span&gt;
  &lt;span class="c1"&gt;// Binaryen needs to know the parameters index and type. We'll get into&lt;/span&gt;
  &lt;span class="c1"&gt;// the index when we define our parameter mapping function.&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;mod&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;info&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;info&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;type&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The final expression type left to compile is a function call. This is interesting enough&lt;br&gt;
to warrant its own section.&lt;/p&gt;
&lt;h2&gt;
  
  
  Compiling Function Calls
&lt;/h2&gt;

&lt;p&gt;In wispy function calls are blocks with multiple expressions where the first expression is an&lt;br&gt;
identifier. The job of &lt;code&gt;compileFunction&lt;/code&gt; is to determine which function is being called, what&lt;br&gt;
its parameters and return type are, and finally, building the call instruction with binaryen.&lt;/p&gt;

&lt;p&gt;Here's the definition:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// src/compiler.mts&lt;/span&gt;

&lt;span class="c1"&gt;// Because function calls are blocks, we can re-use CompileBlockOpts&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;compileFunctionCall&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;CompileBlockOpts&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;expression&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;functionMap&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;mod&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c1"&gt;// The first expression of a function call is the functions identifier&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;identifierNode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;expression&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;expressions&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

  &lt;span class="c1"&gt;// Here we just ensure the identifierNode is *actually* an identifier. Otherwise we throw an error.&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;isNodeType&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;identifierNode&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;identifier&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Expected identifier when compiling function call&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// Next we create a reference to what the actual identifier is&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;identifier&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;identifierNode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;identifier&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c1"&gt;// If the identifier is "fn", the function we are calling is the function to define functions!&lt;/span&gt;
  &lt;span class="c1"&gt;// That's right! Functions are created by another function. Pretty neat if you ask me.&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;identifier&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;fn&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;compileFunction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// We'll define this next&lt;/span&gt;

  &lt;span class="c1"&gt;// Ifs are special functions. They may or may not have an else block. Binaryen needs to know&lt;/span&gt;
  &lt;span class="c1"&gt;// if the else block exists at compile time, so we have a special if compiler for this.&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;identifier&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;if&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;compileIf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// We'll define this later&lt;/span&gt;

  &lt;span class="c1"&gt;// Every other function is either part of the standard library, or is defined&lt;/span&gt;
  &lt;span class="c1"&gt;// within the wispy code itself.&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;functionInfo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;functionMap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;identifier&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;functionInfo&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Function &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;identifier&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; not found`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;expression&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;expressions&lt;/span&gt;
    &lt;span class="c1"&gt;// Every other expression in the block are parameters to the function, so we compile them&lt;/span&gt;
    &lt;span class="c1"&gt;// and then pass them to the call&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;expression&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;compileExpression&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;expression&lt;/span&gt; &lt;span class="p"&gt;}));&lt;/span&gt;

  &lt;span class="c1"&gt;// Now we use binaryen to construct the call expression. The first parameter&lt;/span&gt;
  &lt;span class="c1"&gt;// is the functions identifier, the second are the compiled parameter expression,&lt;/span&gt;
  &lt;span class="c1"&gt;// and the third is the return type which has already been determined by generateFunctionMap&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;mod&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;identifier&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;functionInfo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;returnType&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's define the &lt;code&gt;compileIf&lt;/code&gt; function before we move onto the &lt;code&gt;compileFunction&lt;/code&gt;... function.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// src/compiler.mts&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;compileIf&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;CompileBlockOpts&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;expression&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;mod&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c1"&gt;// The first expression, expression.expressions[0], is the "if" identifier, we don't need&lt;/span&gt;
  &lt;span class="c1"&gt;// to do anything with it since we already know we are compiling an if expression&lt;/span&gt;

  &lt;span class="c1"&gt;// The second expression is the if condition&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;conditionNode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;expression&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;expressions&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

  &lt;span class="c1"&gt;// The third expression is the ifTrueNode, it's what is executed if the conditionNode evaluates to&lt;/span&gt;
  &lt;span class="c1"&gt;// true&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ifTrueNode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;expression&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;expressions&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

  &lt;span class="c1"&gt;// Finally the fourth expression (which may or may not exist) is what is executed if the condition&lt;/span&gt;
  &lt;span class="c1"&gt;// evaluates to false&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ifFalseNode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;expression&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;expressions&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

  &lt;span class="c1"&gt;// Compile the condition expression&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;condition&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;compileExpression&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;expression&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;conditionNode&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="c1"&gt;// Compile the ifTrue Expression&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ifTrue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;compileExpression&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;expression&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ifTrueNode&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="c1"&gt;// Check to see if the ifFalseNode exists, if it does, compile it, otherwise set ifFalse to undefined&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ifFalse&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;ifFalseNode&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;compileExpression&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;expression&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ifFalseNode&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c1"&gt;// Finally we use binaryen to compile the if expression&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;mod&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;condition&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ifTrue&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ifFalse&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Compiling Function Definitions
&lt;/h2&gt;

&lt;p&gt;Function definitions are a whole lot like function calls, so the function structure is pretty similar.&lt;br&gt;
We take &lt;code&gt;CompileBlockOpts&lt;/code&gt; and return a number (the binaryen expression reference).&lt;/p&gt;

&lt;p&gt;Here's the definition:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// src/compiler.mts&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;compileFunction&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;CompileBlockOpts&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;expression&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;block&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;mod&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c1"&gt;// We need to tell binaryen what the identifier and return type of the function is&lt;/span&gt;
  &lt;span class="c1"&gt;// Thankfully, we already wrote a function for that, getFunctionIdentifier. We&lt;/span&gt;
  &lt;span class="c1"&gt;// could also have just looked up this information with the functionMap, but&lt;/span&gt;
  &lt;span class="c1"&gt;// this is more fun.&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;identifier&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;returnType&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;getFunctionIdentifier&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;block&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// Next we grab the function parameters. This is the third expression of the function&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;parameters&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;parameterTypes&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;getFunctionParameters&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;block&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Defined later&lt;/span&gt;

  &lt;span class="c1"&gt;// The rest of the expressions in the function are the functions block. So we create&lt;/span&gt;
  &lt;span class="c1"&gt;// a new BlockNode from the remaining expression.&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;compileBlock&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;expression&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;block&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;expressions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;block&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;expressions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;

    &lt;span class="c1"&gt;// We need to pass the parameters of this function, so they can be referenced in child&lt;/span&gt;
    &lt;span class="c1"&gt;// expressions&lt;/span&gt;
    &lt;span class="nx"&gt;parameters&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="c1"&gt;// Now we register the function with binaryen. Binaryen takes the function identifier,&lt;/span&gt;
  &lt;span class="c1"&gt;// an array of parameter types (each item being the type of parameter in order),&lt;/span&gt;
  &lt;span class="c1"&gt;// the function's return type, a list of variable types (wispy doesn't have any, so we pass an empty array)&lt;/span&gt;
  &lt;span class="c1"&gt;// and finally the compiled body of the function.&lt;/span&gt;
  &lt;span class="nx"&gt;mod&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;addFunction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;identifier&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;parameterTypes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;returnType&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt; &lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// To make things easy we export every single function defined in a wispy file&lt;/span&gt;
  &lt;span class="c1"&gt;// so it can be called by the WebAssembly host.&lt;/span&gt;
  &lt;span class="nx"&gt;mod&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;addFunctionExport&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;identifier&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;identifier&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// Because function definitions are *technically* expressions that can be a part of another function&lt;/span&gt;
  &lt;span class="c1"&gt;// body, we need to return an expression pointer. For this, we just return a nop (do nothing instruction),&lt;/span&gt;
  &lt;span class="c1"&gt;// to make things consistent.&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;mod&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;nop&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, let's define the &lt;code&gt;getFunctionParameters&lt;/code&gt; function. This function takes the function &lt;code&gt;BlockNode&lt;/code&gt;, that is, the entire unmodified function definition, and extracts its parameters. The function returns two values, parameters and parameterTypes.&lt;/p&gt;

&lt;p&gt;The first returned value, &lt;code&gt;parameters&lt;/code&gt;, is a map where the key is the parameter identifier, and the value is the information needed to access the parameter down the line within the function body.&lt;/p&gt;

&lt;p&gt;The second returned value is an array of binaryen types. There is one type for each defined parameter, and they must remain in the order they are defined. This is because binaryen doesn't reference parameters by their names, instead it references them by the index of the array in which they are defined. Don't worry if this is confusing to you, the code should make things a little more clear. If you need, refer to the &lt;code&gt;compileIdentifier&lt;/code&gt; definition, to get a better&lt;br&gt;
understanding of how this works in practice.&lt;/p&gt;

&lt;p&gt;Here's the definition:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// src/compiler.mts&lt;/span&gt;

&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;ParameterMap&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Map&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;index&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;getFunctionParameters&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;block&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;BlockNode&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// The parameters are defined in the third expression of the function definition&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;node&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;block&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;expressions&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

  &lt;span class="c1"&gt;// Check to make sure the third expression is a block&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;isNodeType&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;block&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Expected function parameters&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// Now we reduce the parameters into a parameter map, and a list of binaryen types&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;parameters&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;types&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;expressions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;reduce&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;prev&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// First, ensure that the node is a typed-identifier. Every parameter must be a&lt;/span&gt;
      &lt;span class="c1"&gt;// typed identifier, therefore, every node in this reducer must be a typed identifier.&lt;/span&gt;
      &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;isNodeType&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;typed-identifier&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;All parameters must be typed&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;

      &lt;span class="c1"&gt;// Determine the correct binaryen type of the parameter&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;mapBinaryenType&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;typeIdentifier&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

      &lt;span class="c1"&gt;// Add the parameter's type to the list of types we've defined so far&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;types&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kd"&gt;type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;prev&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;types&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

      &lt;span class="c1"&gt;// Now add the parameter to the parameter map. We save the parameters index and type.&lt;/span&gt;
      &lt;span class="c1"&gt;// The index and type is used binaryen to access the parameter when it is used&lt;/span&gt;
      &lt;span class="c1"&gt;// later in the function body&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;parameters&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;([[&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;identifier&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="p"&gt;}],&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;prev&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;parameters&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

      &lt;span class="c1"&gt;// Return updated parameters map and types array&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;parameters&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;types&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="c1"&gt;// Here we are setting the starting values for our reducer function and casting the default&lt;/span&gt;
    &lt;span class="c1"&gt;// type so typescript can correctly infer the `prev` parameter type&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;parameters&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="na"&gt;types&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;parameters&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ParameterMap&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nl"&gt;types&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;[];&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// Finally we return the parameter map and the parameterTypes&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;parameters&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

    &lt;span class="c1"&gt;// Note: parameterTypes is a number, instead of an array of numbers as you'd expect.&lt;/span&gt;
    &lt;span class="c1"&gt;// So we have to use binaryen.createType to create a new type that is referenced&lt;/span&gt;
    &lt;span class="c1"&gt;// the mod.addFunction function. This is one inconsistency with the binaryen API. Parameters&lt;/span&gt;
    &lt;span class="c1"&gt;// are defined as a number, and variables are defined as an array of numbers. I'm sure there&lt;/span&gt;
    &lt;span class="c1"&gt;// is a reason for this, but I don't know what that reason is.&lt;/span&gt;
    &lt;span class="na"&gt;parameterTypes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;binaryen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;createType&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;types&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now all that's left is to define the standard library. This part of the code isn't super interesting.&lt;br&gt;
We are essentially just mapping primitive WebAssembly instructions to a name to be referenced&lt;br&gt;
within wispy.&lt;/p&gt;

&lt;p&gt;Here's the definitions. The only important information is the name we are associating with each&lt;br&gt;
instruction:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// src/compiler.mts&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;registerStandardFunctions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;mod&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;binaryen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Module&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;FunctionMap&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;i32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;f32&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;binaryen&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;i32&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;i32m&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;f32&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;f32m&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;mod&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;common&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;mod&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;map&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="nx"&gt;registerLogicFunction&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;lt_i32&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;i32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;operator&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;i32m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;lt_s&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;common&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="nx"&gt;registerLogicFunction&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;gt_i32&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;i32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;operator&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;i32m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;gt_s&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;common&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="nx"&gt;registerLogicFunction&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;eq_i32&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;i32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;operator&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;i32m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;eq&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;common&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="nx"&gt;registerLogicFunction&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;lt_f32&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;f32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;operator&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;f32m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;lt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;common&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="nx"&gt;registerLogicFunction&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;gt_f32&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;f32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;operator&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;f32m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;gt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;common&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="nx"&gt;registerLogicFunction&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;eq_f32&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;f32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;operator&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;f32m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;eq&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;common&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="nx"&gt;registerMathFunction&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;add_i32&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;i32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;operator&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;i32m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;add&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;common&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="nx"&gt;registerMathFunction&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;sub_i32&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;i32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;operator&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;i32m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sub&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;common&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="nx"&gt;registerMathFunction&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;mul_i32&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;i32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;operator&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;i32m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;mul&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;common&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="nx"&gt;registerMathFunction&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;add_f32&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;f32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;operator&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;f32m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;add&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;common&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="nx"&gt;registerMathFunction&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;sub_f32&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;f32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;operator&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;f32m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sub&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;common&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="nx"&gt;registerMathFunction&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;mul_f32&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;f32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;operator&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;f32m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;mul&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;common&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="nx"&gt;registerMathFunction&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;div_f32&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;f32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;operator&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;f32m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;common&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;registerMathFunction&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;mod&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;binaryen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Module&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;operator&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;left&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;right&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;map&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;FunctionMap&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;mod&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;operator&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;map&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;registerBinaryFunction&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="nx"&gt;mod&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;paramType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;returnType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;operator&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;registerLogicFunction&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;mod&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;binaryen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Module&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;operator&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;left&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;right&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;map&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;FunctionMap&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;mod&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;operator&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;map&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;registerBinaryFunction&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="nx"&gt;mod&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;paramType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;returnType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;binaryen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;i32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;operator&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;registerBinaryFunction&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;mod&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;binaryen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Module&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;paramType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;returnType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;operator&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;left&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;right&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;map&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;FunctionMap&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;mod&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;paramType&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;returnType&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;operator&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;map&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;mod&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;addFunction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;binaryen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;createType&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="nx"&gt;paramType&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;paramType&lt;/span&gt;&lt;span class="p"&gt;]),&lt;/span&gt;
    &lt;span class="nx"&gt;returnType&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;[],&lt;/span&gt;
    &lt;span class="nx"&gt;mod&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;block&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;operator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;mod&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;paramType&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nx"&gt;mod&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;paramType&lt;/span&gt;&lt;span class="p"&gt;))],&lt;/span&gt;
      &lt;span class="nx"&gt;binaryen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;auto&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;returnType&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By now src/compiler.mts should &lt;a href="https://github.com/drew-y/wispy/blob/58d5872f8d927358c1f8f70ebb4dda6d9458a8c8/src/compiler.mts"&gt;look something like this&lt;/a&gt;. With that, our compiler is finished. It's time to execute some wispy!&lt;/p&gt;

&lt;h2&gt;
  
  
  Putting It All Together
&lt;/h2&gt;

&lt;p&gt;Now that we have finished our compiler, we can finally run our code.&lt;/p&gt;

&lt;p&gt;First, replace the contents of &lt;code&gt;src/index.mts&lt;/code&gt; with this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// src/index.mts&lt;/span&gt;&lt;span class="cp"&gt;

#!/usr/bin/env node
&lt;/span&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;readFileSync&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;fs&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;compile&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./compiler.mjs&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;lex&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./lexer.mjs&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;parse&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./parser.mjs&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;file&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;input&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;readFileSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;utf8&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;tokens&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;lex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ast&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// !! New !!&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;mod&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;compile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ast&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// This is sneakily where the code gen is *actually* happening&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;binary&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;mod&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;emitBinary&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// Use the standard WebAssembly API to convert the wasm binary to a compiled module&lt;/span&gt;
&lt;span class="c1"&gt;// our host NodeJS/v8 can use&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;compiled&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;WebAssembly&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Module&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;binary&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Build the instance, here you would add any external functions you might want to import into&lt;/span&gt;
&lt;span class="c1"&gt;// the WebAssembly module&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;instance&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;WebAssembly&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Instance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;compiled&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{});&lt;/span&gt;

&lt;span class="c1"&gt;// Finally, run the main function and log the result. We have to cast instance.exports to any&lt;/span&gt;
&lt;span class="c1"&gt;// The standard TypeScript types appear to be wrong.&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;instance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now build and run project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx tsc
wispy example.wispy
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If all goes well (and you passed the number 15 to fib), you should see the number &lt;code&gt;610&lt;/code&gt; in the&lt;br&gt;
output of your console. If so, you've done it, you've made a working WebAssembly language. Congrats!&lt;/p&gt;

&lt;p&gt;A full copy of the wispy language implementation is available under an open-source license at &lt;a href="https://github.com/drew-y/wispy"&gt;https://github.com/drew-y/wispy&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>webassembly</category>
      <category>codegeneration</category>
      <category>lisp</category>
    </item>
    <item>
      <title>Build a WebAssembly Language for Fun and Profit: Parsing</title>
      <dc:creator>Charla Bigelow</dc:creator>
      <pubDate>Fri, 26 Aug 2022 15:56:00 +0000</pubDate>
      <link>https://dev.to/courier/build-a-webassembly-language-for-fun-and-profit-parsing-2foj</link>
      <guid>https://dev.to/courier/build-a-webassembly-language-for-fun-and-profit-parsing-2foj</guid>
      <description>&lt;p&gt;&lt;em&gt;By Andrew Youngwerth&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;In the &lt;a href="https://www.courier.com/blog/build-a-webassembly-language-lexing/"&gt;last post&lt;/a&gt; of this series on how to build a WebAssembly programming language, we constructed a lexer. In this post, we’ll cover the next phase of our compiler, parsing. Parsing is the portion of our compiler that takes the token stream generated by the lexer and converts it into an &lt;a href="https://en.wikipedia.org/wiki/Abstract_syntax_tree"&gt;abstract syntax tree&lt;/a&gt; (AST).&lt;/p&gt;

&lt;p&gt;An AST is a tree-like data structure that organizes the tokens into a logical hierarchy that can more easily be translated into machine code. Thankfully, because wispy is an S-expression language, our code is essentially &lt;em&gt;already&lt;/em&gt; an AST. Take the following stream of tokens:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;“(“, “add”, “3”, “(“, “sub”, “2”, “1”, “)”, “)”
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each set of parentheses represents a subtree, where the first token is the operator node and the following tokens are its operands. If we run into another opening parenthesis before the current set is closed, we know it represents an operand that itself is a subtree. The above stream of tokens would be organized into a tree that looks like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/z7iqk1q8njt4/1PWW9Be4KZd8fHeVnwpMGR/45932ae1bd61df370190c56294cefddc/image1.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/z7iqk1q8njt4/1PWW9Be4KZd8fHeVnwpMGR/45932ae1bd61df370190c56294cefddc/image1.png" alt="AST explanation"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you're interested in writing a parser for a more complex C-like syntax, see my previous &lt;a href="https://drew.ltd/blog/posts/2019-7-24.html"&gt;Building A Programming Language&lt;/a&gt; series.&lt;/p&gt;

&lt;h3&gt;
  
  
  More About AST
&lt;/h3&gt;

&lt;p&gt;As we did with the lexer, we'll start by defining our types. These types will define the structure of our AST. Each type represents a “Node”, the circle from our diagram in the intro. Here are the basic nodes. We'll gloss over them, as they aren't a lot different from the tokens we defined in the lexer:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// src/types/ast.mts&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;IntNode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;int&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;FloatNode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;float&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;IdentifierNode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;identifier&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;identifier&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;TypedIdentifierNode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;typed-identifier&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="c1"&gt;// Note that here, we break down the identifier into its components&lt;/span&gt;
  &lt;span class="nl"&gt;identifier&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;typeIdentifier&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A new concept to the AST is the &lt;code&gt;BlockNode&lt;/code&gt;. A &lt;code&gt;BlockNode&lt;/code&gt; is an expression made up of a group of other nodes.&lt;/p&gt;

&lt;p&gt;For example, &lt;code&gt;(add 1 2)&lt;/code&gt; is a block of three nodes:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;An identifier that evaluates to a function, &lt;code&gt;add&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;An Int that simply evaluates to the number &lt;code&gt;1&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;An Int that simply evaluates to the number &lt;code&gt;2&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;How the block itself gets evaluated is up to the compiler. We'll get to that in the next post.&lt;/p&gt;

&lt;p&gt;Here's the definition:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// src/types/ast.mts&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;BlockNode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;block&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;expressions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;AstNode&lt;/span&gt;&lt;span class="p"&gt;[];&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, we define the &lt;code&gt;AstNode&lt;/code&gt;. Like the &lt;code&gt;Token&lt;/code&gt; type from the lexer, &lt;code&gt;AstNode&lt;/code&gt; is a discriminated union that can be one of any other node we previously defined:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;AstNode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;IntNode&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nx"&gt;FloatNode&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nx"&gt;IdentifierNode&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nx"&gt;TypedIdentifierNode&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nx"&gt;BlockNode&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You may have noticed that &lt;code&gt;BlockNode&lt;/code&gt; has an array of &lt;code&gt;AstNode&lt;/code&gt;s, Since &lt;code&gt;AstNode&lt;/code&gt; &lt;em&gt;can be&lt;/em&gt; a &lt;code&gt;BlockNode&lt;/code&gt;, &lt;code&gt;BlockNode&lt;/code&gt;s can contain child &lt;code&gt;BlockNodes&lt;/code&gt;. In other words, &lt;code&gt;BlockNode&lt;/code&gt; is a recursive type. This ability to recursively represent children that can have children is the foundation of our AST. It's where the tree in AST is allowed to form.&lt;/p&gt;

&lt;p&gt;At this point &lt;code&gt;src/types/ast.mts&lt;/code&gt; is finished and should look like &lt;a href="https://github.com/drew-y/wispy/blob/0b03f9237aa4b1d9d07856ebc347927a444bb2d2/src/types/ast.mts"&gt;this file&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Now export the types from &lt;code&gt;src/types/index.mts&lt;/code&gt; as we did with the token types:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// src/types/index.mts&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./token.mjs&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./ast.mjs&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Constructing the AST
&lt;/h3&gt;

&lt;p&gt;Now that we've defined the AST, it's time to build one.&lt;/p&gt;

&lt;p&gt;Create a new &lt;code&gt;src/parser.mts&lt;/code&gt; file and add all the imports we'll use:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// src/parser.mts&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;Token&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;IdentifierNode&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;TypedIdentifierNode&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;IdentifierToken&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;TypedIdentifierToken&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;FloatToken&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;FloatNode&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;IntToken&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;IntNode&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;AstNode&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;BlockNode&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./types/index.mjs&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we can define our top level parse function. The parse function takes the tokens generated by the lexer and returns a &lt;code&gt;BlockNode&lt;/code&gt; that acts as our tree’s root.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// src/parser.mts&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;parse&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Token&lt;/span&gt;&lt;span class="p"&gt;[]):&lt;/span&gt; &lt;span class="nx"&gt;BlockNode&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="na"&gt;blocks&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;BlockNode&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;

  &lt;span class="c1"&gt;// This loop is run as long as there are tokens to consume&lt;/span&gt;
  &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// consumeTokenTree converts an array of tokens into a tree of tokens, more on that later.&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;tree&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;consumeTokenTree&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// parseBlock turns our new tree of tokens into an actual BlockNode, recursively. More on that later as well.&lt;/span&gt;
    &lt;span class="nx"&gt;blocks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;parseBlock&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tree&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// Finally we return the top level BlockNode&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;block&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;expressions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;blocks&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next we define the &lt;code&gt;consumeTokenTree&lt;/code&gt; function. &lt;code&gt;consumeTokenTree&lt;/code&gt; converts a flat array of tokens, into a tree of tokens.&lt;/p&gt;

&lt;p&gt;Given this wispy expression:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;(add (sub 3 1) (sub 5 2))
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The lexer will produce this array of tokens:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Note: I've simplified the Token format to just be strings to keep things short&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;add&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;sub&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;3&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;1&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;)&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;sub&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;5&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;2&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;)&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;)&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;consumeTokenTree&lt;/code&gt; will take that flat array and turn it into a tree. This is as simple as putting every token in between a set of bracket tokens &lt;code&gt;()&lt;/code&gt; into an array. So our token array from above becomes this token tree:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;add&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;sub&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;3&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;1&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;sub&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;5&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;2&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]];&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here's the actual definition of &lt;code&gt;consumeTokenTree&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// src/parser.mts&lt;/span&gt;

&lt;span class="c1"&gt;// This is token besides for the bracket tokens&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;NonBracketToken&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Exclude&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Token&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;parenthesis&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;square-bracket&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// The token tree is made of NonBracketTokens and other TokenTrees&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;TokenTree&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;NonBracketToken&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nx"&gt;TokenTree&lt;/span&gt;&lt;span class="p"&gt;)[];&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;consumeTokenTree&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Token&lt;/span&gt;&lt;span class="p"&gt;[]):&lt;/span&gt; &lt;span class="nx"&gt;TokenTree&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="na"&gt;tree&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;TokenTree&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;

  &lt;span class="c1"&gt;// Ensures the first token is a left bracket and then discards it, defined below this function.&lt;/span&gt;
  &lt;span class="nx"&gt;consumeLeftBracket&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Preview the next token&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

    &lt;span class="c1"&gt;// Check to see if the next token is a left bracket.&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;bracket&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;getBracketDirection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;left&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// If it is, we just ran into a sub-TokenTree. So we can simply call this function within&lt;/span&gt;
      &lt;span class="c1"&gt;// itself. Gotta love recursion.&lt;/span&gt;
      &lt;span class="nx"&gt;tree&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;consumeTokenTree&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
      &lt;span class="k"&gt;continue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// Check to see if the next token is a right bracket&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;bracket&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;getBracketDirection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;right&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// If it is, we just found the end of the tree on our current level&lt;/span&gt;
      &lt;span class="nx"&gt;tree&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;shift&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// Discard the right bracket&lt;/span&gt;
      &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Break the loop&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// If the token isn't a bracket, it can simply be added to the tree on this level&lt;/span&gt;
    &lt;span class="nx"&gt;tree&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Consume / discard the token from the main tokens array&lt;/span&gt;
    &lt;span class="nx"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;shift&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// Return the tree. Don't forget to check out the helper functions below!&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;tree&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;consumeLeftBracket&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Token&lt;/span&gt;&lt;span class="p"&gt;[])&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;bracketDirection&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;getBracketDirection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bracketDirection&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;left&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Expected left bracket&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;shift&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;getBracketDirection&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Token&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;left&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;right&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;bracket&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Expected bracket, got &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;type&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// If we match a left bracket return left&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;[\(\[]&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;left&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c1"&gt;// Otherwise return right&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;right&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now that we have a token tree, we need to turn it into a block. To do so, we create a&lt;code&gt;parseBlock&lt;/code&gt; function that takes the tree as its input and returns a &lt;code&gt;BlockNode&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;parseBlock&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;block&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;TokenTree&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;BlockNode&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;block&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="c1"&gt;// This is where the recursive magic happens&lt;/span&gt;
    &lt;span class="na"&gt;expressions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;block&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;parseExpression&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you may have noticed, &lt;code&gt;parseBlock&lt;/code&gt; maps each item of the tree with a yet to be written&lt;code&gt;parseExpression&lt;/code&gt; function. &lt;code&gt;parseExpression&lt;/code&gt; takes either a &lt;code&gt;TokenTree&lt;/code&gt; or a &lt;code&gt;NonBracketToken&lt;/code&gt; and transforms it to its corresponding &lt;code&gt;AstNode&lt;/code&gt; type. &lt;/p&gt;

&lt;p&gt;Here's the definition:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;parseExpression&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;expression&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;TokenTree&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nx"&gt;NonBracketToken&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;AstNode&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// If the expression is an Array, we were passed another TokenTree, so we can&lt;/span&gt;
  &lt;span class="c1"&gt;// pass the expression back to the parseBlock function&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;expression&lt;/span&gt; &lt;span class="k"&gt;instanceof&lt;/span&gt; &lt;span class="nb"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;parseBlock&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;expression&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// The mapping here is pretty straight forward. Match the token &lt;/span&gt;
 &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;and&lt;/span&gt; &lt;span class="nx"&gt;pass&lt;/span&gt; &lt;span class="nx"&gt;the&lt;/span&gt;
  &lt;span class="c1"&gt;// expression on to a more specific expression parser.&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;isTokenType&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;expression&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;identifier&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;parseIdentifier&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;expression&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;isTokenType&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;expression&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;typed-identifier&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;parseTypedIdentifier&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;expression&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;isTokenType&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;expression&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;float&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;parseFloatToken&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;expression&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;isTokenType&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;expression&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;int&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;parseIntToken&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;expression&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Unrecognized expression &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;expression&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's define the &lt;code&gt;isTokenType&lt;/code&gt; function. This function is pretty neat and demonstrates one of the most powerful features of TypeScript, &lt;a href="https://www.typescriptlang.org/docs/handbook/2/narrowing.html#using-type-predicates"&gt;custom type guards&lt;/a&gt;. Simply put, &lt;code&gt;isTokenType&lt;/code&gt; tests the expression and narrows down the type to a specific &lt;code&gt;TokenType&lt;/code&gt;. This allows TypeScript to be certain we are passing the correct tokens to their corresponding parser functions down the line.&lt;/p&gt;

&lt;p&gt;Here's the definition:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;isTokenType&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nx"&gt;Token&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;type&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;TokenTree&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nx"&gt;NonBracketToken&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="kd"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;T&lt;/span&gt;
&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;item&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="nx"&gt;Extract&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Token&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;T&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;isToken&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;isToken&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;TokenTree&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nx"&gt;NonBracketToken&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;item&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="nx"&gt;NonBracketToken&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt; &lt;span class="k"&gt;instanceof&lt;/span&gt; &lt;span class="nb"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There's a lot happening there, so let's walk through it. First up, we have a generic definition, &lt;code&gt;&amp;lt;T extends Token["type"]&amp;gt;&lt;/code&gt;. This is essentially saying that T must be one of the possible values&lt;br&gt;
of the &lt;code&gt;Token.type&lt;/code&gt; field. Typescript is smart enough to know that means T must be one of &lt;code&gt;"int" | "float" | "identifier" | "typed-identifier" | "bracket&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The next interesting piece of code is the return type predicate &lt;code&gt;item is Extract&amp;lt;Token, { type: T }&amp;gt;&lt;/code&gt;. This predicate tells TypeScript that if the return value of &lt;code&gt;isTokenType&lt;/code&gt; is true, then &lt;code&gt;item&lt;/code&gt; must be the &lt;code&gt;Token&lt;/code&gt; whose type matches the string passed as the &lt;code&gt;type&lt;/code&gt; parameter.&lt;/p&gt;

&lt;p&gt;In practice that means that if we were to pass an unknown &lt;code&gt;Token&lt;/code&gt; to &lt;code&gt;isTokenType&lt;/code&gt;, typescript will be able to correctly narrow the value to a more specific token, like &lt;code&gt;IntToken&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Now that we have our custom type guard defined, we can define the actual token parsers. The first three are simple; they essentially just return a copy or slightly modified copy of the token:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;parseFloatToken&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;float&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;FloatToken&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;FloatNode&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;float&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;parseIntToken&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;int&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;IntToken&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;IntNode&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;int&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;parseIdentifier&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;identifier&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;IdentifierToken&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;IdentifierNode&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;identifier&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;identifier&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;identifier&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The final parser is the &lt;code&gt;parseTypedIdentifier&lt;/code&gt;. Remember that a typed identifier takes the form &lt;code&gt;identifier:type&lt;/code&gt;. Parsing it is as &lt;br&gt;
simple as splitting the string by the colon. The first value&lt;br&gt;
of the returned array is the &lt;code&gt;identifier&lt;/code&gt;, the second is the &lt;code&gt;type&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;Here's the definition:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;parseTypedIdentifier&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;identifier&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;TypedIdentifierToken&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;TypedIdentifierNode&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;vals&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;identifier&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;typed-identifier&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;identifier&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;vals&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="na"&gt;typeIdentifier&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;vals&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://github.com/drew-y/wispy/blob/main/src/parser.mts"&gt;Here’s the finished file&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That's all the code required for a working parser. Before we move on, let's update the main &lt;code&gt;src/index.mts&lt;/code&gt; file to view the output of the parser:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// src/index.mts&lt;/span&gt;&lt;span class="cp"&gt;
#!/usr/bin/env node
&lt;/span&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;readFileSync&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;fs&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;lex&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./lexer.mjs&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;parse&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./parser.mjs&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;file&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;input&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;readFileSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;utf8&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;tokens&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;lex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ast&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Build and run project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx tsc
wispy example.wispy
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If all goes well, the output should look like &lt;a href="https://github.com/drew-y/wispy/blob/58d5872f8d927358c1f8f70ebb4dda6d9458a8c8/example_ast_output.json"&gt;this&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;With that, the parser is finished. We can now convert the stream of tokens from the lexer into an AST. In the next post, we can get into the juicy bits: generating and running machine-readable code.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.courier.com/request-demo/"&gt;Request a demo today&lt;/a&gt; to see how Courier works to make product notifications delightful!&lt;/p&gt;

</description>
      <category>webassembly</category>
      <category>programming</category>
      <category>parsing</category>
    </item>
  </channel>
</rss>
