everything-dev 2.0.0-rc.7 → 2.0.0-rc.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/api/auth-middleware.cjs +14 -1
- package/dist/api/auth-middleware.cjs.map +1 -1
- package/dist/api/auth-middleware.d.cts +9 -0
- package/dist/api/auth-middleware.d.mts +9 -0
- package/dist/api/auth-middleware.mjs +14 -1
- package/dist/api/auth-middleware.mjs.map +1 -1
- package/dist/apps/upload/src/client.cjs +7 -1
- package/dist/apps/upload/src/client.cjs.map +1 -1
- package/dist/apps/upload/src/client.mjs +7 -1
- package/dist/apps/upload/src/client.mjs.map +1 -1
- package/dist/auth-login.cjs +24 -1
- package/dist/auth-login.cjs.map +1 -1
- package/dist/auth-login.mjs +24 -1
- package/dist/auth-login.mjs.map +1 -1
- package/dist/auth-session.cjs +4 -2
- package/dist/auth-session.cjs.map +1 -1
- package/dist/auth-session.mjs +4 -2
- package/dist/auth-session.mjs.map +1 -1
- package/dist/cdn-deploy.cjs +11 -7
- package/dist/cdn-deploy.cjs.map +1 -1
- package/dist/cdn-deploy.mjs +11 -7
- package/dist/cdn-deploy.mjs.map +1 -1
- package/dist/cli/import.cjs +1140 -0
- package/dist/cli/import.cjs.map +1 -0
- package/dist/cli/import.mjs +1126 -0
- package/dist/cli/import.mjs.map +1 -0
- package/dist/cli/init.cjs +33 -14
- package/dist/cli/init.cjs.map +1 -1
- package/dist/cli/init.d.cts +8 -2
- package/dist/cli/init.d.mts +8 -2
- package/dist/cli/init.mjs +33 -15
- package/dist/cli/init.mjs.map +1 -1
- package/dist/cli.cjs +59 -3
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.mjs +59 -3
- package/dist/cli.mjs.map +1 -1
- package/dist/commands/import.cjs +49 -0
- package/dist/commands/import.cjs.map +1 -0
- package/dist/commands/import.mjs +46 -0
- package/dist/commands/import.mjs.map +1 -0
- package/dist/commands/keys.cjs +25 -4
- package/dist/commands/keys.cjs.map +1 -1
- package/dist/commands/keys.mjs +25 -4
- package/dist/commands/keys.mjs.map +1 -1
- package/dist/config.d.cts +1 -0
- package/dist/config.d.mts +1 -0
- package/dist/contract.cjs +89 -0
- package/dist/contract.cjs.map +1 -1
- package/dist/contract.d.cts +190 -1
- package/dist/contract.d.mts +190 -1
- package/dist/contract.meta.cjs +17 -1
- package/dist/contract.meta.cjs.map +1 -1
- package/dist/contract.meta.d.cts +27 -1
- package/dist/contract.meta.d.mts +27 -1
- package/dist/contract.meta.mjs +17 -1
- package/dist/contract.meta.mjs.map +1 -1
- package/dist/contract.mjs +84 -1
- package/dist/contract.mjs.map +1 -1
- package/dist/custody-signer.cjs +70 -0
- package/dist/custody-signer.cjs.map +1 -0
- package/dist/custody-signer.mjs +68 -0
- package/dist/custody-signer.mjs.map +1 -0
- package/dist/db/core.cjs +22 -4
- package/dist/db/core.cjs.map +1 -1
- package/dist/db/core.mjs +22 -4
- package/dist/db/core.mjs.map +1 -1
- package/dist/descriptor/schema.cjs +8 -5
- package/dist/descriptor/schema.cjs.map +1 -1
- package/dist/descriptor/schema.d.cts +1 -0
- package/dist/descriptor/schema.d.mts +1 -0
- package/dist/descriptor/schema.mjs +8 -5
- package/dist/descriptor/schema.mjs.map +1 -1
- package/dist/descriptor/serialize.cjs +1 -1
- package/dist/descriptor/serialize.cjs.map +1 -1
- package/dist/descriptor/serialize.mjs +1 -1
- package/dist/descriptor/serialize.mjs.map +1 -1
- package/dist/http-client.cjs +14 -4
- package/dist/http-client.cjs.map +1 -1
- package/dist/http-client.mjs +14 -4
- package/dist/http-client.mjs.map +1 -1
- package/dist/index.cjs +6 -0
- package/dist/index.d.cts +2 -2
- package/dist/index.d.mts +2 -2
- package/dist/index.mjs +2 -2
- package/dist/infra/planner.cjs +6 -2
- package/dist/infra/planner.cjs.map +1 -1
- package/dist/infra/planner.mjs +6 -2
- package/dist/infra/planner.mjs.map +1 -1
- package/dist/outlayer-login.cjs +54 -0
- package/dist/outlayer-login.cjs.map +1 -0
- package/dist/outlayer-login.mjs +54 -0
- package/dist/outlayer-login.mjs.map +1 -0
- package/dist/plugin.cjs +2 -0
- package/dist/plugin.cjs.map +1 -1
- package/dist/plugin.d.cts +74 -0
- package/dist/plugin.d.mts +74 -0
- package/dist/plugin.mjs +2 -0
- package/dist/plugin.mjs.map +1 -1
- package/dist/publish.cjs +145 -22
- package/dist/publish.cjs.map +1 -1
- package/dist/publish.mjs +147 -24
- package/dist/publish.mjs.map +1 -1
- package/dist/source-archive.cjs +51 -0
- package/dist/source-archive.cjs.map +1 -0
- package/dist/source-archive.mjs +50 -0
- package/dist/source-archive.mjs.map +1 -0
- package/dist/storage-upload.cjs +6 -1
- package/dist/storage-upload.cjs.map +1 -1
- package/dist/storage-upload.mjs +6 -1
- package/dist/storage-upload.mjs.map +1 -1
- package/dist/types.cjs +5 -1
- package/dist/types.cjs.map +1 -1
- package/dist/types.d.cts +2 -0
- package/dist/types.d.mts +2 -0
- package/dist/types.mjs +5 -1
- package/dist/types.mjs.map +1 -1
- package/dist/ui/auth.d.cts +217 -123
- package/dist/ui/auth.d.mts +217 -123
- package/dist/ui/router-client.cjs +2 -1
- package/dist/ui/router-client.cjs.map +1 -1
- package/dist/ui/router-client.mjs +2 -1
- package/dist/ui/router-client.mjs.map +1 -1
- package/dist/utils/workspace-subtree.cjs +37 -0
- package/dist/utils/workspace-subtree.cjs.map +1 -0
- package/dist/utils/workspace-subtree.mjs +36 -0
- package/dist/utils/workspace-subtree.mjs.map +1 -0
- package/dist/version-manifest-deploy.cjs +4 -2
- package/dist/version-manifest-deploy.cjs.map +1 -1
- package/dist/version-manifest-deploy.mjs +4 -2
- package/dist/version-manifest-deploy.mjs.map +1 -1
- package/dist/version-manifest-resolve.cjs +2 -1
- package/dist/version-manifest-resolve.cjs.map +1 -1
- package/dist/version-manifest-resolve.d.cts +3 -0
- package/dist/version-manifest-resolve.d.mts +3 -0
- package/dist/version-manifest-resolve.mjs +2 -1
- package/dist/version-manifest-resolve.mjs.map +1 -1
- package/package.json +11 -10
- package/skills/api-and-auth/SKILL.md +12 -9
- package/skills/api-and-auth/references/middleware.md +38 -0
- package/skills/cli-reference/SKILL.md +38 -6
- package/skills/init-upgrade/SKILL.md +76 -3
- package/skills/talk-to-the-app/SKILL.md +32 -5
package/dist/publish.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"publish.mjs","names":[],"sources":["../src/publish.ts"],"sourcesContent":["import { existsSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport process from \"node:process\";\nimport { hasFolderFormUi } from \"every-plugin/build/ui\";\nimport { readSessionHandle } from \"./auth-session\";\nimport { buildWorkspaceTargets, resolveWorkspaceTarget, selectWorkspaceTargets } from \"./build\";\nimport { type CdnDeployInputs, probeStorageOrigin, resolveCdnDeployInputs } from \"./cdn-deploy\";\nimport { formatDuration } from \"./cli/timing\";\nimport { generateCodeArtifacts } from \"./code-artifacts\";\nimport { findConfigPath, isAppDescriptorPath, resolveUiRuntimeName } from \"./config\";\nimport type { WorkspaceDeployResult } from \"./contract\";\nimport { ensureDelegateKey, submitRegistryWriteDelegated } from \"./delegate-signer\";\nimport {\n diffWorkspaceFiles,\n localSriMap,\n mergedFileIntegrity,\n type PreviousPin,\n pinsFromDeployState,\n pinsFromPublishedConfig,\n upsertDeployStatePins,\n} from \"./deploy-diff\";\nimport {\n buildRegistryConfigUrlForNetwork,\n fetchBosConfigFromFastKv,\n fetchPublishedConfigFromFastKv,\n getRegistryNamespaceForNetwork,\n type NetworkId,\n} from \"./fastkv\";\nimport { pointerFingerprint, slotPins } from \"./fingerprint\";\nimport { fetchResponse } from \"./http-client\";\nimport { applyDeployResults, type DeployResultEntry } from \"./integrity\";\nimport { mergeAuthoredOverPublished } from \"./merge\";\nimport {\n describeSigningStrategy,\n resolveSigningStrategy,\n type SigningStrategy,\n submitRegistryWrite,\n} from \"./near-signer\";\nimport { getNetworkIdForAccount } from \"./network\";\nimport { platformUrlDeployEntries, pluginUiUrlDeployEntries } from \"./platform-deploy\";\nimport { openResolution } from \"./resolution/session\";\nimport { verifyRollbackSnapshot } from \"./rollback\";\nimport {\n collectDistFiles,\n type DistFile,\n uploadBundle,\n uploadWorkspaceDist,\n} from \"./storage-upload\";\nimport type { BosConfig, BosConfigInput, PublishConfig, RuntimeConfig } from \"./types\";\nimport { padRight } from \"./utils/string\";\nimport { colors, icons } from \"./utils/theme\";\nimport { composeWorkspaceVersionManifest, readBuildReport } from \"./version-manifest-deploy\";\nimport { resolveSlotVersion } from \"./version-manifest-resolve\";\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\n/**\n * Compose + upload the workspace's version manifest (atomic-deploys 03): the\n * manifest is built from the dist build report + the **server-computed** SRI\n * map of the just-finished upload, uploaded additively at\n * `versions/<version>.json`, and returned as the slot's pointer. Returns\n * undefined when the dist predates hashed entry names (no report / no entry\n * SRI) — the caller aborts the train: uploaded workspaces must pin.\n */\n/**\n * The manifest's own storage backend — when every dist file was diff-skipped\n * it is the only upload of the leg, so it is the only signal for the\n * memory-backend abort below.\n */\nexport interface VersionManifestPin {\n file: string;\n integrity: string;\n storage?: \"s3\" | \"memory\";\n}\n\nasync function pinWorkspaceVersionManifest(input: {\n origin: string;\n apiKey?: string;\n account: string;\n gateway: string;\n workspace: string;\n report: ReturnType<typeof readBuildReport>;\n ssrReport: ReturnType<typeof readBuildReport>;\n integrityMap: Record<string, string>;\n /** full per-file SRI map of the dist — recorded for the next deploy's diff */\n files?: Record<string, string>;\n}): Promise<VersionManifestPin | undefined> {\n if (!input.report) return undefined;\n const versionManifest = composeWorkspaceVersionManifest({\n report: input.report,\n ssrReport: input.ssrReport,\n integrityMap: input.integrityMap,\n ...(input.files ? { files: input.files } : {}),\n });\n if (!versionManifest) return undefined;\n\n const manifestFile = `versions/${versionManifest.version}.json`;\n const manifestUpload = await uploadBundle({\n origin: input.origin,\n apiKey: input.apiKey,\n account: input.account,\n gateway: input.gateway,\n workspace: input.workspace,\n files: [\n {\n path: manifestFile,\n bytes: new TextEncoder().encode(`${JSON.stringify(versionManifest, null, 2)}\\n`),\n },\n ],\n });\n const manifestIntegrity = manifestUpload.integrity[manifestFile];\n if (!manifestIntegrity) return undefined;\n console.log(\n ` ${colors.dim(`${padRight(\"\", 28)} version manifest ${manifestFile} (SRI pinned)`)}`,\n );\n return { file: manifestFile, integrity: manifestIntegrity, storage: manifestUpload.storage };\n}\n\ninterface DiffUploadOutcome {\n result: {\n stored: number;\n totalBytes: number;\n integrity: Record<string, string>;\n storage?: \"s3\" | \"memory\";\n };\n /** per-file SRI map covering the whole dist (uploaded → server SRI, skipped → previous manifest SRI) */\n fileIntegrity: Record<string, string>;\n uploaded: number;\n skipped: number;\n total: number;\n}\n\n/**\n * One workspace's upload leg with skip-unchanged diffing: resolve the previous\n * manifest's `files` map, hash the local dist, upload only new/changed files,\n * and return the merged per-file SRI map for the version manifest. Any failure\n * to resolve the previous map degrades to a full upload.\n */\nasync function diffUploadWorkspace(input: {\n origin: string;\n apiKey?: string;\n account: string;\n gateway: string;\n workspace: string;\n files: DistFile[];\n base: string;\n previousPin: PreviousPin | undefined;\n}): Promise<DiffUploadOutcome> {\n const localSri = localSriMap(input.files);\n let uploadFiles = input.files;\n let prevFiles: Record<string, string> | undefined;\n if (input.previousPin) {\n try {\n const resolved = await resolveSlotVersion({ base: input.base, pin: input.previousPin });\n if (resolved.files) {\n const diff = diffWorkspaceFiles(input.files, localSri, resolved.files);\n prevFiles = resolved.files;\n uploadFiles = diff.upload;\n if (diff.skipped > 0) {\n console.log(\n ` ${colors.dim(`${padRight(input.workspace, 28)} ${diff.skipped} unchanged file(s) — skipping (manifest diff)`)}`,\n );\n }\n }\n } catch {\n console.log(\n ` ${colors.dim(`${padRight(input.workspace, 28)} previous manifest unusable — uploading everything`)}`,\n );\n }\n }\n\n console.log(\n ` ${padRight(input.workspace, 28)} uploading ${uploadFiles.length} of ${input.files.length} files (${formatBundleMb(uploadFiles)}) → ${input.base}`,\n );\n const result =\n uploadFiles.length > 0\n ? await uploadWorkspaceDist({\n origin: input.origin,\n apiKey: input.apiKey,\n account: input.account,\n gateway: input.gateway,\n workspace: input.workspace,\n files: uploadFiles,\n })\n : { stored: 0, totalBytes: 0, integrity: {}, storage: undefined };\n return {\n result,\n fileIntegrity: mergedFileIntegrity(input.files, result.integrity, prevFiles),\n uploaded: result.stored,\n skipped: input.files.length - uploadFiles.length,\n total: input.files.length,\n };\n}\n\nfunction formatBundleMb(files: Array<{ bytes: Uint8Array }>): string {\n const total = files.reduce((sum, file) => sum + file.bytes.byteLength, 0);\n return `${(total / 1024 / 1024).toFixed(1)} MB`;\n}\n\nexport async function waitForPublishedConfig(opts: {\n account: string;\n gateway: string;\n publishConfig: BosConfigInput;\n registry?: string;\n timeoutMs?: number;\n intervalMs?: number;\n}): Promise<void> {\n const envTimeoutMs = Number(process.env.BOS_PUBLISH_CONFIRMATION_TIMEOUT_MS);\n const envIntervalMs = Number(process.env.BOS_PUBLISH_CONFIRMATION_INTERVAL_MS);\n const timeoutMs =\n opts.timeoutMs ?? (Number.isFinite(envTimeoutMs) ? envTimeoutMs : undefined) ?? 120_000;\n const intervalMs =\n opts.intervalMs ?? (Number.isFinite(envIntervalMs) ? envIntervalMs : undefined) ?? 3_000;\n const startedAt = Date.now();\n let lastError: unknown;\n\n while (Date.now() - startedAt < timeoutMs) {\n try {\n const verifiedConfig = await fetchBosConfigFromFastKv<BosConfigInput>(\n `bos://${opts.account}/${opts.gateway}`,\n opts.registry,\n );\n\n if (JSON.stringify(verifiedConfig) === JSON.stringify(opts.publishConfig)) {\n return;\n }\n } catch (error) {\n lastError = error;\n }\n\n await sleep(intervalMs);\n }\n\n const reason = lastError instanceof Error ? ` Last error: ${lastError.message}` : \"\";\n throw new Error(\n `Timed out waiting for publish confirmation at bos://${opts.account}/${opts.gateway}.${reason}`,\n );\n}\n\nexport async function isConfigAlreadyPublished(opts: {\n account: string;\n gateway: string;\n publishConfig: BosConfigInput;\n registry?: string;\n}): Promise<boolean> {\n try {\n const current = await fetchBosConfigFromFastKv<BosConfigInput>(\n `bos://${opts.account}/${opts.gateway}`,\n opts.registry,\n );\n return JSON.stringify(current) === JSON.stringify(opts.publishConfig);\n } catch {\n return false;\n }\n}\n\ninterface PublishToFastKvInput {\n bosConfig: BosConfig;\n runtimeConfig: RuntimeConfig | null;\n configDir: string;\n env: \"production\" | \"staging\";\n build: boolean;\n dryRun: boolean;\n verbose: boolean;\n packages: string;\n network?: \"mainnet\" | \"testnet\";\n privateKey?: string;\n wallet?: boolean;\n registry?: string;\n /** Skip the diff upload and re-upload every dist file (escape hatch). */\n fullUpload?: boolean;\n /** Publish `bosConfig` verbatim — no authored/published merge, no ts-form\n * config-only guard. Rollbacks use this: the payload is the previous\n * snapshot, already assembled. */\n exactPayload?: boolean;\n}\n\ninterface PublishToFastKvResult {\n status: \"published\" | \"error\" | \"dry-run\";\n registryUrl: string;\n txHash?: string;\n built?: string[];\n skipped?: string[];\n error?: string;\n publishConfig?: BosConfigInput;\n deployResults?: WorkspaceDeployResult[];\n fingerprint?: string;\n slotPins?: Record<string, string>;\n}\n\nexport type PublishedConfigState = \"fetched\" | \"missing\" | \"unreachable\";\n\nexport interface PublishPreflightPlan {\n isStaging: boolean;\n account: string;\n gateway: string;\n network: NetworkId;\n registryUrl: string;\n registryNamespace: string;\n targets: string[];\n useWallet: boolean;\n strategy?: SigningStrategy;\n cdnOrigin: string | undefined;\n storageOrigin: string;\n storageApiKey?: string;\n /** The config currently published to FastKV (null when missing or unreachable). */\n publishedConfig: Record<string, unknown> | null;\n /** Whether the preflight FastKV read succeeded, found nothing, or failed. */\n publishedConfigState: PublishedConfigState;\n}\n\nexport type PublishPreflight =\n | { kind: \"dry-run\"; registryUrl: string }\n | { kind: \"error\"; registryUrl: string; error: string }\n | { kind: \"ready\"; plan: PublishPreflightPlan };\n\nconst FRAMEWORK_PACKAGE_NAMES = [\"better-near-auth\", \"every-plugin\", \"everything-dev\"] as const;\n\n/**\n * Whether npm serves `name@version` (true), definitively does not (404 —\n * false), or whether publishability could not be determined (network error,\n * unexpected status — null; callers must not block on that).\n */\nexport async function npmVersionPublished(name: string, version: string): Promise<boolean | null> {\n try {\n const res = await fetchResponse(`https://registry.npmjs.org/${name}/${version}`, {\n headers: { Accept: \"application/json\" },\n });\n if (res.status === 200) return true;\n if (res.status === 404) return false;\n return null;\n } catch {\n return null;\n }\n}\n\n/**\n * The npm-publish gate: a workspace that vendors the framework packages'\n * sources must not ship bundles ahead of npm — the update check, child\n * scaffolds (`bos init`), and template syncs all read those versions from\n * the registry. Reads only apply when the sources are workspace-local (the\n * parent repo); child projects install the framework from npm and are\n * inherently in sync.\n */\nexport async function findUnpublishedFrameworkPackages(\n configDir: string,\n): Promise<Array<{ name: string; version: string }>> {\n const unpublished: Array<{ name: string; version: string }> = [];\n for (const name of FRAMEWORK_PACKAGE_NAMES) {\n const manifestPath = join(configDir, \"packages\", name, \"package.json\");\n if (!existsSync(manifestPath)) continue;\n const version = (JSON.parse(readFileSync(manifestPath, \"utf-8\")) as { version?: string })\n .version;\n if (!version) continue;\n const published = await npmVersionPublished(name, version);\n if (published === false) {\n unpublished.push({ name, version });\n } else if (published === null) {\n console.log(\n colors.yellow(\n ` ! Could not verify ${name}@${version} on npm (registry unreachable) — continuing`,\n ),\n );\n }\n }\n return unpublished;\n}\n\n// Pure preflight (resolve, don't mutate) — every failure returns before the\n// build train runs (issue #287). Dry-run exits after the auth guards, before\n// any signing (keychain/TTY side effects).\nexport async function preflightPublish(input: PublishToFastKvInput): Promise<PublishPreflight> {\n const { configDir } = input;\n const bosConfig = input.bosConfig;\n\n const isStaging = input.env === \"staging\";\n const account = isStaging ? (bosConfig.staging?.account ?? bosConfig.account) : bosConfig.account;\n const gateway = isStaging ? (bosConfig.staging?.domain ?? bosConfig.domain) : bosConfig.domain;\n if (!gateway) {\n return {\n kind: \"error\",\n registryUrl: \"\",\n error: \"authored config must define domain to publish\",\n };\n }\n\n const network: NetworkId = input.network ?? getNetworkIdForAccount(account);\n const registryUrl = buildRegistryConfigUrlForNetwork(network, account, gateway, input.registry);\n const fail = (error: string): PublishPreflight => ({ kind: \"error\", registryUrl, error });\n\n const publishAuth: PublishConfig[\"auth\"] = bosConfig.publish?.auth;\n const governsWalletPublish = (publishAuth === \"session\" || input.wallet) && !input.privateKey;\n if (governsWalletPublish) {\n const session = readSessionHandle(configDir);\n if (!session?.credential) {\n return fail(\n (input.wallet\n ? \"--wallet requires\"\n : 'the authored config sets publish.auth = \"session\", but') +\n \" no CLI session is stored in .bos/ for this project. Run bos login to create one.\",\n );\n }\n if (session.credential.accountId && session.credential.accountId !== account) {\n return fail(\n `The CLI session was created for ${session.credential.accountId}, but the configured ` +\n `account is ${account}. Gasless wallet publish relays the FastKV write under the session's ` +\n \"NEAR account. Run bos login again under the matching account.\",\n );\n }\n }\n if (publishAuth && !input.privateKey) {\n if (publishAuth === \"custody\") {\n return fail(\n 'the authored config sets publish.auth = \"custody\", but custody publish is not implemented yet (see NEARBuilders/everything-dev#291).',\n );\n }\n }\n\n if (input.dryRun) {\n return { kind: \"dry-run\", registryUrl };\n }\n\n const useWallet = input.wallet === true;\n let strategy: SigningStrategy | undefined;\n if (useWallet) {\n console.log(\n ` Signing via ${colors.cyan(\"gasless NEP-366 delegate action (relayed by the platform relayer)\")}`,\n );\n } else {\n try {\n strategy = await resolveSigningStrategy({ privateKey: input.privateKey, account, network });\n console.log(` Signing via ${colors.cyan(describeSigningStrategy(strategy))}`);\n } catch (error) {\n return fail(error instanceof Error ? error.message : \"Unknown error\");\n }\n }\n\n // CDN deploy credentials (ADR 0020) — resolved before the build train so a\n // missing bundle origin or BOS_STORAGE_API_KEY fails fast instead of after\n // the full build. Config-only publishes ship the committed config verbatim;\n // nothing is uploaded, so no deploy origins are required.\n const session = readSessionHandle(configDir);\n let cdnDeploy: CdnDeployInputs = {\n cdnOrigin: undefined,\n storageOrigin: \"\",\n apiKey: undefined,\n };\n if (input.build) {\n const unpublished = await findUnpublishedFrameworkPackages(configDir);\n if (unpublished.length > 0) {\n const listing = unpublished.map((pkg) => `${pkg.name}@${pkg.version}`).join(\", \");\n return fail(\n `Framework package(s) ${listing} are not published to npm yet — deploys must not ship ` +\n \"bundles ahead of the registry (child scaffolds and update checks read these versions). \" +\n \"Re-run the Release workflow (or `pnpm run release`) first.\",\n );\n }\n\n cdnDeploy = resolveCdnDeployInputs({\n env: process.env as Record<string, string | undefined>,\n bosConfig,\n session: session?.credential ?? null,\n account,\n gateway,\n uploadsPlanned: input.build,\n });\n if (cdnDeploy.error) {\n return fail(cdnDeploy.error);\n }\n if (cdnDeploy.warning) {\n console.log(colors.yellow(` ! ${cdnDeploy.warning}`));\n }\n const cdnOrigin = cdnDeploy.cdnOrigin;\n if (!cdnOrigin) {\n return fail(\"CDN deploy resolved no bundle origin\");\n }\n const probeError = await probeStorageOrigin(cdnDeploy.storageOrigin);\n if (probeError) {\n return fail(\n `The bundle upload origin ${cdnDeploy.storageOrigin} is not running the platform API — ` +\n `${probeError}. Check BOS_STORAGE_ORIGIN / the bos login session, then re-run.`,\n );\n }\n }\n\n // Registry reachability + published-state capture: one FastKV read. A\n // missing config is fine for a first publish — an early signal, not a hard\n // gate. The captured config is the merge base for a config-only publish on\n // a TS-form project (authored hand-edits win; live pipeline state carries).\n let publishedConfig: Record<string, unknown> | null = null;\n let publishedConfigState: PublishedConfigState = \"missing\";\n try {\n publishedConfig = await fetchPublishedConfigFromFastKv<Record<string, unknown>>(registryUrl);\n publishedConfigState = \"fetched\";\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n if (message.startsWith(\"No config found\")) {\n console.log(colors.dim(\" Note: no published config found yet (first publish) — continuing\"));\n } else {\n publishedConfigState = \"unreachable\";\n console.log(colors.dim(\" Note: registry read failed — continuing\"));\n }\n }\n\n return {\n kind: \"ready\",\n plan: {\n isStaging,\n account,\n gateway,\n network,\n registryUrl,\n registryNamespace: getRegistryNamespaceForNetwork(network, input.registry),\n targets: selectWorkspaceTargets(input.packages, bosConfig),\n useWallet,\n strategy,\n cdnOrigin: cdnDeploy.cdnOrigin,\n storageOrigin: cdnDeploy.storageOrigin,\n storageApiKey: cdnDeploy.apiKey,\n publishedConfig,\n publishedConfigState,\n },\n };\n}\n\n/**\n * The deploy-train publish core (ADR 0020, as amended): preflight\n * (storage/CDN creds + signing) fails fast before the build, then build →\n * upload → version-manifest pin → config write-back → FastKV publish +\n * read-back. Scoped to one plugin via `packages`, a single-plugin redeploy\n * ships real bytes to the storage origin — same as the full train, nothing\n * image-native left.\n */\nexport async function publishToFastKv(input: PublishToFastKvInput): Promise<PublishToFastKvResult> {\n const preflight = await preflightPublish(input);\n if (preflight.kind === \"error\") {\n return { status: \"error\", registryUrl: preflight.registryUrl, error: preflight.error };\n }\n if (preflight.kind === \"dry-run\") {\n return { status: \"dry-run\", registryUrl: preflight.registryUrl };\n }\n const plan = preflight.plan;\n const {\n isStaging,\n account,\n gateway,\n network,\n registryUrl,\n registryNamespace,\n targets,\n useWallet,\n strategy,\n } = plan;\n const storageOrigin = plan.storageOrigin;\n const storageApiKey = plan.storageApiKey;\n\n const { configDir } = input;\n let bosConfig = input.bosConfig;\n const runtimeConfig = input.runtimeConfig;\n let built: string[] | undefined;\n let skipped: string[] | undefined;\n let deployResults: WorkspaceDeployResult[] | undefined;\n let refreshedRawConfig: BosConfigInput | null = null;\n\n if (input.build) {\n await generateCodeArtifacts(configDir, bosConfig, {\n env: \"production\",\n runtimeConfig: runtimeConfig ?? undefined,\n });\n\n const result = await buildWorkspaceTargets({\n configDir,\n bosConfig,\n runtimeConfig,\n targets,\n deploy: true,\n verbose: input.verbose,\n });\n built = result.built;\n skipped = result.skipped;\n deployResults = result.deployResults;\n\n if (deployResults) {\n const failures = deployResults.filter((r) => !r.success);\n if (failures.length > 0) {\n const total = deployResults.length;\n console.log();\n console.log(\n colors.error(\n ` ${icons.err} Deploy failed — ${failures.length} of ${total} workspace${total > 1 ? \"s\" : \"\"} failed`,\n ),\n );\n console.log();\n for (const f of failures) {\n const errorLine = (f.error ?? \"Failed\").split(\"\\n\")[0];\n console.log(` ${colors.error(icons.err)} ${padRight(f.key, 28)} ${errorLine}`);\n }\n console.log();\n if (!input.verbose) {\n console.log(colors.dim(\" Run with --verbose for full build output.\"));\n console.log();\n }\n return {\n status: \"error\" as const,\n registryUrl,\n built,\n skipped,\n deployResults,\n error: `${failures.length} of ${total} workspaces failed to deploy`,\n };\n }\n }\n\n const refreshed = await openResolution({ cwd: configDir });\n if (!refreshed?.config) {\n return {\n status: \"error\",\n registryUrl,\n built,\n skipped,\n deployResults,\n error: \"Failed to reload the config after build\",\n };\n }\n\n bosConfig = refreshed.config;\n refreshedRawConfig = refreshed.rawConfig;\n }\n\n const authoredPath = findConfigPath(configDir);\n const tsForm = authoredPath !== null && isAppDescriptorPath(authoredPath);\n let rawConfig: BosConfigInput;\n if (refreshedRawConfig) {\n rawConfig = refreshedRawConfig;\n } else {\n const opened = await openResolution({ cwd: configDir });\n if (!opened?.rawConfig) {\n return {\n status: \"error\",\n registryUrl,\n built,\n skipped,\n deployResults,\n error: \"Failed to load the authored config for publish\",\n };\n }\n rawConfig = opened.rawConfig;\n }\n if (\n tsForm &&\n !input.build &&\n input.exactPayload !== true &&\n plan.publishedConfigState === \"unreachable\"\n ) {\n // A config-only TS-form publish derives its pipeline state from the live\n // published config — publishing without it would wipe bundle URLs.\n return {\n status: \"error\",\n registryUrl,\n built,\n skipped,\n deployResults,\n error:\n \"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.\",\n };\n }\n let publishPayload: BosConfigInput =\n input.exactPayload === true\n ? input.bosConfig\n : tsForm && !input.build\n ? // Config-only publish on a TS-form project: the authored config owns\n // composition (hand-edits win) while live pipeline state (bundle\n // URLs, integrity) carries over from the published config.\n mergeAuthoredOverPublished((plan.publishedConfig ?? {}) as BosConfigInput, rawConfig)\n : rawConfig;\n\n const urlOrigin = plan.cdnOrigin ?? `https://${gateway}`;\n const deployTargets = (built ?? []).filter((key) => targets.includes(key));\n const platformEntries: DeployResultEntry[] = [];\n const fullUpload = input.fullUpload === true || process.env.BOS_FULL_UPLOAD === \"1\";\n // Previous per-workspace pins for the diff upload: the local pointer wins\n // (written after every confirmed publish), the published config is the\n // fallback. Neither → full upload.\n const prevPins: Record<string, PreviousPin> = fullUpload\n ? {}\n : {\n ...pinsFromPublishedConfig(\n (plan.publishedConfig ?? {}) as BosConfigInput,\n account,\n gateway,\n ),\n ...pinsFromDeployState(configDir, account, gateway),\n };\n\n console.log();\n console.log(` CDN deploy — uploading workspace dists to ${storageOrigin}...`);\n for (const key of deployTargets) {\n const ws = resolveWorkspaceTarget(key, bosConfig, runtimeConfig, configDir);\n if (!ws) continue;\n\n const distFiles = await collectDistFiles(join(ws.path, \"dist\"));\n const workspaceBase = `${urlOrigin}/bundles/${account}/${gateway}/${key}/`;\n const startedAt = Date.now();\n // an upload failure aborts the train as a structured error — the\n // previously published version stays fully live (ticket 05)\n let outcome: DiffUploadOutcome;\n try {\n outcome = await diffUploadWorkspace({\n origin: storageOrigin,\n apiKey: storageApiKey,\n account,\n gateway,\n workspace: key,\n files: distFiles,\n base: workspaceBase,\n previousPin: prevPins[key],\n });\n } catch (error) {\n return {\n status: \"error\",\n registryUrl,\n built,\n skipped,\n deployResults,\n error: error instanceof Error ? error.message : `[publish] bundle upload for ${key} failed`,\n };\n }\n const report = readBuildReport(join(ws.path, \"dist\"));\n const ssrReport = readBuildReport(join(ws.path, \"dist\", \"ssr\"));\n // the entry SRI keyed by the build report's hashed name\n const integrity = report ? outcome.fileIntegrity[report.entry] : undefined;\n const ssrIntegrity = ssrReport?.entry\n ? outcome.fileIntegrity[`ssr/${ssrReport.entry}`]\n : undefined;\n\n const manifestPointer = await pinWorkspaceVersionManifest({\n origin: storageOrigin,\n apiKey: storageApiKey,\n account,\n gateway,\n workspace: key,\n report,\n ssrReport,\n integrityMap: outcome.fileIntegrity,\n files: outcome.fileIntegrity,\n });\n if (!manifestPointer) {\n return {\n status: \"error\",\n registryUrl,\n built,\n skipped,\n deployResults,\n error: `bundle upload for ${key}: version manifest missing — the dist predates hashed entry names (rebuild required; atomic-deploys hard break)`,\n };\n }\n\n if (outcome.result.storage === \"memory\" || manifestPointer.storage === \"memory\") {\n return {\n status: \"error\",\n registryUrl,\n built,\n skipped,\n deployResults,\n error:\n \"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.\",\n };\n }\n\n console.log(\n ` ${colors.green(icons.ok)} ${padRight(key, 28)} → ${urlOrigin}/bundles/${account}/${gateway}/${key}/ (${outcome.uploaded} uploaded, ${outcome.skipped} skipped, ${formatDuration(Date.now() - startedAt)})`,\n );\n\n platformEntries.push(\n ...platformUrlDeployEntries({\n origin: urlOrigin,\n account,\n gateway,\n key,\n kind: ws.kind,\n integrity,\n ssrIntegrity,\n pin: manifestPointer,\n }),\n );\n\n // Folder-form plugin ui: the workspace build also produces <plugin>/ui/dist\n // (web remoteEntry + ssr container) — upload it as its own bundle key and\n // pin <slot>.<key>.ui.* so the host can compose the ui surface in production.\n if (hasFolderFormUi(ws.path)) {\n const uiDistDir = join(ws.path, \"ui\", \"dist\");\n const uiSlot = ws.kind === \"app\" ? \"app\" : \"plugins\";\n const rawSlot = (uiSlot === \"app\" ? rawConfig.app : rawConfig.plugins) as\n | Record<string, Record<string, unknown> | undefined>\n | undefined;\n const rawUi = rawSlot?.[key]?.ui as Record<string, unknown> | undefined;\n const uiName = resolveUiRuntimeName(rawUi, join(ws.path, \"ui\"), key);\n let uiIntegrity: string | undefined;\n let uiSsrIntegrity: string | undefined;\n let uiManifestPointer: VersionManifestPin | undefined;\n if (existsSync(uiDistDir)) {\n const uiDistFiles = await collectDistFiles(uiDistDir);\n const uiStartedAt = Date.now();\n const uiOutcome = await diffUploadWorkspace({\n origin: storageOrigin,\n apiKey: storageApiKey,\n account,\n gateway,\n workspace: `${key}-ui`,\n files: uiDistFiles,\n base: `${urlOrigin}/bundles/${account}/${gateway}/${key}-ui/`,\n previousPin: prevPins[`${key}-ui`],\n });\n const uiReport = readBuildReport(uiDistDir);\n const uiSsrReport = readBuildReport(join(uiDistDir, \"ssr\"));\n uiIntegrity = uiReport ? uiOutcome.fileIntegrity[uiReport.entry] : undefined;\n uiSsrIntegrity = uiSsrReport?.entry\n ? uiOutcome.fileIntegrity[`ssr/${uiSsrReport.entry}`]\n : undefined;\n\n uiManifestPointer = await pinWorkspaceVersionManifest({\n origin: storageOrigin,\n apiKey: storageApiKey,\n account,\n gateway,\n workspace: `${key}-ui`,\n report: uiReport,\n ssrReport: uiSsrReport,\n integrityMap: uiOutcome.fileIntegrity,\n files: uiOutcome.fileIntegrity,\n });\n if (!uiManifestPointer) {\n return {\n status: \"error\",\n registryUrl,\n built,\n skipped,\n deployResults,\n error: `bundle upload for ${key}-ui: version manifest missing — the dist predates hashed entry names (rebuild required; atomic-deploys hard break)`,\n };\n }\n\n if (uiOutcome.result.storage === \"memory\" || uiManifestPointer.storage === \"memory\") {\n return {\n status: \"error\",\n registryUrl,\n built,\n skipped,\n deployResults,\n error:\n \"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.\",\n };\n }\n\n console.log(\n ` ${colors.green(icons.ok)} ${padRight(`${key}-ui`, 28)} → ${urlOrigin}/bundles/${account}/${gateway}/${key}-ui/ (${uiOutcome.uploaded} uploaded, ${uiOutcome.skipped} skipped, ${formatDuration(Date.now() - uiStartedAt)})`,\n );\n } else {\n console.log(\n ` ${colors.green(icons.ok)} ${padRight(`${key}-ui`, 28)} → ${urlOrigin}/bundles/${account}/${gateway}/${key}-ui/`,\n );\n }\n\n platformEntries.push(\n ...pluginUiUrlDeployEntries({\n origin: urlOrigin,\n account,\n gateway,\n key,\n kind: ws.kind,\n integrity: uiIntegrity,\n ssrIntegrity: uiSsrIntegrity,\n name: uiName,\n pin: uiManifestPointer,\n }),\n );\n }\n }\n\n if (platformEntries.length > 0) {\n const merged = applyDeployResults(rawConfig as Record<string, unknown>, platformEntries);\n if (!tsForm) {\n // Legacy JSON-form children keep their local write-back; TS-form keeps\n // pipeline state in FastKV only — a write-back would shadow the\n // authored bos.app.ts descriptor.\n try {\n writeFileSync(join(configDir, \"bos.config.json\"), `${JSON.stringify(merged, null, 2)}\\n`);\n } catch (error) {\n return {\n status: \"error\",\n registryUrl,\n built,\n skipped,\n deployResults,\n error: `Failed to write bundle URLs to bos.config.json: ${error instanceof Error ? error.message : String(error)}`,\n };\n }\n }\n publishPayload = merged as BosConfigInput;\n }\n if (isStaging) {\n publishPayload = { ...publishPayload, domain: gateway };\n }\n\n const pinVerification = await verifyRollbackSnapshot(publishPayload);\n if (pinVerification.verifiable && !pinVerification.ok) {\n const failures = pinVerification.slots\n .filter((check) => !check.ok)\n .map((check) => ` ${check.slot}: ${check.reason}`)\n .join(\"\\n\");\n console.error(\n ` ${colors.error(icons.err)} Publish blocked — a pinned slot's bytes do not serve at its production URL:\\n${failures}`,\n );\n return {\n status: \"error\",\n registryUrl,\n built,\n skipped,\n deployResults,\n error: `Publish blocked — pinned slot bytes missing or SRI mismatched:\\n${failures}`,\n };\n }\n\n const registryKey = `apps/${account}/${gateway}/bos.config.json`;\n const publishedAt = new Date().toISOString();\n // version identity printed + returned for every deploy (atomic-deploys 12)\n const fingerprint = pointerFingerprint(publishPayload as never);\n const publishSlotPins = slotPins(publishPayload as never);\n const registryEntries: Record<string, string> = {\n [registryKey]: JSON.stringify(publishPayload),\n };\n const manifestKey = `apps/${account}/${gateway}/manifests/${publishedAt.replace(/[:.]/g, \"-\")}.json`;\n registryEntries[manifestKey] = JSON.stringify({\n account,\n gateway,\n network,\n publishedAt,\n registryUrl,\n });\n\n console.log();\n console.log(\" Publishing to:\");\n console.log(` ${colors.cyan(registryUrl)}`);\n console.log(` ${colors.dim(\"Fingerprint:\")} ${fingerprint}`);\n for (const [slot, pin] of Object.entries(publishSlotPins)) {\n console.log(` ${colors.dim(`${slot}:`)}`);\n console.log(` ${pin}`);\n }\n console.log(\n ` ${colors.dim(`+ per-deploy manifest ${manifestKey.split(\"/\").pop()} (audit trail)`)}`,\n );\n\n // Record the live pins for the next deploy's diff upload — only after the\n // config is confirmed published, so the pointer never claims undeployed state.\n const persistDeployState = () => {\n try {\n const pins = pinsFromPublishedConfig(publishPayload as BosConfigInput, account, gateway);\n if (Object.keys(pins).length > 0) {\n upsertDeployStatePins(configDir, account, gateway, pins);\n }\n } catch (error) {\n console.log(\n colors.dim(\n ` Note: could not update .bos/deploy-state.json — ${error instanceof Error ? error.message : String(error)}`,\n ),\n );\n }\n };\n\n try {\n const alreadyPublished = await isConfigAlreadyPublished({\n account,\n gateway,\n publishConfig: publishPayload,\n registry: input.registry,\n });\n if (alreadyPublished) {\n console.log(\" Already up to date — skipping transaction\");\n persistDeployState();\n return {\n status: \"published\",\n registryUrl,\n built,\n skipped,\n deployResults,\n publishConfig: publishPayload,\n };\n }\n\n console.log(` Submitting transaction on ${network}...`);\n\n let result: { success: boolean; txHash?: string };\n if (useWallet) {\n const session = readSessionHandle(configDir);\n const credential = session?.credential;\n if (!credential || credential.accountId !== account) {\n return {\n status: \"error\",\n registryUrl,\n error: `--wallet requires a CLI session under ${account}. Run bos login first.`,\n };\n }\n const record = await ensureDelegateKey({\n configDir,\n account,\n contract: registryNamespace,\n network,\n siteUrl: credential.siteUrl,\n });\n result = await submitRegistryWriteDelegated({\n account,\n contract: registryNamespace,\n network,\n args: registryEntries,\n delegatePrivateKey: record.privateKey,\n relayEndpoint: `${credential.siteUrl}/api/auth/near/relay`,\n apiKey: credential.apiKey,\n });\n } else {\n if (!strategy) {\n return {\n status: \"error\",\n registryUrl,\n error: \"non-wallet publish requires a resolved signing strategy\",\n };\n }\n result = await submitRegistryWrite(\n {\n account,\n contract: registryNamespace,\n method: \"__fastdata_kv\",\n args: registryEntries,\n network,\n privateKey: input.privateKey,\n },\n strategy,\n );\n }\n\n if (result.txHash) {\n console.log(` Transaction submitted: ${colors.dim(result.txHash)}`);\n }\n\n console.log(\" Waiting for publish confirmation...\");\n await waitForPublishedConfig({\n account,\n gateway,\n publishConfig: publishPayload,\n registry: input.registry,\n });\n persistDeployState();\n\n return {\n status: \"published\",\n registryUrl,\n txHash: result.txHash,\n fingerprint,\n slotPins: publishSlotPins,\n built,\n skipped,\n deployResults,\n publishConfig: publishPayload,\n };\n } catch (error) {\n return {\n status: \"error\",\n registryUrl,\n error: formatNearError(error),\n built,\n skipped,\n deployResults,\n };\n }\n}\n\nfunction formatNearError(error: unknown): string {\n const message = error instanceof Error ? error.message : String(error);\n\n if (message.includes(\"does not have enough allowance\")) {\n return (\n \"The publish access key has insufficient allowance to cover this transaction.\\n\" +\n \" Regenerate your key with a higher allowance:\\n\" +\n \" bos key generate\\n\" +\n ` Original: ${message}`\n );\n }\n\n if (message.includes(\"exceeded gas\") || message.includes(\"GasLimitExceeded\")) {\n return `Transaction exceeded gas limit.\\n Original: ${message}`;\n }\n\n if (message.includes(\"timeout\") || message.includes(\"Timeout\")) {\n return `Transaction timed out. Check NEAR network status.\\n Original: ${message}`;\n }\n\n return message;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"publish.mjs","names":[],"sources":["../src/publish.ts"],"sourcesContent":["import { existsSync, readFileSync, rmSync, writeFileSync } from \"node:fs\";\nimport { basename, dirname, join } from \"node:path\";\nimport process from \"node:process\";\nimport { hasFolderFormUi } from \"every-plugin/build/ui\";\nimport type { SourceRef } from \"every-plugin/version-manifest\";\nimport { readSessionHandle } from \"./auth-session\";\nimport { buildWorkspaceTargets, resolveWorkspaceTarget, selectWorkspaceTargets } from \"./build\";\nimport { type CdnDeployInputs, probeStorageOrigin, resolveCdnDeployInputs } from \"./cdn-deploy\";\nimport { formatDuration } from \"./cli/timing\";\nimport { generateCodeArtifacts } from \"./code-artifacts\";\nimport { findConfigPath, isAppDescriptorPath, resolveUiRuntimeName } from \"./config\";\nimport type { WorkspaceDeployResult } from \"./contract\";\nimport { ensureCustodyBalance, submitRegistryWriteCustody } from \"./custody-signer\";\nimport { ensureDelegateKey, submitRegistryWriteDelegated } from \"./delegate-signer\";\nimport {\n diffWorkspaceFiles,\n localSriMap,\n mergedFileIntegrity,\n type PreviousPin,\n pinsFromDeployState,\n pinsFromPublishedConfig,\n upsertDeployStatePins,\n} from \"./deploy-diff\";\nimport {\n buildRegistryConfigUrlForNetwork,\n fetchBosConfigFromFastKv,\n fetchPublishedConfigFromFastKv,\n getRegistryNamespaceForNetwork,\n type NetworkId,\n} from \"./fastkv\";\nimport { pointerFingerprint, slotPins } from \"./fingerprint\";\nimport { fetchResponse } from \"./http-client\";\nimport { applyDeployResults, type DeployResultEntry } from \"./integrity\";\nimport { mergeAuthoredOverPublished } from \"./merge\";\nimport {\n describeSigningStrategy,\n resolveSigningStrategy,\n type SigningStrategy,\n submitRegistryWrite,\n} from \"./near-signer\";\nimport { getNetworkIdForAccount } from \"./network\";\nimport { platformUrlDeployEntries, pluginUiUrlDeployEntries } from \"./platform-deploy\";\nimport { openResolution } from \"./resolution/session\";\nimport { verifyRollbackSnapshot } from \"./rollback\";\nimport { buildSourceArchive, makeSourceArchiveTempDir } from \"./source-archive\";\nimport {\n collectDistFiles,\n type DistFile,\n uploadBundle,\n uploadWorkspaceDist,\n} from \"./storage-upload\";\nimport type { BosConfig, BosConfigInput, PublishConfig, RuntimeConfig } from \"./types\";\nimport { run } from \"./utils/run\";\nimport { padRight } from \"./utils/string\";\nimport { colors, icons } from \"./utils/theme\";\nimport { composeWorkspaceVersionManifest, readBuildReport } from \"./version-manifest-deploy\";\nimport { resolveSlotVersion } from \"./version-manifest-resolve\";\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\n/**\n * Compose + upload the workspace's version manifest (atomic-deploys 03): the\n * manifest is built from the dist build report + the **server-computed** SRI\n * map of the just-finished upload, uploaded additively at\n * `versions/<version>.json`, and returned as the slot's pointer. Returns\n * undefined when the dist predates hashed entry names (no report / no entry\n * SRI) — the caller aborts the train: uploaded workspaces must pin.\n */\n/**\n * The manifest's own storage backend — when every dist file was diff-skipped\n * it is the only upload of the leg, so it is the only signal for the\n * memory-backend abort below.\n */\nexport interface VersionManifestPin {\n file: string;\n integrity: string;\n storage?: \"s3\" | \"memory\";\n}\n\nasync function pinWorkspaceVersionManifest(input: {\n origin: string;\n apiKey?: string;\n account: string;\n gateway: string;\n workspace: string;\n report: ReturnType<typeof readBuildReport>;\n ssrReport: ReturnType<typeof readBuildReport>;\n integrityMap: Record<string, string>;\n /** full per-file SRI map of the dist — recorded for the next deploy's diff */\n files?: Record<string, string>;\n /** source archive reference — server-computed SRI (MAP decision 6) */\n source?: SourceRef;\n /** repo HEAD the build was produced from — best-effort, may be absent */\n gitSha?: string;\n}): Promise<VersionManifestPin | undefined> {\n if (!input.report) return undefined;\n const versionManifest = composeWorkspaceVersionManifest({\n report: input.report,\n ssrReport: input.ssrReport,\n integrityMap: input.integrityMap,\n ...(input.files ? { files: input.files } : {}),\n ...(input.source ? { source: input.source } : {}),\n ...(input.gitSha ? { gitSha: input.gitSha } : {}),\n });\n if (!versionManifest) return undefined;\n\n const manifestFile = `versions/${versionManifest.version}.json`;\n const manifestUpload = await uploadBundle({\n origin: input.origin,\n apiKey: input.apiKey,\n account: input.account,\n gateway: input.gateway,\n workspace: input.workspace,\n files: [\n {\n path: manifestFile,\n bytes: new TextEncoder().encode(`${JSON.stringify(versionManifest, null, 2)}\\n`),\n },\n ],\n });\n const manifestIntegrity = manifestUpload.integrity[manifestFile];\n if (!manifestIntegrity) return undefined;\n console.log(\n ` ${colors.dim(`${padRight(\"\", 28)} version manifest ${manifestFile} (SRI pinned)`)}`,\n );\n return { file: manifestFile, integrity: manifestIntegrity, storage: manifestUpload.storage };\n}\n\n/**\n * Best-effort repo HEAD for the version manifest (`gitSha`): non-git\n * contexts (child bundles built outside a repo) omit the field silently —\n * it never fails the deploy.\n */\nasync function resolveGitSha(cwd: string): Promise<string | undefined> {\n try {\n const result = await run(\"git\", [\"rev-parse\", \"HEAD\"], { cwd, capture: true });\n const sha = result?.stdout.trim();\n return result && result.exitCode === 0 && sha ? sha : undefined;\n } catch {\n return undefined;\n }\n}\n\n/**\n * The source-archive leg (publish.sourcePublish, #406): tar the workspace\n * subtree, dedupe against the previous pin's manifest by archive SRI (reuse\n * skips the upload), otherwise upload and take the **server-computed** SRI\n * (MAP decision 6). The temp archive dir is cleaned up on every path — the\n * manifest only carries `{ file, integrity }`, not the bytes.\n */\nasync function resolveSourceReference(input: {\n origin: string;\n apiKey?: string;\n account: string;\n gateway: string;\n workspace: string;\n base: string;\n previousPin: PreviousPin | undefined;\n repoRoot: string;\n workspaceDir: string;\n}): Promise<{ source: SourceRef } | { memory: true }> {\n const outDir = makeSourceArchiveTempDir();\n try {\n const archive = await buildSourceArchive({\n repoRoot: input.repoRoot,\n workspace: input.workspaceDir,\n outDir,\n });\n if (input.previousPin) {\n try {\n const resolved = await resolveSlotVersion({ base: input.base, pin: input.previousPin });\n if (resolved.source?.integrity === archive.integrity) {\n console.log(\n ` ${colors.dim(`${padRight(input.workspace, 28)} source unchanged — reusing ${archive.file}`)}`,\n );\n return {\n source: { file: resolved.source.file, integrity: resolved.source.integrity },\n };\n }\n } catch {\n // previous manifest unusable — fall through to a full upload\n }\n }\n const upload = await uploadBundle({\n origin: input.origin,\n apiKey: input.apiKey,\n account: input.account,\n gateway: input.gateway,\n workspace: input.workspace,\n files: [{ path: archive.file, bytes: new Uint8Array(readFileSync(archive.path)) }],\n });\n if (upload.storage === \"memory\") return { memory: true };\n const integrity = upload.integrity[archive.file];\n if (!integrity) {\n throw new Error(\n `[publish] source archive upload for ${input.workspace}: missing server SRI for ${archive.file}`,\n );\n }\n console.log(\n ` ${colors.dim(`${padRight(input.workspace, 28)} source archive ${archive.file}`)}`,\n );\n return { source: { file: archive.file, integrity } };\n } finally {\n rmSync(outDir, { recursive: true, force: true });\n }\n}\n\ninterface DiffUploadOutcome {\n result: {\n stored: number;\n totalBytes: number;\n integrity: Record<string, string>;\n storage?: \"s3\" | \"memory\";\n };\n /** per-file SRI map covering the whole dist (uploaded → server SRI, skipped → previous manifest SRI) */\n fileIntegrity: Record<string, string>;\n uploaded: number;\n skipped: number;\n total: number;\n}\n\n/**\n * One workspace's upload leg with skip-unchanged diffing: resolve the previous\n * manifest's `files` map, hash the local dist, upload only new/changed files,\n * and return the merged per-file SRI map for the version manifest. Any failure\n * to resolve the previous map degrades to a full upload.\n */\nasync function diffUploadWorkspace(input: {\n origin: string;\n apiKey?: string;\n account: string;\n gateway: string;\n workspace: string;\n files: DistFile[];\n base: string;\n previousPin: PreviousPin | undefined;\n}): Promise<DiffUploadOutcome> {\n const localSri = localSriMap(input.files);\n let uploadFiles = input.files;\n let prevFiles: Record<string, string> | undefined;\n if (input.previousPin) {\n try {\n const resolved = await resolveSlotVersion({ base: input.base, pin: input.previousPin });\n if (resolved.files) {\n const diff = diffWorkspaceFiles(input.files, localSri, resolved.files);\n prevFiles = resolved.files;\n uploadFiles = diff.upload;\n if (diff.skipped > 0) {\n console.log(\n ` ${colors.dim(`${padRight(input.workspace, 28)} ${diff.skipped} unchanged file(s) — skipping (manifest diff)`)}`,\n );\n }\n }\n } catch {\n console.log(\n ` ${colors.dim(`${padRight(input.workspace, 28)} previous manifest unusable — uploading everything`)}`,\n );\n }\n }\n\n console.log(\n ` ${padRight(input.workspace, 28)} uploading ${uploadFiles.length} of ${input.files.length} files (${formatBundleMb(uploadFiles)}) → ${input.base}`,\n );\n const result =\n uploadFiles.length > 0\n ? await uploadWorkspaceDist({\n origin: input.origin,\n apiKey: input.apiKey,\n account: input.account,\n gateway: input.gateway,\n workspace: input.workspace,\n files: uploadFiles,\n })\n : { stored: 0, totalBytes: 0, integrity: {}, storage: undefined };\n return {\n result,\n fileIntegrity: mergedFileIntegrity(input.files, result.integrity, prevFiles),\n uploaded: result.stored,\n skipped: input.files.length - uploadFiles.length,\n total: input.files.length,\n };\n}\n\nfunction formatBundleMb(files: Array<{ bytes: Uint8Array }>): string {\n const total = files.reduce((sum, file) => sum + file.bytes.byteLength, 0);\n return `${(total / 1024 / 1024).toFixed(1)} MB`;\n}\n\nexport async function waitForPublishedConfig(opts: {\n account: string;\n gateway: string;\n publishConfig: BosConfigInput;\n registry?: string;\n timeoutMs?: number;\n intervalMs?: number;\n}): Promise<void> {\n const envTimeoutMs = Number(process.env.BOS_PUBLISH_CONFIRMATION_TIMEOUT_MS);\n const envIntervalMs = Number(process.env.BOS_PUBLISH_CONFIRMATION_INTERVAL_MS);\n const timeoutMs =\n opts.timeoutMs ?? (Number.isFinite(envTimeoutMs) ? envTimeoutMs : undefined) ?? 120_000;\n const intervalMs =\n opts.intervalMs ?? (Number.isFinite(envIntervalMs) ? envIntervalMs : undefined) ?? 3_000;\n const startedAt = Date.now();\n let lastError: unknown;\n\n while (Date.now() - startedAt < timeoutMs) {\n try {\n const verifiedConfig = await fetchBosConfigFromFastKv<BosConfigInput>(\n `bos://${opts.account}/${opts.gateway}`,\n opts.registry,\n );\n\n if (JSON.stringify(verifiedConfig) === JSON.stringify(opts.publishConfig)) {\n return;\n }\n } catch (error) {\n lastError = error;\n }\n\n await sleep(intervalMs);\n }\n\n const reason = lastError instanceof Error ? ` Last error: ${lastError.message}` : \"\";\n throw new Error(\n `Timed out waiting for publish confirmation at bos://${opts.account}/${opts.gateway}.${reason}`,\n );\n}\n\nexport async function isConfigAlreadyPublished(opts: {\n account: string;\n gateway: string;\n publishConfig: BosConfigInput;\n registry?: string;\n}): Promise<boolean> {\n try {\n const current = await fetchBosConfigFromFastKv<BosConfigInput>(\n `bos://${opts.account}/${opts.gateway}`,\n opts.registry,\n );\n return JSON.stringify(current) === JSON.stringify(opts.publishConfig);\n } catch {\n return false;\n }\n}\n\ninterface PublishToFastKvInput {\n bosConfig: BosConfig;\n runtimeConfig: RuntimeConfig | null;\n configDir: string;\n env: \"production\" | \"staging\";\n build: boolean;\n dryRun: boolean;\n verbose: boolean;\n packages: string;\n network?: \"mainnet\" | \"testnet\";\n privateKey?: string;\n wallet?: boolean;\n registry?: string;\n /** Skip the diff upload and re-upload every dist file (escape hatch). */\n fullUpload?: boolean;\n /** Publish `bosConfig` verbatim — no authored/published merge, no ts-form\n * config-only guard. Rollbacks use this: the payload is the previous\n * snapshot, already assembled. */\n exactPayload?: boolean;\n}\n\ninterface PublishToFastKvResult {\n status: \"published\" | \"error\" | \"dry-run\";\n registryUrl: string;\n txHash?: string;\n built?: string[];\n skipped?: string[];\n error?: string;\n publishConfig?: BosConfigInput;\n deployResults?: WorkspaceDeployResult[];\n fingerprint?: string;\n slotPins?: Record<string, string>;\n}\n\nexport type PublishedConfigState = \"fetched\" | \"missing\" | \"unreachable\";\n\nexport interface PublishPreflightPlan {\n isStaging: boolean;\n account: string;\n gateway: string;\n network: NetworkId;\n registryUrl: string;\n registryNamespace: string;\n targets: string[];\n useWallet: boolean;\n useCustody: boolean;\n strategy?: SigningStrategy;\n cdnOrigin: string | undefined;\n storageOrigin: string;\n storageApiKey?: string;\n /** The config currently published to FastKV (null when missing or unreachable). */\n publishedConfig: Record<string, unknown> | null;\n /** Whether the preflight FastKV read succeeded, found nothing, or failed. */\n publishedConfigState: PublishedConfigState;\n}\n\nexport type PublishPreflight =\n | { kind: \"dry-run\"; registryUrl: string }\n | { kind: \"error\"; registryUrl: string; error: string }\n | { kind: \"ready\"; plan: PublishPreflightPlan };\n\nconst FRAMEWORK_PACKAGE_NAMES = [\"better-near-auth\", \"every-plugin\", \"everything-dev\"] as const;\n\n/**\n * Whether npm serves `name@version` (true), definitively does not (404 —\n * false), or whether publishability could not be determined (network error,\n * unexpected status — null; callers must not block on that).\n */\nexport async function npmVersionPublished(name: string, version: string): Promise<boolean | null> {\n try {\n const res = await fetchResponse(`https://registry.npmjs.org/${name}/${version}`, {\n headers: { Accept: \"application/json\" },\n });\n if (res.status === 200) return true;\n if (res.status === 404) return false;\n return null;\n } catch {\n return null;\n }\n}\n\n/**\n * The npm-publish gate: a workspace that vendors the framework packages'\n * sources must not ship bundles ahead of npm — the update check, child\n * scaffolds (`bos init`), and template syncs all read those versions from\n * the registry. Reads only apply when the sources are workspace-local (the\n * parent repo); child projects install the framework from npm and are\n * inherently in sync.\n */\nexport async function findUnpublishedFrameworkPackages(\n configDir: string,\n): Promise<Array<{ name: string; version: string }>> {\n const unpublished: Array<{ name: string; version: string }> = [];\n for (const name of FRAMEWORK_PACKAGE_NAMES) {\n const manifestPath = join(configDir, \"packages\", name, \"package.json\");\n if (!existsSync(manifestPath)) continue;\n const version = (JSON.parse(readFileSync(manifestPath, \"utf-8\")) as { version?: string })\n .version;\n if (!version) continue;\n const published = await npmVersionPublished(name, version);\n if (published === false) {\n unpublished.push({ name, version });\n } else if (published === null) {\n console.log(\n colors.yellow(\n ` ! Could not verify ${name}@${version} on npm (registry unreachable) — continuing`,\n ),\n );\n }\n }\n return unpublished;\n}\n\n// Pure preflight (resolve, don't mutate) — every failure returns before the\n// build train runs (issue #287). Dry-run exits after the auth guards, before\n// any signing (keychain/TTY side effects).\nexport async function preflightPublish(input: PublishToFastKvInput): Promise<PublishPreflight> {\n const { configDir } = input;\n const bosConfig = input.bosConfig;\n\n const isStaging = input.env === \"staging\";\n const account = isStaging ? (bosConfig.staging?.account ?? bosConfig.account) : bosConfig.account;\n const gateway = isStaging ? (bosConfig.staging?.domain ?? bosConfig.domain) : bosConfig.domain;\n if (!gateway) {\n return {\n kind: \"error\",\n registryUrl: \"\",\n error: \"authored config must define domain to publish\",\n };\n }\n\n const network: NetworkId = input.network ?? getNetworkIdForAccount(account);\n const registryUrl = buildRegistryConfigUrlForNetwork(network, account, gateway, input.registry);\n const fail = (error: string): PublishPreflight => ({ kind: \"error\", registryUrl, error });\n\n const publishAuth: PublishConfig[\"auth\"] = bosConfig.publish?.auth;\n const governsWalletPublish = (publishAuth === \"session\" || input.wallet) && !input.privateKey;\n if (governsWalletPublish) {\n const session = readSessionHandle(configDir);\n if (!session?.credential) {\n return fail(\n (input.wallet\n ? \"--wallet requires\"\n : 'the authored config sets publish.auth = \"session\", but') +\n \" no CLI session is stored in .bos/ for this project. Run bos login to create one.\",\n );\n }\n if (session.credential.accountId && session.credential.accountId !== account) {\n return fail(\n `The CLI session was created for ${session.credential.accountId}, but the configured ` +\n `account is ${account}. Gasless wallet publish relays the FastKV write under the session's ` +\n \"NEAR account. Run bos login again under the matching account.\",\n );\n }\n }\n const useCustody = publishAuth === \"custody\" && !input.privateKey && !input.wallet;\n if (useCustody) {\n const session = readSessionHandle(configDir);\n const custodyWallet = session?.custodyWallet;\n if (!custodyWallet) {\n return fail(\n 'the authored config sets publish.auth = \"custody\", but no custody wallet is stored in ' +\n \".bos/. Run `bos login --custody` first.\",\n );\n }\n if (custodyWallet.network !== network) {\n return fail(\n `The custody wallet was registered on ${custodyWallet.network}, but this project publishes ` +\n `on ${network}. Run \\`bos login --custody\\` from a ${network} account.`,\n );\n }\n if (custodyWallet.nearAccountId !== account) {\n return fail(\n `publish.auth = \"custody\" publishes under the custody wallet's account, but the configured ` +\n `account is ${account} and the custody wallet account is ${custodyWallet.nearAccountId}. ` +\n \"Set the authored config's account to the custody wallet account.\",\n );\n }\n }\n\n if (input.dryRun) {\n return { kind: \"dry-run\", registryUrl };\n }\n\n const useWallet = input.wallet === true;\n let strategy: SigningStrategy | undefined;\n if (useWallet) {\n console.log(\n ` Signing via ${colors.cyan(\"gasless NEP-366 delegate action (relayed by the platform relayer)\")}`,\n );\n } else {\n try {\n strategy = await resolveSigningStrategy({ privateKey: input.privateKey, account, network });\n console.log(` Signing via ${colors.cyan(describeSigningStrategy(strategy))}`);\n } catch (error) {\n return fail(error instanceof Error ? error.message : \"Unknown error\");\n }\n }\n\n // CDN deploy credentials (ADR 0020) — resolved before the build train so a\n // missing bundle origin or BOS_STORAGE_API_KEY fails fast instead of after\n // the full build. Config-only publishes ship the committed config verbatim;\n // nothing is uploaded, so no deploy origins are required.\n const session = readSessionHandle(configDir);\n let cdnDeploy: CdnDeployInputs = {\n cdnOrigin: undefined,\n storageOrigin: \"\",\n apiKey: undefined,\n };\n if (input.build) {\n const unpublished = await findUnpublishedFrameworkPackages(configDir);\n if (unpublished.length > 0) {\n const listing = unpublished.map((pkg) => `${pkg.name}@${pkg.version}`).join(\", \");\n return fail(\n `Framework package(s) ${listing} are not published to npm yet — deploys must not ship ` +\n \"bundles ahead of the registry (child scaffolds and update checks read these versions). \" +\n \"Re-run the Release workflow (or `pnpm run release`) first.\",\n );\n }\n\n cdnDeploy = resolveCdnDeployInputs({\n env: process.env as Record<string, string | undefined>,\n bosConfig,\n session: session?.credential ?? null,\n custodyAccount: useCustody ? session?.custodyWallet?.nearAccountId : undefined,\n account,\n gateway,\n uploadsPlanned: input.build,\n });\n if (cdnDeploy.error) {\n return fail(cdnDeploy.error);\n }\n if (cdnDeploy.warning) {\n console.log(colors.yellow(` ! ${cdnDeploy.warning}`));\n }\n const cdnOrigin = cdnDeploy.cdnOrigin;\n if (!cdnOrigin) {\n return fail(\"CDN deploy resolved no bundle origin\");\n }\n const probeError = await probeStorageOrigin(cdnDeploy.storageOrigin);\n if (probeError) {\n return fail(\n `The bundle upload origin ${cdnDeploy.storageOrigin} is not running the platform API — ` +\n `${probeError}. Check BOS_STORAGE_ORIGIN / the bos login session, then re-run.`,\n );\n }\n }\n\n // Registry reachability + published-state capture: one FastKV read. A\n // missing config is fine for a first publish — an early signal, not a hard\n // gate. The captured config is the merge base for a config-only publish on\n // a TS-form project (authored hand-edits win; live pipeline state carries).\n let publishedConfig: Record<string, unknown> | null = null;\n let publishedConfigState: PublishedConfigState = \"missing\";\n try {\n publishedConfig = await fetchPublishedConfigFromFastKv<Record<string, unknown>>(registryUrl);\n publishedConfigState = \"fetched\";\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n if (message.startsWith(\"No config found\")) {\n console.log(colors.dim(\" Note: no published config found yet (first publish) — continuing\"));\n } else {\n publishedConfigState = \"unreachable\";\n console.log(colors.dim(\" Note: registry read failed — continuing\"));\n }\n }\n\n return {\n kind: \"ready\",\n plan: {\n isStaging,\n account,\n gateway,\n network,\n registryUrl,\n registryNamespace: getRegistryNamespaceForNetwork(network, input.registry),\n targets: selectWorkspaceTargets(input.packages, bosConfig),\n useWallet,\n useCustody,\n strategy,\n cdnOrigin: cdnDeploy.cdnOrigin,\n storageOrigin: cdnDeploy.storageOrigin,\n storageApiKey: cdnDeploy.apiKey,\n publishedConfig,\n publishedConfigState,\n },\n };\n}\n\n/**\n * The deploy-train publish core (ADR 0020, as amended): preflight\n * (storage/CDN creds + signing) fails fast before the build, then build →\n * upload → version-manifest pin → config write-back → FastKV publish +\n * read-back. Scoped to one plugin via `packages`, a single-plugin redeploy\n * ships real bytes to the storage origin — same as the full train, nothing\n * image-native left.\n */\nexport async function publishToFastKv(input: PublishToFastKvInput): Promise<PublishToFastKvResult> {\n const preflight = await preflightPublish(input);\n if (preflight.kind === \"error\") {\n return { status: \"error\", registryUrl: preflight.registryUrl, error: preflight.error };\n }\n if (preflight.kind === \"dry-run\") {\n return { status: \"dry-run\", registryUrl: preflight.registryUrl };\n }\n const plan = preflight.plan;\n const {\n isStaging,\n account,\n gateway,\n network,\n registryUrl,\n registryNamespace,\n targets,\n useWallet,\n useCustody,\n strategy,\n } = plan;\n const storageOrigin = plan.storageOrigin;\n const storageApiKey = plan.storageApiKey;\n\n const { configDir } = input;\n let bosConfig = input.bosConfig;\n const runtimeConfig = input.runtimeConfig;\n let built: string[] | undefined;\n let skipped: string[] | undefined;\n let deployResults: WorkspaceDeployResult[] | undefined;\n let refreshedRawConfig: BosConfigInput | null = null;\n\n if (input.build) {\n await generateCodeArtifacts(configDir, bosConfig, {\n env: \"production\",\n runtimeConfig: runtimeConfig ?? undefined,\n });\n\n const result = await buildWorkspaceTargets({\n configDir,\n bosConfig,\n runtimeConfig,\n targets,\n deploy: true,\n verbose: input.verbose,\n });\n built = result.built;\n skipped = result.skipped;\n deployResults = result.deployResults;\n\n if (deployResults) {\n const failures = deployResults.filter((r) => !r.success);\n if (failures.length > 0) {\n const total = deployResults.length;\n console.log();\n console.log(\n colors.error(\n ` ${icons.err} Deploy failed — ${failures.length} of ${total} workspace${total > 1 ? \"s\" : \"\"} failed`,\n ),\n );\n console.log();\n for (const f of failures) {\n const errorLine = (f.error ?? \"Failed\").split(\"\\n\")[0];\n console.log(` ${colors.error(icons.err)} ${padRight(f.key, 28)} ${errorLine}`);\n }\n console.log();\n if (!input.verbose) {\n console.log(colors.dim(\" Run with --verbose for full build output.\"));\n console.log();\n }\n return {\n status: \"error\" as const,\n registryUrl,\n built,\n skipped,\n deployResults,\n error: `${failures.length} of ${total} workspaces failed to deploy`,\n };\n }\n }\n\n const refreshed = await openResolution({ cwd: configDir });\n if (!refreshed?.config) {\n return {\n status: \"error\",\n registryUrl,\n built,\n skipped,\n deployResults,\n error: \"Failed to reload the config after build\",\n };\n }\n\n bosConfig = refreshed.config;\n refreshedRawConfig = refreshed.rawConfig;\n }\n\n const authoredPath = findConfigPath(configDir);\n const tsForm = authoredPath !== null && isAppDescriptorPath(authoredPath);\n let rawConfig: BosConfigInput;\n if (refreshedRawConfig) {\n rawConfig = refreshedRawConfig;\n } else {\n const opened = await openResolution({ cwd: configDir });\n if (!opened?.rawConfig) {\n return {\n status: \"error\",\n registryUrl,\n built,\n skipped,\n deployResults,\n error: \"Failed to load the authored config for publish\",\n };\n }\n rawConfig = opened.rawConfig;\n }\n if (\n tsForm &&\n !input.build &&\n input.exactPayload !== true &&\n plan.publishedConfigState === \"unreachable\"\n ) {\n // A config-only TS-form publish derives its pipeline state from the live\n // published config — publishing without it would wipe bundle URLs.\n return {\n status: \"error\",\n registryUrl,\n built,\n skipped,\n deployResults,\n error:\n \"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.\",\n };\n }\n let publishPayload: BosConfigInput =\n input.exactPayload === true\n ? input.bosConfig\n : tsForm && !input.build\n ? // Config-only publish on a TS-form project: the authored config owns\n // composition (hand-edits win) while live pipeline state (bundle\n // URLs, integrity) carries over from the published config.\n mergeAuthoredOverPublished((plan.publishedConfig ?? {}) as BosConfigInput, rawConfig)\n : rawConfig;\n\n const urlOrigin = plan.cdnOrigin ?? `https://${gateway}`;\n const deployTargets = (built ?? []).filter((key) => targets.includes(key));\n const platformEntries: DeployResultEntry[] = [];\n const fullUpload = input.fullUpload === true || process.env.BOS_FULL_UPLOAD === \"1\";\n // Previous per-workspace pins for the diff upload: the local pointer wins\n // (written after every confirmed publish), the published config is the\n // fallback. Neither → full upload.\n const prevPins: Record<string, PreviousPin> = fullUpload\n ? {}\n : {\n ...pinsFromPublishedConfig(\n (plan.publishedConfig ?? {}) as BosConfigInput,\n account,\n gateway,\n ),\n ...pinsFromDeployState(configDir, account, gateway),\n };\n\n console.log();\n console.log(` CDN deploy — uploading workspace dists to ${storageOrigin}...`);\n const sourcePublish = bosConfig.publish?.sourcePublish === true;\n const gitSha = await resolveGitSha(configDir);\n const memoryBackendAbort = (): PublishToFastKvResult => ({\n status: \"error\",\n registryUrl,\n built,\n skipped,\n deployResults,\n error:\n \"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.\",\n });\n for (const key of deployTargets) {\n const ws = resolveWorkspaceTarget(key, bosConfig, runtimeConfig, configDir);\n if (!ws) continue;\n\n const distFiles = await collectDistFiles(join(ws.path, \"dist\"));\n const workspaceBase = `${urlOrigin}/bundles/${account}/${gateway}/${key}/`;\n const startedAt = Date.now();\n // an upload failure aborts the train as a structured error — the\n // previously published version stays fully live (ticket 05)\n let outcome: DiffUploadOutcome;\n try {\n outcome = await diffUploadWorkspace({\n origin: storageOrigin,\n apiKey: storageApiKey,\n account,\n gateway,\n workspace: key,\n files: distFiles,\n base: workspaceBase,\n previousPin: prevPins[key],\n });\n } catch (error) {\n return {\n status: \"error\",\n registryUrl,\n built,\n skipped,\n deployResults,\n error: error instanceof Error ? error.message : `[publish] bundle upload for ${key} failed`,\n };\n }\n const report = readBuildReport(join(ws.path, \"dist\"));\n const ssrReport = readBuildReport(join(ws.path, \"dist\", \"ssr\"));\n // the entry SRI keyed by the build report's hashed name\n const integrity = report ? outcome.fileIntegrity[report.entry] : undefined;\n const ssrIntegrity = ssrReport?.entry\n ? outcome.fileIntegrity[`ssr/${ssrReport.entry}`]\n : undefined;\n\n let sourceRef: SourceRef | undefined;\n if (sourcePublish) {\n if (outcome.result.storage === \"memory\") {\n return memoryBackendAbort();\n }\n try {\n const sourceOutcome = await resolveSourceReference({\n origin: storageOrigin,\n apiKey: storageApiKey,\n account,\n gateway,\n workspace: key,\n base: workspaceBase,\n previousPin: prevPins[key],\n repoRoot: dirname(ws.path),\n workspaceDir: basename(ws.path),\n });\n if (\"memory\" in sourceOutcome) {\n return memoryBackendAbort();\n }\n sourceRef = sourceOutcome.source;\n } catch (error) {\n return {\n status: \"error\",\n registryUrl,\n built,\n skipped,\n deployResults,\n error:\n error instanceof Error ? error.message : `[publish] source archive for ${key} failed`,\n };\n }\n }\n\n const manifestPointer = await pinWorkspaceVersionManifest({\n origin: storageOrigin,\n apiKey: storageApiKey,\n account,\n gateway,\n workspace: key,\n report,\n ssrReport,\n integrityMap: outcome.fileIntegrity,\n files: outcome.fileIntegrity,\n ...(sourceRef ? { source: sourceRef } : {}),\n ...(gitSha ? { gitSha } : {}),\n });\n if (!manifestPointer) {\n return {\n status: \"error\",\n registryUrl,\n built,\n skipped,\n deployResults,\n error: `bundle upload for ${key}: version manifest missing — the dist predates hashed entry names (rebuild required; atomic-deploys hard break)`,\n };\n }\n\n if (outcome.result.storage === \"memory\" || manifestPointer.storage === \"memory\") {\n return memoryBackendAbort();\n }\n\n console.log(\n ` ${colors.green(icons.ok)} ${padRight(key, 28)} → ${urlOrigin}/bundles/${account}/${gateway}/${key}/ (${outcome.uploaded} uploaded, ${outcome.skipped} skipped, ${formatDuration(Date.now() - startedAt)})`,\n );\n\n platformEntries.push(\n ...platformUrlDeployEntries({\n origin: urlOrigin,\n account,\n gateway,\n key,\n kind: ws.kind,\n integrity,\n ssrIntegrity,\n pin: manifestPointer,\n }),\n );\n\n // Folder-form plugin ui: the workspace build also produces <plugin>/ui/dist\n // (web remoteEntry + ssr container) — upload it as its own bundle key and\n // pin <slot>.<key>.ui.* so the host can compose the ui surface in production.\n if (hasFolderFormUi(ws.path)) {\n const uiDistDir = join(ws.path, \"ui\", \"dist\");\n const uiSlot = ws.kind === \"app\" ? \"app\" : \"plugins\";\n const rawSlot = (uiSlot === \"app\" ? rawConfig.app : rawConfig.plugins) as\n | Record<string, Record<string, unknown> | undefined>\n | undefined;\n const rawUi = rawSlot?.[key]?.ui as Record<string, unknown> | undefined;\n const uiName = resolveUiRuntimeName(rawUi, join(ws.path, \"ui\"), key);\n let uiIntegrity: string | undefined;\n let uiSsrIntegrity: string | undefined;\n let uiManifestPointer: VersionManifestPin | undefined;\n if (existsSync(uiDistDir)) {\n const uiDistFiles = await collectDistFiles(uiDistDir);\n const uiStartedAt = Date.now();\n const uiOutcome = await diffUploadWorkspace({\n origin: storageOrigin,\n apiKey: storageApiKey,\n account,\n gateway,\n workspace: `${key}-ui`,\n files: uiDistFiles,\n base: `${urlOrigin}/bundles/${account}/${gateway}/${key}-ui/`,\n previousPin: prevPins[`${key}-ui`],\n });\n const uiReport = readBuildReport(uiDistDir);\n const uiSsrReport = readBuildReport(join(uiDistDir, \"ssr\"));\n uiIntegrity = uiReport ? uiOutcome.fileIntegrity[uiReport.entry] : undefined;\n uiSsrIntegrity = uiSsrReport?.entry\n ? uiOutcome.fileIntegrity[`ssr/${uiSsrReport.entry}`]\n : undefined;\n\n uiManifestPointer = await pinWorkspaceVersionManifest({\n origin: storageOrigin,\n apiKey: storageApiKey,\n account,\n gateway,\n workspace: `${key}-ui`,\n report: uiReport,\n ssrReport: uiSsrReport,\n integrityMap: uiOutcome.fileIntegrity,\n files: uiOutcome.fileIntegrity,\n });\n if (!uiManifestPointer) {\n return {\n status: \"error\",\n registryUrl,\n built,\n skipped,\n deployResults,\n error: `bundle upload for ${key}-ui: version manifest missing — the dist predates hashed entry names (rebuild required; atomic-deploys hard break)`,\n };\n }\n\n if (uiOutcome.result.storage === \"memory\" || uiManifestPointer.storage === \"memory\") {\n return memoryBackendAbort();\n }\n\n console.log(\n ` ${colors.green(icons.ok)} ${padRight(`${key}-ui`, 28)} → ${urlOrigin}/bundles/${account}/${gateway}/${key}-ui/ (${uiOutcome.uploaded} uploaded, ${uiOutcome.skipped} skipped, ${formatDuration(Date.now() - uiStartedAt)})`,\n );\n } else {\n console.log(\n ` ${colors.green(icons.ok)} ${padRight(`${key}-ui`, 28)} → ${urlOrigin}/bundles/${account}/${gateway}/${key}-ui/`,\n );\n }\n\n platformEntries.push(\n ...pluginUiUrlDeployEntries({\n origin: urlOrigin,\n account,\n gateway,\n key,\n kind: ws.kind,\n integrity: uiIntegrity,\n ssrIntegrity: uiSsrIntegrity,\n name: uiName,\n pin: uiManifestPointer,\n }),\n );\n }\n }\n\n if (platformEntries.length > 0) {\n const merged = applyDeployResults(rawConfig as Record<string, unknown>, platformEntries);\n if (!tsForm) {\n // Legacy JSON-form children keep their local write-back; TS-form keeps\n // pipeline state in FastKV only — a write-back would shadow the\n // authored bos.app.ts descriptor.\n try {\n writeFileSync(join(configDir, \"bos.config.json\"), `${JSON.stringify(merged, null, 2)}\\n`);\n } catch (error) {\n return {\n status: \"error\",\n registryUrl,\n built,\n skipped,\n deployResults,\n error: `Failed to write bundle URLs to bos.config.json: ${error instanceof Error ? error.message : String(error)}`,\n };\n }\n }\n publishPayload = merged as BosConfigInput;\n }\n if (isStaging) {\n publishPayload = { ...publishPayload, domain: gateway };\n }\n\n const pinVerification = await verifyRollbackSnapshot(publishPayload);\n if (pinVerification.verifiable && !pinVerification.ok) {\n const failures = pinVerification.slots\n .filter((check) => !check.ok)\n .map((check) => ` ${check.slot}: ${check.reason}`)\n .join(\"\\n\");\n console.error(\n ` ${colors.error(icons.err)} Publish blocked — a pinned slot's bytes do not serve at its production URL:\\n${failures}`,\n );\n return {\n status: \"error\",\n registryUrl,\n built,\n skipped,\n deployResults,\n error: `Publish blocked — pinned slot bytes missing or SRI mismatched:\\n${failures}`,\n };\n }\n\n const registryKey = `apps/${account}/${gateway}/bos.config.json`;\n const publishedAt = new Date().toISOString();\n // version identity printed + returned for every deploy (atomic-deploys 12)\n const fingerprint = pointerFingerprint(publishPayload as never);\n const publishSlotPins = slotPins(publishPayload as never);\n const registryEntries: Record<string, string> = {\n [registryKey]: JSON.stringify(publishPayload),\n };\n const manifestKey = `apps/${account}/${gateway}/manifests/${publishedAt.replace(/[:.]/g, \"-\")}.json`;\n registryEntries[manifestKey] = JSON.stringify({\n account,\n gateway,\n network,\n publishedAt,\n registryUrl,\n });\n\n console.log();\n console.log(\" Publishing to:\");\n console.log(` ${colors.cyan(registryUrl)}`);\n console.log(` ${colors.dim(\"Fingerprint:\")} ${fingerprint}`);\n for (const [slot, pin] of Object.entries(publishSlotPins)) {\n console.log(` ${colors.dim(`${slot}:`)}`);\n console.log(` ${pin}`);\n }\n console.log(\n ` ${colors.dim(`+ per-deploy manifest ${manifestKey.split(\"/\").pop()} (audit trail)`)}`,\n );\n\n // Record the live pins for the next deploy's diff upload — only after the\n // config is confirmed published, so the pointer never claims undeployed state.\n const persistDeployState = () => {\n try {\n const pins = pinsFromPublishedConfig(publishPayload as BosConfigInput, account, gateway);\n if (Object.keys(pins).length > 0) {\n upsertDeployStatePins(configDir, account, gateway, pins);\n }\n } catch (error) {\n console.log(\n colors.dim(\n ` Note: could not update .bos/deploy-state.json — ${error instanceof Error ? error.message : String(error)}`,\n ),\n );\n }\n };\n\n try {\n const alreadyPublished = await isConfigAlreadyPublished({\n account,\n gateway,\n publishConfig: publishPayload,\n registry: input.registry,\n });\n if (alreadyPublished) {\n console.log(\" Already up to date — skipping transaction\");\n persistDeployState();\n return {\n status: \"published\",\n registryUrl,\n built,\n skipped,\n deployResults,\n publishConfig: publishPayload,\n };\n }\n\n console.log(` Submitting transaction on ${network}...`);\n\n let result: { success: boolean; txHash?: string };\n if (useCustody) {\n const custodyWallet = readSessionHandle(configDir)?.custodyWallet;\n if (!custodyWallet) {\n return {\n status: \"error\",\n registryUrl,\n error:\n 'the authored config sets publish.auth = \"custody\", but no custody wallet is stored in ' +\n \".bos/. Run `bos login --custody` first.\",\n };\n }\n await ensureCustodyBalance(custodyWallet);\n console.log(\n ` Signing via ${colors.cyan(`OutLayer custody wallet ${custodyWallet.nearAccountId}`)}`,\n );\n result = await submitRegistryWriteCustody({\n contract: registryNamespace,\n args: registryEntries,\n custodyWallet,\n idempotencyKey: `bos-publish-${fingerprint}`,\n });\n } else if (useWallet) {\n const session = readSessionHandle(configDir);\n const credential = session?.credential;\n if (!credential || credential.accountId !== account) {\n return {\n status: \"error\",\n registryUrl,\n error: `--wallet requires a CLI session under ${account}. Run bos login first.`,\n };\n }\n const record = await ensureDelegateKey({\n configDir,\n account,\n contract: registryNamespace,\n network,\n siteUrl: credential.siteUrl,\n });\n result = await submitRegistryWriteDelegated({\n account,\n contract: registryNamespace,\n network,\n args: registryEntries,\n delegatePrivateKey: record.privateKey,\n relayEndpoint: `${credential.siteUrl}/api/auth/near/relay`,\n apiKey: credential.apiKey,\n });\n } else {\n if (!strategy) {\n return {\n status: \"error\",\n registryUrl,\n error: \"non-wallet publish requires a resolved signing strategy\",\n };\n }\n result = await submitRegistryWrite(\n {\n account,\n contract: registryNamespace,\n method: \"__fastdata_kv\",\n args: registryEntries,\n network,\n privateKey: input.privateKey,\n },\n strategy,\n );\n }\n\n if (result.txHash) {\n console.log(` Transaction submitted: ${colors.dim(result.txHash)}`);\n }\n\n console.log(\" Waiting for publish confirmation...\");\n await waitForPublishedConfig({\n account,\n gateway,\n publishConfig: publishPayload,\n registry: input.registry,\n });\n persistDeployState();\n\n return {\n status: \"published\",\n registryUrl,\n txHash: result.txHash,\n fingerprint,\n slotPins: publishSlotPins,\n built,\n skipped,\n deployResults,\n publishConfig: publishPayload,\n };\n } catch (error) {\n return {\n status: \"error\",\n registryUrl,\n error: formatNearError(error),\n built,\n skipped,\n deployResults,\n };\n }\n}\n\nfunction formatNearError(error: unknown): string {\n const message = error instanceof Error ? error.message : String(error);\n\n if (message.includes(\"does not have enough allowance\")) {\n return (\n \"The publish access key has insufficient allowance to cover this transaction.\\n\" +\n \" Regenerate your key with a higher allowance:\\n\" +\n \" bos key generate\\n\" +\n ` Original: ${message}`\n );\n }\n\n if (message.includes(\"exceeded gas\") || message.includes(\"GasLimitExceeded\")) {\n return `Transaction exceeded gas limit.\\n Original: ${message}`;\n }\n\n if (message.includes(\"timeout\") || message.includes(\"Timeout\")) {\n return `Transaction timed out. Check NEAR network status.\\n Original: ${message}`;\n }\n\n return message;\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
const require_integrity = require('./integrity.cjs');
|
|
2
|
+
const require_workspace_subtree = require('./utils/workspace-subtree.cjs');
|
|
3
|
+
let node_fs = require("node:fs");
|
|
4
|
+
let node_path = require("node:path");
|
|
5
|
+
let node_crypto = require("node:crypto");
|
|
6
|
+
let node_os = require("node:os");
|
|
7
|
+
let tar = require("tar");
|
|
8
|
+
|
|
9
|
+
//#region src/source-archive.ts
|
|
10
|
+
/** Temp directory for a source archive leg (caller passes it as `outDir`). */
|
|
11
|
+
function makeSourceArchiveTempDir(prefix = "bos-source-archive-") {
|
|
12
|
+
return (0, node_fs.mkdtempSync)((0, node_path.join)((0, node_os.tmpdir)(), prefix));
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Build the workspace's source archive in `outDir`: the shared subtree ignore
|
|
16
|
+
* set (`listWorkspaceSubtreeFiles`) selects the files; `tar.create` receives
|
|
17
|
+
* that explicit list, so its own ignore logic never re-derives the selection.
|
|
18
|
+
* The archive name is content-addressed — identical source bytes produce the
|
|
19
|
+
* identical `source.<16hex>.tar.gz` (gzip is deterministic for the same input
|
|
20
|
+
* bytes; file mtimes differ only when source files change, which changes the
|
|
21
|
+
* name anyway).
|
|
22
|
+
*/
|
|
23
|
+
async function buildSourceArchive(input) {
|
|
24
|
+
const files = await require_workspace_subtree.listWorkspaceSubtreeFiles(input.repoRoot, input.workspace);
|
|
25
|
+
if (files.length === 0) throw new Error(`[source-archive] workspace subtree is empty: ${input.workspace}`);
|
|
26
|
+
const pendingPath = (0, node_path.join)(input.outDir, ".pending.tar.gz");
|
|
27
|
+
await (0, tar.create)({
|
|
28
|
+
file: pendingPath,
|
|
29
|
+
cwd: input.repoRoot,
|
|
30
|
+
portable: true,
|
|
31
|
+
gzip: { level: 9 }
|
|
32
|
+
}, files);
|
|
33
|
+
const bytes = (0, node_fs.readFileSync)(pendingPath);
|
|
34
|
+
const name = `source.${(0, node_crypto.createHash)("sha256").update(bytes).digest("hex").slice(0, 16)}.tar.gz`;
|
|
35
|
+
const path = (0, node_path.join)(input.outDir, name);
|
|
36
|
+
(0, node_fs.renameSync)(pendingPath, path);
|
|
37
|
+
return {
|
|
38
|
+
file: `source/${name}`,
|
|
39
|
+
path,
|
|
40
|
+
integrity: require_integrity.computeSriHash(bytes),
|
|
41
|
+
cleanup: () => (0, node_fs.rmSync)(input.outDir, {
|
|
42
|
+
recursive: true,
|
|
43
|
+
force: true
|
|
44
|
+
})
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
//#endregion
|
|
49
|
+
exports.buildSourceArchive = buildSourceArchive;
|
|
50
|
+
exports.makeSourceArchiveTempDir = makeSourceArchiveTempDir;
|
|
51
|
+
//# sourceMappingURL=source-archive.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"source-archive.cjs","names":["mkdtempSync","join","tmpdir","listWorkspaceSubtreeFiles","tarCreate","readFileSync","createHash","computeSriHash","rmSync"],"sources":["../src/source-archive.ts"],"sourcesContent":["import { createHash } from \"node:crypto\";\nimport { mkdtempSync, readFileSync, renameSync, rmSync } from \"node:fs\";\nimport { tmpdir } from \"node:os\";\nimport { join } from \"node:path\";\nimport { create as tarCreate } from \"tar\";\nimport { computeSriHash } from \"./integrity\";\nimport { listWorkspaceSubtreeFiles } from \"./utils/workspace-subtree\";\n\n/** Temp directory for a source archive leg (caller passes it as `outDir`). */\nexport function makeSourceArchiveTempDir(prefix = \"bos-source-archive-\"): string {\n return mkdtempSync(join(tmpdir(), prefix));\n}\n\n/**\n * A per-workspace source archive built for the deploy train (#406): the\n * workspace subtree tar'd under repo-root-relative paths, content-addressed\n * by the sha256 of its own bytes, and paired with the local SRI used for\n * client-side dedupe (the manifest field itself carries the server SRI).\n */\nexport interface SourceArchive {\n /** Object path within the workspace's bundle namespace: `source/<name>.tar.gz`. */\n file: string;\n /** Absolute path of the built archive on disk. */\n path: string;\n /** Local SRI of the archive bytes — dedupe key against the previous pin. */\n integrity: string;\n /** Removes the archive's temp directory. */\n cleanup: () => void;\n}\n\n/**\n * Build the workspace's source archive in `outDir`: the shared subtree ignore\n * set (`listWorkspaceSubtreeFiles`) selects the files; `tar.create` receives\n * that explicit list, so its own ignore logic never re-derives the selection.\n * The archive name is content-addressed — identical source bytes produce the\n * identical `source.<16hex>.tar.gz` (gzip is deterministic for the same input\n * bytes; file mtimes differ only when source files change, which changes the\n * name anyway).\n */\nexport async function buildSourceArchive(input: {\n repoRoot: string;\n workspace: string;\n outDir: string;\n}): Promise<SourceArchive> {\n const files = await listWorkspaceSubtreeFiles(input.repoRoot, input.workspace);\n if (files.length === 0) {\n throw new Error(`[source-archive] workspace subtree is empty: ${input.workspace}`);\n }\n\n const pendingPath = join(input.outDir, \".pending.tar.gz\");\n await tarCreate(\n { file: pendingPath, cwd: input.repoRoot, portable: true, gzip: { level: 9 } },\n files,\n );\n\n const bytes = readFileSync(pendingPath);\n const name = `source.${createHash(\"sha256\").update(bytes).digest(\"hex\").slice(0, 16)}.tar.gz`;\n const path = join(input.outDir, name);\n renameSync(pendingPath, path);\n\n return {\n file: `source/${name}`,\n path,\n integrity: computeSriHash(bytes),\n cleanup: () => rmSync(input.outDir, { recursive: true, force: true }),\n };\n}\n"],"mappings":";;;;;;;;;;AASA,SAAgB,yBAAyB,SAAS,uBAA+B;CAC/E,WAAOA,yBAAYC,oBAAKC,gBAAO,GAAG,MAAM,CAAC;AAC3C;;;;;;;;;;AA4BA,eAAsB,mBAAmB,OAId;CACzB,MAAM,QAAQ,MAAMC,oDAA0B,MAAM,UAAU,MAAM,SAAS;CAC7E,IAAI,MAAM,WAAW,GACnB,MAAM,IAAI,MAAM,gDAAgD,MAAM,WAAW;CAGnF,MAAM,kBAAcF,gBAAK,MAAM,QAAQ,iBAAiB;CACxD,UAAMG,YACJ;EAAE,MAAM;EAAa,KAAK,MAAM;EAAU,UAAU;EAAM,MAAM,EAAE,OAAO,EAAE;CAAE,GAC7E,KACF;CAEA,MAAM,YAAQC,sBAAa,WAAW;CACtC,MAAM,OAAO,cAAUC,wBAAW,QAAQ,CAAC,CAAC,OAAO,KAAK,CAAC,CAAC,OAAO,KAAK,CAAC,CAAC,MAAM,GAAG,EAAE,EAAE;CACrF,MAAM,WAAOL,gBAAK,MAAM,QAAQ,IAAI;CACpC,wBAAW,aAAa,IAAI;CAE5B,OAAO;EACL,MAAM,UAAU;EAChB;EACA,WAAWM,iCAAe,KAAK;EAC/B,mBAAeC,gBAAO,MAAM,QAAQ;GAAE,WAAW;GAAM,OAAO;EAAK,CAAC;CACtE;AACF"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { computeSriHash } from "./integrity.mjs";
|
|
2
|
+
import { listWorkspaceSubtreeFiles } from "./utils/workspace-subtree.mjs";
|
|
3
|
+
import { mkdtempSync, readFileSync, renameSync, rmSync } from "node:fs";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
import { createHash } from "node:crypto";
|
|
6
|
+
import { tmpdir } from "node:os";
|
|
7
|
+
import { create } from "tar";
|
|
8
|
+
|
|
9
|
+
//#region src/source-archive.ts
|
|
10
|
+
/** Temp directory for a source archive leg (caller passes it as `outDir`). */
|
|
11
|
+
function makeSourceArchiveTempDir(prefix = "bos-source-archive-") {
|
|
12
|
+
return mkdtempSync(join(tmpdir(), prefix));
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Build the workspace's source archive in `outDir`: the shared subtree ignore
|
|
16
|
+
* set (`listWorkspaceSubtreeFiles`) selects the files; `tar.create` receives
|
|
17
|
+
* that explicit list, so its own ignore logic never re-derives the selection.
|
|
18
|
+
* The archive name is content-addressed — identical source bytes produce the
|
|
19
|
+
* identical `source.<16hex>.tar.gz` (gzip is deterministic for the same input
|
|
20
|
+
* bytes; file mtimes differ only when source files change, which changes the
|
|
21
|
+
* name anyway).
|
|
22
|
+
*/
|
|
23
|
+
async function buildSourceArchive(input) {
|
|
24
|
+
const files = await listWorkspaceSubtreeFiles(input.repoRoot, input.workspace);
|
|
25
|
+
if (files.length === 0) throw new Error(`[source-archive] workspace subtree is empty: ${input.workspace}`);
|
|
26
|
+
const pendingPath = join(input.outDir, ".pending.tar.gz");
|
|
27
|
+
await create({
|
|
28
|
+
file: pendingPath,
|
|
29
|
+
cwd: input.repoRoot,
|
|
30
|
+
portable: true,
|
|
31
|
+
gzip: { level: 9 }
|
|
32
|
+
}, files);
|
|
33
|
+
const bytes = readFileSync(pendingPath);
|
|
34
|
+
const name = `source.${createHash("sha256").update(bytes).digest("hex").slice(0, 16)}.tar.gz`;
|
|
35
|
+
const path = join(input.outDir, name);
|
|
36
|
+
renameSync(pendingPath, path);
|
|
37
|
+
return {
|
|
38
|
+
file: `source/${name}`,
|
|
39
|
+
path,
|
|
40
|
+
integrity: computeSriHash(bytes),
|
|
41
|
+
cleanup: () => rmSync(input.outDir, {
|
|
42
|
+
recursive: true,
|
|
43
|
+
force: true
|
|
44
|
+
})
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
//#endregion
|
|
49
|
+
export { buildSourceArchive, makeSourceArchiveTempDir };
|
|
50
|
+
//# sourceMappingURL=source-archive.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"source-archive.mjs","names":["tarCreate"],"sources":["../src/source-archive.ts"],"sourcesContent":["import { createHash } from \"node:crypto\";\nimport { mkdtempSync, readFileSync, renameSync, rmSync } from \"node:fs\";\nimport { tmpdir } from \"node:os\";\nimport { join } from \"node:path\";\nimport { create as tarCreate } from \"tar\";\nimport { computeSriHash } from \"./integrity\";\nimport { listWorkspaceSubtreeFiles } from \"./utils/workspace-subtree\";\n\n/** Temp directory for a source archive leg (caller passes it as `outDir`). */\nexport function makeSourceArchiveTempDir(prefix = \"bos-source-archive-\"): string {\n return mkdtempSync(join(tmpdir(), prefix));\n}\n\n/**\n * A per-workspace source archive built for the deploy train (#406): the\n * workspace subtree tar'd under repo-root-relative paths, content-addressed\n * by the sha256 of its own bytes, and paired with the local SRI used for\n * client-side dedupe (the manifest field itself carries the server SRI).\n */\nexport interface SourceArchive {\n /** Object path within the workspace's bundle namespace: `source/<name>.tar.gz`. */\n file: string;\n /** Absolute path of the built archive on disk. */\n path: string;\n /** Local SRI of the archive bytes — dedupe key against the previous pin. */\n integrity: string;\n /** Removes the archive's temp directory. */\n cleanup: () => void;\n}\n\n/**\n * Build the workspace's source archive in `outDir`: the shared subtree ignore\n * set (`listWorkspaceSubtreeFiles`) selects the files; `tar.create` receives\n * that explicit list, so its own ignore logic never re-derives the selection.\n * The archive name is content-addressed — identical source bytes produce the\n * identical `source.<16hex>.tar.gz` (gzip is deterministic for the same input\n * bytes; file mtimes differ only when source files change, which changes the\n * name anyway).\n */\nexport async function buildSourceArchive(input: {\n repoRoot: string;\n workspace: string;\n outDir: string;\n}): Promise<SourceArchive> {\n const files = await listWorkspaceSubtreeFiles(input.repoRoot, input.workspace);\n if (files.length === 0) {\n throw new Error(`[source-archive] workspace subtree is empty: ${input.workspace}`);\n }\n\n const pendingPath = join(input.outDir, \".pending.tar.gz\");\n await tarCreate(\n { file: pendingPath, cwd: input.repoRoot, portable: true, gzip: { level: 9 } },\n files,\n );\n\n const bytes = readFileSync(pendingPath);\n const name = `source.${createHash(\"sha256\").update(bytes).digest(\"hex\").slice(0, 16)}.tar.gz`;\n const path = join(input.outDir, name);\n renameSync(pendingPath, path);\n\n return {\n file: `source/${name}`,\n path,\n integrity: computeSriHash(bytes),\n cleanup: () => rmSync(input.outDir, { recursive: true, force: true }),\n };\n}\n"],"mappings":";;;;;;;;;;AASA,SAAgB,yBAAyB,SAAS,uBAA+B;CAC/E,OAAO,YAAY,KAAK,OAAO,GAAG,MAAM,CAAC;AAC3C;;;;;;;;;;AA4BA,eAAsB,mBAAmB,OAId;CACzB,MAAM,QAAQ,MAAM,0BAA0B,MAAM,UAAU,MAAM,SAAS;CAC7E,IAAI,MAAM,WAAW,GACnB,MAAM,IAAI,MAAM,gDAAgD,MAAM,WAAW;CAGnF,MAAM,cAAc,KAAK,MAAM,QAAQ,iBAAiB;CACxD,MAAMA,OACJ;EAAE,MAAM;EAAa,KAAK,MAAM;EAAU,UAAU;EAAM,MAAM,EAAE,OAAO,EAAE;CAAE,GAC7E,KACF;CAEA,MAAM,QAAQ,aAAa,WAAW;CACtC,MAAM,OAAO,UAAU,WAAW,QAAQ,CAAC,CAAC,OAAO,KAAK,CAAC,CAAC,OAAO,KAAK,CAAC,CAAC,MAAM,GAAG,EAAE,EAAE;CACrF,MAAM,OAAO,KAAK,MAAM,QAAQ,IAAI;CACpC,WAAW,aAAa,IAAI;CAE5B,OAAO;EACL,MAAM,UAAU;EAChB;EACA,WAAW,eAAe,KAAK;EAC/B,eAAe,OAAO,MAAM,QAAQ;GAAE,WAAW;GAAM,OAAO;EAAK,CAAC;CACtE;AACF"}
|
package/dist/storage-upload.cjs
CHANGED
|
@@ -38,8 +38,13 @@ async function uploadBundle(input) {
|
|
|
38
38
|
}))
|
|
39
39
|
});
|
|
40
40
|
}
|
|
41
|
+
/** Per-request file-count cap for `uploadWorkspaceDist`: each file costs the
|
|
42
|
+
* server a stat+put round trip against object storage, so request count — not
|
|
43
|
+
* byte size — is what keeps a batch inside the CDN proxy's response timeout. */
|
|
44
|
+
const DEFAULT_MAX_FILES_PER_REQUEST = 100;
|
|
41
45
|
async function uploadWorkspaceDist(input) {
|
|
42
46
|
const maxRequestBytes = input.maxRequestBytes ?? (Number(process.env.BOS_MAX_BUNDLE_UPLOAD_BYTES) || 67108864);
|
|
47
|
+
const maxFilesPerRequest = input.maxFilesPerRequest ?? 100;
|
|
43
48
|
const merged = {
|
|
44
49
|
stored: 0,
|
|
45
50
|
totalBytes: 0,
|
|
@@ -69,7 +74,7 @@ async function uploadWorkspaceDist(input) {
|
|
|
69
74
|
for (const file of input.files) {
|
|
70
75
|
const cost = overheadPerFile(file);
|
|
71
76
|
if (cost > maxRequestBytes) throw new Error(`[publish] ${input.workspace}/${file.path} (${file.bytes.byteLength} bytes) exceeds the per-request upload ceiling`);
|
|
72
|
-
if (batchBytes + cost > maxRequestBytes) await flush();
|
|
77
|
+
if (batchBytes + cost > maxRequestBytes || batch.length >= maxFilesPerRequest) await flush();
|
|
73
78
|
batch.push(file);
|
|
74
79
|
batchBytes += cost;
|
|
75
80
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"storage-upload.cjs","names":["readdir","join","stat","relative","sep","readFile","createUploadClient"],"sources":["../src/storage-upload.ts"],"sourcesContent":["import { readdir, readFile, stat } from \"node:fs/promises\";\nimport { join, relative, sep } from \"node:path\";\nimport { createUploadClient } from \"@everything-dev/upload-app/client\";\n\nexport interface DistFile {\n path: string;\n bytes: Uint8Array;\n}\n\nexport interface BundleUploadResult {\n stored: number;\n unchanged?: number;\n totalBytes: number;\n integrity: Record<string, string>;\n storage?: \"s3\" | \"memory\";\n}\n\nasync function walkDist(dir: string, baseDir: string): Promise<DistFile[]> {\n const entries = await readdir(dir, { withFileTypes: true });\n const files: DistFile[] = [];\n for (const entry of entries.sort((a, b) => a.name.localeCompare(b.name))) {\n const fullPath = join(dir, entry.name);\n if (entry.isDirectory()) {\n files.push(...(await walkDist(fullPath, baseDir)));\n continue;\n }\n const info = await stat(fullPath);\n if (!info.isFile()) continue;\n files.push({\n path: relative(baseDir, fullPath).split(sep).join(\"/\"),\n bytes: new Uint8Array(await readFile(fullPath)),\n });\n }\n return files;\n}\n\nexport async function collectDistFiles(distDir: string): Promise<DistFile[]> {\n return walkDist(distDir, distDir);\n}\n\nexport async function uploadBundle(input: {\n origin: string;\n apiKey?: string;\n account: string;\n gateway: string;\n workspace: string;\n files: DistFile[];\n fetchImpl?: typeof fetch;\n}): Promise<BundleUploadResult> {\n const client = createUploadClient({\n origin: input.origin,\n apiKey: input.apiKey,\n fetchImpl: input.fetchImpl,\n });\n const result = await client.uploadBundle({\n account: input.account,\n gateway: input.gateway,\n workspace: input.workspace,\n files: input.files.map((file) => ({\n path: file.path,\n contentBase64: Buffer.from(file.bytes).toString(\"base64\"),\n })),\n });\n return result;\n}\n\nexport async function uploadWorkspaceDist(input: {\n origin: string;\n apiKey?: string;\n account: string;\n gateway: string;\n workspace: string;\n files: DistFile[];\n maxRequestBytes?: number;\n}): Promise<BundleUploadResult> {\n const maxRequestBytes =\n input.maxRequestBytes ?? (Number(process.env.BOS_MAX_BUNDLE_UPLOAD_BYTES) || 64 * 1024 * 1024);\n\n const merged: BundleUploadResult = { stored: 0, totalBytes: 0, integrity: {} };\n let batch: DistFile[] = [];\n let batchBytes = 0;\n const overheadPerFile = (file: DistFile): number =>\n Math.ceil(file.bytes.byteLength / 3) * 4 + file.path.length + 64;\n\n const flush = async () => {\n if (batch.length === 0) return;\n const result = await uploadBundle({\n origin: input.origin,\n apiKey: input.apiKey,\n account: input.account,\n gateway: input.gateway,\n workspace: input.workspace,\n files: batch,\n });\n merged.stored += result.stored;\n merged.totalBytes += result.totalBytes;\n merged.unchanged = (merged.unchanged ?? 0) + (result.unchanged ?? 0);\n Object.assign(merged.integrity, result.integrity);\n merged.storage = result.storage;\n batch = [];\n batchBytes = 0;\n };\n\n for (const file of input.files) {\n const cost = overheadPerFile(file);\n if (cost > maxRequestBytes) {\n throw new Error(\n `[publish] ${input.workspace}/${file.path} (${file.bytes.byteLength} bytes) exceeds the per-request upload ceiling`,\n );\n }\n if (batchBytes + cost > maxRequestBytes) {\n await flush();\n }\n batch.push(file);\n batchBytes += cost;\n }\n await flush();\n\n return merged;\n}\n"],"mappings":";;;;;AAiBA,eAAe,SAAS,KAAa,SAAsC;CACzE,MAAM,UAAU,UAAMA,0BAAQ,KAAK,EAAE,eAAe,KAAK,CAAC;CAC1D,MAAM,QAAoB,CAAC;CAC3B,KAAK,MAAM,SAAS,QAAQ,MAAM,GAAG,MAAM,EAAE,KAAK,cAAc,EAAE,IAAI,CAAC,GAAG;EACxE,MAAM,eAAWC,gBAAK,KAAK,MAAM,IAAI;EACrC,IAAI,MAAM,YAAY,GAAG;GACvB,MAAM,KAAK,GAAI,MAAM,SAAS,UAAU,OAAO,CAAE;GACjD;EACF;EAEA,IAAI,EAAC,UADcC,uBAAK,QAAQ,EACvB,CAAC,OAAO,GAAG;EACpB,MAAM,KAAK;GACT,UAAMC,oBAAS,SAAS,QAAQ,CAAC,CAAC,MAAMC,aAAG,CAAC,CAAC,KAAK,GAAG;GACrD,OAAO,IAAI,WAAW,UAAMC,2BAAS,QAAQ,CAAC;EAChD,CAAC;CACH;CACA,OAAO;AACT;AAEA,eAAsB,iBAAiB,SAAsC;CAC3E,OAAO,SAAS,SAAS,OAAO;AAClC;AAEA,eAAsB,aAAa,OAQH;CAe9B,OAAO,MAdQC,kCAAmB;EAChC,QAAQ,MAAM;EACd,QAAQ,MAAM;EACd,WAAW,MAAM;CACnB,CAC0B,CAAC,CAAC,aAAa;EACvC,SAAS,MAAM;EACf,SAAS,MAAM;EACf,WAAW,MAAM;EACjB,OAAO,MAAM,MAAM,KAAK,UAAU;GAChC,MAAM,KAAK;GACX,eAAe,OAAO,KAAK,KAAK,KAAK,CAAC,CAAC,SAAS,QAAQ;EAC1D,EAAE;CACJ,CAAC;AAEH;
|
|
1
|
+
{"version":3,"file":"storage-upload.cjs","names":["readdir","join","stat","relative","sep","readFile","createUploadClient"],"sources":["../src/storage-upload.ts"],"sourcesContent":["import { readdir, readFile, stat } from \"node:fs/promises\";\nimport { join, relative, sep } from \"node:path\";\nimport { createUploadClient } from \"@everything-dev/upload-app/client\";\n\nexport interface DistFile {\n path: string;\n bytes: Uint8Array;\n}\n\nexport interface BundleUploadResult {\n stored: number;\n unchanged?: number;\n totalBytes: number;\n integrity: Record<string, string>;\n storage?: \"s3\" | \"memory\";\n}\n\nasync function walkDist(dir: string, baseDir: string): Promise<DistFile[]> {\n const entries = await readdir(dir, { withFileTypes: true });\n const files: DistFile[] = [];\n for (const entry of entries.sort((a, b) => a.name.localeCompare(b.name))) {\n const fullPath = join(dir, entry.name);\n if (entry.isDirectory()) {\n files.push(...(await walkDist(fullPath, baseDir)));\n continue;\n }\n const info = await stat(fullPath);\n if (!info.isFile()) continue;\n files.push({\n path: relative(baseDir, fullPath).split(sep).join(\"/\"),\n bytes: new Uint8Array(await readFile(fullPath)),\n });\n }\n return files;\n}\n\nexport async function collectDistFiles(distDir: string): Promise<DistFile[]> {\n return walkDist(distDir, distDir);\n}\n\nexport async function uploadBundle(input: {\n origin: string;\n apiKey?: string;\n account: string;\n gateway: string;\n workspace: string;\n files: DistFile[];\n fetchImpl?: typeof fetch;\n}): Promise<BundleUploadResult> {\n const client = createUploadClient({\n origin: input.origin,\n apiKey: input.apiKey,\n fetchImpl: input.fetchImpl,\n });\n const result = await client.uploadBundle({\n account: input.account,\n gateway: input.gateway,\n workspace: input.workspace,\n files: input.files.map((file) => ({\n path: file.path,\n contentBase64: Buffer.from(file.bytes).toString(\"base64\"),\n })),\n });\n return result;\n}\n\n/** Per-request file-count cap for `uploadWorkspaceDist`: each file costs the\n * server a stat+put round trip against object storage, so request count — not\n * byte size — is what keeps a batch inside the CDN proxy's response timeout. */\nexport const DEFAULT_MAX_FILES_PER_REQUEST = 100;\n\nexport async function uploadWorkspaceDist(input: {\n origin: string;\n apiKey?: string;\n account: string;\n gateway: string;\n workspace: string;\n files: DistFile[];\n maxRequestBytes?: number;\n maxFilesPerRequest?: number;\n}): Promise<BundleUploadResult> {\n const maxRequestBytes =\n input.maxRequestBytes ?? (Number(process.env.BOS_MAX_BUNDLE_UPLOAD_BYTES) || 64 * 1024 * 1024);\n const maxFilesPerRequest = input.maxFilesPerRequest ?? DEFAULT_MAX_FILES_PER_REQUEST;\n\n const merged: BundleUploadResult = { stored: 0, totalBytes: 0, integrity: {} };\n let batch: DistFile[] = [];\n let batchBytes = 0;\n const overheadPerFile = (file: DistFile): number =>\n Math.ceil(file.bytes.byteLength / 3) * 4 + file.path.length + 64;\n\n const flush = async () => {\n if (batch.length === 0) return;\n const result = await uploadBundle({\n origin: input.origin,\n apiKey: input.apiKey,\n account: input.account,\n gateway: input.gateway,\n workspace: input.workspace,\n files: batch,\n });\n merged.stored += result.stored;\n merged.totalBytes += result.totalBytes;\n merged.unchanged = (merged.unchanged ?? 0) + (result.unchanged ?? 0);\n Object.assign(merged.integrity, result.integrity);\n merged.storage = result.storage;\n batch = [];\n batchBytes = 0;\n };\n\n for (const file of input.files) {\n const cost = overheadPerFile(file);\n if (cost > maxRequestBytes) {\n throw new Error(\n `[publish] ${input.workspace}/${file.path} (${file.bytes.byteLength} bytes) exceeds the per-request upload ceiling`,\n );\n }\n // The serving CDN in front of the upload service rejects requests whose\n // handler time exceeds its proxy timeout — the per-file stat/put count,\n // not the byte total, dominates that wall clock, so bound the op count too.\n if (batchBytes + cost > maxRequestBytes || batch.length >= maxFilesPerRequest) {\n await flush();\n }\n batch.push(file);\n batchBytes += cost;\n }\n await flush();\n\n return merged;\n}\n"],"mappings":";;;;;AAiBA,eAAe,SAAS,KAAa,SAAsC;CACzE,MAAM,UAAU,UAAMA,0BAAQ,KAAK,EAAE,eAAe,KAAK,CAAC;CAC1D,MAAM,QAAoB,CAAC;CAC3B,KAAK,MAAM,SAAS,QAAQ,MAAM,GAAG,MAAM,EAAE,KAAK,cAAc,EAAE,IAAI,CAAC,GAAG;EACxE,MAAM,eAAWC,gBAAK,KAAK,MAAM,IAAI;EACrC,IAAI,MAAM,YAAY,GAAG;GACvB,MAAM,KAAK,GAAI,MAAM,SAAS,UAAU,OAAO,CAAE;GACjD;EACF;EAEA,IAAI,EAAC,UADcC,uBAAK,QAAQ,EACvB,CAAC,OAAO,GAAG;EACpB,MAAM,KAAK;GACT,UAAMC,oBAAS,SAAS,QAAQ,CAAC,CAAC,MAAMC,aAAG,CAAC,CAAC,KAAK,GAAG;GACrD,OAAO,IAAI,WAAW,UAAMC,2BAAS,QAAQ,CAAC;EAChD,CAAC;CACH;CACA,OAAO;AACT;AAEA,eAAsB,iBAAiB,SAAsC;CAC3E,OAAO,SAAS,SAAS,OAAO;AAClC;AAEA,eAAsB,aAAa,OAQH;CAe9B,OAAO,MAdQC,kCAAmB;EAChC,QAAQ,MAAM;EACd,QAAQ,MAAM;EACd,WAAW,MAAM;CACnB,CAC0B,CAAC,CAAC,aAAa;EACvC,SAAS,MAAM;EACf,SAAS,MAAM;EACf,WAAW,MAAM;EACjB,OAAO,MAAM,MAAM,KAAK,UAAU;GAChC,MAAM,KAAK;GACX,eAAe,OAAO,KAAK,KAAK,KAAK,CAAC,CAAC,SAAS,QAAQ;EAC1D,EAAE;CACJ,CAAC;AAEH;;;;AAKA,MAAa,gCAAgC;AAE7C,eAAsB,oBAAoB,OASV;CAC9B,MAAM,kBACJ,MAAM,oBAAoB,OAAO,QAAQ,IAAI,2BAA2B,KAAK;CAC/E,MAAM,qBAAqB,MAAM;CAEjC,MAAM,SAA6B;EAAE,QAAQ;EAAG,YAAY;EAAG,WAAW,CAAC;CAAE;CAC7E,IAAI,QAAoB,CAAC;CACzB,IAAI,aAAa;CACjB,MAAM,mBAAmB,SACvB,KAAK,KAAK,KAAK,MAAM,aAAa,CAAC,IAAI,IAAI,KAAK,KAAK,SAAS;CAEhE,MAAM,QAAQ,YAAY;EACxB,IAAI,MAAM,WAAW,GAAG;EACxB,MAAM,SAAS,MAAM,aAAa;GAChC,QAAQ,MAAM;GACd,QAAQ,MAAM;GACd,SAAS,MAAM;GACf,SAAS,MAAM;GACf,WAAW,MAAM;GACjB,OAAO;EACT,CAAC;EACD,OAAO,UAAU,OAAO;EACxB,OAAO,cAAc,OAAO;EAC5B,OAAO,aAAa,OAAO,aAAa,MAAM,OAAO,aAAa;EAClE,OAAO,OAAO,OAAO,WAAW,OAAO,SAAS;EAChD,OAAO,UAAU,OAAO;EACxB,QAAQ,CAAC;EACT,aAAa;CACf;CAEA,KAAK,MAAM,QAAQ,MAAM,OAAO;EAC9B,MAAM,OAAO,gBAAgB,IAAI;EACjC,IAAI,OAAO,iBACT,MAAM,IAAI,MACR,aAAa,MAAM,UAAU,GAAG,KAAK,KAAK,IAAI,KAAK,MAAM,WAAW,+CACtE;EAKF,IAAI,aAAa,OAAO,mBAAmB,MAAM,UAAU,oBACzD,MAAM,MAAM;EAEd,MAAM,KAAK,IAAI;EACf,cAAc;CAChB;CACA,MAAM,MAAM;CAEZ,OAAO;AACT"}
|
package/dist/storage-upload.mjs
CHANGED
|
@@ -38,8 +38,13 @@ async function uploadBundle(input) {
|
|
|
38
38
|
}))
|
|
39
39
|
});
|
|
40
40
|
}
|
|
41
|
+
/** Per-request file-count cap for `uploadWorkspaceDist`: each file costs the
|
|
42
|
+
* server a stat+put round trip against object storage, so request count — not
|
|
43
|
+
* byte size — is what keeps a batch inside the CDN proxy's response timeout. */
|
|
44
|
+
const DEFAULT_MAX_FILES_PER_REQUEST = 100;
|
|
41
45
|
async function uploadWorkspaceDist(input) {
|
|
42
46
|
const maxRequestBytes = input.maxRequestBytes ?? (Number(process.env.BOS_MAX_BUNDLE_UPLOAD_BYTES) || 67108864);
|
|
47
|
+
const maxFilesPerRequest = input.maxFilesPerRequest ?? 100;
|
|
43
48
|
const merged = {
|
|
44
49
|
stored: 0,
|
|
45
50
|
totalBytes: 0,
|
|
@@ -69,7 +74,7 @@ async function uploadWorkspaceDist(input) {
|
|
|
69
74
|
for (const file of input.files) {
|
|
70
75
|
const cost = overheadPerFile(file);
|
|
71
76
|
if (cost > maxRequestBytes) throw new Error(`[publish] ${input.workspace}/${file.path} (${file.bytes.byteLength} bytes) exceeds the per-request upload ceiling`);
|
|
72
|
-
if (batchBytes + cost > maxRequestBytes) await flush();
|
|
77
|
+
if (batchBytes + cost > maxRequestBytes || batch.length >= maxFilesPerRequest) await flush();
|
|
73
78
|
batch.push(file);
|
|
74
79
|
batchBytes += cost;
|
|
75
80
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"storage-upload.mjs","names":[],"sources":["../src/storage-upload.ts"],"sourcesContent":["import { readdir, readFile, stat } from \"node:fs/promises\";\nimport { join, relative, sep } from \"node:path\";\nimport { createUploadClient } from \"@everything-dev/upload-app/client\";\n\nexport interface DistFile {\n path: string;\n bytes: Uint8Array;\n}\n\nexport interface BundleUploadResult {\n stored: number;\n unchanged?: number;\n totalBytes: number;\n integrity: Record<string, string>;\n storage?: \"s3\" | \"memory\";\n}\n\nasync function walkDist(dir: string, baseDir: string): Promise<DistFile[]> {\n const entries = await readdir(dir, { withFileTypes: true });\n const files: DistFile[] = [];\n for (const entry of entries.sort((a, b) => a.name.localeCompare(b.name))) {\n const fullPath = join(dir, entry.name);\n if (entry.isDirectory()) {\n files.push(...(await walkDist(fullPath, baseDir)));\n continue;\n }\n const info = await stat(fullPath);\n if (!info.isFile()) continue;\n files.push({\n path: relative(baseDir, fullPath).split(sep).join(\"/\"),\n bytes: new Uint8Array(await readFile(fullPath)),\n });\n }\n return files;\n}\n\nexport async function collectDistFiles(distDir: string): Promise<DistFile[]> {\n return walkDist(distDir, distDir);\n}\n\nexport async function uploadBundle(input: {\n origin: string;\n apiKey?: string;\n account: string;\n gateway: string;\n workspace: string;\n files: DistFile[];\n fetchImpl?: typeof fetch;\n}): Promise<BundleUploadResult> {\n const client = createUploadClient({\n origin: input.origin,\n apiKey: input.apiKey,\n fetchImpl: input.fetchImpl,\n });\n const result = await client.uploadBundle({\n account: input.account,\n gateway: input.gateway,\n workspace: input.workspace,\n files: input.files.map((file) => ({\n path: file.path,\n contentBase64: Buffer.from(file.bytes).toString(\"base64\"),\n })),\n });\n return result;\n}\n\nexport async function uploadWorkspaceDist(input: {\n origin: string;\n apiKey?: string;\n account: string;\n gateway: string;\n workspace: string;\n files: DistFile[];\n maxRequestBytes?: number;\n}): Promise<BundleUploadResult> {\n const maxRequestBytes =\n input.maxRequestBytes ?? (Number(process.env.BOS_MAX_BUNDLE_UPLOAD_BYTES) || 64 * 1024 * 1024);\n\n const merged: BundleUploadResult = { stored: 0, totalBytes: 0, integrity: {} };\n let batch: DistFile[] = [];\n let batchBytes = 0;\n const overheadPerFile = (file: DistFile): number =>\n Math.ceil(file.bytes.byteLength / 3) * 4 + file.path.length + 64;\n\n const flush = async () => {\n if (batch.length === 0) return;\n const result = await uploadBundle({\n origin: input.origin,\n apiKey: input.apiKey,\n account: input.account,\n gateway: input.gateway,\n workspace: input.workspace,\n files: batch,\n });\n merged.stored += result.stored;\n merged.totalBytes += result.totalBytes;\n merged.unchanged = (merged.unchanged ?? 0) + (result.unchanged ?? 0);\n Object.assign(merged.integrity, result.integrity);\n merged.storage = result.storage;\n batch = [];\n batchBytes = 0;\n };\n\n for (const file of input.files) {\n const cost = overheadPerFile(file);\n if (cost > maxRequestBytes) {\n throw new Error(\n `[publish] ${input.workspace}/${file.path} (${file.bytes.byteLength} bytes) exceeds the per-request upload ceiling`,\n );\n }\n if (batchBytes + cost > maxRequestBytes) {\n await flush();\n }\n batch.push(file);\n batchBytes += cost;\n }\n await flush();\n\n return merged;\n}\n"],"mappings":";;;;;AAiBA,eAAe,SAAS,KAAa,SAAsC;CACzE,MAAM,UAAU,MAAM,QAAQ,KAAK,EAAE,eAAe,KAAK,CAAC;CAC1D,MAAM,QAAoB,CAAC;CAC3B,KAAK,MAAM,SAAS,QAAQ,MAAM,GAAG,MAAM,EAAE,KAAK,cAAc,EAAE,IAAI,CAAC,GAAG;EACxE,MAAM,WAAW,KAAK,KAAK,MAAM,IAAI;EACrC,IAAI,MAAM,YAAY,GAAG;GACvB,MAAM,KAAK,GAAI,MAAM,SAAS,UAAU,OAAO,CAAE;GACjD;EACF;EAEA,IAAI,EAAC,MADc,KAAK,QAAQ,EACvB,CAAC,OAAO,GAAG;EACpB,MAAM,KAAK;GACT,MAAM,SAAS,SAAS,QAAQ,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,KAAK,GAAG;GACrD,OAAO,IAAI,WAAW,MAAM,SAAS,QAAQ,CAAC;EAChD,CAAC;CACH;CACA,OAAO;AACT;AAEA,eAAsB,iBAAiB,SAAsC;CAC3E,OAAO,SAAS,SAAS,OAAO;AAClC;AAEA,eAAsB,aAAa,OAQH;CAe9B,OAAO,MAdQ,mBAAmB;EAChC,QAAQ,MAAM;EACd,QAAQ,MAAM;EACd,WAAW,MAAM;CACnB,CAC0B,CAAC,CAAC,aAAa;EACvC,SAAS,MAAM;EACf,SAAS,MAAM;EACf,WAAW,MAAM;EACjB,OAAO,MAAM,MAAM,KAAK,UAAU;GAChC,MAAM,KAAK;GACX,eAAe,OAAO,KAAK,KAAK,KAAK,CAAC,CAAC,SAAS,QAAQ;EAC1D,EAAE;CACJ,CAAC;AAEH;
|
|
1
|
+
{"version":3,"file":"storage-upload.mjs","names":[],"sources":["../src/storage-upload.ts"],"sourcesContent":["import { readdir, readFile, stat } from \"node:fs/promises\";\nimport { join, relative, sep } from \"node:path\";\nimport { createUploadClient } from \"@everything-dev/upload-app/client\";\n\nexport interface DistFile {\n path: string;\n bytes: Uint8Array;\n}\n\nexport interface BundleUploadResult {\n stored: number;\n unchanged?: number;\n totalBytes: number;\n integrity: Record<string, string>;\n storage?: \"s3\" | \"memory\";\n}\n\nasync function walkDist(dir: string, baseDir: string): Promise<DistFile[]> {\n const entries = await readdir(dir, { withFileTypes: true });\n const files: DistFile[] = [];\n for (const entry of entries.sort((a, b) => a.name.localeCompare(b.name))) {\n const fullPath = join(dir, entry.name);\n if (entry.isDirectory()) {\n files.push(...(await walkDist(fullPath, baseDir)));\n continue;\n }\n const info = await stat(fullPath);\n if (!info.isFile()) continue;\n files.push({\n path: relative(baseDir, fullPath).split(sep).join(\"/\"),\n bytes: new Uint8Array(await readFile(fullPath)),\n });\n }\n return files;\n}\n\nexport async function collectDistFiles(distDir: string): Promise<DistFile[]> {\n return walkDist(distDir, distDir);\n}\n\nexport async function uploadBundle(input: {\n origin: string;\n apiKey?: string;\n account: string;\n gateway: string;\n workspace: string;\n files: DistFile[];\n fetchImpl?: typeof fetch;\n}): Promise<BundleUploadResult> {\n const client = createUploadClient({\n origin: input.origin,\n apiKey: input.apiKey,\n fetchImpl: input.fetchImpl,\n });\n const result = await client.uploadBundle({\n account: input.account,\n gateway: input.gateway,\n workspace: input.workspace,\n files: input.files.map((file) => ({\n path: file.path,\n contentBase64: Buffer.from(file.bytes).toString(\"base64\"),\n })),\n });\n return result;\n}\n\n/** Per-request file-count cap for `uploadWorkspaceDist`: each file costs the\n * server a stat+put round trip against object storage, so request count — not\n * byte size — is what keeps a batch inside the CDN proxy's response timeout. */\nexport const DEFAULT_MAX_FILES_PER_REQUEST = 100;\n\nexport async function uploadWorkspaceDist(input: {\n origin: string;\n apiKey?: string;\n account: string;\n gateway: string;\n workspace: string;\n files: DistFile[];\n maxRequestBytes?: number;\n maxFilesPerRequest?: number;\n}): Promise<BundleUploadResult> {\n const maxRequestBytes =\n input.maxRequestBytes ?? (Number(process.env.BOS_MAX_BUNDLE_UPLOAD_BYTES) || 64 * 1024 * 1024);\n const maxFilesPerRequest = input.maxFilesPerRequest ?? DEFAULT_MAX_FILES_PER_REQUEST;\n\n const merged: BundleUploadResult = { stored: 0, totalBytes: 0, integrity: {} };\n let batch: DistFile[] = [];\n let batchBytes = 0;\n const overheadPerFile = (file: DistFile): number =>\n Math.ceil(file.bytes.byteLength / 3) * 4 + file.path.length + 64;\n\n const flush = async () => {\n if (batch.length === 0) return;\n const result = await uploadBundle({\n origin: input.origin,\n apiKey: input.apiKey,\n account: input.account,\n gateway: input.gateway,\n workspace: input.workspace,\n files: batch,\n });\n merged.stored += result.stored;\n merged.totalBytes += result.totalBytes;\n merged.unchanged = (merged.unchanged ?? 0) + (result.unchanged ?? 0);\n Object.assign(merged.integrity, result.integrity);\n merged.storage = result.storage;\n batch = [];\n batchBytes = 0;\n };\n\n for (const file of input.files) {\n const cost = overheadPerFile(file);\n if (cost > maxRequestBytes) {\n throw new Error(\n `[publish] ${input.workspace}/${file.path} (${file.bytes.byteLength} bytes) exceeds the per-request upload ceiling`,\n );\n }\n // The serving CDN in front of the upload service rejects requests whose\n // handler time exceeds its proxy timeout — the per-file stat/put count,\n // not the byte total, dominates that wall clock, so bound the op count too.\n if (batchBytes + cost > maxRequestBytes || batch.length >= maxFilesPerRequest) {\n await flush();\n }\n batch.push(file);\n batchBytes += cost;\n }\n await flush();\n\n return merged;\n}\n"],"mappings":";;;;;AAiBA,eAAe,SAAS,KAAa,SAAsC;CACzE,MAAM,UAAU,MAAM,QAAQ,KAAK,EAAE,eAAe,KAAK,CAAC;CAC1D,MAAM,QAAoB,CAAC;CAC3B,KAAK,MAAM,SAAS,QAAQ,MAAM,GAAG,MAAM,EAAE,KAAK,cAAc,EAAE,IAAI,CAAC,GAAG;EACxE,MAAM,WAAW,KAAK,KAAK,MAAM,IAAI;EACrC,IAAI,MAAM,YAAY,GAAG;GACvB,MAAM,KAAK,GAAI,MAAM,SAAS,UAAU,OAAO,CAAE;GACjD;EACF;EAEA,IAAI,EAAC,MADc,KAAK,QAAQ,EACvB,CAAC,OAAO,GAAG;EACpB,MAAM,KAAK;GACT,MAAM,SAAS,SAAS,QAAQ,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,KAAK,GAAG;GACrD,OAAO,IAAI,WAAW,MAAM,SAAS,QAAQ,CAAC;EAChD,CAAC;CACH;CACA,OAAO;AACT;AAEA,eAAsB,iBAAiB,SAAsC;CAC3E,OAAO,SAAS,SAAS,OAAO;AAClC;AAEA,eAAsB,aAAa,OAQH;CAe9B,OAAO,MAdQ,mBAAmB;EAChC,QAAQ,MAAM;EACd,QAAQ,MAAM;EACd,WAAW,MAAM;CACnB,CAC0B,CAAC,CAAC,aAAa;EACvC,SAAS,MAAM;EACf,SAAS,MAAM;EACf,WAAW,MAAM;EACjB,OAAO,MAAM,MAAM,KAAK,UAAU;GAChC,MAAM,KAAK;GACX,eAAe,OAAO,KAAK,KAAK,KAAK,CAAC,CAAC,SAAS,QAAQ;EAC1D,EAAE;CACJ,CAAC;AAEH;;;;AAKA,MAAa,gCAAgC;AAE7C,eAAsB,oBAAoB,OASV;CAC9B,MAAM,kBACJ,MAAM,oBAAoB,OAAO,QAAQ,IAAI,2BAA2B,KAAK;CAC/E,MAAM,qBAAqB,MAAM;CAEjC,MAAM,SAA6B;EAAE,QAAQ;EAAG,YAAY;EAAG,WAAW,CAAC;CAAE;CAC7E,IAAI,QAAoB,CAAC;CACzB,IAAI,aAAa;CACjB,MAAM,mBAAmB,SACvB,KAAK,KAAK,KAAK,MAAM,aAAa,CAAC,IAAI,IAAI,KAAK,KAAK,SAAS;CAEhE,MAAM,QAAQ,YAAY;EACxB,IAAI,MAAM,WAAW,GAAG;EACxB,MAAM,SAAS,MAAM,aAAa;GAChC,QAAQ,MAAM;GACd,QAAQ,MAAM;GACd,SAAS,MAAM;GACf,SAAS,MAAM;GACf,WAAW,MAAM;GACjB,OAAO;EACT,CAAC;EACD,OAAO,UAAU,OAAO;EACxB,OAAO,cAAc,OAAO;EAC5B,OAAO,aAAa,OAAO,aAAa,MAAM,OAAO,aAAa;EAClE,OAAO,OAAO,OAAO,WAAW,OAAO,SAAS;EAChD,OAAO,UAAU,OAAO;EACxB,QAAQ,CAAC;EACT,aAAa;CACf;CAEA,KAAK,MAAM,QAAQ,MAAM,OAAO;EAC9B,MAAM,OAAO,gBAAgB,IAAI;EACjC,IAAI,OAAO,iBACT,MAAM,IAAI,MACR,aAAa,MAAM,UAAU,GAAG,KAAK,KAAK,IAAI,KAAK,MAAM,WAAW,+CACtE;EAKF,IAAI,aAAa,OAAO,mBAAmB,MAAM,UAAU,oBACzD,MAAM,MAAM;EAEd,MAAM,KAAK,IAAI;EACf,cAAc;CAChB;CACA,MAAM,MAAM;CAEZ,OAAO;AACT"}
|
package/dist/types.cjs
CHANGED
|
@@ -252,7 +252,11 @@ const PublishAuthSchema = zod.enum([
|
|
|
252
252
|
"key",
|
|
253
253
|
"custody"
|
|
254
254
|
]);
|
|
255
|
-
const PublishConfigSchema = zod.object({
|
|
255
|
+
const PublishConfigSchema = zod.object({
|
|
256
|
+
auth: PublishAuthSchema.optional(),
|
|
257
|
+
/** Publish per-workspace source archives beside the dists (#406). */
|
|
258
|
+
sourcePublish: zod.boolean().optional()
|
|
259
|
+
});
|
|
256
260
|
const PluginKeySchema = zod.string().regex(/^[a-zA-Z0-9._-]+$/, "plugin key must match [a-zA-Z0-9._-] — keys from remote/extended configs are interpolated into shells and generated code");
|
|
257
261
|
const BosConfigSchema = zod.object({
|
|
258
262
|
account: zod.string(),
|