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 tomainonce 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.go—Process,Limits,Result, and the fail-closedLimits.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, thesaw*flags, anddoneare added alongside their first reader in later parts.token.go— the tokenizer core (expectKind,readToken,readObjectKey,skipValue,valueComplete,mapDecodeErr). The depth/key bound checks andabortErrare threaded in with the bounds (a later part).- GOEXPERIMENT wiring in
.mise.tomland.gitlab-ci.yml— this part is the firstencoding/json/jsontextimporter (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.*ProcessErroris now exercised black-box throughProcess.
🧪 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 issuesGOEXPERIMENT=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.goAPI improvements (typeProcessError, unexportedcode/wrapped): every*Errorreference in this part's code and tests uses*ProcessError. Limits.validatechecks all six caps, not four. The reference validates onlyMaxEnvelopeDepth,MaxEnvelopeObjectKeys,MaxEnvelopePackageJSONSize, andMaxTarballSize. This part also rejects a non-positiveMaxPackageJSONSizeand a zeroMaxEnvelopeDistTags, 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).
🔗 References
- Reference MR (read-only): !420 (closed)
- Related work item: #131 (closed) (not closed by this MR)