question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Platform context fallbacks

See original GitHub issue

Describe the problem

  • When developing with svelte-kit dev, the event.platform object is always empty, with no great way to mock it.
  • When building using an adapter like Cloudflare Workers, event.platform is empty for prerendering.

Describe the proposed solution

Ability to provide a platform object in svelte.config.js that is

  • Substituted for event.platform when event.platform is undefined.
  • Merged with event.platform, useful for specifying optional/default platform values.
  • Both?

Alternatives considered

Perhaps allowing a ‘transform’ function that accepts an event may be better, in case platform needs to change based on inputs?

Importance

would make my life easier

Additional Information

No response

Issue Analytics

  • State:open
  • Created 2 years ago
  • Reactions:6
  • Comments:8 (2 by maintainers)

github_iconTop GitHub Comments

5reactions
denizkenancommented, May 3, 2022

My proposal would be, having adapters optionally provide an adapt_dev function similar to adapt where adapter author provides a mocked platform before request is sent for render. Setting up mocked platform should be adapter specific though.

I have managed fix this issue when using adapter-cloudflare-workers. it is a great workaround that served me well so far.

in hooks I use:

//src/hooks/index.ts
export const interceptPlatform: Handle = async ({ event, resolve }) => {
  event.platform = await cloudflareAdapterPlatform(event.platform)
  return resolve(event)
}
...

export const handle: Handle = sequence(interceptPlatform, ...)

Every request is intercepted by following code:

//src/cloudflareAdapterPlatform.ts

import { dev } from '$app/env';
import type { CFENV } from '../../app';

let context:ExecutionContext
const exposeCFGlobals = (globalObjects:object,ctx:ExecutionContext)=>{
    Object.entries(globalObjects).forEach(([key,val])=>{
         global[key]=val;
    })
    context = ctx;
}
const fn = (ctx:ExecutionContext) => {
        exposeCFGlobals({crypto},ctx)
        return;
}
export default async (_platform:App.Platform) => {
        if(!dev){
            return _platform;
        }
        if(_platform){
            return _platform;
        }
        
        console.log("!!!!!INITIALIZED!!!!!")
        const dotenv = await import("dotenv");
        const esbuild = await import("esbuild")
        const path = await import("path")
        const toml = await import("toml")
        const fs = await import("fs");
        const sourcefile = path.join(process.cwd(),"/durable_objects/src/objects.ts")
        const tsconfpath = path.join(process.cwd(),"/tsconfig.json")
        const wranglerPath = path.join(process.cwd(),"/wrangler.toml")
        const sourceCode = fs.readFileSync(sourcefile).toString('utf8')
        const tsconfigRaw = fs.readFileSync(tsconfpath).toString('utf8')
        const wranglerConfigRaw = fs.readFileSync(wranglerPath).toString('utf8')
        const wranglerConfig = toml.parse(wranglerConfigRaw)
        const bindings = wranglerConfig?.vars ??{}
        const durableObjects = (wranglerConfig?.durable_objects?.bindings ?? []).reduce((p,{name,class_name})=>{
            p[name] = class_name;
            return  p;
        },{})
        
        const {code } =  esbuild.transformSync(
            `
            const fn =  ${fn.toString()};
            export default {
                fetch: async (request, env2, ctx2) => {
                    fn(ctx2);
                    return new Response("Hello Miniflare!");
                }
            };
            ${sourceCode}
            `
            ,{
            loader: 'ts',
            sourcefile,
            tsconfigRaw,
            sourcemap:"inline"
        });
        const {parsed} =  dotenv.config()
        const miniflare = await (await import("miniflare")).Miniflare;
        const mf = new miniflare({
            modules:true,
            durableObjectsPersist:true,
            wranglerConfigPath:false,
            envPath:path.join(process.cwd(),"/.env"),
            script: code,
            durableObjects,
            bindings,
            globals:{exposeCFGlobals}
            })
            await mf.dispatchFetch("https://host.tld")
            const env = {...parsed, ...bindings} as unknown as CFENV;
            for await (const [k,_] of Object.entries(durableObjects)){
                env[k] = await mf.getDurableObjectNamespace(k) as DurableObjectNamespace;
            }  
            
        
        const platform:App.Platform = {env,context}
        return platform;
        
    }

In brief cloudflareAdapterPlatform.ts:

  • checks if it is called in dev mode if not exits without any action
  • does platform specific logic.
    • spin up miniflare.
    • load Durable Objects
    • expose runtime specific modules back to node such as Crypto
    • load environment variables defined in wrangler.toml
    • etc…
  • attach mocked platform to event

This logic should be part of adapter and every adapter should fulfil its platform specific requirements. I understand that there is a desire for keeping codebase free from adapter specific logic. However, I don’t see this happening when endpoint events expose adapter specific platform.

3reactions
tyvdhcommented, Jun 29, 2022

Hi @denizkenan, your looks great! Would you by any chance have an public repo with this implementation available? I am trying to figure out how you are piecing everything together but I can’t figure it out.

It’s not perfect but here’s what I’ve been using for my base repo: https://github.com/tyvdh/test-kit

Read more comments on GitHub >

github_iconTop Results From Across the Web

Fallback Context Policy - Anaplan Community
Hi, Today, when I pulled Comparison Report in our Model, I got additional lines in the "Modules Changed" section, which is like.
Read more >
How to use Contextual Fallback with Dialogflow to handle ...
In Part 1, I demonstrated how you can use Context in Dialogflow to build a simple Knock Knock joke. In Part 2, I...
Read more >
AMAZON.FallbackIntent - Amazon Lex - AWS Documentation
Invoking a fallback intent uses two steps. In the first step the fallback intent is matched based on the input from the user....
Read more >
What is Human Fallback | Helpshift
It's a platform that connects messaging, phone, email, self-service, bots, and AI. Conversations can occur within a single messaging thread, so ...
Read more >
ConfigReader · Backstage Software Catalog and Developer ...
(constructor)(data, context, fallback, prefix), Constructs a new instance of the ConfigReader class. Methods. Method, Modifiers, Description ...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found