Skip to content

4.0.0

Latest

Choose a tag to compare

@ivklgn ivklgn released this 11 May 13:47

4.0.0 — TS-type, performance, and compiler-strictness pass

A consolidated feat! that tightens the public type surface, hardens error objects at runtime, and makes the library compile cleanly under stricter TS settings. No new features — existing code that uses the factory API and treats IConwayError as read-only will keep working.

Breaking changes

  • IConwayError is now fully readonly. rootContext, contextsChunk, feature, originalError, extendedParams, and emit are declared readonly and the constructed instance is Object.freezed. Consumers that mutated these fields post-construction (e.g. reassigning error.feature = "...") will get a TypeError in strict mode at runtime and a TS error at compile time.
  • originalError and extendedParams are now T | undefined (always-present, possibly-undefined) instead of optional (?:). This is intentional for compatibility with exactOptionalPropertyTypes: true. Reading them is unchanged; spreading or property-presence checks ("originalError" in err) now always observe the key.
  • OriginalError is now unknown. Previously Error | Record<string, unknown> | unknown. Callers passing odd values still work; downstream code that narrowed against the old union must re-narrow.
  • isConwayError is now brand-based, not instanceof-based. Errors carry a non-enumerable Symbol.for("conway-errors:brand") property. Cross-realm errors and library-produced errors now both round-trip through isConwayError. Anything that hand-constructed a ConwayError-like object without going through the factory will no longer be recognised.
  • package.json#exports uses the nested conditional form ("." subpath with import / require / types). Modern bundlers and Node ESM resolution are unaffected; very old toolchains that did not understand subpath exports may need an update.

Improvements

  • Public type exports. EmitFn, ExtendedParams, CreateErrorOptions, ErrorTypeConfig, and ErrorFnOptions are now exported from the package root.
  • Stricter publish gate. prepublishOnly now runs type-check before lint/test/build, catching type regressions before they hit the registry.
  • Compile-time type tests. A new index.test-d.ts (100 lines) asserts type-level guarantees alongside the runtime test suite.
  • Expanded runtime test suite. 28 tests covering branded paths, freeze semantics, brand-based recognition, and message-postfix behaviour.
  • Cleaner constructor flow. feature is set in the constructor rather than mutated afterwards — required for the freeze, and removes a footgun where the field briefly held "".

Migration

Most consumers need no changes. If you do any of these, adjust:

  1. Mutating error.feature, error.extendedParams, etc. after construction → stop; pass the values through the factory.
  2. Relying on error instanceof ConwayError from outside the package → switch to isConwayError(error).
  3. Type-narrowing on the old OriginalError union → narrow against unknown instead.
  4. exactOptionalPropertyTypes: true users who previously got undefined rejected when assigning to originalError/extendedParams → those slots are now declared T | undefined, so explicit undefined is allowed.

Internals

  • Bundle size unchanged (~1.3 kB gzipped main entry).
  • Zero runtime dependencies preserved.