6.6 KiB
Barcode batch generator
Package: @suaveplan/barcode
Status: PROPOSED
Type: spec-conformance + feature
Why
The 2026-05-02 audit of @suaveplan/barcode against legacy packages/universal/barcode/src/ found that genesis ships only the single-call generator surface (encode, renderSvg, the per-symbology helpers, validateBarcode). Legacy ships a full batch/batch-generator.ts (generateBatch(items, options): Promise<BatchResult>) used by every consumer that needs to produce ten or ten-thousand barcodes in one pass — bulk-label printing, ticketing, asset registers, and inventory imports all rely on it. CLAUDE.md scopes this package as "generator only" (barcode reading is intentionally out of scope), but the batch generator is part of the generator surface, not the reader. Its absence forces every consumer to re-implement an error-isolating loop in user code, which is exactly the duplication §0a (migration parity) was written to prevent.
What changes
- ADDED:
BatchBarcodeRequestinterface —{ id?: string; symbology: BarcodeSymbology; value: string; options?: BarcodeOptions }. Per-itemidis optional; when omitted the request index is used as the result key. - ADDED:
BatchBarcodeResultdiscriminated union —{ id: string; index: number; ok: true; result: BarcodeResult } | { id: string; index: number; ok: false; error: BarcodeError }. Failed items carry a codedBarcodeError, never a bare string. - ADDED:
BatchBarcodeOptionsinterface —{ concurrency?: number; signal?: AbortSignal; onItem?: (result: BatchBarcodeResult) => void }.concurrencydefaults to1(sequential) and is clamped to[1, 64]. - ADDED:
BatchBarcodeSummaryinterface —{ total: number; succeeded: number; failed: number; durationMs: number; results: BatchBarcodeResult[] }.durationMsuses an injectableClock(defaults toperformance.now()shim) so tests stay deterministic. - ADDED:
generateBatch(requests, options?): Promise<BatchBarcodeSummary>— produces oneBatchBarcodeResultper input request in input order; per-item failures are isolated (a thrown encode error becomes{ ok: false, error }, every other item keeps generating). - ADDED:
BatchBarcodeGeneratorclass —class BatchBarcodeGenerator { constructor(deps?: { clock?: Clock }); generate(requests, options?): Promise<BatchBarcodeSummary> }for callers that want to inject a deterministic clock or share a generator across calls. - ADDED: subpath export
./batchmapping todist/core/batch/batch-generator.js. - ADDED: error code
BARCODE.BATCH_ABORTEDregistered onBARCODE_CODES, thrown when the suppliedAbortSignalfires before completion.
Out of scope
- Barcode reading / decoding — explicitly out of scope per CLAUDE.md package description ("generator only"). Tracked separately under a future
barcode-reader-portchange if business requirements ever demand it. Not deferred for "complexity" reasons; deferred because the package contract intentionally excludes it. - 2D symbologies (DataMatrix, PDF417, Aztec) — not in legacy upstream either; would be its own package.
- Streaming / async-iterable batch API —
generateBatchreturns a single resolved summary. A streaming variant would let callers consume results as they emit (useful for very large batches, > 10 000 items). Punted because no current consumer needs it; theonItemcallback covers the common "show progress" case.
Source mapping
This change derives from ../suaveplan/packages/universal/barcode/src/batch/.
| Requirement | Source lines | Notes |
|---|---|---|
BatchBarcodeRequest shape |
batch/batch-generator.ts L9-L13 |
Reworded: data → value; added optional id |
BatchBarcodeResult discriminated union |
batch/batch-generator.ts L16-L21 |
Reworded: legacy returns { index, result?, svg?, error?: string }; new shape uses a discriminated union with coded BarcodeError instead of a string |
BatchBarcodeOptions shape with concurrency, signal, onItem |
batch/batch-generator.ts L24-L28 |
Extended: legacy has outputFormat and onProgress; new shape replaces onProgress with onItem (richer payload), drops outputFormat (caller picks via options.includeText/SVG availability already on BarcodeResult), adds concurrency and signal |
generateBatch function |
batch/batch-generator.ts L42-L95 |
Verbatim semantics — per-item failure isolation, sequential default, in-input-order results |
BatchBarcodeGenerator class |
(no source line) | New: legacy is a free function only; class is added for clock injection |
Requirements with no source line
| Requirement | Justification |
|---|---|
BatchBarcodeGenerator class with injectable Clock |
Genesis-wide CLAUDE.md determinism rule (enterprise/bitpacker.md L40 "no module-scope Date.now()"). Legacy reads Date.now() implicitly through performance.now; genesis routes through @suaveplan/types::Clock. |
BARCODE.BATCH_ABORTED error code on BARCODE_CODES |
CLAUDE.md §Workflow rule 2 — every thrown error routes through @suaveplan/error with a stable code. Legacy throws a plain Error("aborted") from the loop. |
concurrency clamping to [1, 64] |
Genesis-wide guardrail — unbounded Promise.all over user input is a denial-of-service vector. Legacy ships sequential only (concurrency 1) so the clamp is additive. |
Verification approach
src/core/batch/batch-generator.test.ts— coversBatchBarcodeRequestvalidation, in-order results, per-item failure isolation,concurrencyclamping,onItemcallback invocation, abort-via-AbortSignal, sequential-vs-concurrent equivalence on success-only inputs.src/core/batch/batch-generator.spec-conformance.test.ts— pins injectableClock(deterministicdurationMs), Zod-validatedBatchBarcodeRequestshape,BARCODE.BATCH_ABORTEDerror code emission, OTel counterbarcode.batch.items_total/barcode.batch.items_failedrecorded against an in-memoryMeterProvider.
Impact
Affected dependents
Run bunx turbo ls --filter=...@suaveplan/barcode to enumerate. As of 2026-05-02 there are no in-repo dependents:
- No dependents (verified by
grep -rl "@suaveplan/barcode" /home/ubuntu/web/genesis/packages/ --include="package.json"returning onlypackages/barcode/barcode/package.jsonitself).
This change is purely additive (new exports, no signature changes to existing surface), so the absence of dependents is not a risk gate.
Migration impact
None — additive only. Existing single-call surface (encode, renderSvg, validateBarcode, per-symbology encoders) is unchanged. Consumers wanting batch generation opt in by importing generateBatch from @suaveplan/barcode or @suaveplan/barcode/batch.