everything-dev
Version:
A consolidated product package for building Module Federation apps with oRPC APIs.
1,197 lines (1,195 loc) • 40.6 kB
JavaScript
import { buildRegistryConfigUrl, buildRegistryConfigUrlForNetwork, fetchBosConfigFromFastKv, fetchRemotePluginManifest, getRegistryNamespaceForAccount, getRegistryNamespaceForNetwork } from "./fastkv.mjs";
import { getNetworkIdForAccount } from "./network.mjs";
import { buildRuntimePluginsForConfig, findConfigPath, getHostDevelopmentPort, getProjectRoot, loadConfig, resolveLocalDevelopmentPath, writeResolvedConfig } from "./config.mjs";
import { createPlugin, z } from "./sdk.mjs";
import { bosContract } from "./contract.mjs";
import { writePluginSidebarGen } from "./sidebar.mjs";
import { computeSriHashForUrl } from "./integrity.mjs";
import { syncApiContractBridge } from "./api-contract.mjs";
import { buildRuntimeConfig, detectLocalPackages, prepareDevelopmentRuntimeConfig } from "./app.mjs";
import { ensureEnvFile, loadProjectEnv, writeGeneratedInfra } from "./cli/infra.mjs";
import { saveBosConfig } from "./utils/save-config.mjs";
import { buildInitPatterns, copyFilteredFiles, detectGitRemoteUrl, fetchParentConfig, generateDatabaseMigrations, personalizeConfig, removeInitLockfile, resolveSourceDir, runBunInstall, runTypesGen, scaffoldMinimalProject, stripOrphanedWorkspacesFromLockfile, writeInitSnapshot } from "./cli/init.mjs";
import { getStatus } from "./cli/status.mjs";
import { syncTemplate } from "./cli/sync.mjs";
import { upgradeTemplate } from "./cli/upgrade.mjs";
import { addFunctionCallAccessKey, ensureNearCli, executeTransaction } from "./near-cli.mjs";
import { buildDescription, buildServiceDescriptorMap } from "./service-descriptor.mjs";
import { syncAndGenerateSharedUi } from "./shared.mjs";
import { run } from "./utils/run.mjs";
import { existsSync, readFileSync, writeFileSync } from "node:fs";
import { basename, dirname, join, resolve } from "node:path";
import { EventEmitter } from "node:events";
import { access, readFile } from "node:fs/promises";
import process from "node:process";
import { Effect } from "effect";
//#region src/plugin.ts
const pluginEvents = new EventEmitter();
let pendingSession = null;
let pendingStartSummary = null;
function consumeDevSession() {
const data = pendingSession;
const summary = pendingStartSummary;
pendingSession = null;
pendingStartSummary = null;
if (!data) return null;
return summary ? {
...data,
summary
} : data;
}
async function timePhase(timings, name, fn) {
pluginEvents.emit("progress", {
phase: name,
status: "running"
});
const startedAt = Date.now();
try {
const result = await fn();
timings.push({
name,
durationMs: Date.now() - startedAt
});
pluginEvents.emit("progress", {
phase: name,
status: "done",
durationMs: Date.now() - startedAt
});
return result;
} catch (error) {
pluginEvents.emit("progress", {
phase: name,
status: "error",
durationMs: Date.now() - startedAt
});
throw error;
}
}
const buildCommands = {
host: {
cmd: "bun",
args: ["run", "build"]
},
ui: {
cmd: "bun",
args: ["run", "build"]
},
api: {
cmd: "bun",
args: ["run", "build"]
}
};
const PUBLISH_FUNCTION_NAMES = ["__fastdata_kv"];
function getPluginRef(entry) {
if (!entry || typeof entry === "string") return null;
return entry;
}
function parseSourceMode(value, defaultValue) {
if (value === "local" || value === "remote") return value;
return defaultValue;
}
function buildConfigResult(bosConfig) {
const packages = bosConfig ? Object.keys(bosConfig.app) : [];
return {
config: bosConfig,
packages,
remotes: packages.filter((name) => name !== "host")
};
}
async function fileExists(path) {
try {
await access(path);
return true;
} catch {
return false;
}
}
async function readJsonFile(path) {
return JSON.parse(await readFile(path, "utf8"));
}
function resolveWorkspaceTarget(key, bosConfig, runtimeConfig, configDir) {
if (bosConfig?.app && key in bosConfig.app) {
const appEntry = bosConfig.app[key];
const devPath = resolveLocalDevelopmentPath(appEntry?.development, configDir);
if (devPath) return {
key,
kind: "app",
path: devPath
};
return {
key,
kind: "app",
path: `${configDir}/${key}`
};
}
const pluginPath = (runtimeConfig?.plugins?.[key])?.localPath ?? resolveLocalDevelopmentPath(getPluginRef(bosConfig?.plugins?.[key])?.development, configDir);
if (pluginPath) return {
key,
kind: "plugin",
path: pluginPath
};
return null;
}
function isValidProxyUrl(url) {
try {
const parsed = new URL(url);
return parsed.protocol === "http:" || parsed.protocol === "https:";
} catch {
return false;
}
}
function resolveProxyUrl(bosConfig) {
if (!bosConfig) return null;
const apiConfig = bosConfig.app.api;
if (!apiConfig) return null;
if (apiConfig.proxy && isValidProxyUrl(apiConfig.proxy)) return apiConfig.proxy;
if (apiConfig.production && isValidProxyUrl(apiConfig.production)) return apiConfig.production;
return null;
}
function sanitizePluginKey(value) {
return value.replace(/[^A-Za-z0-9/_-]/g, "-").replace(/\/+/g, "/").split("/").filter(Boolean).map((segment) => segment.replace(/[^A-Za-z0-9_-]/g, "-")).join("/").replace(/^\/+|\/+$/g, "");
}
function defaultPluginKey(source) {
const normalized = source.replace(/^local:/, "").replace(/\/$/, "");
if (source.startsWith("local:")) return sanitizePluginKey(basename(normalized)) || "plugin";
try {
const url = new URL(source);
return sanitizePluginKey(basename(url.pathname) || url.hostname) || "plugin";
} catch {
return sanitizePluginKey(source) || "plugin";
}
}
function pluginLocalPath(configDir, attachment) {
const ref = getPluginRef(attachment);
const source = ref?.development ?? ref?.production;
if (!source?.startsWith("local:")) return null;
return join(configDir, source.slice(6));
}
function listPluginAttachments(config) {
return Object.entries(config?.plugins ?? {}).map(([key, attachment]) => {
const ref = getPluginRef(attachment);
return {
key,
development: ref?.development,
production: ref?.production,
localPath: ref?.development?.startsWith("local:") ? ref.development.slice(6) : void 0,
source: ref?.development?.startsWith("local:") ? "local" : "remote",
integrity: ref?.integrity,
version: ref?.version,
name: ref?.name
};
}).sort((a, b) => a.key.localeCompare(b.key));
}
async function generateCodeArtifacts(configDir, config, opts) {
if (opts?.env) writeResolvedConfig(configDir, config, opts.env, opts.extendsChain);
const runtimeConfig = opts?.runtimeConfig ?? (await loadConfig({ cwd: configDir }))?.runtime;
if (!runtimeConfig) return null;
writePluginSidebarGen(configDir, runtimeConfig);
const bridge = await syncApiContractBridge({
configDir,
runtimeConfig,
apiBaseUrl: runtimeConfig.api.url
});
return {
sidebarPath: join(configDir, "ui/src/lib/plugin-sidebar.gen.ts"),
resolvedConfigPath: opts?.env ? join(configDir, ".bos/bos.resolved-config.json") : void 0,
contractBridgePath: bridge.bridgePath
};
}
function extractPublishedUrl(output) {
const match = output.match(/https?:\/\/[^\s"'<>]+/g);
if (!match || match.length === 0) return null;
return match[match.length - 1] ?? null;
}
async function buildEveryPluginQuietly(cwd) {
if (!await fileExists(`${`${cwd}/packages/every-plugin`}/package.json`)) return;
if (await fileExists(`${cwd}/packages/every-plugin/dist/build/rspack/plugin.mjs`)) return;
const result = await run("bun", [
"run",
"--cwd",
"packages/every-plugin",
"build"
], {
cwd,
capture: true
});
if (result.exitCode === 0) {
console.log("[build:ssr] build succeeded");
return;
}
if (result.stdout.trim()) process.stdout.write(result.stdout);
if (result.stderr.trim()) process.stderr.write(result.stderr);
throw new Error(`bun run --cwd packages/every-plugin build failed with exit code ${result.exitCode}`);
}
async function buildEverythingDevQuietly(cwd) {
if (!await fileExists(`${`${cwd}/packages/everything-dev`}/package.json`)) return;
if (await fileExists(`${cwd}/packages/everything-dev/dist/index.mjs`)) return;
const result = await run("bun", [
"run",
"--cwd",
"packages/everything-dev",
"build"
], {
cwd,
capture: true
});
if (result.exitCode === 0) {
console.log("[everything-dev] build succeeded");
return;
}
if (result.stdout.trim()) process.stdout.write(result.stdout);
if (result.stderr.trim()) process.stderr.write(result.stderr);
throw new Error(`bun run --cwd packages/everything-dev build failed with exit code ${result.exitCode}`);
}
async function fetchPublishedConfig(accountId, gatewayId) {
try {
return await fetchBosConfigFromFastKv(`bos://${accountId}/${gatewayId}`);
} catch {
return null;
}
}
function selectWorkspaceTargets(packages, bosConfig) {
const allPackages = [...Object.keys(bosConfig?.app ?? {}), ...Object.keys(bosConfig?.plugins ?? {})];
if (packages === "all") return allPackages;
return packages.split(",").map((pkg) => pkg.trim()).filter((pkg) => allPackages.includes(pkg));
}
async function buildWorkspaceTargets(opts) {
const existing = [];
const skipped = [];
for (const target of opts.targets) {
const resolved = resolveWorkspaceTarget(target, opts.bosConfig, opts.runtimeConfig, opts.configDir);
if (!resolved) {
skipped.push(target);
continue;
}
if (await fileExists(`${resolved.path}/package.json`)) existing.push(resolved);
else skipped.push(target);
}
if (existing.length === 0) return {
built: [],
skipped
};
if ((await syncAndGenerateSharedUi({
configDir: opts.configDir,
hostMode: "local",
bosConfig: opts.bosConfig ?? void 0,
extendsChain: []
})).catalogChanged) await run("bun", ["install"], { cwd: opts.configDir });
if (existing.some((entry) => entry.key === "api")) await buildEveryPluginQuietly(opts.configDir);
await buildEverythingDevQuietly(opts.configDir);
const env = {
...process.env,
NODE_ENV: opts.deploy ? "production" : "development"
};
if (opts.deploy) env.DEPLOY = "true";
else delete env.DEPLOY;
const orderedExisting = opts.deploy ? [
...existing.filter((entry) => entry.kind === "app" && entry.key !== "host"),
...existing.filter((entry) => entry.kind === "plugin"),
...existing.filter((entry) => entry.kind === "app" && entry.key === "host")
] : existing;
const built = [];
for (const resolved of orderedExisting) {
const pkgJson = await readJsonFile(`${resolved.path}/package.json`);
const buildConfig = opts.deploy && pkgJson.scripts?.deploy ? {
cmd: "bun",
args: ["run", "deploy"]
} : buildCommands[resolved.key] ?? {
cmd: "bun",
args: ["run", "build"]
};
await run(buildConfig.cmd, buildConfig.args, {
cwd: resolved.path,
env
});
built.push(resolved.key);
}
return {
built,
skipped
};
}
var plugin_default = createPlugin({
variables: z.object({ configPath: z.string().optional() }),
secrets: z.object({}),
contract: bosContract,
initialize: (config) => Effect.promise(async () => {
const configResult = await loadConfig({ path: config.variables.configPath });
return {
bosConfig: configResult?.config ?? null,
runtimeConfig: configResult?.runtime ?? null,
configDir: getProjectRoot()
};
}),
shutdown: () => Effect.void,
createRouter: (deps, builder) => ({
config: builder.config.handler(async () => buildConfigResult(deps.bosConfig)),
pluginAdd: builder.pluginAdd.handler(async ({ input }) => {
if (!deps.bosConfig) return {
status: "error",
key: "",
error: "No bos.config.json found"
};
const isBosRef = input.source.startsWith("bos://");
const isLocal = input.source.startsWith("local:");
const key = sanitizePluginKey(input.as ?? (isBosRef ? input.source.split("/").pop() ?? "plugin" : defaultPluginKey(input.source)));
const existing = deps.bosConfig.plugins?.[key];
const existingEntry = existing && typeof existing === "object" ? existing : {};
const nextPlugins = { ...deps.bosConfig.plugins ?? {} };
if (isBosRef) nextPlugins[key] = {
...existingEntry,
extends: input.source
};
else if (isLocal) nextPlugins[key] = {
...existingEntry,
development: input.source,
...existingEntry.extends ? {} : {}
};
else nextPlugins[key] = {
...existingEntry,
production: input.production ?? input.source
};
deps.bosConfig = {
...deps.bosConfig,
plugins: nextPlugins
};
await saveBosConfig(deps.configDir, deps.bosConfig);
await generateCodeArtifacts(deps.configDir, deps.bosConfig);
const stored = deps.bosConfig.plugins?.[key];
const storedObj = stored && typeof stored === "object" ? stored : {};
return {
status: "added",
key,
development: storedObj.development,
production: storedObj.production,
integrity: storedObj.integrity,
version: storedObj.version
};
}),
pluginRemove: builder.pluginRemove.handler(async ({ input }) => {
if (!deps.bosConfig) return {
status: "error",
key: input.key,
error: "No bos.config.json found"
};
if (!deps.bosConfig.plugins?.[input.key]) return {
status: "error",
key: input.key,
error: `Plugin '${input.key}' is not configured`
};
const nextPlugins = { ...deps.bosConfig.plugins ?? {} };
delete nextPlugins[input.key];
deps.bosConfig = {
...deps.bosConfig,
plugins: Object.keys(nextPlugins).length > 0 ? nextPlugins : void 0
};
await saveBosConfig(deps.configDir, deps.bosConfig);
await generateCodeArtifacts(deps.configDir, deps.bosConfig);
return {
status: "removed",
key: input.key
};
}),
pluginList: builder.pluginList.handler(async () => {
return {
status: "listed",
plugins: listPluginAttachments(deps.bosConfig)
};
}),
pluginPublish: builder.pluginPublish.handler(async ({ input }) => {
if (!deps.bosConfig) return {
status: "error",
key: input.key,
error: "No bos.config.json found"
};
const attachment = deps.bosConfig.plugins?.[input.key];
if (!attachment) return {
status: "error",
key: input.key,
error: `Plugin '${input.key}' is not configured`
};
const attachmentRef = getPluginRef(attachment);
const localPath = pluginLocalPath(deps.configDir, attachment);
if (!localPath) return {
status: "error",
key: input.key,
error: `Plugin '${input.key}' does not have a local development path`
};
const pkgPath = join(localPath, "package.json");
if (!await fileExists(pkgPath)) return {
status: "error",
key: input.key,
error: `Missing package.json at ${localPath}`
};
const pkgJson = await readJsonFile(pkgPath);
const script = pkgJson.scripts?.deploy ? "deploy" : "build";
const { stdout, stderr, exitCode } = await run("bun", ["run", script], {
cwd: localPath,
capture: true
});
if (exitCode !== 0) {
if (stdout.trim()) process.stdout.write(stdout);
if (stderr.trim()) process.stderr.write(stderr);
return {
status: "error",
key: input.key,
error: `Publish failed with exit code ${exitCode}`
};
}
if (stdout.trim()) process.stdout.write(stdout);
if (stderr.trim()) process.stderr.write(stderr);
let publishedUrl = extractPublishedUrl(`${stdout}\n${stderr}`);
let manifest = null;
if (publishedUrl) manifest = await fetchRemotePluginManifest(publishedUrl);
else if (attachmentRef?.production) {
manifest = await fetchRemotePluginManifest(attachmentRef.production);
if (manifest) publishedUrl = attachmentRef.production;
}
const integrity = publishedUrl ? await computeSriHashForUrl(publishedUrl) : null;
const version = manifest?.plugin.version ?? pkgJson.version;
if (publishedUrl) {
const rootConfigPath = join(deps.configDir, "bos.config.json");
try {
const rootConfig = JSON.parse(readFileSync(rootConfigPath, "utf-8"));
if (!rootConfig.plugins || typeof rootConfig.plugins !== "object") rootConfig.plugins = {};
const plugins = rootConfig.plugins;
if (!plugins[input.key] || typeof plugins[input.key] !== "object") plugins[input.key] = {};
const entry = plugins[input.key];
entry.production = publishedUrl;
if (integrity) entry.integrity = integrity;
else delete entry.integrity;
writeFileSync(rootConfigPath, `${JSON.stringify(rootConfig, null, 2)}\n`);
console.log(` ✅ Updated bos.config.json: plugins.${input.key}.production`);
} catch (err) {
console.error(` ❌ Failed to update bos.config.json:`, err instanceof Error ? err.message : err);
}
await generateCodeArtifacts(deps.configDir, deps.bosConfig);
}
return {
status: "published",
key: input.key,
path: localPath,
script,
production: publishedUrl ?? attachmentRef?.production,
integrity: integrity ?? void 0,
version: version ?? void 0
};
}),
dev: builder.dev.handler(async ({ input }) => {
ensureEnvFile(deps.configDir);
loadProjectEnv(deps.configDir);
pluginEvents.emit("progress", {
phase: "config",
status: "running"
});
const localPackages = detectLocalPackages(deps.bosConfig ?? void 0, deps.runtimeConfig ?? void 0);
const hostSource = localPackages.includes("host") ? parseSourceMode(input.host, "local") : "remote";
const uiSource = localPackages.includes("ui") ? parseSourceMode(input.ui, "local") : "remote";
const apiSource = localPackages.includes("api") ? parseSourceMode(input.api, "local") : "remote";
const authSource = localPackages.includes("auth") ? parseSourceMode(input.auth, "local") : "remote";
const ssr = input.ssr ?? false;
const proxy = input.proxy ?? false;
if ((await syncAndGenerateSharedUi({
configDir: deps.configDir,
hostMode: hostSource,
bosConfig: deps.bosConfig ?? void 0,
extendsChain: []
})).catalogChanged) {
pluginEvents.emit("progress", {
phase: "install",
status: "running"
});
await run("bun", ["install"], { cwd: deps.configDir });
pluginEvents.emit("progress", {
phase: "install",
status: "done"
});
}
if (apiSource === "local" && !proxy || localPackages.some((pkg) => pkg.startsWith("plugin:"))) {
pluginEvents.emit("progress", {
phase: "build plugin",
status: "running"
});
await buildEveryPluginQuietly(deps.configDir);
pluginEvents.emit("progress", {
phase: "build plugin",
status: "done"
});
}
pluginEvents.emit("progress", {
phase: "build",
status: "running"
});
await buildEverythingDevQuietly(deps.configDir);
pluginEvents.emit("progress", {
phase: "build",
status: "done"
});
pluginEvents.emit("progress", {
phase: "config",
status: "done"
});
const refreshed = await loadConfig({ cwd: deps.configDir });
deps.bosConfig = refreshed?.config ?? deps.bosConfig;
deps.runtimeConfig = refreshed?.runtime ?? deps.runtimeConfig;
if (!deps.bosConfig) return {
status: "error",
description: "No bos.config.json found",
processes: []
};
if (proxy && !resolveProxyUrl(deps.bosConfig)) return {
status: "error",
description: "No valid proxy URL configured in bos.config.json",
processes: []
};
const hostPort = input.port ?? getHostDevelopmentPort(deps.bosConfig.app.host.development);
const runtimeConfig = await prepareDevelopmentRuntimeConfig(buildRuntimeConfig(deps.bosConfig, {
uiSource,
apiSource,
authSource,
hostSource,
env: "development",
plugins: deps.runtimeConfig?.plugins
}), {
hostPort,
ssr
});
await generateCodeArtifacts(deps.configDir, deps.bosConfig, {
env: "development",
extendsChain: refreshed?.source.extended,
runtimeConfig
});
const services = buildServiceDescriptorMap(runtimeConfig, {
ssr,
proxy
});
const packages = [...services.keys()];
const displayEnv = {};
if (services.get("api")?.proxy) {
const proxyUrl = resolveProxyUrl(deps.bosConfig);
if (proxyUrl) displayEnv.API_PROXY = proxyUrl;
}
const orchestrator = {
packages,
env: displayEnv,
description: buildDescription(services),
port: runtimeConfig.host.port,
interactive: input.interactive
};
pendingSession = {
orchestrator,
services,
runtimeConfig
};
return {
status: "started",
description: orchestrator.description,
processes: packages
};
}),
start: builder.start.handler(async ({ input }) => {
ensureEnvFile(deps.configDir);
loadProjectEnv(deps.configDir);
pluginEvents.emit("progress", {
phase: "config",
status: "running"
});
const account = input.account ?? process.env.BOS_ACCOUNT;
const domain = input.domain ?? process.env.BOS_GATEWAY;
let config = null;
let remoteConfig = null;
if (account && domain) {
remoteConfig = await fetchPublishedConfig(account, domain);
if (remoteConfig) config = remoteConfig;
else console.warn(`[Start] Failed to fetch remote config for ${account}/${domain}, falling back to local bos.config.json`);
}
if (!config) config = deps.bosConfig;
if (!config) return {
status: "error",
url: "",
error: "No configuration found. Set BOS_ACCOUNT and BOS_GATEWAY environment variables, or provide a local bos.config.json."
};
if (account) config = {
...config,
account
};
if (domain) config = {
...config,
domain
};
const port = input.port ?? getHostDevelopmentPort(config.app.host.development);
const isStaging = input.env === "staging";
const runtimePlugins = await buildRuntimePluginsForConfig(config, deps.configDir, "production");
const runtimeConfig = buildRuntimeConfig(config, {
uiSource: "remote",
apiSource: "remote",
authSource: "remote",
hostSource: "remote",
env: "production",
plugins: runtimePlugins
});
pluginEvents.emit("progress", {
phase: "generate artifacts",
status: "running"
});
await generateCodeArtifacts(deps.configDir, config, {
env: "production",
runtimeConfig
});
pluginEvents.emit("progress", {
phase: "generate artifacts",
status: "done"
});
const productionEnv = {};
const warnings = [];
if (!process.env.CORS_ORIGIN && config.domain) {
const defaultOrigin = `https://${config.domain}`;
productionEnv.CORS_ORIGIN = defaultOrigin;
warnings.push(`CORS_ORIGIN defaulting to ${defaultOrigin}`);
}
const requiredSecrets = /* @__PURE__ */ new Set();
const missingSecrets = [];
if (runtimeConfig.auth?.secrets) for (const s of runtimeConfig.auth.secrets) requiredSecrets.add(s);
if (runtimeConfig.api?.secrets) for (const s of runtimeConfig.api.secrets) requiredSecrets.add(s);
for (const plugin of Object.values(runtimeConfig.plugins ?? {})) if (plugin.secrets) for (const s of plugin.secrets) requiredSecrets.add(s);
for (const secret of requiredSecrets) {
const value = process.env[secret];
if (!value || value.length === 0) missingSecrets.push(secret);
}
if (missingSecrets.length > 0) warnings.push(`Missing ${missingSecrets.length} secret(s): ${missingSecrets.join(", ")}`);
const services = buildServiceDescriptorMap(runtimeConfig);
const stagingEnvVars = isStaging ? { BOS_GATEWAY: config.staging?.domain ?? config.domain ?? "" } : {};
const summary = {
configSource: remoteConfig ? `bos://${account}/${domain}` : findConfigPath() ?? "bos.config.json",
configSourceHttp: remoteConfig && account && domain ? buildRegistryConfigUrl(account, domain) : void 0,
account: config.account,
domain: config.domain ?? void 0,
modules: {
host: runtimeConfig.host.remoteUrl ?? runtimeConfig.host.url ?? "local",
ui: runtimeConfig.ui.url ?? "local",
api: runtimeConfig.api.url ?? "local",
auth: runtimeConfig.auth?.url ?? void 0
},
warnings
};
pendingSession = {
orchestrator: {
packages: ["host"],
env: {
NODE_ENV: "production",
...productionEnv,
...stagingEnvVars
},
description: `${isStaging ? "Staging" : "Production"} Mode (${config.account})`,
port,
interactive: input.interactive,
noLogs: true
},
services,
runtimeConfig
};
pendingStartSummary = summary;
pluginEvents.emit("progress", {
phase: "config",
status: "done"
});
return {
status: "running",
url: `http://localhost:${port}`
};
}),
build: builder.build.handler(async ({ input }) => {
if (!deps.bosConfig) return {
status: "error",
built: [],
skipped: []
};
const buildEnv = input.deploy ? "production" : "development";
const targets = selectWorkspaceTargets(input.packages, deps.bosConfig);
if (targets.length === 0) return {
status: "error",
built: [],
skipped: []
};
const runtimeConfig = buildRuntimeConfig(deps.bosConfig, {
uiSource: deps.bosConfig.app.ui?.development ? "local" : "remote",
apiSource: deps.bosConfig.app.api?.development ? "local" : "remote",
authSource: deps.bosConfig.app.auth?.development ? "local" : "remote",
hostSource: deps.bosConfig.app.host?.development ? "local" : "remote",
env: buildEnv,
plugins: deps.runtimeConfig?.plugins
});
await generateCodeArtifacts(deps.configDir, deps.bosConfig, {
env: buildEnv,
runtimeConfig
});
const { built, skipped } = await buildWorkspaceTargets({
configDir: deps.configDir,
bosConfig: deps.bosConfig,
runtimeConfig,
targets,
deploy: input.deploy
});
if (built.length === 0) return {
status: "error",
built: [],
skipped
};
return {
status: "success",
built,
skipped,
deployed: input.deploy
};
}),
publish: builder.publish.handler(async ({ input }) => {
if (!deps.bosConfig) return {
status: "error",
registryUrl: "",
error: "No bos.config.json found"
};
const account = deps.bosConfig.account;
const gateway = deps.bosConfig.domain;
if (!gateway) return {
status: "error",
registryUrl: "",
error: "bos.config.json must define domain to publish"
};
const network = input.network ?? getNetworkIdForAccount(account);
const bosUrl = `bos://${account}/${gateway}`;
const registryUrl = buildRegistryConfigUrlForNetwork(network, account, gateway);
const targets = selectWorkspaceTargets(input.packages, deps.bosConfig);
let publishConfig = deps.bosConfig;
let built;
let skipped;
if (input.dryRun) return {
status: "dry-run",
registryUrl,
built,
skipped
};
if (input.deploy) {
await generateCodeArtifacts(deps.configDir, deps.bosConfig, {
env: "production",
runtimeConfig: deps.runtimeConfig ?? void 0
});
const result = await buildWorkspaceTargets({
configDir: deps.configDir,
bosConfig: deps.bosConfig,
runtimeConfig: deps.runtimeConfig,
targets,
deploy: true
});
built = result.built;
skipped = result.skipped;
const refreshed = await loadConfig({ cwd: deps.configDir });
if (refreshed?.config) {
deps.bosConfig = refreshed.config;
deps.runtimeConfig = refreshed.runtime;
publishConfig = refreshed.config;
}
}
const registryEntries = { [`apps/${account}/${gateway}/bos.config.json`]: JSON.stringify(publishConfig) };
const payload = JSON.stringify(registryEntries);
const argsBase64 = Buffer.from(payload).toString("base64");
const privateKey = input.privateKey || process.env.NEAR_PRIVATE_KEY || process.env.BOS_NEAR_PRIVATE_KEY;
try {
await Effect.runPromise(ensureNearCli);
let txHash;
try {
txHash = (await Effect.runPromise(executeTransaction({
account,
contract: getRegistryNamespaceForNetwork(network),
method: "__fastdata_kv",
argsBase64,
network,
privateKey,
gas: "300Tgas",
deposit: "0NEAR"
}))).txHash;
} catch (error) {
txHash = extractTransactionHash(error);
if (!txHash) throw error;
try {
const verifiedConfig = await fetchBosConfigFromFastKv(bosUrl);
if (JSON.stringify(verifiedConfig) !== JSON.stringify(publishConfig)) throw error;
} catch {}
}
return {
status: "published",
registryUrl,
txHash,
built,
skipped
};
} catch (error) {
return {
status: "error",
registryUrl,
error: error instanceof Error ? error.message : "Unknown error",
built,
skipped
};
}
}),
keyPublish: builder.keyPublish.handler(async ({ input }) => {
if (!deps.bosConfig) return {
status: "error",
account: "",
network: "mainnet",
contract: "",
allowance: input.allowance,
functionNames: PUBLISH_FUNCTION_NAMES,
error: "No bos.config.json found"
};
const account = deps.bosConfig.account;
const network = getNetworkIdForAccount(account);
const contract = getRegistryNamespaceForAccount(account);
try {
await Effect.runPromise(ensureNearCli);
const keyPair = await addFunctionCallAccessKey({
account,
contract,
allowance: input.allowance,
functionNames: PUBLISH_FUNCTION_NAMES,
network
});
return {
status: "published",
account,
network,
contract,
allowance: input.allowance,
functionNames: PUBLISH_FUNCTION_NAMES,
publicKey: keyPair.publicKey,
privateKey: keyPair.privateKey
};
} catch (error) {
return {
status: "error",
account,
network,
contract,
allowance: input.allowance,
functionNames: PUBLISH_FUNCTION_NAMES,
error: error instanceof Error ? error.message : "Unknown error"
};
}
}),
init: builder.init.handler(async ({ input }) => {
try {
const timings = [];
let extendsAccount = "";
let extendsGateway = "";
let directory = input.directory;
const account = input.account;
const domain = input.domain;
let overrides = input.overrides;
let plugins = input.plugins;
if (input.extends) {
const match = (input.extends.startsWith("bos://") ? input.extends : `bos://${input.extends}`).match(/^bos:\/\/([^/]+)\/(.+)$/);
if (match) {
extendsAccount = match[1];
extendsGateway = match[2];
}
}
extendsAccount = extendsAccount || "dev.everything.near";
extendsGateway = extendsGateway || "everything.dev";
let parentPluginKeys = [];
let parentConfig = null;
try {
parentConfig = await timePhase(timings, "parent config", () => fetchParentConfig(extendsAccount, extendsGateway));
if (parentConfig?.plugins && typeof parentConfig.plugins === "object") parentPluginKeys = Object.keys(parentConfig.plugins);
} catch {}
overrides = overrides?.length ? overrides : ["ui", "api"];
if (overrides.includes("plugins") && plugins === void 0) plugins = parentPluginKeys;
plugins = plugins ?? [];
directory = directory || domain || extendsGateway;
const targetDir = resolve(directory);
const extendsRef = `bos://${extendsAccount}/${extendsGateway}`;
const repository = await detectGitRemoteUrl(process.cwd()).catch(() => void 0) ?? parentConfig?.repository;
if (!parentConfig) try {
parentConfig = await timePhase(timings, "parent config", () => fetchParentConfig(extendsAccount, extendsGateway));
} catch {
return {
status: "error",
directory,
extendsRef,
account,
domain,
extends: extendsRef,
plugins,
overrides,
filesCopied: 0,
timings,
error: `No config found at ${extendsRef} — are you sure this is the right parent?`
};
}
const { sourceDir, parentConfig: resolvedParentConfig, cleanup } = await timePhase(timings, "template source", () => resolveSourceDir({
extendsAccount,
extendsGateway,
source: input.source
}));
parentConfig = resolvedParentConfig;
const isMinimalScaffold = sourceDir === "";
try {
let filesCopied;
if (isMinimalScaffold) {
filesCopied = await timePhase(timings, "scaffold project", () => scaffoldMinimalProject(targetDir, parentConfig, {
extendsAccount,
extendsGateway,
account: account || extendsAccount,
domain,
plugins,
overrides,
repository,
title: parentConfig?.title,
description: parentConfig?.description
}));
await timePhase(timings, "personalize config", () => personalizeConfig(targetDir, {
extendsAccount,
extendsGateway,
account: account || extendsAccount,
domain: domain || extendsGateway,
plugins,
overrides,
mode: "init",
repository,
title: parentConfig?.title,
description: parentConfig?.description,
testnet: parentConfig?.testnet,
staging: parentConfig?.staging
}));
} else {
const patterns = buildInitPatterns(overrides, plugins);
filesCopied = await timePhase(timings, "copy files", () => copyFilteredFiles(sourceDir, targetDir, patterns, {
overrides,
plugins
}));
await timePhase(timings, "personalize config", () => personalizeConfig(targetDir, {
extendsAccount,
extendsGateway,
account: account || extendsAccount,
domain: domain || extendsGateway,
plugins,
overrides,
workspaceOpts: { sourceDir },
repository,
title: parentConfig?.title,
description: parentConfig?.description,
testnet: parentConfig?.testnet,
staging: parentConfig?.staging
}));
await timePhase(timings, "write snapshot", () => writeInitSnapshot(targetDir, extendsAccount, extendsGateway, sourceDir, patterns, {
overrides,
plugins
}));
}
const lockfilePath = join(targetDir, "bun.lock");
stripOrphanedWorkspacesFromLockfile(lockfilePath, computeAllowedWorkspaces(overrides, plugins));
removeInitLockfile(lockfilePath);
const initConfig = await timePhase(timings, "resolve config", () => loadConfig({ cwd: targetDir }));
if (initConfig?.runtime) await timePhase(timings, "generate env/docker", async () => {
writeGeneratedInfra(targetDir, initConfig.runtime);
});
await timePhase(timings, "create env file", async () => {
ensureEnvFile(targetDir);
});
if (!input.noInstall) {
await timePhase(timings, "install dependencies", () => runBunInstall(targetDir));
await timePhase(timings, "generate types", () => runTypesGen(targetDir));
await timePhase(timings, "generate migrations", () => generateDatabaseMigrations(targetDir));
}
if (input.noInstall && initConfig?.config) await timePhase(timings, "generate code artifacts", () => generateCodeArtifacts(targetDir, initConfig.config));
return {
status: "initialized",
directory,
extendsRef,
account,
domain,
extends: extendsRef,
plugins,
overrides,
filesCopied,
timings,
targetDir
};
} finally {
await cleanup();
}
} catch (error) {
const extendsRef = input.extends ? input.extends.startsWith("bos://") ? input.extends : `bos://${input.extends}` : "bos://dev.everything.near/everything.dev";
return {
status: "error",
directory: input.directory ?? "",
extendsRef,
account: input.account,
domain: input.domain,
extends: extendsRef,
plugins: input.plugins ?? [],
overrides: input.overrides,
filesCopied: 0,
timings: [],
error: error instanceof Error ? error.message : "Unknown error"
};
}
}),
sync: builder.sync.handler(async ({ input }) => {
try {
const configPath = findConfigPath();
if (!configPath) return {
status: "error",
updated: [],
skipped: [],
added: [],
error: "No bos.config.json found in current directory"
};
const projectDir = resolve(dirname(configPath));
const result = await syncTemplate(projectDir, input);
if (result.status === "synced" || result.status === "dry-run") {
const syncedConfig = await loadConfig({ cwd: projectDir });
if (syncedConfig?.config) await generateCodeArtifacts(projectDir, syncedConfig.config);
}
return result;
} catch (error) {
return {
status: "error",
updated: [],
skipped: [],
added: [],
error: error instanceof Error ? error.message : "Unknown error"
};
}
}),
upgrade: builder.upgrade.handler(async ({ input }) => {
try {
const configPath = findConfigPath();
if (!configPath) return {
status: "error",
packages: [],
error: "No bos.config.json found in current directory"
};
return await upgradeTemplate(resolve(dirname(configPath)), input);
} catch (error) {
return {
status: "error",
packages: [],
error: error instanceof Error ? error.message : "Unknown error"
};
}
}),
typesGen: builder.typesGen.handler(async ({ input }) => {
try {
const configPath = findConfigPath();
if (!configPath) return {
status: "error",
generated: [],
fetched: [],
skipped: [],
failed: [],
error: "No bos.config.json found in current directory"
};
const projectDir = resolve(dirname(configPath));
const refreshed = await loadConfig({
cwd: projectDir,
env: input.env ?? (process.env.NODE_ENV === "production" ? "production" : "development")
});
if (!refreshed) return {
status: "error",
generated: [],
fetched: [],
skipped: [],
failed: [],
error: "Failed to load bos.config.json"
};
if (input.dryRun) {
const pluginEntries = Object.entries(refreshed.runtime.plugins ?? {});
const fetched = [];
const skipped = [];
if (refreshed.runtime.api.source !== "local") fetched.push(refreshed.runtime.api.url);
else skipped.push("api (local)");
if (refreshed.runtime.auth) if (refreshed.runtime.auth.source !== "local") fetched.push(refreshed.runtime.auth.url);
else skipped.push("auth (local)");
for (const [key, plugin] of pluginEntries) if (plugin.url && plugin.source !== "local") fetched.push(plugin.url);
else if (plugin.localPath) skipped.push(`${key} (local)`);
const generated = [
"ui/src/lib/api-types.gen.ts",
"ui/src/lib/auth-types.gen.ts",
"api/src/lib/plugins-types.gen.ts",
"api/src/lib/auth-types.gen.ts"
];
if (existsSync(join(projectDir, "host", "src"))) generated.push("host/src/lib/auth-types.gen.ts");
return {
status: "success",
generated,
fetched,
skipped,
failed: [],
source: refreshed.runtime.api.source
};
}
await generateCodeArtifacts(projectDir, refreshed.config, { runtimeConfig: refreshed.runtime });
const generated = [
"ui/src/lib/plugin-sidebar.gen.ts",
"ui/src/lib/api-types.gen.ts",
"api/src/lib/plugins-types.gen.ts",
"api/src/lib/auth-types.gen.ts"
];
if (refreshed.runtime.auth && (refreshed.runtime.auth.source !== "local" || refreshed.runtime.auth.localPath)) generated.push("ui/src/lib/auth-types.gen.ts");
if (existsSync(join(projectDir, "host", "src"))) generated.push("host/src/lib/auth-types.gen.ts");
return {
status: "success",
generated,
fetched: refreshed.runtime.api.source === "remote" ? [refreshed.runtime.api.url] : [],
skipped: refreshed.runtime.api.source === "local" ? ["api (local)"] : [],
failed: [],
source: refreshed.runtime.api.source
};
} catch (error) {
return {
status: "error",
generated: [],
fetched: [],
skipped: [],
failed: [],
error: error instanceof Error ? error.message : "Unknown error"
};
}
}),
status: builder.status.handler(async () => {
try {
const configPath = findConfigPath();
if (!configPath) return {
status: "error",
packages: [],
envFile: "missing",
error: "No bos.config.json found in current directory"
};
return await getStatus(resolve(dirname(configPath)));
} catch (error) {
return {
status: "error",
packages: [],
envFile: "missing",
error: error instanceof Error ? error.message : "Unknown error"
};
}
})
})
});
function extractTransactionHash(error) {
return (error instanceof Error ? error.message : String(error)).match(/Transaction ID:\s*([A-Za-z0-9]+)/i)?.[1];
}
function computeAllowedWorkspaces(overrides, plugins) {
const workspaces = [];
for (const section of overrides) {
if (section === "host") workspaces.push("host");
if (section === "ui") workspaces.push("ui");
if (section === "api") workspaces.push("api");
}
if (plugins && plugins.length > 0) workspaces.push("plugins/*");
return workspaces;
}
//#endregion
export { consumeDevSession, plugin_default as default, pluginEvents };
//# sourceMappingURL=plugin.mjs.map