66 lines
6.6 KiB
Markdown
66 lines
6.6 KiB
Markdown
# 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: `BatchBarcodeRequest` interface — `{ id?: string; symbology: BarcodeSymbology; value: string; options?: BarcodeOptions }`. Per-item `id` is optional; when omitted the request index is used as the result key.
|
|
- ADDED: `BatchBarcodeResult` discriminated union — `{ id: string; index: number; ok: true; result: BarcodeResult } | { id: string; index: number; ok: false; error: BarcodeError }`. Failed items carry a coded `BarcodeError`, never a bare string.
|
|
- ADDED: `BatchBarcodeOptions` interface — `{ concurrency?: number; signal?: AbortSignal; onItem?: (result: BatchBarcodeResult) => void }`. `concurrency` defaults to `1` (sequential) and is clamped to `[1, 64]`.
|
|
- ADDED: `BatchBarcodeSummary` interface — `{ total: number; succeeded: number; failed: number; durationMs: number; results: BatchBarcodeResult[] }`. `durationMs` uses an injectable `Clock` (defaults to `performance.now()` shim) so tests stay deterministic.
|
|
- ADDED: `generateBatch(requests, options?): Promise<BatchBarcodeSummary>` — produces one `BatchBarcodeResult` per input request in input order; per-item failures are isolated (a thrown encode error becomes `{ ok: false, error }`, every other item keeps generating).
|
|
- ADDED: `BatchBarcodeGenerator` class — `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 `./batch` mapping to `dist/core/batch/batch-generator.js`.
|
|
- ADDED: error code `BARCODE.BATCH_ABORTED` registered on `BARCODE_CODES`, thrown when the supplied `AbortSignal` fires 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-port` change 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** — `generateBatch` returns 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; the `onItem` callback 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` — covers `BatchBarcodeRequest` validation, in-order results, per-item failure isolation, `concurrency` clamping, `onItem` callback invocation, abort-via-`AbortSignal`, sequential-vs-concurrent equivalence on success-only inputs.
|
|
- `src/core/batch/batch-generator.spec-conformance.test.ts` — pins injectable `Clock` (deterministic `durationMs`), Zod-validated `BatchBarcodeRequest` shape, `BARCODE.BATCH_ABORTED` error code emission, OTel counter `barcode.batch.items_total` / `barcode.batch.items_failed` recorded against an in-memory `MeterProvider`.
|
|
|
|
## 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 only `packages/barcode/barcode/package.json` itself).
|
|
|
|
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`.
|