Skip to content

Replace ts keyof transformer usage with runtime code #3413

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
May 2, 2025
Merged

Conversation

CarsonF
Copy link
Member

@CarsonF CarsonF commented May 2, 2025

This removes all the usage of this transformer (besides the Props/SecuredProps which will be addressed separately).
This transformer prevents us from using different compilers.

CarsonF added 6 commits May 2, 2025 12:22
… esm classes

This works as long as there is no type mapping in the inheritance chain.
This is fine for this single use case.
And this can also go away once we finish Gel migration.
This enum value needs to be lazily referenced, so the actual enum defers
creation until after files have had a chance to run their side effects to add their members.
JS first calls ownKeys and then looks at the property descriptors to see
if the key is enumerable.
So we need to forward the descriptors as well for this to work.
Copy link

coderabbitai bot commented May 2, 2025

📝 Walkthrough

Walkthrough

This update introduces several structural and type system changes across the codebase. The main focus is the replacement of string literal union types and dynamic key extraction utilities (such as ts-transformer-keys) with explicit enum types created using makeEnum and related helpers. Action types, transition names, and producible types are now managed as enums, with lazy or runtime registration in some cases. The registration of producible types is refactored to use a mutable set with module augmentation, supporting dynamic extensibility. Imports and logic relying on key extraction from types are updated to use the new enums or sets directly. Several DTOs now register themselves as valid producible types. No changes were made to the signatures of exported or public entities except where type definitions were updated to use the new enums.

Changes

Files/Paths Change Summary
package.json Updated dependency versions for @seedcompany/common and @seedcompany/nest.
src/common/lazy-ref.ts Added getOwnPropertyDescriptor trap to the lazyRef proxy handler for improved property descriptor support.
src/components/authorization/policy/actions.ts Refactored action types from string unions to enums using makeEnum and updated type aliases accordingly.
src/components/authorization/policy/executor/all-permissions-view.ts Replaced dynamic key extraction with enum-based compatible action keys; introduced CompatAction enum and helper for legacy action names.
src/components/authorization/policy/executor/policy-dumper.ts Switched from dynamic key extraction to direct use of action enums for permission mapping.
src/components/authorization/policy/policy.factory.ts Changed child action key extraction from dynamic to direct use of action enums, with explicit array spreading.
src/components/engagement/dto/create-engagement.dto.ts Changed DTO base class to extend DataObject; replaced static key extraction with runtime property enumeration; made engagement DTOs concrete classes.
src/components/ethno-art/dto/ethno-art.dto.ts
src/components/film/dto/film.dto.ts
src/components/story/dto/story.dto.ts
Registered respective producible types (EthnoArt, Film, Story) in ProducibleTypeEntries set and updated module augmentation.
src/components/product/dto/producible.dto.ts Refactored ProducibleType to use a lazily evaluated enum based on a mutable set (ProducibleTypeEntries); added GraphQL enum registration.
src/components/product/dto/product.dto.ts Registered three product types in ProducibleTypeEntries and updated module augmentation.
src/components/progress-report/workflow/dto/workflow-transition.dto.ts Updated import path for ProgressReportStatus to a more specific module.
src/components/progress-report/workflow/transitions.ts Changed TransitionName from a union of keys to an enum created with makeEnum; updated imports.
src/core/config/config.service.ts Replaced dynamic key extraction for transition names with direct use of the new TransitionName enum in environment parsing.
✨ Finishing Touches
  • 📝 Generate Docstrings

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
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@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: 0

🧹 Nitpick comments (2)
src/components/progress-report/workflow/transitions.ts (1)

94-95: Refactored TransitionName from type-only to enum-based implementation

The TransitionName has been refactored from a simple TypeScript type to a proper enum created with makeEnum. This provides both compile-time and runtime benefits - you can now use TransitionName as a value, not just as a type.

Consider adding a concise comment explaining this pattern for future developers, especially if this change is part of a broader refactoring effort across the codebase:

+// Convert TransitionName from a simple type to an enum that can be used as both type and value
 export type TransitionName = EnumType<typeof TransitionName>;
 export const TransitionName = makeEnum(entries(Transitions).map(([k]) => k));
src/components/product/dto/producible.dto.ts (1)

54-63: Refactored ProducibleType to use lazy initialization pattern

The implementation has been redesigned to:

  1. Use lazy initialization with lazyRef
  2. Cache the created enum instance
  3. Allow dynamic extension via the ProducibleTypeEntries set
  4. Register the enum with GraphQL schema eagerly

This is a significant improvement in extensibility, allowing modules to register their producible types in a decentralized manner while maintaining type safety.

Consider adding a brief comment explaining the extensibility pattern for developers unfamiliar with this approach:

// Augment this with each implementation of Producible via declaration merging
// eslint-disable-next-line @typescript-eslint/no-empty-interface
export interface ProducibleTypeEntries {}
export const ProducibleTypeEntries = new Set<string>();

+// Lazy enum that builds from all registered ProducibleTypeEntries
+// Other modules can augment this enum by adding to ProducibleTypeEntries
export type ProducibleType = EnumType<typeof ProducibleType>;
export const ProducibleType = lazyRef(
  (): MadeEnum<keyof ProducibleTypeEntries> =>
    (realProducibleType ??= makeEnum({
      values: ProducibleTypeEntries as Iterable<keyof ProducibleTypeEntries>,
    })),
);
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b47812d and 18527ee.

⛔ Files ignored due to path filters (1)
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (15)
  • package.json (1 hunks)
  • src/common/lazy-ref.ts (1 hunks)
  • src/components/authorization/policy/actions.ts (1 hunks)
  • src/components/authorization/policy/executor/all-permissions-view.ts (3 hunks)
  • src/components/authorization/policy/executor/policy-dumper.ts (1 hunks)
  • src/components/authorization/policy/policy.factory.ts (1 hunks)
  • src/components/engagement/dto/create-engagement.dto.ts (4 hunks)
  • src/components/ethno-art/dto/ethno-art.dto.ts (1 hunks)
  • src/components/film/dto/film.dto.ts (1 hunks)
  • src/components/product/dto/producible.dto.ts (2 hunks)
  • src/components/product/dto/product.dto.ts (2 hunks)
  • src/components/progress-report/workflow/dto/workflow-transition.dto.ts (1 hunks)
  • src/components/progress-report/workflow/transitions.ts (2 hunks)
  • src/components/story/dto/story.dto.ts (1 hunks)
  • src/core/config/config.service.ts (2 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (9)
src/components/story/dto/story.dto.ts (1)
src/components/product/dto/producible.dto.ts (2)
  • ProducibleTypeEntries (51-51)
  • ProducibleTypeEntries (52-52)
src/components/product/dto/product.dto.ts (1)
src/components/product/dto/producible.dto.ts (2)
  • ProducibleTypeEntries (51-51)
  • ProducibleTypeEntries (52-52)
src/components/film/dto/film.dto.ts (1)
src/components/product/dto/producible.dto.ts (2)
  • ProducibleTypeEntries (51-51)
  • ProducibleTypeEntries (52-52)
src/components/progress-report/workflow/transitions.ts (1)
src/common/index.ts (2)
  • EnumType (2-2)
  • makeEnum (2-2)
src/components/authorization/policy/executor/policy-dumper.ts (1)
src/components/authorization/policy/actions.ts (8)
  • ResourceAction (6-6)
  • ResourceAction (7-7)
  • PropAction (12-12)
  • PropAction (13-13)
  • ChildSingleAction (30-30)
  • ChildSingleAction (31-31)
  • ChildListAction (24-24)
  • ChildListAction (25-25)
src/components/authorization/policy/policy.factory.ts (1)
src/components/authorization/policy/actions.ts (4)
  • ChildListAction (24-24)
  • ChildListAction (25-25)
  • ChildSingleAction (30-30)
  • ChildSingleAction (31-31)
src/components/ethno-art/dto/ethno-art.dto.ts (1)
src/components/product/dto/producible.dto.ts (2)
  • ProducibleTypeEntries (51-51)
  • ProducibleTypeEntries (52-52)
src/components/authorization/policy/executor/all-permissions-view.ts (2)
src/components/authorization/policy/actions.ts (2)
  • AnyAction (37-37)
  • AnyAction (38-42)
src/common/index.ts (2)
  • EnumType (2-2)
  • makeEnum (2-2)
src/components/authorization/policy/actions.ts (1)
src/common/index.ts (2)
  • EnumType (2-2)
  • makeEnum (2-2)
⏰ Context from checks skipped due to timeout of 90000ms (11)
  • GitHub Check: E2E Tests (neo4j 6/6)
  • GitHub Check: E2E Tests (neo4j 5/6)
  • GitHub Check: E2E Tests (neo4j 4/6)
  • GitHub Check: E2E Tests (neo4j 3/6)
  • GitHub Check: E2E Tests (neo4j 2/6)
  • GitHub Check: Generate (head)
  • GitHub Check: E2E Tests (neo4j 1/6)
  • GitHub Check: lint
  • GitHub Check: Clean
  • GitHub Check: Generate (base)
  • GitHub Check: Unit
🔇 Additional comments (30)
package.json (1)

52-52: Package version update aligns with transformer removal strategy

The version updates for @seedcompany/common (to >=0.19.1) and @seedcompany/nest (to ^1.6.1) support the PR's goal of replacing the TypeScript keyof transformer with runtime code. These updated packages provide the necessary enum utilities and related features used throughout the refactoring.

Also applies to: 54-54

src/components/progress-report/workflow/dto/workflow-transition.dto.ts (1)

4-4: Import path updated for consistency

The more specific import path for ProgressReportStatus aligns with the broader refactoring to use explicit enum types instead of relying on the TypeScript keyof transformer.

src/components/story/dto/story.dto.ts (2)

12-14: Story type registration implements the new runtime approach

The code now imports ProducibleTypeEntries and registers 'Story' as a valid producible type at runtime, effectively replacing the static type extraction previously handled by the TypeScript keyof transformer. This is consistent with the PR's objective of moving away from transformer usage to runtime code.


15-19: Module augmentation completes the type registration pattern

The TypeScript module augmentation ensures type safety by declaring 'Story' in the ProducibleTypeEntries interface. This dual approach (runtime registration + type augmentation) maintains both runtime functionality and compile-time type checking.

src/components/film/dto/film.dto.ts (2)

12-15: Import formatting and extension for runtime type registration

The import statement has been restructured and extended to include ProducibleTypeEntries, preparing for the runtime registration of the Film type. This is consistent with the PR's goal of replacing transformer-based approaches with runtime code.


17-22: Film type registered using the new pattern

Similar to the Story DTO, this code registers 'Film' as a valid producible type at runtime and augments the TypeScript module to maintain type safety. This implementation correctly follows the new pattern established for producible types across the codebase.

src/components/ethno-art/dto/ethno-art.dto.ts (2)

12-15: Import updated to register EthnoArt as a producible type.

The import was expanded to include ProducibleTypeEntries which is necessary for the next step where EthnoArt is registered as a producible type. This aligns with the broader refactoring approach replacing the static type extraction with runtime registration.


17-17: EthnoArt successfully registered as a producible type.

The code now adds 'EthnoArt' to the ProducibleTypeEntries set, properly registering it at runtime as a valid producible type. This matches the module augmentation in lines 18-22 and aligns with the pattern used for other producible types in the codebase.

src/components/product/dto/product.dto.ts (2)

30-35: Import expanded to support runtime type registration.

The import statement was updated to include ProducibleTypeEntries, allowing the file to register product types at runtime rather than relying on the TypeScript keyof transformer. This is consistent with the overall refactoring strategy.


269-271: Product types correctly registered with ProducibleTypeEntries.

The code now registers three product types ('DirectScriptureProduct', 'DerivativeScriptureProduct', and 'OtherProduct') with the ProducibleTypeEntries set. This ensures they're recognized as valid producible types at runtime and matches the module augmentation in lines 272-278.

src/core/config/config.service.ts (2)

16-16: Value import replaces type-only import for TransitionName.

Changed from importing only the type to importing the actual enum value. This is necessary for the conversion from using the TypeScript keyof transformer to using runtime code.


135-135: Replaced keyof transformer with direct enum reference.

The code now directly uses the ProgressReportTransitionName enum instead of using keysOf<Record<ProgressReportTransitionName, ''>>(). This eliminates the dependency on the ts-transformer-keys library for this case and aligns with the PR objective of replacing keyof transformer usage.

src/components/authorization/policy/policy.factory.ts (2)

316-316: Replaced keyof extraction with direct enum reference.

The code now directly assigns the appropriate enum (ChildListAction or ChildSingleAction) instead of using the keysOf transformer to extract keys at runtime. This simplifies the code and removes a dependency on the ts-transformer-keys library.


320-321: Spread operator correctly converts enum to array.

The call to pick now uses [...childActions] to spread the enum values into an array, as required by the function. This is an appropriate way to convert an enum object to an array of its values for use with the pick function.

src/common/lazy-ref.ts (1)

26-28: Good enhancement to support property descriptor reflection!

This addition of the getOwnPropertyDescriptor trap to the proxy handler enhances the lazyRef function's completeness. It allows for proper handling of reflective operations like Object.getOwnPropertyDescriptor, which is likely needed by frameworks or utilities that inspect object properties.

src/components/progress-report/workflow/transitions.ts (2)

1-2: Updated imports for new enum implementation

The added imports are necessary for the new enum implementation pattern that replaces the string literal union type.


5-6: More specific import paths

The import paths have been updated to more specific module paths, which is good for clarity and maintainability.

src/components/authorization/policy/executor/policy-dumper.ts (2)

190-191: Using ResourceAction enum directly instead of dynamic key extraction

The code now uses the ResourceAction enum directly in mapValues.fromList instead of dynamically extracting keys. This improves type safety and makes the dependencies explicit.


195-197: Using action enums directly for property and child actions

Similar to the previous change, the code now uses the enum objects directly (PropAction, ChildSingleAction, ChildListAction) instead of dynamic key extraction. This is consistent with the pattern applied elsewhere in the codebase.

src/components/product/dto/producible.dto.ts (2)

1-7: Updated imports for enhanced enum implementation

Added import for registerEnumType from GraphQL and other related utilities needed for the new lazy enum implementation.


52-52: Added mutable set for dynamic producible type registration

The ProducibleTypeEntries set provides a central registry for producible types, allowing different modules to contribute their own types.

src/components/authorization/policy/executor/all-permissions-view.ts (3)

43-43: Good refactoring to use the newly defined enum values

The change from dynamic key extraction to using CompatAction.values improves type safety and eliminates the dependency on the TypeScript keyof transformer, aligning with the PR objectives.


77-84: Well-designed enum-based compatibility layer

Good approach creating the CompatAction enum that combines both new action types and legacy "canXxx" prefixed variants. The asLegacyAction helper function encapsulates the transformation logic nicely, making the code more maintainable.


86-99: Clean implementation of compatibility mappings

The updated implementation of compatMap using mapValues.fromList with enum values provides a more robust solution than the previous dynamic key extraction approach. This maintains the same functionality while removing dependencies on TypeScript transformers.

src/components/engagement/dto/create-engagement.dto.ts (3)

19-19: Good inheritance refactoring

The change to extend DataObject provides additional functionality to the CreateEngagement class. This is a positive architectural improvement.


40-44: Runtime property extraction approach needs clarification

The implementation of static Props using runtime property extraction is a good replacement for the TypeScript keyof transformer. However, the warning comment suggests potential limitations with inheritance type mapping.

Can you clarify under what conditions this approach might not work with inheritance type mapping? This seems like an important constraint that should be documented.


87-91: Consistent implementation of Props extraction

Good consistency in applying the same pattern to both engagement types, using the same runtime property extraction approach.

src/components/authorization/policy/actions.ts (3)

1-14: Well-structured enum-based actions implementation

The refactoring of action types from string literal unions to enum-based types using makeEnum and EnumType is a significant improvement. This approach provides better type safety and runtime capabilities while removing dependencies on TypeScript transformers.


18-32: Consistent enum pattern across action types

Good consistency in applying the same enum pattern to all action types. The clear separation between different categories of actions (resource, property, child relationships) makes the code more maintainable and self-documenting.


37-42: Elegant composition of action types

Using spread syntax to compose the AnyAction enum from other action enums is elegant and maintainable. This clearly communicates that AnyAction is a union of the other action types while providing all the benefits of the enum approach.

@CarsonF CarsonF merged commit e630d92 into develop May 2, 2025
18 checks passed
@CarsonF CarsonF deleted the no-ts-keys-of branch May 2, 2025 17:48
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