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

Use AsyncLocalStorage #40

Open
wants to merge 11 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions examples/remix-cms/app/components/Toast.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ export function Toast({
useEffect(() => {
if (message) {
setShow(true);
} else {
setShow(false);
}
}, [message]);

Expand All @@ -31,6 +33,8 @@ export function Toast({
return () => {
clearTimeout(timeout);
};
} else {
setShow(false);
}
}, [show]);

Expand All @@ -57,12 +61,13 @@ export function Toast({
<div className="p-4">
<div className="flex items-start">
<div className="flex-shrink-0">
{success ? (
{success && (
<CheckCircleIcon
className="h-6 w-6 text-green-400"
aria-hidden="true"
/>
) : (
)}
{error && (
<ExclamationCircleIcon
className="h-6 w-6 text-red-400"
aria-hidden="true"
Expand Down
4 changes: 3 additions & 1 deletion examples/remix-cms/app/routes/admin/articles.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ import { Article } from "~/models/Article";
import { useChannel } from "~/utils/use-channel";

export async function loader() {
const articles = await Article.with("user").orderBy("createdAt", "desc");
const articles = await Article.with("user")
.orderBy("createdAt", "desc")
.get();
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A limitation with ALS in CF Workers today is that custom thenables don't properly retain context: https://twitter.com/jasnell/status/1634764772121145344

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.


return json({ articles });
}
Expand Down
1 change: 1 addition & 0 deletions examples/remix-cms/app/utils/markdown.server.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { marked } from "marked";
// TODO: Find a lighter solution for syntax highlighting
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not related to ALS per se, but my CF Workers bundle is like 4MB because of this 🙃

import hljs from "highlight.js";

export async function convertToHtml(input: string) {
Expand Down
12 changes: 6 additions & 6 deletions examples/remix-cms/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,9 @@
"@cloudflare/kv-asset-handler": "^0.3.0",
"@headlessui/react": "^1.7.13",
"@heroicons/react": "^2.0.16",
"@remix-run/cloudflare": "^1.14.1",
"@remix-run/react": "^1.14.1",
"@remix-run/serve": "^1.14.1",
"@remix-run/cloudflare": "0.0.0-nightly-da5486d-20230323",
"@remix-run/react": "0.0.0-nightly-da5486d-20230323",
"@remix-run/serve": "0.0.0-nightly-da5486d-20230323",
"@superflare/remix": "workspace:*",
"@tailwindcss/forms": "^0.5.3",
"clsx": "^1.2.1",
Expand All @@ -38,9 +38,9 @@
"devDependencies": {
"@cloudflare/workers-types": "^4.20230307.0",
"@faker-js/faker": "^7.6.0",
"@remix-run/dev": "^1.14.1",
"@remix-run/eslint-config": "^1.14.1",
"@remix-run/server-runtime": "^1.14.1",
"@remix-run/dev": "0.0.0-nightly-da5486d-20230323",
"@remix-run/eslint-config": "0.0.0-nightly-da5486d-20230323",
"@remix-run/server-runtime": "0.0.0-nightly-da5486d-20230323",
"@tailwindcss/typography": "^0.5.9",
"@types/marked": "^4.0.8",
"@types/react": "^18.0.28",
Expand Down
2 changes: 1 addition & 1 deletion examples/remix-cms/worker.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { createRequestHandler } from "@remix-run/cloudflare";
import config from "./superflare.config";
import * as build from "./build";
import {
getAssetFromKV,
Expand All @@ -9,6 +8,7 @@ import {
import manifestJSON from "__STATIC_CONTENT_MANIFEST";
import { handleQueue } from "superflare";
import { handleFetch } from "@superflare/remix";
import config from "./superflare.config";

export { Channel } from "superflare";

Expand Down
5 changes: 0 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,5 @@
"dependencies": {
"@changesets/changelog-git": "^0.1.14",
"@changesets/cli": "^2.26.0"
},
"pnpm": {
"patchedDependencies": {
"@remix-run/[email protected]": "patches/@[email protected]"
}
}
}
5 changes: 2 additions & 3 deletions packages/superflare-remix/index.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { createCookieSessionStorage } from "@remix-run/cloudflare";
import {
defineConfig,
handleFetch as superflareHandleFetch,
SuperflareAuth,
SuperflareSession,
type defineConfig,
} from "superflare";

/**
Expand Down Expand Up @@ -51,8 +51,7 @@ export async function handleFetch<Env extends { APP_KEY: string }>(
},
async () => {
/**
* We inject env and session into the Remix load context.
* Someday, we could replace this with AsyncLocalStorage.
* TODO: REMOVE THIS since we're using AsyncLocalStorage
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or just deprecate it?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For me, I think I'll still put some separate things in the context for now. But a lot of people probably never should need to care about context.

*/
const loadContext: SuperflareAppLoadContext<Env> = {
session,
Expand Down
1 change: 1 addition & 0 deletions packages/superflare-remix/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
},
"dependencies": {
"@cloudflare/kv-asset-handler": "^0.3.0",
"@types/node": "^18.14.1",
"superflare": "workspace:*"
}
}
36 changes: 29 additions & 7 deletions packages/superflare/cli/console.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { inspect } from "node:util";
import path from "node:path";
import { CommonYargsArgv, StrictYargsOptionsToInterface } from "./yargs-types";
import { createD1Database } from "./d1-database";
import { Script } from "node:vm";

export function consoleOptions(yargs: CommonYargsArgv) {
return yargs
Expand Down Expand Up @@ -88,14 +89,35 @@ export async function createRepl({
server.context["db"] = db;

/**
* Run the Superflare `config` to ensure Models have access to the database.
* Define a custom `eval` function to wrap the code in a `runWithContext` call.
*/
server.eval(
`const {setConfig} = require('superflare'); setConfig({database: { default: db }});`,
server.context,
"repl",
() => {}
);
const customEval = (
cmd: string,
_: any,
filename: string,
callback: (err: Error | null, result: any) => void
) => {
const wrappedCmd = `
const { getContextFromUserConfig, runWithContext } = require('superflare');
const context = getContextFromUserConfig({database: { default: db }});
runWithContext(context, async () => {
return ${cmd}
});
`;

const response = new Script(wrappedCmd, { filename }).runInThisContext();

if (response instanceof Promise) {
response
.then((result) => callback(null, result))
.catch((err) => callback(err, null));
} else {
callback(null, response);
}
};

// @ts-ignore
server["eval"] = customEval;

/**
* Get a list of the models in the user's dir.
Expand Down
10 changes: 9 additions & 1 deletion packages/superflare/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export { setConfig, defineConfig } from "./src/config";
export { defineConfig } from "./src/config";
export { Model } from "./src/model";
export { SuperflareSession } from "./src/session";
export { DatabaseException } from "./src/query-builder";
Expand All @@ -14,4 +14,12 @@ export { Event } from "./src/event";
export { Listener } from "./src/listener";
export { handleWebSockets } from "./src/websockets";
export { Channel } from "./src/durable-objects/Channel";
export {
getContext,

// Internal use only:
runWithContext,
enterWithConfig,
getContextFromUserConfig,
} from "./src/context";
export { Schema } from "./src/schema";
9 changes: 8 additions & 1 deletion packages/superflare/index.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
**/

export {
setConfig,
defineConfig,
type SuperflareUserConfig,
type DefineConfigResult,
Expand All @@ -23,6 +22,14 @@ export { Event } from "./src/event";
export { Listener } from "./src/listener";
export { handleWebSockets } from "./src/websockets";
export { Channel } from "./src/durable-objects/Channel";
export {
getContext,

// Internal use only:
enterWithConfig,
getContextFromUserConfig,
runWithContext,
} from "./src/context";
export { Schema } from "./src/schema";

/**
Expand Down
3 changes: 2 additions & 1 deletion packages/superflare/src/channels.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { ChannelConfig, getChannel, getChannelNames } from "./config";
import { ChannelConfig } from "./config";
import { getChannel, getChannelNames } from "./context";

export function getConfigForChannelName(
channelName: string
Expand Down
127 changes: 1 addition & 126 deletions packages/superflare/src/config.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import { sanitizeModuleName } from "./string";

export interface StorageDiskConfig {
binding: R2Bucket;
/**
Expand Down Expand Up @@ -47,126 +45,6 @@ export interface SuperflareUserConfig {
channels?: ChannelsConfig;
}

export function setConfig(userConfig: SuperflareUserConfig) {
Config.appKey = userConfig.appKey;

if (userConfig.database) {
Config.database = {
connections: userConfig.database,
};
}
if (userConfig.storage) {
Config.storage = {
disks: userConfig.storage,
};
}
if (userConfig.queues) {
Config.queues = userConfig.queues;
}
if (userConfig.channels) {
Config.channels = userConfig.channels;
}
if (userConfig.listeners) {
Config.listeners = new Map(Object.entries(userConfig.listeners));
}

return userConfig;
}

/**
* Register a model into the Superflare config.
*/
export function registerModel(model: any) {
Config.models = Config.models || {};
Config.models[model.name] = model;
}

export function getModel(name: string) {
return Config.models?.[name];
}

/**
* Register a job into the Superflare config.
*/
export function registerJob(job: any) {
const jobName = sanitizeModuleName(job.name);
Config.jobs = Config.jobs || {};
Config.jobs[jobName] = job;
}

export function getJob(name: string) {
return Config.jobs?.[name];
}

export function getQueue(name: string) {
return Config.queues?.[name];
}

export function registerEvent(event: any) {
const eventName = sanitizeModuleName(event.name);
Config.events = Config.events || {};
Config.events[eventName] = event;
}

export function getEvent(name: string) {
return Config.events?.[name];
}

export function getListenersForEventClass(eventClass: any) {
const eventName = sanitizeModuleName(eventClass.name);
return Config.listeners.get(eventName) || [];
}

export function setEnv(env: any) {
Config.env = env;
}

export function getEnv() {
return Config.env;
}

export function getChannelNames() {
return Object.keys(Config.channels || {});
}

export function getChannel(name: string) {
return Config.channels?.[name as keyof typeof Config.channels];
}

export class Config {
static appKey: SuperflareUserConfig["appKey"];

static env: any;

static ctx: ExecutionContext;

static database?: {
connections: SuperflareUserConfig["database"];
};

static storage?: {
disks: SuperflareUserConfig["storage"];
};

static queues?: SuperflareUserConfig["queues"];

static models?: {
[name: string]: any;
};

static jobs?: {
[name: string]: any;
};

static events?: {
[name: string]: any;
};

static listeners: Map<string, any[]> = new Map();

static channels: SuperflareUserConfig["channels"];
}

/**
* Accept a general set of request attributes in order to work
* with both Cloudflare Workers and Pages.
Expand All @@ -189,8 +67,5 @@ export type DefineConfigResult = SuperflareUserConfig;
export function defineConfig<Env = Record<string, any>>(
callback: (ctx: DefineConfigContext<Env>) => SuperflareUserConfig
): (ctx: DefineConfigContext<Env>) => DefineConfigResult {
return (ctx: DefineConfigContext<Env>) => {
setEnv(ctx.env);
return setConfig(callback(ctx));
};
return (ctx: DefineConfigContext<Env>) => callback(ctx);
}
Loading