<p dir="auto">This has long been missing from JS/TS environments: the ability to easily serialize and reify a function, along with its contextual dependencies. I see this being useful to enable colocation of code intended for execution in different environments. This will be useful to the creation of experiences that blend build time, server side, and frontend code.</p>
<p dir="auto">Take the NextJS 9.3 experience, for instance:</p>
<div class="highlight highlight-source-tsx notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="import {getDynamicValue} from &quot;./somewhere&quot;;
// we DO NOT want this import to appear in the frontend code
import {sensitiveFn} from &quot;./sensitive-module&quot;;  

// gather some values, on which our fns will depend
const GREETING_ENDING = await getDynamicValue();
const USED_IN_BOTH_ENVIRONMENTS = await getDynamicValue();

// export fn for the serverless environment
export const getServerSideProps = () =&gt; {
  sensitiveFn();
  console.log(USED_IN_BOTH_ENVIRONMENTS);
  return {
    name: &quot;Deno&quot;,
  }
}

// export the front-end component
export default (props: {name: string}) =&gt; {
  console.log(USED_IN_BOTH_ENVIRONMENTS);
  return (
    &lt;div&gt;
      Hello {props.name}{GREETING_ENDING}
    &lt;/div&gt;
  )
}"><pre class="notranslate"><span class="pl-k">import</span> <span class="pl-kos">{</span><span class="pl-s1">getDynamicValue</span><span class="pl-kos">}</span> <span class="pl-k">from</span> <span class="pl-s">"./somewhere"</span><span class="pl-kos">;</span>
<span class="pl-c">// we DO NOT want this import to appear in the frontend code</span>
<span class="pl-k">import</span> <span class="pl-kos">{</span><span class="pl-s1">sensitiveFn</span><span class="pl-kos">}</span> <span class="pl-k">from</span> <span class="pl-s">"./sensitive-module"</span><span class="pl-kos">;</span>  

<span class="pl-c">// gather some values, on which our fns will depend</span>
<span class="pl-k">const</span> <span class="pl-smi">GREETING_ENDING</span> <span class="pl-c1">=</span> <span class="pl-k">await</span> <span class="pl-en">getDynamicValue</span><span class="pl-kos">(</span><span class="pl-kos">)</span><span class="pl-kos">;</span>
<span class="pl-k">const</span> <span class="pl-smi">USED_IN_BOTH_ENVIRONMENTS</span> <span class="pl-c1">=</span> <span class="pl-k">await</span> <span class="pl-en">getDynamicValue</span><span class="pl-kos">(</span><span class="pl-kos">)</span><span class="pl-kos">;</span>

<span class="pl-c">// export fn for the serverless environment</span>
<span class="pl-k">export</span> <span class="pl-k">const</span> <span class="pl-en">getServerSideProps</span> <span class="pl-c1">=</span> <span class="pl-kos">(</span><span class="pl-kos">)</span> <span class="pl-c1">=&gt;</span> <span class="pl-kos">{</span>
  <span class="pl-en">sensitiveFn</span><span class="pl-kos">(</span><span class="pl-kos">)</span><span class="pl-kos">;</span>
  <span class="pl-smi">console</span><span class="pl-kos">.</span><span class="pl-en">log</span><span class="pl-kos">(</span><span class="pl-smi">USED_IN_BOTH_ENVIRONMENTS</span><span class="pl-kos">)</span><span class="pl-kos">;</span>
  <span class="pl-k">return</span> <span class="pl-kos">{</span>
    <span class="pl-c1">name</span>: <span class="pl-s">"Deno"</span><span class="pl-kos">,</span>
  <span class="pl-kos">}</span>
<span class="pl-kos">}</span>

<span class="pl-c">// export the front-end component</span>
<span class="pl-k">export</span> <span class="pl-k">default</span> <span class="pl-kos">(</span><span class="pl-s1">props</span>: <span class="pl-kos">{</span><span class="pl-c1">name</span>: <span class="pl-smi">string</span><span class="pl-kos">}</span><span class="pl-kos">)</span> <span class="pl-c1">=&gt;</span> <span class="pl-kos">{</span>
  <span class="pl-smi">console</span><span class="pl-kos">.</span><span class="pl-en">log</span><span class="pl-kos">(</span><span class="pl-smi">USED_IN_BOTH_ENVIRONMENTS</span><span class="pl-kos">)</span><span class="pl-kos">;</span>
  <span class="pl-k">return</span> <span class="pl-kos">(</span>
    <span class="pl-c1">&lt;</span><span class="pl-ent">div</span><span class="pl-c1">&gt;</span>
      Hello <span class="pl-kos">{</span><span class="pl-s1">props</span><span class="pl-kos">.</span><span class="pl-c1">name</span><span class="pl-kos">}</span><span class="pl-kos">{</span><span class="pl-smi">GREETING_ENDING</span><span class="pl-kos">}</span>
    <span class="pl-c1">&lt;</span><span class="pl-c1">/</span><span class="pl-ent">div</span><span class="pl-c1">&gt;</span>
  <span class="pl-kos">)</span>
<span class="pl-kos">}</span></pre></div>
<p dir="auto">While this example doesn't perfectly-illustrate NextJS constraints (the top-level awaiting of dynamic values), it conveys the use case nonetheless: the build process is responsible for extracting the page component (<code class="notranslate">default</code>), along with a serverless pre-rendering handler (<code class="notranslate">getServerSideProps</code>). These functions make use of in-scope variables, some of which cannot be statically analyzed. I believe it will become increasingly important to have a standard approach to capturing any given function. Let's also take a look at the serverless use case:</p>
<p dir="auto">In the <a href="https://github.com/aws/aws-cdk">aws-cdk</a>, users describe their infrastructure separately from the runtime code that executes on that infrastructure:</p>
<div class="highlight highlight-source-ts notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="const getOneLambda = new lambda.Function(this, 'getOneItemFunction', {
  code: new lambda.AssetCode('src'),
  handler: 'get-one.handler',
  runtime: lambda.Runtime.NODEJS_10_X,
  environment: {
    TABLE_NAME: dynamoTable.tableName,
    PRIMARY_KEY: 'itemId'
  }
});"><pre class="notranslate"><span class="pl-k">const</span> <span class="pl-s1">getOneLambda</span> <span class="pl-c1">=</span> <span class="pl-k">new</span> <span class="pl-s1">lambda</span><span class="pl-kos">.</span><span class="pl-c1">Function</span><span class="pl-kos">(</span><span class="pl-smi">this</span><span class="pl-kos">,</span> <span class="pl-s">'getOneItemFunction'</span><span class="pl-kos">,</span> <span class="pl-kos">{</span>
  <span class="pl-c1">code</span>: <span class="pl-k">new</span> <span class="pl-s1">lambda</span><span class="pl-kos">.</span><span class="pl-c1">AssetCode</span><span class="pl-kos">(</span><span class="pl-s">'src'</span><span class="pl-kos">)</span><span class="pl-kos">,</span>
  <span class="pl-c1">handler</span>: <span class="pl-s">'get-one.handler'</span><span class="pl-kos">,</span>
  <span class="pl-c1">runtime</span>: <span class="pl-s1">lambda</span><span class="pl-kos">.</span><span class="pl-c1">Runtime</span><span class="pl-kos">.</span><span class="pl-c1">NODEJS_10_X</span><span class="pl-kos">,</span>
  <span class="pl-c1">environment</span>: <span class="pl-kos">{</span>
    <span class="pl-c1">TABLE_NAME</span>: <span class="pl-s1">dynamoTable</span><span class="pl-kos">.</span><span class="pl-c1">tableName</span><span class="pl-kos">,</span>
    <span class="pl-c1">PRIMARY_KEY</span>: <span class="pl-s">'itemId'</span>
  <span class="pl-kos">}</span>
<span class="pl-kos">}</span><span class="pl-kos">)</span><span class="pl-kos">;</span></pre></div>
<p dir="auto">If there were a seamless mechanism to serialize a function, one could enable the following DX:</p>
<div class="highlight highlight-source-ts notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="import * as getOne from &quot;./src/get-one&quot;;

const getOneLambda = new lambda.Function(this, 'getOneItemFunction', getOne.handler);"><pre class="notranslate"><span class="pl-k">import</span> <span class="pl-c1">*</span> <span class="pl-k">as</span> <span class="pl-s1">getOne</span> <span class="pl-k">from</span> <span class="pl-s">"./src/get-one"</span><span class="pl-kos">;</span>

<span class="pl-k">const</span> <span class="pl-s1">getOneLambda</span> <span class="pl-c1">=</span> <span class="pl-k">new</span> <span class="pl-s1">lambda</span><span class="pl-kos">.</span><span class="pl-c1">Function</span><span class="pl-kos">(</span><span class="pl-smi">this</span><span class="pl-kos">,</span> <span class="pl-s">'getOneItemFunction'</span><span class="pl-kos">,</span> <span class="pl-s1">getOne</span><span class="pl-kos">.</span><span class="pl-c1">handler</span><span class="pl-kos">)</span><span class="pl-kos">;</span></pre></div>
<p dir="auto">The location of the code and handler are no longer necessary, the runtime is inferred as the currently-executing runtime, and the environment variables can be directly referenced from within the Lamdba handler.</p>
<p dir="auto"><a href="https://github.com/pulumi/pulumi/blob/master/sdk/nodejs/runtime/closure/createClosure.ts">Here's Pulumi's approach</a> to this problem. They're using V8 stack tracing and the TypeScript compiler at runtime.</p>
<p dir="auto">Apologies that I cannot offer any suggestions as to how the ideal implementation looks. Not sure if it lays in Rusty V8 and Deno core, or if this would better-exist as a 3rd-party library. Nevertheless, I hope you'll consider this feature request, as I do believe it paves the way for much better experiences in many domains.</p>
<p dir="auto">Thank you for your consideration.</p>
<p dir="auto">Hi, I'm trying out the unstable <code class="notranslate">Deno.upgradeWebSocket</code> and encountered an issue accepting a WebSocket from Firefox.</p>
<p dir="auto">Firefox is sending this <code class="notranslate">connection: "keep-alive, Upgrade"</code> header when connecting to a websocket. That causes throw <a href="https://github.com/denoland/deno/blob/8b34f07bb000a16b60b119ee24f5c0b4a5f7f937/extensions/http/01_http.js#L324">here</a></p>
<div class="highlight highlight-source-ts notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="  function upgradeWebSocket(request, options = {}) {

    // ...

    if (request.headers.get(&quot;connection&quot;)?.toLowerCase() !== &quot;upgrade&quot;) {
      throw new TypeError(
        &quot;Invalid Header: 'connection' header must be 'Upgrade'&quot;,
      );
    }

    // ...
"><pre class="notranslate">  <span class="pl-k">function</span> <span class="pl-en">upgradeWebSocket</span><span class="pl-kos">(</span><span class="pl-s1">request</span><span class="pl-kos">,</span> <span class="pl-s1">options</span> <span class="pl-c1">=</span> <span class="pl-kos">{</span><span class="pl-kos">}</span><span class="pl-kos">)</span> <span class="pl-kos">{</span>

    <span class="pl-c">// ...</span>

    <span class="pl-k">if</span> <span class="pl-kos">(</span><span class="pl-s1">request</span><span class="pl-kos">.</span><span class="pl-c1">headers</span><span class="pl-kos">.</span><span class="pl-en">get</span><span class="pl-kos">(</span><span class="pl-s">"connection"</span><span class="pl-kos">)</span><span class="pl-kos">?.</span><span class="pl-en">toLowerCase</span><span class="pl-kos">(</span><span class="pl-kos">)</span> <span class="pl-c1">!==</span> <span class="pl-s">"upgrade"</span><span class="pl-kos">)</span> <span class="pl-kos">{</span>
      <span class="pl-k">throw</span> <span class="pl-k">new</span> <span class="pl-smi">TypeError</span><span class="pl-kos">(</span>
        <span class="pl-s">"Invalid Header: 'connection' header must be 'Upgrade'"</span><span class="pl-kos">,</span>
      <span class="pl-kos">)</span><span class="pl-kos">;</span>
    <span class="pl-kos">}</span>

    <span class="pl-c">// ...</span><span class="pl-kos"></span></pre></div>
<p dir="auto">while the <a href="https://github.com/denoland/deno_std/blob/bf73521428ce724d31dba0d403d14acc929f39ab/ws/mod.ts#L396">current std logic for accepting sockets</a> doesn't care about the <code class="notranslate">connection</code> header at all.</p>
<div class="highlight highlight-source-ts notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="export function acceptable(req: { headers: Headers }): boolean {
 const upgrade = req.headers.get(&quot;upgrade&quot;);
 if (!upgrade || upgrade.toLowerCase() !== &quot;websocket&quot;) {
   return false;
 }
 const secKey = req.headers.get(&quot;sec-websocket-key&quot;);
 return (
   req.headers.has(&quot;sec-websocket-key&quot;) &amp;&amp;
   typeof secKey === &quot;string&quot; &amp;&amp;
   secKey.length &gt; 0
 );
}"><pre class="notranslate"><span class="pl-k">export</span> <span class="pl-k">function</span> <span class="pl-en">acceptable</span><span class="pl-kos">(</span><span class="pl-s1">req</span>: <span class="pl-kos">{</span> <span class="pl-c1">headers</span>: <span class="pl-smi">Headers</span> <span class="pl-kos">}</span><span class="pl-kos">)</span>: <span class="pl-smi">boolean</span> <span class="pl-kos">{</span>
 <span class="pl-k">const</span> <span class="pl-s1">upgrade</span> <span class="pl-c1">=</span> <span class="pl-s1">req</span><span class="pl-kos">.</span><span class="pl-c1">headers</span><span class="pl-kos">.</span><span class="pl-en">get</span><span class="pl-kos">(</span><span class="pl-s">"upgrade"</span><span class="pl-kos">)</span><span class="pl-kos">;</span>
 <span class="pl-k">if</span> <span class="pl-kos">(</span><span class="pl-c1">!</span><span class="pl-s1">upgrade</span> <span class="pl-c1">||</span> <span class="pl-s1">upgrade</span><span class="pl-kos">.</span><span class="pl-en">toLowerCase</span><span class="pl-kos">(</span><span class="pl-kos">)</span> <span class="pl-c1">!==</span> <span class="pl-s">"websocket"</span><span class="pl-kos">)</span> <span class="pl-kos">{</span>
   <span class="pl-k">return</span> <span class="pl-c1">false</span><span class="pl-kos">;</span>
 <span class="pl-kos">}</span>
 <span class="pl-k">const</span> <span class="pl-s1">secKey</span> <span class="pl-c1">=</span> <span class="pl-s1">req</span><span class="pl-kos">.</span><span class="pl-c1">headers</span><span class="pl-kos">.</span><span class="pl-en">get</span><span class="pl-kos">(</span><span class="pl-s">"sec-websocket-key"</span><span class="pl-kos">)</span><span class="pl-kos">;</span>
 <span class="pl-k">return</span> <span class="pl-kos">(</span>
   <span class="pl-s1">req</span><span class="pl-kos">.</span><span class="pl-c1">headers</span><span class="pl-kos">.</span><span class="pl-en">has</span><span class="pl-kos">(</span><span class="pl-s">"sec-websocket-key"</span><span class="pl-kos">)</span> <span class="pl-c1">&amp;&amp;</span>
   <span class="pl-k">typeof</span> <span class="pl-s1">secKey</span> <span class="pl-c1">===</span> <span class="pl-s">"string"</span> <span class="pl-c1">&amp;&amp;</span>
   <span class="pl-s1">secKey</span><span class="pl-kos">.</span><span class="pl-c1">length</span> <span class="pl-c1">&gt;</span> <span class="pl-c1">0</span>
 <span class="pl-kos">)</span><span class="pl-kos">;</span>
<span class="pl-kos">}</span></pre></div>