Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Vercel serverless function bug? #121

Open
jscottsf opened this issue Sep 6, 2024 · 13 comments
Open

Vercel serverless function bug? #121

jscottsf opened this issue Sep 6, 2024 · 13 comments
Labels
bug 💥 Something isn't working

Comments

@jscottsf
Copy link
Sponsor

jscottsf commented Sep 6, 2024

Followed the examples and deployed a Vercel serverless handler. Upon calling a function, a "bug" is logged. Everything builds correctly and it runs locally with a dev server. Using Build Output API v3.

Vercel serverless log:

Calling: {
  method: 'POST',
  url: '/_telefunc',
  rawBody: '{"file":"/src/telefuncs/Cart.telefunc.js","name":"fnLoadCart","args":[true,null,null]}'
}
Error: [[email protected]][Bug] You stumbled upon a Telefunc bug. Go to https://github.com/brillout/telefunc/issues/new and copy-paste this error. A maintainer will fix the bug (usually under 24 hours). 
    at isWebpack (file:///var/task/index.js:65:47023)
    at loadTelefuncFiles (file:///var/task/index.js:65:16854)
    at runTelefunc_ (file:///var/task/index.js:65:7443)
    at runTelefunc (file:///var/task/index.js:65:6584)
    at telefunc (file:///var/task/index.js:65:32015)
    at handler (file:///var/task/index.js:71:1612366)
    at async Server.<anonymous> (/opt/rust/nodejs.js:1:10566)

The handler:

import { telefunc } from 'telefunc'
import { text } from 'micro'

export default async function handler(req, res) {
  const { method, url } = req

  if (url === '/_telefunc') {
    const context = {}
    const rawBody = await text(req)

    console.log('Calling:', { method, url, rawBody })

    const httpResponse = await telefunc({
      url,
      method,
      body: rawBody,
      context
    })
    const { body, statusCode, contentType } = httpResponse

    res.statusCode = statusCode
    res.setHeader('content-type', contentType)
    res.end(body)
  }
}

Config.json

{
    "routes":
    [
       {
            "dest": "/_telefunc",
            "methods":
            [
                "POST"
            ],
            "src": "/_telefunc"
        }
    ],
    "version": 3
}

NOTE: I have a Vike serverless renderer as another function and it works perfectly.

Any ideas on what to try?

@jscottsf
Copy link
Sponsor Author

jscottsf commented Sep 6, 2024

Additional info

The build step is:

# 7. Bundle telefunc function to a single file.
echo "INFO: Bundling SSR render functions"
cd .vercel/output/functions/_telefunc.func
npx ncc build --minify --out . index.js
if [ $? -ne 0 ]
then
  echo "ERRO: Bundling failed, exiting"
  exit 1
fi

The runtime config is:

{
    "handler": "index.js",
    "launcherType": "Nodejs",
    "runtime": "nodejs20.x",
    "shouldAddHelpers": true
}

Seems to be happening at this line inside loadTelefuncFiles:

if (!(0, o.isWebpack)() || (0, o.isVikeApp)()) {

@jscottsf
Copy link
Sponsor Author

jscottsf commented Sep 6, 2024

Looks like ncc is Webpack-based, and the assertion is failing on the Webpack test.

@brillout brillout added the bug 💥 Something isn't working label Sep 7, 2024
@brillout
Copy link
Owner

brillout commented Sep 7, 2024

It seems to be a relatively simple issue: the failing assertion inside isWebpack() means that the test for checking whether the environment is webpack isn't working as expected. Ideas for improving the isWebpack() function? I guess there is a newer/better way to check?

@jscottsf
Copy link
Sponsor Author

jscottsf commented Sep 7, 2024

Here is the ncc packed output for the current function, if this helps.

;(s, t, a) => {
  Object.defineProperty(t, '__esModule', { value: true })
  t.isWebpack = void 0
  const o = a(80241)
  function isWebpack() {
    const s = typeof require === 'function'
    const t = typeof a === 'function'
    ;(0, o.assert)(s === t)
    return s || t
  }
  t.isWebpack = isWebpack
}

Also looks like they replace __webpack_require__ with some form of __nccwpck_require<n>_

https://github.com/vercel/ncc/blob/b2a325dec1dc54f2168f838dee0b9e48ee701d18/src/index.js#L552

Maybe this could be a config setting or ENV var to guide/override the loading process in challenging installs?

@brillout
Copy link
Owner

brillout commented Sep 7, 2024

Good to know, thank you!

Fix released as 0.1.79.

@jscottsf
Copy link
Sponsor Author

jscottsf commented Sep 7, 2024

Well, now Vercel is throwing this. Hmmm.

Calling: {
  method: 'POST',
  url: '/_telefunc',
  rawBody: '{"file":"/src/telefuncs/Cart.telefunc.js","name":"fnLoadCart","args":[true,null,null]}'
}
Error: [[email protected]][Wrong Usage] You don't seem to be using Telefunc with a supported stack. Reach out on GitHub.
    at loadTelefuncFiles (file:///var/task/index.js:65:17127)
    at runTelefunc_ (file:///var/task/index.js:65:7443)
    at runTelefunc (file:///var/task/index.js:65:6584)
    at telefunc (file:///var/task/index.js:65:32015)
    at handler (file:///var/task/index.js:71:1612340)
    at async Server.<anonymous> (/opt/rust/nodejs.js:1:10566)
    at async Server.<anonymous> (/opt/rust/nodejs.js:8:4242)

@brillout brillout reopened this Sep 7, 2024
@brillout
Copy link
Owner

brillout commented Sep 7, 2024

Hm. What does isWebpack() return? Ideas to make the the isWebpack() check work?

@jscottsf
Copy link
Sponsor Author

jscottsf commented Sep 7, 2024

A bit confused actually. What code path are we trying to go down? The web app is Vike, but the serverless function is built with ncc (webpack). The serverless load is failing on invocation.

Would it help to specify the Telefunc file to load? I only have one with all server side behaviors for a cart system.

Since we reached the assertUsage at the end:

isWebpack() must be true now (the inner assert was removed and one of the tests passed).
isVikeApp() must be false since we didn't enter the block.

!isWebpack() || isVikeApp()
false || false

Is the intent to have this block run?

 if (!isWebpack() || isVikeApp()) {
    const { telefuncFilesLoaded, viteProvider, telefuncFilesAll } = await loadTelefuncFilesWithVite(runContext)
    assertUsage(Object.keys(telefuncFilesAll).length > 0, getNothingFoundErr(`Vite [\`${viteProvider}\`]`))
    return { telefuncFilesLoaded, telefuncFilesAll }
  }

@brillout
Copy link
Owner

brillout commented Sep 7, 2024

You're right.

isVikeApp() must be false since we didn't enter the block.

That seems to be the culprit. Why is it false even though you're using Vike? What Vike version are you using? Vike sets globalThis._isVikeApp at node_modules/vike/dist/esm/node/runtime/index-common.js (or dist/cjs if you aren't using ESM).

(Btw. reproduction welcome if you prefer.)

@jscottsf
Copy link
Sponsor Author

jscottsf commented Sep 7, 2024

I have two serverless function builds. One for a Vike SSR, and another just for Telefunc. I wanted separate functions. Since Vike is not imported in the Telefunc one, is the global not getting set?

# 6. Bundle render function to a single file.
echo "INFO: Bundling SSR render functions"
cd .vercel/output/functions/ssr_.func
npx ncc build --minify --out . index.js
if [ $? -ne 0 ]
then
  echo "ERRO: Bundling failed, exiting"
  exit 1
fi
cd ../../../..

# 7. Bundle telefunc function to a single file.
echo "INFO: Bundling SSR render functions"
cd .vercel/output/functions/_telefunc.func
npx ncc build --minify --out . index.js
if [ $? -ne 0 ]
then
  echo "ERRO: Bundling failed, exiting"
  exit 1
fi
cd ../../../..

@brillout
Copy link
Owner

brillout commented Sep 7, 2024

That makes sense. I've an idea to make it work. Let me finish what I'm currently working on then I'll implement it.

@jscottsf
Copy link
Sponsor Author

jscottsf commented Sep 7, 2024

Solved it.

If you are using a Vike file structure, but not importing it in a serverless function implementation, then just do:

globalThis._isVikeApp = true
import { telefunc } from 'telefunc'
import { text } from 'micro'

export default async function handler(req, res) {
  const { method, url } = req

  if (url === '/_telefunc') {
    const context = {}
    const rawBody = await text(req)

    console.log('Calling:', { method, url, rawBody })

    globalThis._isVikeApp = true  // DO THIS

    const httpResponse = await telefunc({
      url,
      method,
      body: rawBody,
      context
    })
    const { body, statusCode, contentType } = httpResponse

    res.statusCode = statusCode
    res.setHeader('content-type', contentType)
    res.end(body)
  }
}

Also, the Webpack fix you made allows this to work with ncc since it's a bit more relaxed.

Thank you!!!

@brillout
Copy link
Owner

brillout commented Sep 8, 2024

Indeed, that workaround should work. I'll be implementing a proper fix next week.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug 💥 Something isn't working
Projects
None yet
Development

No branches or pull requests

2 participants