Quick start
Add log and env to a framework, read a validated secret and assert on the log trace.
Log and env in four steps
This page is for a framework author. If you build on @moku-labs/web or another framework,
ctx.log and ctx.env are already there: you only configure them through pluginConfigs.
-
Install.
@moku-labs/coreis a peer dependency.install bun add @moku-labs/common @moku-labs/core -
Register both plugins. In
createCoreConfig, with their types in the third generic. -
Use them.
ctx.logandctx.envare on every plugin. -
Configure them per app. The app picks the providers, the variables and the log mode.
The code
import { envPlugin, logPlugin } from "@moku-labs/common";
import { createCoreConfig } from "@moku-labs/core";
type Config = { stage: "production" | "development" };
type Events = {};
export const coreConfig = createCoreConfig<Config, Events, [typeof logPlugin, typeof envPlugin]>(
"my-framework",
{
config: { stage: "production" },
plugins: [logPlugin, envPlugin]
}
);
export const { createPlugin, createCore } = coreConfig;- The tuple types
ctx.logandctx.env.
import { createPlugin } from "../config";
export const mailerPlugin = createPlugin("mailer", {
api: ctx => ({
send: (to: string) => {
const from = ctx.env.require("MAIL_FROM");
ctx.log.info("mailer:sent", { from, to });
}
})
});- Throws when the variable has no value.
- The name is
domain:actionby convention. The data is any value.
import { coreConfig, createCore } from "./config";
import { mailerPlugin } from "./plugins/mailer";
export const { createApp, createPlugin } = createCore(coreConfig, { plugins: [mailerPlugin] });import { dotenv, processEnv } from "@moku-labs/common";
import { createApp } from "./my-framework";
const app = createApp({
pluginConfigs: {
log: { mode: "silent" },
env: {
providers: [dotenv(), processEnv()],
schema: {
MAIL_FROM: { public: false, required: true },
PUBLIC_SITE_URL: { public: true, default: "https://example.com" }
}
}
}
});
app.mailer.send("[email protected]");
app.log.expect().toHaveEvent("mailer:sent", { to: "[email protected]" });
console.log(app.log.trace().map(entry => entry.event));
console.log(app.env.getPublic());- Records everything, prints nothing.
- The first provider with a value wins, so
.env.localbeats the shell. - Missing here means
createAppthrows. The app never starts half-configured. - Throws when the trace has no match.
MAIL_FROM=[email protected]Run
bun app.ts [ "mailer:sent" ] { PUBLIC_SITE_URL: "https://example.com", }
Remove MAIL_FROM from .env.local and createApp throws, before app.mailer.send ever runs.
Pick the providers for the target
The env plugin has no providers until you pass some, on every entry.
| Target | Providers | Import from |
|---|---|---|
| Node or Bun | [dotenv(), processEnv()] | @moku-labs/common |
| Browser | [browserEnv()] | @moku-labs/common/browser |
| Cloudflare Worker | [cloudflareBindings()], [workerSafeProcessEnv()] | @moku-labs/common |
ctx.logandctx.envare not on a core plugin's context. They reach regular plugins,app, and the app callbacks.- Pass the tuple. With
ConfigandEventswritten out, TypeScript stops inferring the third generic, andpluginsno longer compiles without it. productionis the default log mode. It printsinfoand above to the console. Usetestorsilentin tests.
Next
- How it works: the catalog, the entries, the order of a run.
- log plugin and env plugin: config, API, examples.
- Branded CLI: a console and prompts for a command-line tool.
- Write a framework: the rest of the package.