Skip to content

fix!: project review fixes and owner decisions for 0.3.0 - #49

Merged
Systerr merged 2 commits into
mainfrom
fix/review-2026-10-05
Oct 6, 2026
Merged

Systerr merged 2 commits into
mainfrom
fix/review-2026-10-05

Conversation

@Systerr

@Systerr Systerr commented Oct 5, 2026 •

Copy link
Copy Markdown
Member

Fixes from the 2026-10-05 project review, plus the decisions made after it, for release 0.3.0. Two commits:

  1. fix: findings of the 2026-10-05 project review
  2. feat!: SVG rendered in a separate process, the original never served, one preview row per identity, one task queue per backend

Docs-site counterpart: adaptivestone/framework-documentation#23 (stacked on #22 there).

What changes for a host

  • Pipelines: an unregistered pipeline name is refused everywhere (RESIZE_PIPELINE_UNKNOWN). Register a new or renamed pipeline in the worker before the API requests it.
  • Formats: per-call formats, and formats added by a beforeEnqueue hook, must be keys of encode.formats (RESIZE_FORMAT_NOT_CONFIGURED).
  • The original is never served: resolve() returns only stored previews. ReadyEntry.isOriginal and ResizeStorage.canServeOriginalPublicly are removed. A raster original no larger than a requested WxH box gets a preview at its own size with metadata removed; a pipeline with variantSteps keeps the full box.
  • SVG: rendered once per task into a PNG in a child Node process, killed at limits.processingTimeoutSeconds; beforeSteps receive the PNG. New codes RESIZE_SVG_RENDER_TIMEOUT (dead-lettered at once), RESIZE_SVG_RENDER_FAILED, RESIZE_SVG_RENDER_UNAVAILABLE.
  • Media model: preview rows have an identity field (in resizeMediaSchemaFragment); the database stores one row per identity. verify() reports a model without it (RESIZE_MONGO_MEDIA_MODEL_OUTDATED).
  • ResizeTask collection: new field availableAt; index { queue, status, availableAt } replaces { queue, status, createdAt }. Create the new index with the deploy, then drop the old one. An ejected model must add the field and the index.
  • Queue: one task queue per backend in the framework adapter, so several Resizers run one worker loop; different timing in config files that share a queue is RESIZE_CONFIG_QUEUE_TIMING_CONFLICT. After a dead-letter, the task's previews are not queued again for lockTtlMs.failed (default 10 minutes). A task stopped by a worker shutdown goes back to the queue without a failed attempt (new optional TaskQueue.release()).
  • Worker: npm run cli ResizeWorker -- --config=<name>; worker.concurrency in a framework config file is an error.
  • Scaffold: the model shim imports from the new export …/framework/ResizeTaskModel.js, so npm run gen types getModel('ResizeTask'); --check reports an old shim and an outdated ejected model.
  • Images: animated: true works (WebP and GIF, pipelines without variantSteps); width-only and height-only sizes respect limits.resultDimension; EXIF-rotated originals are not re-encoded before resizing; default JPEG quality is 88.
  • Public surface: internal runtime exports are removed from the main entry and from …/framework.js (list in the CHANGELOG).

Everything is listed in CHANGELOG.md under # 0.3.0; host steps are in docs/host-adoption.md.

Checks

  • types:check, check, build, smoke: pass
  • npm test: 797 tests, 795 pass, 0 fail, 2 skipped (611 before this branch)
  • The same on Node 24.18 with the minimum peers (framework 5.1.0, mongoose 9.0.0, AWS SDK 3.572.0): pass. A new CI job min-versions repeats this.
  • End to end from the built package, run from another directory: SVG in 3 sizes × 3 formats with one render; a heavy SVG stopped at the 2 s limit with nothing stored; a 100×80 JPEG with EXIF requested at 300×300 gives 100×80 previews without EXIF.
  • Each round was reviewed independently (Opus and Codex) and the findings fixed.

Known limits

  • During the dead-letter cooldown prewarm() reports RESIZE_ENQUEUE_LOCK_CONTENDED (retryable), not a dedicated issue; a re-uploaded original under the same media id waits for the cooldown unless the host uses eager generate().
  • A handler that finishes long after a task timeout can end that cooldown early.
  • processTask is no longer exported, so there is no public way to process one externally delivered task (for example a Lambda SQS trigger); runWorker is the entry.
  • On SQS a task given back at shutdown still counts the delivery.
  • Preview rows written before this change have no identity and are not migrated.

Read path and core
- an unregistered pipeline is refused in resolve, prewarm, generate and the worker
  (RESIZE_PIPELINE_UNKNOWN); Resizer.hasPipeline()
- the original-fits shortcut is taken only for a pipeline without steps
- per-call formats and formats from a beforeEnqueue tap must be keys of encode.formats
  (RESIZE_FORMAT_NOT_CONFIGURED)
- a publicUrl throw skips only that preview; resolve(undefined) returns the safe decision
- task payloads are built from the rounded size key; a failed lazy part is retried alone
- dispatch locks are acquired in parallel; prewarm issues list only unconfirmed variants

Images
- animated: true works: WebP and GIF keep the frames that fit, other formats and pipelines
  with variantSteps get the first frame
- width-only and height-only sizes never exceed limits.resultDimension
- EXIF-rotated originals are no longer re-encoded before resizing
- very long or thin SVG renders; an own __proto__ filter key keeps its identity

Queue and worker
- optional TaskQueue.release(): a task stopped by a worker shutdown goes back to the queue
  with no event and no backoff
- unusable sources are dead-lettered on the first failure
- MongoTaskQueue.verify() rejects a model without the fields the queue writes
  (RESIZE_MONGO_MODEL_OUTDATED); leasedBy is hostname:pid

Framework adapter, scaffold, packaging
- export ./framework/ResizeTaskModel.js for the scaffolded model shim, so npm run gen
  types getModel('ResizeTask'); --check reports an old shim and an outdated ejected model
- ResizeWorker --config=<name>; worker.concurrency fails in framework config files
- a missing AWS SDK peer for a config-selected driver is RESIZE_PEER_MISSING
- S3: absolute path-style URL without an endpoint, China regions, SVG as attachment
- repository URL; CI job on Node 24 with the minimum peer versions
… one preview row per identity, one task queue per backend

Owner decisions for 0.3.0, made after the 2026-10-05 project review.

- SVG is treated like any other image: it is rendered once per task into a PNG in a child
  Node process that is killed at limits.processingTimeoutSeconds, and every format is made
  from that raster; beforeSteps receive the PNG (RESIZE_SVG_RENDER_TIMEOUT is terminal;
  RESIZE_SVG_RENDER_FAILED; RESIZE_SVG_RENDER_UNAVAILABLE)
- the original file is never served: the original-fits shortcut, ReadyEntry.isOriginal and
  ResizeStorage.canServeOriginalPublicly are removed; a raster original no larger than a
  requested WxH box gets a preview at its own size with metadata removed (a pipeline with
  variantSteps keeps the full box)
- every preview row carries its identity; appendPreviews stores one row per identity and
  resolves with the stored rows, written with findOneAndUpdate
  (RESIZE_MONGO_MEDIA_MODEL_OUTDATED)
- ResizeTask.availableAt and the index { queue, status, availableAt } replace
  { queue, status, createdAt }: a claim reads one document however many tasks wait in backoff
- after a dead-letter the task's previews are not queued again for lockTtlMs.failed
  (default 10 minutes)
- one task queue per backend in the framework adapter: Resizers share one queue object and
  one worker loop; different timing is RESIZE_CONFIG_QUEUE_TIMING_CONFLICT
- internal runtime exports removed from the main entry (calculateResizedDimensions,
  getFilterSig, getImageContentType, getPreviewIdentity, consumeQueue, timingOf,
  listResizers, processTask) and from framework.js (appEvents, appLogger, getApp,
  getResizeConfig)
- default JPEG quality 88; CHANGELOG heading 0.3.0
@github-code-quality

Copy link
Copy Markdown

Code Coverage Overview

Languages: TypeScript

TypeScript / code-coverage/node-test

The overall line coverage in commit 13d8a74 in the fix/review-2026-10-0... branch remains at 97%, unchanged from commit 0335e0a in the main branch.

Show a line coverage summary of the most impacted files.
File main 0335e0a fix/review-2026-10-0... 13d8a74 +/-
src/framework/database.ts 100% 99% -1%
src/worker.ts 95% 94% -1%
src/resizeTask.ts 96% 96% 0%
src/drivers/mongo/database.ts 99% 99% 0%
src/engine.ts 98% 99% +1%
src/testHelpers/fakes.ts 93% 94% +1%
src/queue.ts 96% 98% +2%
src/helpers/imageFormat.ts 75% 89% +14%
src/helpers/svgRaster.ts 0% 98% +98%
src/helpers/svgRasterChild.ts 0% 100% +100%

@Systerr
Systerr merged commit cdb8381 into main Oct 6, 2026
11 checks passed
@Systerr
Systerr deleted the fix/review-2026-10-05 branch October 6, 2026 18:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant