feat(npm): streampub walk skeleton + tokenizer core (part 2/8)

📦 What this MR does

Part 2/8 of the stack splitting the streampub streaming publish-envelope library (S11 Step 13). See Part 1/8 (!432) for the stack overview and the byte-identity contract against the reference, !420 (closed).

  • Targets 10io/npm-local-step-13-part1of8 (Part 1/8). GitLab retargets this MR to main once Part 1 merges.
  • Depends on Part 1/8 (set as a merge-request dependency).

This part lands the public API surface and the JSON tokenizer core. Process walks a publish envelope as a top-level object and skips every member value; per-field handlers, the five envelope memory bounds, and the streaming attachment pipeline arrive in later parts.

  • streampub.goProcess, Limits, Result, and the fail-closed Limits.validate (a non-positive cap is operator misconfiguration, returned as a plain error, never a *ProcessError).
  • walker.go — the walker skeleton (newWalker / run): requires a top-level object, iterates keys, skips each value through the bound-aware token reader. The struct carries only the fields read at this stage; src, lim, the saw* flags, and done are added alongside their first reader in later parts.
  • token.go — the tokenizer core (expectKind, readToken, readObjectKey, skipValue, valueComplete, mapDecodeErr). The depth/key bound checks and abortErr are threaded in with the bounds (a later part).
  • GOEXPERIMENT wiring in .mise.toml and .gitlab-ci.yml — this part is the first encoding/json/jsontext importer (Go 1.26). The full unit suite passes identically with the flag on or off (verified module-wide), so it is safe.
  • Deletes error_test.go — the Part 1/8 throwaway scaffolding. *ProcessError is now exercised black-box through Process.

🧪 Tests

streampub_test.go (package streampub_test) lands the shared helpers (defaultLimits, asStreampubError, assertCode) and TestProcess_InvalidJSON (S11 AC 37): malformed, truncated, trailing-comma, and empty bodies all surface bad_request. These exercise only the failure path, which the skeleton implements fully — no throwaway tests.

  • golangci-lint (repo-pinned v2.12) → 0 issues
  • GOEXPERIMENT=jsonv2 go test -race -count=1 ./internal/format/npm/streampub/... → pass
  • Full module unit suite under GOEXPERIMENT=jsonv2 → pass (zero regressions)

🚩 Deviations from the reference (!420 (closed))

  • Carries forward the Part 1/8 error.go API improvements (type ProcessError, unexported code / wrapped): every *Error reference in this part's code and tests uses *ProcessError.
  • Limits.validate checks all six caps, not four. The reference validates only MaxEnvelopeDepth, MaxEnvelopeObjectKeys, MaxEnvelopePackageJSONSize, and MaxTarballSize. This part also rejects a non-positive MaxPackageJSONSize and a zero MaxEnvelopeDistTags, so the single fail-closed gate is complete: operator misconfiguration is caught up front instead of failing every publish at runtime (a zero cap rejects all manifests / all dist-tags once those bounds are enforced).
Edited by David Fernandez

Merge request reports

Loading
Loading