Skip to content

Release notes

Eugene Lazutkin edited this page Apr 19, 2026 · 7 revisions

Release notes

Reverse-chronological. Links to the npm version history and this repo's git tags.

3.1.1 — 2026-04-19

Security + correctness hardening sweep. Full module-by-module audit — 19 bug fixes + 9 defensive improvements. All changes are either bug fixes restoring the documented / intended contract, or additive opt-in options with sensible defaults — so this is a patch release.

Security

  • Prototype pollution guards (4 vectors closed):
    • paths/getPath no longer reads through the prototype chain. getPath({}, 'toString') now returns defaultValue instead of Function.prototype.toString.
    • paths/setPath / paths/deletePath refuse to walk or write through __proto__ / constructor / prototype segments.
    • rest-core/parsePatch uses a null-prototype accumulator — a PATCH body containing "__proto__" no longer reassigns the patch's prototype.
    • mass/readOrderedListByKeys uses Object.create(null) accumulators — a record with partition-key value '__proto__' no longer pollutes Object.prototype globally.
  • DoS-on-self vectors closed (3):
    • batchWrite / batchGet cap at 8 retry attempts (was infinite loop on persistent throttling).
    • handler rejects bodies larger than 1 MB with HTTP 413 PayloadTooLarge (was unbounded memory accumulation).
    • parsePaging caps ?offset= at 100 000 by default (was unbounded — ?offset=1e15 could DoS via astronomical skip-page calls).
  • AWS-aligned retry backoff: backoff() default per-retry cap is now 20 s (was undocumented 60 s with infinite retries). Matches AWS SDK v3 default and AWS DynamoDB retry guidance ("stop around one minute"). With MAX_ATTEMPTS = 8, worst-case total wait is ~43 s.

Correctness

  • Adapter.moveByKeys and mass/moveList now pair put + delete per item. Previously, a falsy mapFn return could drop the put but leave the delete in flight — silent data loss. Now both legs drop together.
  • Adapter constructor rejects non-array keyFields. new Adapter({..., keyFields: 'name'}) previously silently produced broken queries (attribute alias 'n'); now throws.
  • applyBatch / applyTransaction throw on unknown action values (were silently dropped — caused phantom "processed" counts and missing writes).
  • buildUpdate throws on unknown arrayOp.op values (typos like 'increment' were silently dropped, producing partial UpdateExpressions).
  • buildUpdate guards options.delete and options.arrayOps with Array.isArray (string input no longer iterates characters).
  • applyBatch total counts only items actually added to the batch (was inflating the count with dropped items).
  • normalizeFields(',,,') now returns null instead of [] (restores the "no fields requested → project everything" contract).
  • applyPatch(o, null) returns o unchanged instead of crashing on Object.keys(null).
  • applyPatch options.delete now gated with Array.isArray.
  • cloneParams(null) returns a fresh clone instead of crashing.
  • handler strips leading // from req.url so GET //evil.com/path cannot pivot the URL's origin; sanitizes the Host header to prevent crashes on malformed headers.
  • handler 405 error body no longer echoes url.pathname (minor information disclosure removed).

Additive options (opt-in, sensible defaults)

  • createHandler(adapter, {maxBodyBytes}) — HTTP body-size cap. Default 1 MB.
  • parseFields(input, {maxItems}) — output-length cap. Default 1000.
  • parseNames(input, {maxItems}) — output-length cap. Default 1000.
  • parseFilter(input, {maxLength}) — query-string length cap. Default 1024.
  • parsePaging(input, {maxOffset}) — offset ceiling. Default 100 000.
  • defaultPolicy.maxOffset added (same default).

Behavior changes to be aware of (all fix footguns)

Change Before After
backoff() default cap undocumented 60 s 20 s
batchWrite / batchGet attempts infinite retry 8 (throws after)
Unknown action in applyBatch / applyTransaction silent drop throws
Unknown arrayOp.op in buildUpdate silent drop throws
Adapter with non-array keyFields silently accepted (broken) throws
Handler request body > 1 MB unbounded 413 PayloadTooLarge
?offset= on list routes unbounded capped at 100 000
getPath({}, 'toString') Function.prototype.toString defaultValue

See the per-page wiki updates (REST core, HTTP handler, Batch and transactions, Mass operations) for the detailed surface.

3.1.0 — 2026-04-18

Additive features. No breaking changes.

  • returnFailedItem option on post / put / patch / delete and their make* batch builders. Sets ReturnValuesOnConditionCheckFailure: 'ALL_OLD'; the thrown ConditionalCheckFailedException carries the colliding item on its Item field (or the matching CancellationReason.Item inside a transaction).
  • {options: ...} sentinel for applyTransaction — position-independent descriptor carrying transaction-level knobs: clientRequestToken (idempotency), returnConsumedCapacity, returnItemCollectionMetrics.
  • explainTransactionCancellation(err, ...descriptors) in /batch. Maps TransactionCanceledException.CancellationReasons back to the input descriptors and returns {failures, message} | null. Paired with returnFailedItem to surface the colliding item in one call.
  • Cross-runtime test matrix — Node, Deno, Bun (npm test, npm run test:deno, npm run test:bun).
  • CJS smoke test + TypeScript smoke test wired into the suite.
  • LICENSE file shipped (BSD-3-Clause). llms.txt + llms-full.txt included in the npm tarball.
  • CI lint step re-enabled (.github/dependabot.yml in .prettierignore).
  • Three CodeQL alerts cleared (workflow permissions, two ReDoS candidates).

Typed .d.ts sidecars updated throughout. See Adapter: CRUD methods, Batch and transactions, and Compatibility for the detailed coverage.

3.0.0 — 2026-04-16

Green-field rewrite on AWS SDK v3. Not a drop-in upgrade from v2. v2 consumers stay on dynamodb-toolkit@2.3.0. The v3 line is the actively-developed branch.

Highlights:

  • AWS SDK v3 (@aws-sdk/client-dynamodb + @aws-sdk/lib-dynamodb) peer deps, replacing v2's aws-sdk.
  • ESM-only, "type": "module", hand-written .d.ts sidecars next to every .js. No build step.
  • One data format — plain JS via lib-dynamodb middleware. The v2 Raw / DbRaw pair collapsed into a single Raw<T> bypass marker.
  • Options bags everywhere: put(item, {force: true}) instead of put(item, true); patch(key, patch, {delete, separator, arrayOps, conditions}).
  • Hooks renamed: prepareListParamsprepareListInput, updateParamsupdateInput. Hooks bag (options.hooks) is the canonical extension point.
  • Patch wire format: _delete / _separator (single underscore); configurable via policy.metaPrefix.
  • REST split: dynamodb-toolkit/rest-core is framework-agnostic; dynamodb-toolkit/handler is the node:http adapter. Koa / Express / Hono / Fetch / Lambda adapters live in separate packages.
  • Transaction auto-upgrade: single-op CRUD automatically upgrades to TransactWriteItems when checkConsistency returns extra descriptors.
  • Indirect-index second-hop for sparse GSIs with key-only projection.
  • Dropped: makeClient, getProfileName, specialTypes, converter, converterOptions — use @aws-sdk/credential-providers directly.

See Migration: v2 to v3 and SDK v2 to v3 cheat sheet.

v2.x and earlier

The v2 documentation snapshot lives at the v2.3-docs git tag of this wiki repo. The v2 source code remains on npm as dynamodb-toolkit@2.3.0 and on GitHub at the matching git tag.

Clone this wiki locally