everything-dev
Version:
A consolidated product package for building Module Federation apps with oRPC APIs.
708 lines (706 loc) • 28.7 kB
JavaScript
const require_runtime = require('./_virtual/_rolldown/runtime.cjs');
const require_http_client = require('./http-client.cjs');
const require_merge = require('./merge.cjs');
const require_fastkv = require('./fastkv.cjs');
const require_network = require('./network.cjs');
const require_version_manifest_resolve = require('./version-manifest-resolve.cjs');
const require_config = require('./config.cjs');
const require_theme = require('./utils/theme.cjs');
const require_resolution_session = require('./resolution/session.cjs');
const require_fingerprint = require('./fingerprint.cjs');
const require_integrity = require('./integrity.cjs');
const require_timing = require('./cli/timing.cjs');
const require_string = require('./utils/string.cjs');
const require_build = require('./build.cjs');
const require_code_artifacts = require('./code-artifacts.cjs');
const require_auth_session = require('./auth-session.cjs');
const require_cdn_deploy = require('./cdn-deploy.cjs');
const require_delegate_signer = require('./delegate-signer.cjs');
const require_rollback = require('./rollback.cjs');
const require_deploy_diff = require('./deploy-diff.cjs');
const require_near_signer = require('./near-signer.cjs');
const require_platform_deploy = require('./platform-deploy.cjs');
const require_storage_upload = require('./storage-upload.cjs');
const require_version_manifest_deploy = require('./version-manifest-deploy.cjs');
let node_fs = require("node:fs");
let node_path = require("node:path");
let node_process = require("node:process");
node_process = require_runtime.__toESM(node_process, 1);
let every_plugin_build_ui = require("every-plugin/build/ui");
//#region src/publish.ts
function sleep(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
async function pinWorkspaceVersionManifest(input) {
if (!input.report) return void 0;
const versionManifest = require_version_manifest_deploy.composeWorkspaceVersionManifest({
report: input.report,
ssrReport: input.ssrReport,
integrityMap: input.integrityMap,
...input.files ? { files: input.files } : {}
});
if (!versionManifest) return void 0;
const manifestFile = `versions/${versionManifest.version}.json`;
const manifestUpload = await require_storage_upload.uploadBundle({
origin: input.origin,
apiKey: input.apiKey,
account: input.account,
gateway: input.gateway,
workspace: input.workspace,
files: [{
path: manifestFile,
bytes: new TextEncoder().encode(`${JSON.stringify(versionManifest, null, 2)}\n`)
}]
});
const manifestIntegrity = manifestUpload.integrity[manifestFile];
if (!manifestIntegrity) return void 0;
console.log(` ${require_theme.colors.dim(`${require_string.padRight("", 28)} version manifest ${manifestFile} (SRI pinned)`)}`);
return {
file: manifestFile,
integrity: manifestIntegrity,
storage: manifestUpload.storage
};
}
/**
* One workspace's upload leg with skip-unchanged diffing: resolve the previous
* manifest's `files` map, hash the local dist, upload only new/changed files,
* and return the merged per-file SRI map for the version manifest. Any failure
* to resolve the previous map degrades to a full upload.
*/
async function diffUploadWorkspace(input) {
const localSri = require_deploy_diff.localSriMap(input.files);
let uploadFiles = input.files;
let prevFiles;
if (input.previousPin) try {
const resolved = await require_version_manifest_resolve.resolveSlotVersion({
base: input.base,
pin: input.previousPin
});
if (resolved.files) {
const diff = require_deploy_diff.diffWorkspaceFiles(input.files, localSri, resolved.files);
prevFiles = resolved.files;
uploadFiles = diff.upload;
if (diff.skipped > 0) console.log(` ${require_theme.colors.dim(`${require_string.padRight(input.workspace, 28)} ${diff.skipped} unchanged file(s) — skipping (manifest diff)`)}`);
}
} catch {
console.log(` ${require_theme.colors.dim(`${require_string.padRight(input.workspace, 28)} previous manifest unusable — uploading everything`)}`);
}
console.log(` ${require_string.padRight(input.workspace, 28)} uploading ${uploadFiles.length} of ${input.files.length} files (${formatBundleMb(uploadFiles)}) → ${input.base}`);
const result = uploadFiles.length > 0 ? await require_storage_upload.uploadWorkspaceDist({
origin: input.origin,
apiKey: input.apiKey,
account: input.account,
gateway: input.gateway,
workspace: input.workspace,
files: uploadFiles
}) : {
stored: 0,
totalBytes: 0,
integrity: {},
storage: void 0
};
return {
result,
fileIntegrity: require_deploy_diff.mergedFileIntegrity(input.files, result.integrity, prevFiles),
uploaded: result.stored,
skipped: input.files.length - uploadFiles.length,
total: input.files.length
};
}
function formatBundleMb(files) {
return `${(files.reduce((sum, file) => sum + file.bytes.byteLength, 0) / 1024 / 1024).toFixed(1)} MB`;
}
async function waitForPublishedConfig(opts) {
const envTimeoutMs = Number(node_process.default.env.BOS_PUBLISH_CONFIRMATION_TIMEOUT_MS);
const envIntervalMs = Number(node_process.default.env.BOS_PUBLISH_CONFIRMATION_INTERVAL_MS);
const timeoutMs = opts.timeoutMs ?? (Number.isFinite(envTimeoutMs) ? envTimeoutMs : void 0) ?? 12e4;
const intervalMs = opts.intervalMs ?? (Number.isFinite(envIntervalMs) ? envIntervalMs : void 0) ?? 3e3;
const startedAt = Date.now();
let lastError;
while (Date.now() - startedAt < timeoutMs) {
try {
const verifiedConfig = await require_fastkv.fetchBosConfigFromFastKv(`bos://${opts.account}/${opts.gateway}`, opts.registry);
if (JSON.stringify(verifiedConfig) === JSON.stringify(opts.publishConfig)) return;
} catch (error) {
lastError = error;
}
await sleep(intervalMs);
}
const reason = lastError instanceof Error ? ` Last error: ${lastError.message}` : "";
throw new Error(`Timed out waiting for publish confirmation at bos://${opts.account}/${opts.gateway}.${reason}`);
}
async function isConfigAlreadyPublished(opts) {
try {
const current = await require_fastkv.fetchBosConfigFromFastKv(`bos://${opts.account}/${opts.gateway}`, opts.registry);
return JSON.stringify(current) === JSON.stringify(opts.publishConfig);
} catch {
return false;
}
}
const FRAMEWORK_PACKAGE_NAMES = [
"better-near-auth",
"every-plugin",
"everything-dev"
];
/**
* Whether npm serves `name@version` (true), definitively does not (404 —
* false), or whether publishability could not be determined (network error,
* unexpected status — null; callers must not block on that).
*/
async function npmVersionPublished(name, version) {
try {
const res = await require_http_client.fetchResponse(`https://registry.npmjs.org/${name}/${version}`, { headers: { Accept: "application/json" } });
if (res.status === 200) return true;
if (res.status === 404) return false;
return null;
} catch {
return null;
}
}
/**
* The npm-publish gate: a workspace that vendors the framework packages'
* sources must not ship bundles ahead of npm — the update check, child
* scaffolds (`bos init`), and template syncs all read those versions from
* the registry. Reads only apply when the sources are workspace-local (the
* parent repo); child projects install the framework from npm and are
* inherently in sync.
*/
async function findUnpublishedFrameworkPackages(configDir) {
const unpublished = [];
for (const name of FRAMEWORK_PACKAGE_NAMES) {
const manifestPath = (0, node_path.join)(configDir, "packages", name, "package.json");
if (!(0, node_fs.existsSync)(manifestPath)) continue;
const version = JSON.parse((0, node_fs.readFileSync)(manifestPath, "utf-8")).version;
if (!version) continue;
const published = await npmVersionPublished(name, version);
if (published === false) unpublished.push({
name,
version
});
else if (published === null) console.log(require_theme.colors.yellow(` ! Could not verify ${name}@${version} on npm (registry unreachable) — continuing`));
}
return unpublished;
}
async function preflightPublish(input) {
const { configDir } = input;
const bosConfig = input.bosConfig;
const isStaging = input.env === "staging";
const account = isStaging ? bosConfig.staging?.account ?? bosConfig.account : bosConfig.account;
const gateway = isStaging ? bosConfig.staging?.domain ?? bosConfig.domain : bosConfig.domain;
if (!gateway) return {
kind: "error",
registryUrl: "",
error: "authored config must define domain to publish"
};
const network = input.network ?? require_network.getNetworkIdForAccount(account);
const registryUrl = require_fastkv.buildRegistryConfigUrlForNetwork(network, account, gateway, input.registry);
const fail = (error) => ({
kind: "error",
registryUrl,
error
});
const publishAuth = bosConfig.publish?.auth;
if ((publishAuth === "session" || input.wallet) && !input.privateKey) {
const session = require_auth_session.readSessionHandle(configDir);
if (!session?.credential) return fail((input.wallet ? "--wallet requires" : "the authored config sets publish.auth = \"session\", but") + " no CLI session is stored in .bos/ for this project. Run bos login to create one.");
if (session.credential.accountId && session.credential.accountId !== account) return fail(`The CLI session was created for ${session.credential.accountId}, but the configured account is ${account}. Gasless wallet publish relays the FastKV write under the session's NEAR account. Run bos login again under the matching account.`);
}
if (publishAuth && !input.privateKey) {
if (publishAuth === "custody") return fail("the authored config sets publish.auth = \"custody\", but custody publish is not implemented yet (see NEARBuilders/everything-dev#291).");
}
if (input.dryRun) return {
kind: "dry-run",
registryUrl
};
const useWallet = input.wallet === true;
let strategy;
if (useWallet) console.log(` Signing via ${require_theme.colors.cyan("gasless NEP-366 delegate action (relayed by the platform relayer)")}`);
else try {
strategy = await require_near_signer.resolveSigningStrategy({
privateKey: input.privateKey,
account,
network
});
console.log(` Signing via ${require_theme.colors.cyan(require_near_signer.describeSigningStrategy(strategy))}`);
} catch (error) {
return fail(error instanceof Error ? error.message : "Unknown error");
}
const session = require_auth_session.readSessionHandle(configDir);
let cdnDeploy = {
cdnOrigin: void 0,
storageOrigin: "",
apiKey: void 0
};
if (input.build) {
const unpublished = await findUnpublishedFrameworkPackages(configDir);
if (unpublished.length > 0) return fail(`Framework package(s) ${unpublished.map((pkg) => `${pkg.name}@${pkg.version}`).join(", ")} are not published to npm yet — deploys must not ship bundles ahead of the registry (child scaffolds and update checks read these versions). Re-run the Release workflow (or \`pnpm run release\`) first.`);
cdnDeploy = require_cdn_deploy.resolveCdnDeployInputs({
env: node_process.default.env,
bosConfig,
session: session?.credential ?? null,
account,
gateway,
uploadsPlanned: input.build
});
if (cdnDeploy.error) return fail(cdnDeploy.error);
if (cdnDeploy.warning) console.log(require_theme.colors.yellow(` ! ${cdnDeploy.warning}`));
if (!cdnDeploy.cdnOrigin) return fail("CDN deploy resolved no bundle origin");
const probeError = await require_cdn_deploy.probeStorageOrigin(cdnDeploy.storageOrigin);
if (probeError) return fail(`The bundle upload origin ${cdnDeploy.storageOrigin} is not running the platform API — ${probeError}. Check BOS_STORAGE_ORIGIN / the bos login session, then re-run.`);
}
let publishedConfig = null;
let publishedConfigState = "missing";
try {
publishedConfig = await require_fastkv.fetchBosConfigFromFastKv(registryUrl, input.registry);
publishedConfigState = "fetched";
} catch (error) {
if ((error instanceof Error ? error.message : String(error)).startsWith("No config found")) console.log(require_theme.colors.dim(" Note: no published config found yet (first publish) — continuing"));
else {
publishedConfigState = "unreachable";
console.log(require_theme.colors.dim(" Note: registry read failed — continuing"));
}
}
return {
kind: "ready",
plan: {
isStaging,
account,
gateway,
network,
registryUrl,
registryNamespace: require_fastkv.getRegistryNamespaceForNetwork(network, input.registry),
targets: require_build.selectWorkspaceTargets(input.packages, bosConfig),
useWallet,
strategy,
cdnOrigin: cdnDeploy.cdnOrigin,
storageOrigin: cdnDeploy.storageOrigin,
storageApiKey: cdnDeploy.apiKey,
publishedConfig,
publishedConfigState
}
};
}
/**
* The deploy-train publish core (ADR 0020, as amended): preflight
* (storage/CDN creds + signing) fails fast before the build, then build →
* upload → version-manifest pin → config write-back → FastKV publish +
* read-back. Scoped to one plugin via `packages`, a single-plugin redeploy
* ships real bytes to the storage origin — same as the full train, nothing
* image-native left.
*/
async function publishToFastKv(input) {
const preflight = await preflightPublish(input);
if (preflight.kind === "error") return {
status: "error",
registryUrl: preflight.registryUrl,
error: preflight.error
};
if (preflight.kind === "dry-run") return {
status: "dry-run",
registryUrl: preflight.registryUrl
};
const plan = preflight.plan;
const { isStaging, account, gateway, network, registryUrl, registryNamespace, targets, useWallet, strategy } = plan;
const storageOrigin = plan.storageOrigin;
const storageApiKey = plan.storageApiKey;
const { configDir } = input;
let bosConfig = input.bosConfig;
const runtimeConfig = input.runtimeConfig;
let built;
let skipped;
let deployResults;
let refreshedRawConfig = null;
if (input.build) {
await require_code_artifacts.generateCodeArtifacts(configDir, bosConfig, {
env: "production",
runtimeConfig: runtimeConfig ?? void 0
});
const result = await require_build.buildWorkspaceTargets({
configDir,
bosConfig,
runtimeConfig,
targets,
deploy: true,
verbose: input.verbose
});
built = result.built;
skipped = result.skipped;
deployResults = result.deployResults;
if (deployResults) {
const failures = deployResults.filter((r) => !r.success);
if (failures.length > 0) {
const total = deployResults.length;
console.log();
console.log(require_theme.colors.error(` ${require_theme.icons.err} Deploy failed — ${failures.length} of ${total} workspace${total > 1 ? "s" : ""} failed`));
console.log();
for (const f of failures) {
const errorLine = (f.error ?? "Failed").split("\n")[0];
console.log(` ${require_theme.colors.error(require_theme.icons.err)} ${require_string.padRight(f.key, 28)} ${errorLine}`);
}
console.log();
if (!input.verbose) {
console.log(require_theme.colors.dim(" Run with --verbose for full build output."));
console.log();
}
return {
status: "error",
registryUrl,
built,
skipped,
deployResults,
error: `${failures.length} of ${total} workspaces failed to deploy`
};
}
}
const refreshed = await require_resolution_session.openResolution({ cwd: configDir });
if (!refreshed?.config) return {
status: "error",
registryUrl,
built,
skipped,
deployResults,
error: "Failed to reload the config after build"
};
bosConfig = refreshed.config;
refreshedRawConfig = refreshed.rawConfig;
}
const authoredPath = require_config.findConfigPath(configDir);
const tsForm = authoredPath !== null && require_config.isAppDescriptorPath(authoredPath);
let rawConfig;
if (refreshedRawConfig) rawConfig = refreshedRawConfig;
else {
const opened = await require_resolution_session.openResolution({ cwd: configDir });
if (!opened?.rawConfig) return {
status: "error",
registryUrl,
built,
skipped,
deployResults,
error: "Failed to load the authored config for publish"
};
rawConfig = opened.rawConfig;
}
if (tsForm && !input.build && plan.publishedConfigState === "unreachable") return {
status: "error",
registryUrl,
built,
skipped,
deployResults,
error: "Cannot read the currently published config from the registry — a config-only publish would overwrite live bundle URLs. Retry when the registry is reachable, or run a full deploy."
};
let publishPayload = tsForm && !input.build ? require_merge.mergeAuthoredOverPublished(plan.publishedConfig ?? {}, rawConfig) : rawConfig;
const urlOrigin = plan.cdnOrigin ?? `https://${gateway}`;
const deployTargets = (built ?? []).filter((key) => targets.includes(key));
const platformEntries = [];
const prevPins = input.fullUpload === true || node_process.default.env.BOS_FULL_UPLOAD === "1" ? {} : {
...require_deploy_diff.pinsFromPublishedConfig(plan.publishedConfig ?? {}, account, gateway),
...require_deploy_diff.pinsFromDeployState(configDir, account, gateway)
};
console.log();
console.log(` CDN deploy — uploading workspace dists to ${storageOrigin}...`);
for (const key of deployTargets) {
const ws = require_build.resolveWorkspaceTarget(key, bosConfig, runtimeConfig, configDir);
if (!ws) continue;
const distFiles = await require_storage_upload.collectDistFiles((0, node_path.join)(ws.path, "dist"));
const workspaceBase = `${urlOrigin}/bundles/${account}/${gateway}/${key}/`;
const startedAt = Date.now();
let outcome;
try {
outcome = await diffUploadWorkspace({
origin: storageOrigin,
apiKey: storageApiKey,
account,
gateway,
workspace: key,
files: distFiles,
base: workspaceBase,
previousPin: prevPins[key]
});
} catch (error) {
return {
status: "error",
registryUrl,
built,
skipped,
deployResults,
error: error instanceof Error ? error.message : `[publish] bundle upload for ${key} failed`
};
}
const report = require_version_manifest_deploy.readBuildReport((0, node_path.join)(ws.path, "dist"));
const ssrReport = require_version_manifest_deploy.readBuildReport((0, node_path.join)(ws.path, "dist", "ssr"));
const integrity = report ? outcome.fileIntegrity[report.entry] : void 0;
const ssrIntegrity = ssrReport?.entry ? outcome.fileIntegrity[`ssr/${ssrReport.entry}`] : void 0;
const manifestPointer = await pinWorkspaceVersionManifest({
origin: storageOrigin,
apiKey: storageApiKey,
account,
gateway,
workspace: key,
report,
ssrReport,
integrityMap: outcome.fileIntegrity,
files: outcome.fileIntegrity
});
if (!manifestPointer) return {
status: "error",
registryUrl,
built,
skipped,
deployResults,
error: `bundle upload for ${key}: version manifest missing — the dist predates hashed entry names (rebuild required; atomic-deploys hard break)`
};
if (outcome.result.storage === "memory" || manifestPointer.storage === "memory") return {
status: "error",
registryUrl,
built,
skipped,
deployResults,
error: "The receiving instance is serving bundle storage from memory (BOS_STORAGE_* R2 credentials are not configured there) — uploaded bytes would be lost on restart. Aborting before publish."
};
console.log(` ${require_theme.colors.green(require_theme.icons.ok)} ${require_string.padRight(key, 28)} → ${urlOrigin}/bundles/${account}/${gateway}/${key}/ (${outcome.uploaded} uploaded, ${outcome.skipped} skipped, ${require_timing.formatDuration(Date.now() - startedAt)})`);
platformEntries.push(...require_platform_deploy.platformUrlDeployEntries({
origin: urlOrigin,
account,
gateway,
key,
kind: ws.kind,
integrity,
ssrIntegrity,
pin: manifestPointer
}));
if ((0, every_plugin_build_ui.hasFolderFormUi)(ws.path)) {
const uiDistDir = (0, node_path.join)(ws.path, "ui", "dist");
const rawUi = ((ws.kind === "app" ? "app" : "plugins") === "app" ? rawConfig.app : rawConfig.plugins)?.[key]?.ui;
const uiName = require_config.resolveUiRuntimeName(rawUi, (0, node_path.join)(ws.path, "ui"), key);
let uiIntegrity;
let uiSsrIntegrity;
let uiManifestPointer;
if ((0, node_fs.existsSync)(uiDistDir)) {
const uiDistFiles = await require_storage_upload.collectDistFiles(uiDistDir);
const uiStartedAt = Date.now();
const uiOutcome = await diffUploadWorkspace({
origin: storageOrigin,
apiKey: storageApiKey,
account,
gateway,
workspace: `${key}-ui`,
files: uiDistFiles,
base: `${urlOrigin}/bundles/${account}/${gateway}/${key}-ui/`,
previousPin: prevPins[`${key}-ui`]
});
const uiReport = require_version_manifest_deploy.readBuildReport(uiDistDir);
const uiSsrReport = require_version_manifest_deploy.readBuildReport((0, node_path.join)(uiDistDir, "ssr"));
uiIntegrity = uiReport ? uiOutcome.fileIntegrity[uiReport.entry] : void 0;
uiSsrIntegrity = uiSsrReport?.entry ? uiOutcome.fileIntegrity[`ssr/${uiSsrReport.entry}`] : void 0;
uiManifestPointer = await pinWorkspaceVersionManifest({
origin: storageOrigin,
apiKey: storageApiKey,
account,
gateway,
workspace: `${key}-ui`,
report: uiReport,
ssrReport: uiSsrReport,
integrityMap: uiOutcome.fileIntegrity,
files: uiOutcome.fileIntegrity
});
if (!uiManifestPointer) return {
status: "error",
registryUrl,
built,
skipped,
deployResults,
error: `bundle upload for ${key}-ui: version manifest missing — the dist predates hashed entry names (rebuild required; atomic-deploys hard break)`
};
if (uiOutcome.result.storage === "memory" || uiManifestPointer.storage === "memory") return {
status: "error",
registryUrl,
built,
skipped,
deployResults,
error: "The receiving instance is serving bundle storage from memory (BOS_STORAGE_* R2 credentials are not configured there) — uploaded bytes would be lost on restart. Aborting before publish."
};
console.log(` ${require_theme.colors.green(require_theme.icons.ok)} ${require_string.padRight(`${key}-ui`, 28)} → ${urlOrigin}/bundles/${account}/${gateway}/${key}-ui/ (${uiOutcome.uploaded} uploaded, ${uiOutcome.skipped} skipped, ${require_timing.formatDuration(Date.now() - uiStartedAt)})`);
} else console.log(` ${require_theme.colors.green(require_theme.icons.ok)} ${require_string.padRight(`${key}-ui`, 28)} → ${urlOrigin}/bundles/${account}/${gateway}/${key}-ui/`);
platformEntries.push(...require_platform_deploy.pluginUiUrlDeployEntries({
origin: urlOrigin,
account,
gateway,
key,
kind: ws.kind,
integrity: uiIntegrity,
ssrIntegrity: uiSsrIntegrity,
name: uiName,
pin: uiManifestPointer
}));
}
}
if (platformEntries.length > 0) {
const merged = require_integrity.applyDeployResults(rawConfig, platformEntries);
if (!tsForm) try {
(0, node_fs.writeFileSync)((0, node_path.join)(configDir, "bos.config.json"), `${JSON.stringify(merged, null, 2)}\n`);
} catch (error) {
return {
status: "error",
registryUrl,
built,
skipped,
deployResults,
error: `Failed to write bundle URLs to bos.config.json: ${error instanceof Error ? error.message : String(error)}`
};
}
publishPayload = merged;
}
if (isStaging) publishPayload = {
...publishPayload,
domain: gateway
};
const pinVerification = await require_rollback.verifyRollbackSnapshot(publishPayload);
if (pinVerification.verifiable && !pinVerification.ok) {
const failures = pinVerification.slots.filter((check) => !check.ok).map((check) => ` ${check.slot}: ${check.reason}`).join("\n");
console.error(` ${require_theme.colors.error(require_theme.icons.err)} Publish blocked — a pinned slot's bytes do not serve at its production URL:\n${failures}`);
return {
status: "error",
registryUrl,
built,
skipped,
deployResults,
error: `Publish blocked — pinned slot bytes missing or SRI mismatched:\n${failures}`
};
}
const registryKey = `apps/${account}/${gateway}/bos.config.json`;
const publishedAt = (/* @__PURE__ */ new Date()).toISOString();
const fingerprint = require_fingerprint.pointerFingerprint(publishPayload);
const publishSlotPins = require_fingerprint.slotPins(publishPayload);
const registryEntries = { [registryKey]: JSON.stringify(publishPayload) };
const manifestKey = `apps/${account}/${gateway}/manifests/${publishedAt.replace(/[:.]/g, "-")}.json`;
registryEntries[manifestKey] = JSON.stringify({
account,
gateway,
network,
publishedAt,
registryUrl
});
console.log();
console.log(" Publishing to:");
console.log(` ${require_theme.colors.cyan(registryUrl)}`);
console.log(` ${require_theme.colors.dim("Fingerprint:")} ${fingerprint}`);
for (const [slot, pin] of Object.entries(publishSlotPins)) {
console.log(` ${require_theme.colors.dim(`${slot}:`)}`);
console.log(` ${pin}`);
}
console.log(` ${require_theme.colors.dim(`+ per-deploy manifest ${manifestKey.split("/").pop()} (audit trail)`)}`);
const persistDeployState = () => {
try {
const pins = require_deploy_diff.pinsFromPublishedConfig(publishPayload, account, gateway);
if (Object.keys(pins).length > 0) require_deploy_diff.upsertDeployStatePins(configDir, account, gateway, pins);
} catch (error) {
console.log(require_theme.colors.dim(` Note: could not update .bos/deploy-state.json — ${error instanceof Error ? error.message : String(error)}`));
}
};
try {
if (await isConfigAlreadyPublished({
account,
gateway,
publishConfig: publishPayload,
registry: input.registry
})) {
console.log(" Already up to date — skipping transaction");
persistDeployState();
return {
status: "published",
registryUrl,
built,
skipped,
deployResults,
publishConfig: publishPayload
};
}
console.log(` Submitting transaction on ${network}...`);
let result;
if (useWallet) {
const credential = require_auth_session.readSessionHandle(configDir)?.credential;
if (!credential || credential.accountId !== account) return {
status: "error",
registryUrl,
error: `--wallet requires a CLI session under ${account}. Run bos login first.`
};
const record = await require_delegate_signer.ensureDelegateKey({
configDir,
account,
contract: registryNamespace,
network,
siteUrl: credential.siteUrl
});
result = await require_delegate_signer.submitRegistryWriteDelegated({
account,
contract: registryNamespace,
network,
args: registryEntries,
delegatePrivateKey: record.privateKey,
relayEndpoint: `${credential.siteUrl}/api/auth/near/relay`,
apiKey: credential.apiKey
});
} else {
if (!strategy) return {
status: "error",
registryUrl,
error: "non-wallet publish requires a resolved signing strategy"
};
result = await require_near_signer.submitRegistryWrite({
account,
contract: registryNamespace,
method: "__fastdata_kv",
args: registryEntries,
network,
privateKey: input.privateKey
}, strategy);
}
if (result.txHash) console.log(` Transaction submitted: ${require_theme.colors.dim(result.txHash)}`);
console.log(" Waiting for publish confirmation...");
await waitForPublishedConfig({
account,
gateway,
publishConfig: publishPayload,
registry: input.registry
});
persistDeployState();
return {
status: "published",
registryUrl,
txHash: result.txHash,
fingerprint,
slotPins: publishSlotPins,
built,
skipped,
deployResults,
publishConfig: publishPayload
};
} catch (error) {
return {
status: "error",
registryUrl,
error: formatNearError(error),
built,
skipped,
deployResults
};
}
}
function formatNearError(error) {
const message = error instanceof Error ? error.message : String(error);
if (message.includes("does not have enough allowance")) return `The publish access key has insufficient allowance to cover this transaction.
Regenerate your key with a higher allowance:
bos key generate
Original: ${message}`;
if (message.includes("exceeded gas") || message.includes("GasLimitExceeded")) return `Transaction exceeded gas limit.\n Original: ${message}`;
if (message.includes("timeout") || message.includes("Timeout")) return `Transaction timed out. Check NEAR network status.\n Original: ${message}`;
return message;
}
//#endregion
exports.findUnpublishedFrameworkPackages = findUnpublishedFrameworkPackages;
exports.isConfigAlreadyPublished = isConfigAlreadyPublished;
exports.npmVersionPublished = npmVersionPublished;
exports.preflightPublish = preflightPublish;
exports.publishToFastKv = publishToFastKv;
exports.waitForPublishedConfig = waitForPublishedConfig;
//# sourceMappingURL=publish.cjs.map