Skip to content

refactor(thermidor): Introduce SingletonFactory to DRY getOrCreate functions#7892

Closed
lavoiesl wants to merge 3 commits into
mainfrom
seb-singleton-factory
Closed

refactor(thermidor): Introduce SingletonFactory to DRY getOrCreate functions#7892
lavoiesl wants to merge 3 commits into
mainfrom
seb-singleton-factory

Conversation

@lavoiesl

@lavoiesl lavoiesl commented Jun 26, 2026

Copy link
Copy Markdown
Collaborator

While reading through the thermidor code, I found the has/get/set repetitive, so I made a generic helper to DRY it out.

Adds a generic SingletonFactory<K, V, A> function that wraps a Map with lazy-initialization semantics — call it with an argument, get back the cached value or create it once via the factory.

Refactors 33 instances of the repeated Map + has/set/get pattern across thermidor/src/core/internal/ into one-liner SingletonFactory(createX) calls.

Also rewrites createFacadeCache to delegate to SingletonFactory with a custom keyFn.

Changes:

  • New: packages/thermidor/src/core/internal/singleton-factory/singleton-factory.ts
  • Refactored: all *-actions.ts, *-slice.ts, *-selectors.ts files in internal/
  • Refactored: facade-cache.ts, generative-hydration.ts, search-thunk-slice.ts, commerce-search-thunk-slice.ts
  • Excluding the added test file, this is:
    159 insertions(+), 323 deletions(-)
    

Non changes:

  • Not refactored: getOrCreateSearchEndpointSlice and getOrCreateCommerceSearchEndpointSlice
    • They are two-argument factories that don't fit the (key) => V shape.
    • Having arguments that are not part of the cache is an anti-pattern that can break guarantees, but that's out of scope for this PR.

Points of debate:

  • I’m not sold on the SingletonFactory, I’m happy to take other suggestions

@changeset-bot

changeset-bot Bot commented Jun 26, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: a1e6b10

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@svcsnykcoveo

svcsnykcoveo commented Jun 26, 2026

Copy link
Copy Markdown

Snyk checks have passed. No issues have been found so far.

Status Scan Engine Critical High Medium Low Total (0)
Open Source Security 0 0 0 0 0 issues
Licenses 0 0 0 0 0 issues
Code Security 0 0 0 0 0 issues

💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse.

@pkg-pr-new

pkg-pr-new Bot commented Jun 26, 2026

Copy link
Copy Markdown
@coveo/atomic

npm i https://pkg.pr.new/@coveo/atomic@7892

@coveo/atomic-hosted-page

npm i https://pkg.pr.new/@coveo/atomic-hosted-page@7892

@coveo/atomic-legacy

npm i https://pkg.pr.new/@coveo/atomic-legacy@7892

@coveo/atomic-react

npm i https://pkg.pr.new/@coveo/atomic-react@7892

@coveo/auth

npm i https://pkg.pr.new/@coveo/auth@7892

@coveo/bueno

npm i https://pkg.pr.new/@coveo/bueno@7892

@coveo/create-atomic

npm i https://pkg.pr.new/@coveo/create-atomic@7892

@coveo/create-atomic-component

npm i https://pkg.pr.new/@coveo/create-atomic-component@7892

@coveo/create-atomic-component-project

npm i https://pkg.pr.new/@coveo/create-atomic-component-project@7892

@coveo/create-atomic-result-component

npm i https://pkg.pr.new/@coveo/create-atomic-result-component@7892

@coveo/create-atomic-rollup-plugin

npm i https://pkg.pr.new/@coveo/create-atomic-rollup-plugin@7892

@coveo/headless

npm i https://pkg.pr.new/@coveo/headless@7892

@coveo/headless-react

npm i https://pkg.pr.new/@coveo/headless-react@7892

@coveo/shopify

npm i https://pkg.pr.new/@coveo/shopify@7892

commit: a1e6b10

@lavoiesl
lavoiesl marked this pull request as ready for review June 26, 2026 19:52
Copilot AI review requested due to automatic review settings June 26, 2026 19:52

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR refactors @coveo/thermidor internals to remove repeated Map-backed getOrCreate* caching patterns by introducing a shared SingletonFactory helper, and applies it across slices/actions/selectors and a couple of interface utilities.

Changes:

  • Added a generic SingletonFactory utility (with unit tests) to memoize values per derived key.
  • Replaced per-module Map caches in many getOrCreate* exports with SingletonFactory(createX) calls.
  • Refactored createFacadeCache and getOrCreateHydrateFromSnapshotAction to reuse the same caching helper.

Reviewed changes

Copilot reviewed 37 out of 37 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
packages/thermidor/src/core/internal/triggers/triggers-slice.ts Replace local slice cache with SingletonFactory(createTriggersSlice).
packages/thermidor/src/core/internal/triggers/triggers-actions.ts Replace local actions cache with SingletonFactory(createTriggersActions).
packages/thermidor/src/core/internal/sort/sort-slice.ts Replace local slice cache with SingletonFactory(createSortSlice).
packages/thermidor/src/core/internal/sort/sort-selectors.ts Replace local selectors cache with SingletonFactory(createSortSelectors).
packages/thermidor/src/core/internal/sort/sort-actions.ts Replace local actions cache with SingletonFactory(createSortActions).
packages/thermidor/src/core/internal/singleton-factory/singleton-factory.ts Add shared memoization helper used by many getOrCreate* functions.
packages/thermidor/src/core/internal/singleton-factory/singleton-factory.test.ts Add unit tests for SingletonFactory behavior and custom key derivation.
packages/thermidor/src/core/internal/search-parameters/search-parameters-slice.ts Replace local slice cache with SingletonFactory(createSearchParametersSlice).
packages/thermidor/src/core/internal/search-parameters/search-parameters-selectors.ts Replace local selectors cache with SingletonFactory(createSearchParametersSelectors).
packages/thermidor/src/core/internal/search-parameters/search-parameters-actions.ts Replace local actions cache with SingletonFactory(createSearchParametersActions).
packages/thermidor/src/core/internal/search-box/search-box-slice.ts Replace local slice cache with SingletonFactory(createSearchBoxSlice).
packages/thermidor/src/core/internal/search-box/search-box-selectors.ts Replace local selectors cache with SingletonFactory(createSearchBoxSelectors).
packages/thermidor/src/core/internal/search-box/search-box-actions.ts Replace local actions cache with SingletonFactory(createSearchBoxActions).
packages/thermidor/src/core/internal/result-list/result-list-slice.ts Replace local slice cache with SingletonFactory(createResultsSlice).
packages/thermidor/src/core/internal/result-list/result-list-selectors.ts Replace local selectors cache with SingletonFactory(createResultsSelectors).
packages/thermidor/src/core/internal/result-list/result-list-actions.ts Replace local actions cache with SingletonFactory(createResultsActions).
packages/thermidor/src/core/internal/query-correction/query-correction-slice.ts Replace local slice cache with SingletonFactory(createQueryCorrectionSlice).
packages/thermidor/src/core/internal/query-correction/query-correction-actions.ts Replace local actions cache with SingletonFactory(createQueryCorrectionActions).
packages/thermidor/src/core/internal/product-list/product-list-slice.ts Replace local slice cache with SingletonFactory(createProductListSlice).
packages/thermidor/src/core/internal/product-list/product-list-selectors.ts Replace local selectors cache with SingletonFactory(createProductListSelectors).
packages/thermidor/src/core/internal/product-list/product-list-actions.ts Replace local actions cache with SingletonFactory(createProductListActions).
packages/thermidor/src/core/internal/pagination/pagination-slice.ts Replace local slice cache with SingletonFactory(createPaginationSlice).
packages/thermidor/src/core/internal/pagination/pagination-selectors.ts Replace local selectors cache with SingletonFactory(createPaginationSelectors).
packages/thermidor/src/core/internal/pagination/pagination-actions.ts Replace local actions cache with SingletonFactory(createPaginationActions).
packages/thermidor/src/core/internal/generative/generative-slice.ts Replace local slice cache with SingletonFactory(createGenerativeSlice).
packages/thermidor/src/core/internal/generative/generative-selectors.ts Replace local selectors cache with SingletonFactory(createGenerativeSelectors).
packages/thermidor/src/core/internal/generative/generative-actions.ts Replace local actions cache with SingletonFactory(createGenerativeActions).
packages/thermidor/src/core/internal/facets/facets-slice.ts Replace local slice cache with SingletonFactory(createFacetsSlice).
packages/thermidor/src/core/internal/facets/facets-selectors.ts Replace local selectors cache with SingletonFactory(createFacetsSelectors).
packages/thermidor/src/core/internal/facets/facets-actions.ts Replace local actions cache with SingletonFactory(createFacetsActions).
packages/thermidor/src/core/internal/cart/cart-slice.ts Replace local slice cache with SingletonFactory(createCartSlice).
packages/thermidor/src/core/internal/cart/cart-selectors.ts Replace local selectors cache with SingletonFactory(createCartSelectors).
packages/thermidor/src/core/internal/cart/cart-actions.ts Replace local actions cache with SingletonFactory(createCartActions).
packages/thermidor/src/core/internal/api/search/search-thunk-slice.ts Replace local selectors cache with SingletonFactory(createSearchEndpointSelectors).
packages/thermidor/src/core/internal/api/search/commerce-search-thunk-slice.ts Replace local selectors cache with SingletonFactory(createCommerceSearchEndpointSelectors).
packages/thermidor/src/core/interface/utils/facade-cache.ts Refactor facade cache to use SingletonFactory with a derived scope key.
packages/thermidor/src/core/interface/generative/generative-hydration.ts Replace hydrate action cache with SingletonFactory(createHydrateAction).

Comment on lines +28 to +31
export function SingletonFactory<K, V, A = K>(
factory: (arg: A) => V,
keyFn: (arg: A) => K = (arg) => arg as unknown as K
): (arg: A) => V {

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Makes sense to me. I’ll fix if we agree on this PR

@lavoiesl

lavoiesl commented Jul 9, 2026

Copy link
Copy Markdown
Collaborator Author

Obsolete

@lavoiesl lavoiesl closed this Jul 9, 2026
@lavoiesl
lavoiesl deleted the seb-singleton-factory branch July 9, 2026 14:33
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