name: Publish to npm

# Publishes when a GitHub Release is published, using npm TRUSTED PUBLISHING
# (OIDC) -- no NPM_TOKEN, and no long-lived credential in this repo at all.
#
# Why not a token: npm has been retiring long-lived automation tokens in
# response to the 2025 supply-chain worms. A stolen token publishes forever;
# an OIDC credential is minted per-run, scoped to this repository and this
# workflow file, and expires in minutes. It is also what produces provenance
# attestations automatically -- `--provenance` is implied and must NOT be
# passed explicitly.
#
# REQUIRES a one-time setup on npmjs.com, per package:
#   Package -> Settings -> Trusted Publisher -> GitHub Actions
#     Organization or user: simpleworkjs
#     Repository:           frontend
#     Workflow filename:    publish.yml      <- this file's NAME, not its path
#     Environment:          (leave empty)
# All fields are case-sensitive. Renaming this file breaks publishing until the
# npmjs.com side is updated to match.
#
# The gap this closes: v0.4.3 was tagged and had a GitHub Release, but was never
# published, so the three apps that install it from the registry went on using
# 0.4.1. "Released" and "installable" were two different things.
on:
  release:
    types: [published]
  # So an auth problem can be diagnosed without burning a version number.
  workflow_dispatch:

jobs:
  publish:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      id-token: write # mints the OIDC token npm exchanges for publish rights
    steps:
      - uses: actions/checkout@v4

      # Node 24 for npm >= 11.5.1, which is the floor for trusted publishing.
      # Node 22.x still ships npm 10.x, which fails with a plain auth error and
      # no hint that the CLI is simply too old -- so npm is pinned explicitly
      # rather than left to whatever the Node line happens to bundle.
      #
      # setup-node@v6, not v4. v4 writes an .npmrc containing
      # `_authToken=${NODE_AUTH_TOKEN}` and sets NODE_AUTH_TOKEN to the literal
      # placeholder `XXXXX-XXXXX-XXXXX-XXXXX` when no token is supplied. npm
      # then finds a credential, uses it instead of falling back to OIDC, and
      # the registry rejects the garbage as `E404 ... PUT ... Not Found` --
      # which reads like a missing package, not an auth failure, on a package
      # that plainly exists. This is the exact shape npm's own documented
      # workflow avoids; it pins v6 and disables the package-manager cache.
      # NO registry-url, deliberately.
      #
      # setup-node writes an .npmrc containing `_authToken=${NODE_AUTH_TOKEN}`
      # whenever registry-url is given, and sets NODE_AUTH_TOKEN to the literal
      # placeholder `XXXXX-XXXXX-XXXXX-XXXXX` when no token is supplied --
      # confirmed on v4 and v6 alike. npm then has a credential, uses it rather
      # than falling back to OIDC, and the registry rejects the garbage as
      # `E404 ... PUT` on a package that plainly exists -- an error that reads
      # like a missing package rather than an auth failure.
      #
      # With the input dropped, `npm config get //registry.npmjs.org/:_authToken`
      # is empty and npm reaches its OIDC exchange. registry.npmjs.org is the
      # default registry, so nothing else is lost.
      - uses: actions/setup-node@v6
        with:
          node-version: 24.x
          package-manager-cache: false

      - name: Ensure npm supports trusted publishing
        run: |
          npm install -g npm@latest
          npm --version

      - name: Install dependencies
        run: npm ci

      - name: Test
        run: npm test

      - name: Publish
        # --loglevel verbose so an auth failure names the mechanism that was
        # tried, instead of the bare E404 that looks like a missing package.
        run: npm publish --access public --loglevel verbose
