Documentation menu

Publishing

flog publish uses a prepare-then-execute model. It completes the full plan before the first registry upload, so a manifest, lifecycle, packing, git, or registry-state failure in any selected package leaves every package unpublished.

Selection and order#

Without --recursive or --workspace, flog publishes the package containing the selected directory. Private packages are rejected. --workspace selects by exact package name, workspace directory, owned file, or workspace path glob; --recursive separately selects every workspace. Exact and glob selectors cannot be mixed, and duplicate aliases or overlapping globs are errors. Private workspace packages matched by broad path globs or a plain recursive selection are omitted with explicit package name, importer path, and private reason in human and JSON plans and summaries. An exact workspace selector is a direct request and remains an error. A broad selection containing only private workspaces is also an error.

Selected packages are ordered dependency-first using the workspace graph. Versions already present in the registry are skipped only when their published integrity or shasum matches the newly prepared artifact. A mismatch is a hard error and flog never attempts to replace the version.

Preflight and lifecycle#

For every selected package, flog:

  1. validates every project dependency declaration against flog.lock, then validates package identity, version, requested tag and access, git state, and workspace selection;
  2. reads registry baselines, rejects an incoherent workspace release set, and reports newer compatible direct dependency versions as advisory warnings;
  3. runs prepublishOnly;
  4. packs the package, which runs prepack, prepare, and postpack, rewrites workspace: ranges, and stages the exact tarball to upload;
  5. validates the exact packed dependency declarations against the reviewed lock after lifecycle changes and workspace: rewriting;
  6. revalidates the release set against post-lifecycle manifests; and
  7. displays the complete ordered plan.

Only after the complete plan succeeds and is confirmed does flog upload staged artifacts sequentially. A successful upload runs publish and postpublish. Artifacts are never rebuilt between the plan and upload.

A source or packed manifest/lock mismatch is always fatal, including dry-run, and no force or acknowledgement option bypasses it. Lifecycle scripts therefore cannot make an unreviewed dependency change eligible for upload. A newer registry version that still satisfies a selected package's declared range is different: the reviewed lock remains authoritative, publication continues, and the deterministic warning reports the locked and available versions with flog upgrade guidance. Exact declarations and ranges with no newer compatible version produce no warning.

Workspace release sets#

Publication preflight computes a release closure from public workspace dependencies. If a selected package crosses a compatibility line and a published dependent's range excludes the proposed version, that dependent must be included with its own compatibility-line bump. The check propagates through transitive dependents. For SemVer, compatibility lines are a major version, a minor version within 0.x, or an individual 0.0.x version.

Flog also rejects packed workspace dependencies whose ranges are not satisfied by an existing registry version or an earlier package in the publication plan. It never selects packages or edits versions automatically. Use --allow-incomplete-release-set only to accept a reviewed exception; the override and every affected edge remain in plans and summaries. --yes and git overrides do not bypass release-set validation. Development-only workspace edges are reported but do not require publication.

See workspace release sets for compatibility-line rules, dependency-chain examples, availability checks, and override behavior.

Immediately before every upload, flog reads that package's registry metadata again. It aborts if the prepared version appeared, if latest changed, or if a registry-reported access value changed after preflight. Registry baselines used by release-set analysis are also checked before the first mutation and between uploads. When an OTP challenge occurs, the same check is repeated after the OTP is obtained and before the authenticated retry. A mutating request that starts web authentication is not automatically replayed; after authentication, flog rechecks metadata and explicitly retries it. These checks narrow the stale-plan window, but npm-compatible publication endpoints do not provide a portable conditional update that atomically compares the old dist-tag or access while publishing a version. A registry change in the final interval between this read and its upload can therefore still race; flog does not claim race-free latest or access safety.

Lifecycle scripts and packages run sequentially. Cancellation during a prompt, web login, OTP request, lifecycle script, or upload prevents subsequent uploads.

Tags and confirmation#

Stable versions default to latest. A prerelease requires an explicit non-latest tag, for example:

flog publish --recursive --tag next

--tag latest is rejected for every prerelease. For stable versions, flog also rejects a latest update whose SemVer precedence is lower than or equal to the registry's current latest version. An equal version is accepted only as a verified already-published resume.

Interactive publication asks for confirmation after displaying the plan. Automation must deliberately pass --yes; redirected or otherwise noninteractive input is never treated as confirmation.

Package access#

For a first publication, flog follows npm defaults: unscoped packages are public, while scoped packages are restricted. Use an explicit access level when the default is not the intended registry visibility:

flog publish --recursive --access public --tag next

One --access value applies to every selected package. public is valid for mixed scoped and unscoped workspaces; restricted is rejected for any unscoped package during initial validation, before lifecycle scripts or registry requests. For an existing scoped package with no option, flog preserves a known registry-reported public or restricted value. If the registry does not report existing visibility, the plan says preserve and flog omits the access field rather than risk requesting a downgrade to the scoped default.

The npm-compatible upload document carries the requested top-level access field except for that preserve case. Requested access is included in every plan entry, confirmation display, JSON summary, and summary file.

A successful upload response proves that a publication mutation was accepted; it does not prove the registry applied the access field. After every accepted upload, flog verifies the exact artifact through authenticated metadata and checks whether that version is anonymously readable. It reports published only when the observed public/restricted visibility matches the access intent. An accepted upload with different visibility is published-access-mismatch; unavailable verification or an unknown prior value under preserve is published-access-unverified. Both outcomes return an error, stop later workspace uploads, record access intent and observed access separately, and warn that the accepted publication may be irreversible. publish and postpublish still run because the registry accepted the package.

If a registry rejects the field, flog reports the ordinary failed upload. If it accepts but ignores the field, flog never silently substitutes visibility or reports confirmed success. Dry runs show the access intent but remain anonymous and never mutate package visibility.

Dry runs, git checks, and summaries#

--dry-run performs lifecycle, packing, registry-state, and safety checks but does not upload or authenticate. Metadata requests omit configured and session credentials and cannot start web authentication.

By default the worktree must be clean and the current branch must be main or master. --allow-dirty and --allow-branch override those checks individually. --no-git-checks disables both. Every active override is shown as a warning in the plan.

--json writes a machine-readable package summary to stdout. The plan is written to stderr first so it remains visible before mutation. Use --summary <file> to atomically record the same summary in a JSON file, including failed, published-access-mismatch, published-access-unverified, and not-attempted package statuses after a partial or unverifiable publication. A rerun rebuilds and revalidates already-present versions before continuing.

If a registry requests a one-time password, interactive use prompts for one. Automation may provide NPM_CONFIG_OTP in its environment.