import { type NpmRegistryClient, type NpmRegistryFetchResult } from "@superblocksteam/vite-plugin-file-sync/npm-registry"; import { type Logger } from "../telemetry/logging.js"; /** * File mode applied to the userconfig after every write. `0o400` (owner * read, no write) strips the owner write bit so any `npm config set` or * stray write that opens the file `O_WRONLY` fails loudly instead of * silently retargeting the configured registry. The atomic rename inside * `writeNpmrc` is governed by parent-dir perms — not the target's mode — * so this writer can still re-sync the file on subsequent config changes. * The stricter mode is a speed bump, not a security boundary: the CLI * user can always `chmod +w` first. */ export declare const HOME_NPMRC_MODE = 256; /** * Resolves the Superblocks-owned npm userconfig path. Centralised so the * image-build and CLI auto-upgrade `--userconfig=` plumbing and the * runtime writer all agree on the same location. The file lives inside a * hidden directory, so it is named `npmrc` (without a leading dot) by * convention; the directory itself carries the visibility bit. `homeDir` * defaults to `os.homedir()`; tests override it. */ export declare function superblocksNpmrcPath(homeDir?: string): string; /** * Resolves the directory npm should write its per-run debug logs to during * Superblocks-spawned installs in the live-edit pod. Unlike * `superblocksNpmrcPath` (home-based), this is rooted at the application * working directory — `/.superblocks/logs` — so each app's install * logs are co-located with the app and collectable from a predictable * place. Wired into the subprocess env as `NPM_CONFIG_LOGS_DIR` via * `buildInstallEnv`; npm creates the directory if it does not exist. */ export declare function superblocksLogsPath(appDir: string): string; /** * Resolves the on-disk backup path for the Superblocks-owned npm * userconfig. Symmetric with `superblocksNpmrcPath`: lives next to the * userconfig under `~/.superblocks/`. `syncHomeNpmrc` hardlinks the * image-baked userconfig to this path on the first `configured` resolve * so a later transition to `not-configured` has a baseline to restore * from (APPS-4328) — mirroring the project-`.npmrc` capture/restore added * in APPS-4320. * * The leading-dot filename mirrors the project-side `NPMRC_DEFAULT_FILENAME` * convention even though the live userconfig is `npmrc` (no dot). The dot * here is a convention-only marker that this file is the backup, not the * active userconfig. */ export declare function superblocksNpmrcBackupPath(homeDir?: string): string; /** * Outcome reported by `syncHomeNpmrc`. Surfaced for tracing + tests rather * than passed back to the caller's control flow — every outcome here is a * success from the dev-startup point of view (the writer is best-effort * and never throws). */ type SyncHomeNpmrcOutcome = /** Config is set and `~/.superblocks/npmrc` was created or rewritten. */ "written" /** * Server returned `not-configured`. The image-default userconfig (if * any) was restored from the `~/.superblocks/npmrc.default` backup * captured on a prior configured boot. If no backup exists and the * userconfig is Superblocks-owned, the file is removed entirely rather * than left pinned at a previous org's registry/token (APPS-4328). * * This is what the gpoulios-sb review on PR #19621 flagged: long-lived * Superblocks-spawned npm/pnpm processes pinned at the userconfig must * not silently inherit a configured-org's registry after the org has * been deconfigured. While live-edit pods are single-tenant per EE * deployment so cross-org leakage isn't reachable, leaving the stale * file behind is still wrong — rollback-to-default is the right * semantic. */ | "restored-not-configured" /** * Cold cache + the registry server is unreachable (or JWT refresh * failed). We can't tell "deliberately unset" from a transient outage, * so we leave the userconfig whatever it currently is rather than * punch a hole in the CLI auto-upgrade about to run. */ | "skipped-unreachable" /** * `snapshotInitialNpmrc` failed silently this pod (e.g. EXDEV — target * and backup straddle filesystems), so a real baked-in userconfig * exists with NO restore baseline. We fail closed on both sides of the * lifecycle so the baked file is never lost (APPS-4428): * * - On a `configured` resolve we refuse to overwrite the baked file * with the managed private-registry content. A later * `not-configured` transition could not undo the rewrite — restore * from backup is impossible (no backup) and unlinking would delete * the baseline. Leave the baked file in place; the customer's * install routes through the image-baked registry until the next * sync (or pod boot) where the snapshot succeeds. * * - On a `not-configured` resolve (separate cache-refresh invocation) * we withhold the unlink for the same reason: the file IS the baked * baseline (the configured branch skipped the rewrite), so deleting * it would break the install path. * * Distinct from `restored-not-configured` so telemetry doesn't conflate * "rolled back to baseline" with "left alone because we couldn't". */ | "skipped-snapshot-failed" /** The sync failed; the file is unchanged. Details in the warn log. */ | "error"; interface SyncHomeNpmrcResult { outcome: SyncHomeNpmrcOutcome; /** Absolute path that was inspected / mutated. */ path: string; /** Resolved fetch source when `outcome === "written"`. */ source?: NpmRegistryFetchResult["source"]; } interface SyncHomeNpmrcDeps { /** * Pre-constructed client with the LD-flag check, JWT provider, and * `refreshJwt` hook bound. We deliberately do not instantiate one * inside this helper: AppShell + TemplateRenderer share a single * client with the dev-server session so cache TTL, 401 retry, and * stale-fallback are coherent across all userconfig materialisation * sites. */ npmRegistryClient: NpmRegistryClient; logger: Logger; /** * Override for `os.homedir()`. Tests redirect to an isolated tmpdir so * the real `~/.superblocks/` is never touched. Production callers * leave it unset. */ homeDir?: string; /** * Organization id, surfaced on the `npm_registry.sync_home_npmrc` span * (per-trace context, never a metric label). Absent in tests / when no org * context is available at the call site. */ orgId?: string; } /** * Materialises `~/.superblocks/npmrc` from the server-fetched per-org * npm registry config. Designed to run at dev-server CLI startup, after * the `NpmRegistryClient` is wired but BEFORE the global Superblocks CLI * auto-upgrade: with the file in place and the auto-upgrade invoking * npm with `NPM_CONFIG_USERCONFIG=~/.superblocks/npmrc`, the * `npm install -g @superblocksteam/cli@…` invocation resolves through * the customer's private registry rather than `registry.npmjs.org`. * * Behaviour: * * - `client.getConfig()` resolves `configured` (or `stale`): snapshot * the image-default userconfig to `~/.superblocks/npmrc.default` * (once per pod boot via `link(2)`), then write * `~/.superblocks/npmrc` atomically with mode `0o400`. The snapshot * captures whatever the image baked (EE GHPR scope + token, or a * no-file baseline on a fresh developer laptop) so the * `not-configured` branch below has a baseline to restore from. The * parent dir is `mkdir -p`'d first so a fresh `$HOME` on a * developer's laptop is handled the same as a pod where the image * already pre-creates `/home/node/.superblocks/`. If the snapshot * attempt FAILS against a real baked file (e.g. EXDEV), we fail * closed and SKIP the rewrite: see `skipped-snapshot-failed` in * `SyncHomeNpmrcOutcome` for why (APPS-4428). * * - `client.getConfig()` resolves `not-configured`: restore the * image-default userconfig from the `~/.superblocks/npmrc.default` * backup captured on a prior `configured` boot. When no backup * exists (cold boot where the org was deconfigured before the first * `configured` resolve), the userconfig is unlinked entirely so a * long-lived Superblocks-spawned npm/pnpm process cannot silently * inherit a previously-configured org's registry/token. Closes the * stale-userconfig hole flagged on PR #19621 (APPS-4328). One * exception: if this process already SAW a snapshot attempt FAIL for * this path, the configured branch above will have left the baked * userconfig in place (no managed rewrite happened), so unlinking * here would delete the baked baseline. Withhold the unlink (APPS-4428). * * - `client.getConfig()` resolves `unreachable`: cold cache + the * registry server is down (or JWT refresh failed). Leave the file * alone — the fail-closed gate in the CLI auto-upgrade is the * backstop if the upgrade reaches a public registry. * * - Any throw (fs error, etc.): log + return `outcome: "error"`. We * must not crash dev-server startup on a best-effort step. * * Idempotent for a given resolved config — safe to invoke on every * `NpmRegistryClient` cache refresh as well as on startup. */ export declare function syncHomeNpmrc(deps: SyncHomeNpmrcDeps): Promise; export {}; //# sourceMappingURL=home-npmrc.d.mts.map