Skip to content

chore: prep 4.1.0 β€” fix peer-dep cascade, shim removed utils API#371

Merged
brianmhunt merged 4 commits into
mainfrom
claude/review-halt-v5-release-AXR0q
Apr 27, 2026
Merged

chore: prep 4.1.0 β€” fix peer-dep cascade, shim removed utils API#371
brianmhunt merged 4 commits into
mainfrom
claude/review-halt-v5-release-AXR0q

Conversation

@brianmhunt
Copy link
Copy Markdown
Member

@brianmhunt brianmhunt commented Apr 27, 2026

Summary

A previous release attempt minted v5.0.0 because Changesets promoted minor bumps to major across the 27-package fixed group. The trigger wasn't the change content β€” it was a chain of intra-fixed-group peerDependencies (mostly spec-only or type-only) that caused any minor in the group to cascade to a major for every dependent.

This PR lands the next release as 4.1.0 minor by fixing the cascade and restoring the one removed ko.utils identifier as a @deprecated passthrough.

What changed

1. Drop spurious intra-fixed-group peerDependencies (55360445)

Verified each by grepping the depender's src/ for runtime imports of the peer:

Depender Peer(s) Real use
@tko/bind @tko/binding.foreach spec only
@tko/binding.template @tko/binding.if spec only
@tko/lifecycle @tko/observable import type (erased)
@tko/utils.component bind, binding.core, computed, provider.multi, provider.virtual spec only
@tko/utils.parser bind, binding.core, provider.databind spec only

Workspace-resolved specs don't need a devDep declaration in this Bun monorepo. Lifecycle's import type { Observable } is erased post-compilation; consumers using the type already have @tko/observable via their own deps.

2. Restore removed @tko/utils API as @deprecated compat shims (cc5ddf19, 52b00839)

Commits 6f4b5f2b and 517ddc14 removed four public identifiers. Each was a trivial wrapper around a now-universal native API; restoring them as one-line passthroughs preserves the public surface at zero behavior cost.

New packages/utils/src/compat.ts (each @deprecated with the standardized replacement called out for IDE hover):

  • createSymbolOrString(id) => Symbol(id)
  • stringTrim(v) => String(v ?? '').trim()
  • stringStartsWith(s, p) => s.startsWith(p)
  • overwriteLengthPropertyIfSupported(fn, d) => Object.defineProperty(fn, 'length', d)

@tko/builder re-wires createSymbolOrString into the Utils type, the utils object literal, and the import list so ko.utils.createSymbolOrString continues to resolve. Removal is scheduled for the next major.

Internal probes (useSymbols, functionSupportsLengthOverwrite) had no monorepo callers and stay removed.

3. Changeset adjustments

  • modernize-utils-dead-polyfills: @tko/utils and @tko/builder demoted from minor β†’ patch. Description updated to call out the passthrough preservation.
  • fix-jsx-clean-teardown-race: @tko/utils.jsx promoted from patch β†’ minor. Adding options.jsxCleanBatchSize is a public option β€” minor by strict semver. Cascades the fixed group to 4.1.0.

Verification

  • bun run build clean across all 27 packages
  • bun run tsc clean
  • bunx @changesets/cli status --verbose reports all 27 fixed-group packages β†’ 4.1.0 minor, zero majors
  • createSymbolOrString confirmed end-to-end:
    • in source packages/utils/src/compat.ts
    • in compiled packages/utils/dist/compat.js
    • in bundled builds/reference/dist/browser.js and builds/knockout/dist/browser.js
    • in the ko.utils object literal at the same site

Release path

After merge:

bunx @changesets/cli version
bunx @changesets/cli publish

Test plan

  • bun run build
  • bun run tsc
  • bunx @changesets/cli status --verbose shows 4.1.0 minor cascade
  • grep verification of every dropped peer-dep
  • dist artifacts contain the four shims
  • full bun run verify (browser tests) β€” please run on CI before merge

https://claude.ai/code/session_01Jnv99bwWtFWFmsyL7A2cJv


Generated by Claude Code

Summary by CodeRabbit

Release Notes

  • New Features

    • Added new compatibility utility functions to the utils package, including string trimming, prefix matching, and function property configuration tools.
    • Exposed symbol and string creation utilities within the builder framework for public use.
  • Chores

    • Streamlined package dependency configuration by removing peer dependency requirements across multiple packages, providing greater flexibility in dependency management and package composition.

claude added 3 commits April 27, 2026 13:54
Five fixed-group packages declared `peerDependencies` on other fixed-group
packages whose only usages were in spec files (or, for `@tko/lifecycle`, a
type-only import that's erased at compile). With Changesets' fixed-version
group plus its minor→major peer promotion, any minor in the group cascaded
to a major version bump for everything β€” that's the v5.0 trigger from the
previous release attempt, not the actual change content.

Verified each by grepping for runtime imports in `src/`:

| Depender              | Peer(s)                                                    | Real use         |
|-----------------------|------------------------------------------------------------|------------------|
| @tko/bind             | @tko/binding.foreach                                       | spec only        |
| @tko/binding.template | @tko/binding.if                                            | spec only        |
| @tko/lifecycle        | @tko/observable                                            | `import type`    |
| @tko/utils.component  | bind, binding.core, computed, provider.multi, .virtual     | spec only        |
| @tko/utils.parser     | bind, binding.core, provider.databind                      | spec only        |

Workspace-resolved specs do not need devDep declarations in the Bun
monorepo. No `src/` imports are affected. Lifecycle's `import type
{ Observable }` is erased post-compilation; consumers who use the type
already have `@tko/observable` via their own deps.

Verification:
- `bun run build` β€” all packages build clean
- `bun run tsc` β€” type-check passes
- `bunx @changesets/cli status --verbose` β€” reports 4.1.0 minor for all
  27 fixed-group packages with current changesets, no majors

https://claude.ai/code/session_01Jnv99bwWtFWFmsyL7A2cJv
The polyfill modernization (commits 6f4b5f2, 517ddc1) removed four
public identifiers from `@tko/utils` and one from `ko.utils`. Each was a
trivial wrapper around a now-universal native API; restoring them as
inline passthroughs preserves the public surface at zero behavior cost.

New `packages/utils/src/compat.ts`:
- `createSymbolOrString(id) => Symbol(id)`
- `stringTrim(v) => String(v ?? '').trim()`
- `stringStartsWith(s, p) => s.startsWith(p)`
- `overwriteLengthPropertyIfSupported(fn, d) => Object.defineProperty(fn, 'length', d)`

Wired through:
- `packages/utils/src/index.ts` β€” added `export * from './compat'`
- `packages/builder/src/Builder.ts` β€” restored `createSymbolOrString`
  in import list, `Utils` type, and `utils` object literal so
  `ko.utils.createSymbolOrString` continues to resolve

Changeset updates:
- `modernize-utils-dead-polyfills`: demoted from minor β†’ patch for
  `@tko/utils` and `@tko/builder`. Description updated to call out the
  passthrough preservation. Internal probes (`useSymbols`,
  `functionSupportsLengthOverwrite`) had no monorepo callers and stay
  removed.
- `fix-jsx-clean-teardown-race`: promoted from patch β†’ minor for
  `@tko/utils.jsx`. Adding `options.jsxCleanBatchSize` is a public
  option β€” minor by strict semver. Cascades the fixed group to 4.1.0.

`bunx @changesets/cli status --verbose` now reports all 27 fixed-group
packages bumping to 4.1.0 minor, zero majors.

Verification:
- `bun run build` clean
- `bun run tsc` clean
- `createSymbolOrString` confirmed in compiled `dist/compat.js`,
  bundled `builds/reference/dist/browser.js`, and the `ko.utils`
  object literal at the same site

https://claude.ai/code/session_01Jnv99bwWtFWFmsyL7A2cJv
JSDoc on each passthrough so IDEs strike through usage and signal the
native equivalent:

- createSymbolOrString β†’ Symbol(identifier)
- stringTrim β†’ String(value ?? '').trim()
- stringStartsWith β†’ String.prototype.startsWith
- overwriteLengthPropertyIfSupported β†’ Object.defineProperty(fn, 'length', d)

All four scheduled for removal in the next major.

https://claude.ai/code/session_01Jnv99bwWtFWFmsyL7A2cJv
Copilot AI review requested due to automatic review settings April 27, 2026 14:05
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 27, 2026

Warning

Rate limit exceeded

@brianmhunt has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 26 minutes and 10 seconds before requesting another review.

To keep reviews running without waiting, you can enable usage-based add-on for your organization. This allows additional reviews beyond the hourly cap. Account admins can enable it under billing.

βŒ› How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
βš™οΈ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: af5c83ca-f93f-4a2e-8f87-a9b273cda338

πŸ“₯ Commits

Reviewing files that changed from the base of the PR and between 52b0083 and 8c35203.

πŸ“’ Files selected for processing (2)
  • packages/lifecycle/package.json
  • packages/utils/src/compat.ts
πŸ“ Walkthrough

Walkthrough

This PR introduces a new compatibility module in @tko/utils providing four deprecated API-preserving wrapper functions, re-exports them through the package, exposes one function through Builder utilities, and removes peer dependency declarations from five packages via their package.json files. Two changesets are updated to reflect version bump strategy adjustments.

Changes

Cohort / File(s) Summary
Changeset Metadata
.changeset/fix-jsx-clean-teardown-race.md, .changeset/modernize-utils-dead-polyfills.md
Updated release level strategies; first changed from patch to minor, second adjusted multiple packages from minor to patch and clarified preservation of public API surface via inline passthrough shims.
Peer Dependency Removals
packages/bind/package.json, packages/binding.template/package.json, packages/lifecycle/package.json, packages/utils.component/package.json, packages/utils.parser/package.json
Removed peerDependencies declarations across five packages, eliminating external dependency requirements from their published manifests.
Compatibility Module
packages/utils/src/compat.ts, packages/utils/src/index.ts
Added new compat.ts module exporting four deprecated API-preserving wrapper functions: createSymbolOrString, stringTrim, stringStartsWith, and overwriteLengthPropertyIfSupported; re-exported all from package index.
Builder Utilities Integration
packages/builder/src/Builder.ts
Imported and exposed createSymbolOrString from @tko/utils, adding it to the exported Utils type and making it available as knockout.utils.createSymbolOrString.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

Possibly related PRs

Poem

🐰 Compatibility shims are here to stay,
Peer deps removed to brighten the day,
Functions wrapped with a nostalgic flair,
The API preserved beyond all repair! ✨

πŸš₯ Pre-merge checks | βœ… 5
βœ… Passed checks (5 passed)
Check name Status Explanation
Description Check βœ… Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check βœ… Passed The title accurately reflects the main objectives: preparing version 4.1.0 by fixing peer dependency cascades and restoring removed utils API as deprecated shims.
Docstring Coverage βœ… Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check βœ… Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check βœ… Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
πŸ§ͺ Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/review-halt-v5-release-AXR0q

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❀️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

πŸ’‘ Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 52b008392a

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with πŸ‘.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread packages/utils/src/compat.ts Outdated
Comment on lines +24 to +25
export function stringStartsWith(value: string, prefix: string): boolean {
return value.startsWith(prefix)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Restore null-safe behavior in stringStartsWith shim

The compatibility shim changes stringStartsWith semantics from the legacy helper: the previous implementation coerced falsy input to '' and returned false, while the new code calls value.startsWith(prefix) directly and throws when value is null/undefined. Because this commit is explicitly restoring removed public APIs for backward compatibility, this runtime throw can still break existing consumer code that used nullable inputs with ko.utils.stringStartsWith.

Useful? React with πŸ‘Β / πŸ‘Ž.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Real bug, thanks. The original stringStartsWith did string = string || '' at the top so stringStartsWith(null, 'x') returned false instead of throwing. The shim was calling .startsWith on the raw value, breaking the passthrough promise.

Fixed in 8c35203f with (value ?? '').startsWith(prefix) β€” same null-safety as the original, just expressed via the modern coalesce. stringTrim already does String(value ?? '').trim() and was unaffected.


Generated by Claude Code

Comment on lines 5 to 7
"@tko/computed": "^4.0.1",
"@tko/utils": "^4.0.1"
},
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Keep observable as declared dependency for lifecycle types

Removing @tko/observable from peerDependencies leaves @tko/lifecycle with a public type surface that still references that package (import type { Observable } in LifeCycle.subscribe), so emitted declarations require consumers to resolve @tko/observable. Under non-hoisting/isolated installs (for example strict pnpm-style layouts), relying on it as only a transitive dependency through @tko/computed can make @tko/lifecycle type resolution fail with TS2307.

Useful? React with πŸ‘Β / πŸ‘Ž.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch. LifeCycle.subscribe(observable: Observable, ...) is a public method, so Observable lands in the emitted .d.ts β€” strict-isolation installs (pnpm, yarn pnp) need @tko/observable resolvable directly, and the transitive path through @tko/computed only works under flat hoisting.

Fixed in 8c35203f by adding @tko/observable back to @tko/lifecycle's dependencies (its proper home for type-imports in public surface β€” peerDependencies was overkill, but full removal was too much).

I audited the other four packages where peerDeps were dropped β€” none of their src/ files reference the removed peers (the imports were all in spec/ only), so no public type surface depends on them. Lifecycle was the only one with this concern.


Generated by Claude Code

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Prepares the 4.1.0 release line by removing intra-fixed-group peer dependency links that were forcing a major-version cascade, while restoring removed @tko/utils identifiers as deprecated compatibility shims (including re-surfacing ko.utils.createSymbolOrString via @tko/builder).

Changes:

  • Remove several intra-monorepo peerDependencies that are spec-only or type-only to prevent fixed-group version cascade.
  • Add @tko/utils deprecated compat passthrough exports (new compat.ts) and re-wire createSymbolOrString back onto ko.utils via @tko/builder.
  • Adjust Changeset bump levels to match the intended semver impact (e.g., demote polyfill removal to patch; promote JSX option addition to minor).

Reviewed changes

Copilot reviewed 10 out of 11 changed files in this pull request and generated no comments.

Show a summary per file
File Description
packages/utils/src/index.ts Re-exports the new compat module from @tko/utils entrypoint.
packages/utils/src/compat.ts Introduces deprecated passthrough shims for previously removed public utils identifiers.
packages/builder/src/Builder.ts Re-adds createSymbolOrString to the Utils type and ko.utils object surface.
packages/utils.parser/package.json Drops peer deps deemed spec-only for release-cascade prevention.
packages/utils.component/package.json Drops peer deps deemed spec-only for release-cascade prevention.
packages/lifecycle/package.json Drops type-only peer dep to avoid fixed-group peer cascade.
packages/binding.template/package.json Drops spec-only peer dep to avoid fixed-group peer cascade.
packages/bind/package.json Drops spec-only peer dep to avoid fixed-group peer cascade.
bun.lock Updates lockfile metadata to reflect removed peer dependency edges.
.changeset/modernize-utils-dead-polyfills.md Demotes bumps to patch and documents compat shims preserving surface area.
.changeset/fix-jsx-clean-teardown-race.md Promotes @tko/utils.jsx bump to minor due to new public option.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
packages/utils/src/compat.ts (1)

24-26: Optional: null-safety parity for stringStartsWith.

value.startsWith(prefix) throws on null/undefined, whereas the historical ko.utils.stringStartsWith was generally tolerant of nullish input (matching the old stringTrim style). Since this is an explicit compat shim destined for removal, low-effort defensiveness keeps existing consumers from regressing:

♻️ Suggested adjustment
-export function stringStartsWith(value: string, prefix: string): boolean {
-  return value.startsWith(prefix)
-}
+export function stringStartsWith(value: string, prefix: string): boolean {
+  return String(value ?? '').startsWith(prefix)
+}

Skip if you've already audited that all known callers pass strings.

πŸ€– Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/utils/src/compat.ts` around lines 24 - 26, The compat shim
stringStartsWith should be null-safe like the old ko.utils counterpart: in
stringStartsWith(value, prefix) guard for null/undefined inputs (e.g., if value
== null or prefix == null) and return false for those cases, otherwise call
value.startsWith(prefix); update the function body accordingly so callers that
pass nullish values don't throw at runtime.
πŸ€– Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/utils/src/compat.ts`:
- Around line 32-35: Remove the trailing comma from the parameter list of the
function overwriteLengthPropertyIfSupported so the signature reads with two
parameters only (fn: Function, descriptor: PropertyDescriptor) and conforms to
Biome formatting rules (no trailing commas); keep the existing multiline layout
or collapse to one line as long as it stays under 120 chars.

---

Nitpick comments:
In `@packages/utils/src/compat.ts`:
- Around line 24-26: The compat shim stringStartsWith should be null-safe like
the old ko.utils counterpart: in stringStartsWith(value, prefix) guard for
null/undefined inputs (e.g., if value == null or prefix == null) and return
false for those cases, otherwise call value.startsWith(prefix); update the
function body accordingly so callers that pass nullish values don't throw at
runtime.
πŸͺ„ Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
βš™οΈ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 3c0607d9-c6d3-4e35-8642-97502053e76a

πŸ“₯ Commits

Reviewing files that changed from the base of the PR and between c36af2e and 52b0083.

β›” Files ignored due to path filters (1)
  • bun.lock is excluded by !**/*.lock
πŸ“’ Files selected for processing (10)
  • .changeset/fix-jsx-clean-teardown-race.md
  • .changeset/modernize-utils-dead-polyfills.md
  • packages/bind/package.json
  • packages/binding.template/package.json
  • packages/builder/src/Builder.ts
  • packages/lifecycle/package.json
  • packages/utils.component/package.json
  • packages/utils.parser/package.json
  • packages/utils/src/compat.ts
  • packages/utils/src/index.ts
πŸ’€ Files with no reviewable changes (5)
  • packages/lifecycle/package.json
  • packages/bind/package.json
  • packages/binding.template/package.json
  • packages/utils.parser/package.json
  • packages/utils.component/package.json

Comment thread packages/utils/src/compat.ts Outdated
Three fixes from review on #371:

1. **stringStartsWith null safety (P1, codex)** β€” original
   `stringStartsWith(string, startsWith)` did `string = string || ''`,
   coercing falsy/null/undefined to empty so the call returned `false`
   without throwing. The shim was calling `.startsWith` on the raw value
   and would throw on `null`/`undefined`. Restored with `(value ?? '')
   .startsWith(prefix)`. `stringTrim` already handled null via
   `String(value ?? '').trim()` and stays as-is.

2. **Lifecycle's @tko/observable (P2, codex)** β€” `LifeCycle.subscribe`
   has signature `subscribe(observable: Observable, ...)`, where
   `Observable` is imported from `@tko/observable`. The type appears in
   the emitted `.d.ts`, so consumers under strict-isolation installs
   (pnpm-style, yarn pnp) need `@tko/observable` resolvable directly
   rather than transitively through `@tko/computed`. Added to
   `dependencies` (correct home for type-imports in public surface;
   was overkill as a peerDependency).

3. **Trailing-comma on overwriteLengthPropertyIfSupported (P2,
   coderabbitai)** β€” Biome's actual complaint was that the param list
   fits on one line under the 120-col limit and so should be collapsed,
   not the comma per se (Biome enforces trailing commas in genuinely
   multi-line lists by default). Collapsed to single line.

Verification:
- `bun run build` clean
- `bun run tsc` clean
- `bunx @biomejs/biome check` clean
- `bunx @changesets/cli status` still 4.1.0 minor across the fixed group

https://claude.ai/code/session_01Jnv99bwWtFWFmsyL7A2cJv
@brianmhunt brianmhunt merged commit 7a92404 into main Apr 27, 2026
9 checks passed
@brianmhunt brianmhunt deleted the claude/review-halt-v5-release-AXR0q branch April 27, 2026 15:09
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.

3 participants