feat: introduce CaveatType enum and normalize caveat type handling in builders#170
feat: introduce CaveatType enum and normalize caveat type handling in builders#170
Conversation
packages/smart-accounts-kit/src/caveatBuilder/resolveCaveats.ts
Outdated
Show resolved
Hide resolved
jeffsmale90
left a comment
There was a problem hiding this comment.
This looks super nice!
Would it make sense to add test coverage? I think for the most part behaviour hasn't changed (as the enum is a string anyways), but maybe it would be nice to have tests showing how to pass an enum in each of the cases.
I hope typescript errors in the tests would cause the tests to fail, so we can at least assert the type resolutions work as expected.
packages/smart-accounts-kit/src/caveatBuilder/resolveCaveats.ts
Outdated
Show resolved
Hide resolved
…ad of returning a value, allowing TypeScript to narrow the type and eliminating the need for variable reassignment - Remove unnecessary 'as string' type assertion in resolveCaveats.ts - the type is already correctly inferred - Move CaveatType enum from constants.ts to coreCaveatBuilder.ts to better organize the caveat-specific type definition - Remove ConvertCaveatConfigsToInputs and CoreCaveatConfiguration from public exports - only expose CaveatType and essential builder types - Update imports across the codebase to reflect the new locations
packages/smart-accounts-kit/src/caveatBuilder/resolveCaveats.ts
Outdated
Show resolved
Hide resolved
packages/smart-accounts-kit/src/caveatBuilder/coreCaveatBuilder.ts
Outdated
Show resolved
Hide resolved
| export type ConvertCaveatConfigsToInputs<T extends { type: string }> = | ||
| T extends { type: string } | ||
| ? Omit<T, 'type'> & { type: T['type'] | `${T['type']}` } | ||
| : never; |
There was a problem hiding this comment.
Duplicated generic type utility for enum flexibility
Low Severity
ConvertCaveatConfigsToInputs is a near-duplicate of the existing ConvertScopeConfigsToInputs in scope/index.ts. Both have identical logic (Omit<T, 'type'> & { type: T['type'] | \${T['type']}` }), differing only in the constraint ({ type: string }vs{ type: ScopeType }). The caveat version with { type: string }` is strictly more general and could serve both use cases as a single shared utility type, avoiding duplicated logic that risks diverging over time.
There was a problem hiding this comment.
If we can define one generic utility type that would be helpful - it doesn't get exported in the public API, so no need for backwards compatibility, we can just drop the unused util.
…lders - Updated caveat builders to use the CaveatType enum instead of string literals for better type safety and maintainability. - Removed the validateCaveatType function from utils as it is no longer necessary. - Adjusted tests to reflect the changes in caveat type usage.
…gToInputs in scope types
jeffsmale90
left a comment
There was a problem hiding this comment.
Looking really good! It's definitely a struggle when changes touch every caveat type!
A couple minor comments - mostly I think we need to be able to:
caveatBuilder.add(CaveatType.AllowedCaveats, { ... }); // works
caveatBuilder.add("allowedCaveats", { ...}); // doesn't work
| export type ConvertCaveatConfigsToInputs<T extends { type: string }> = | ||
| T extends { type: string } | ||
| ? Omit<T, 'type'> & { type: T['type'] | `${T['type']}` } | ||
| : never; |
There was a problem hiding this comment.
If we can define one generic utility type that would be helpful - it doesn't get exported in the public API, so no need for backwards compatibility, we can just drop the unused util.
| const { type, ...config } = caveat; | ||
| scopeCaveatBuilder.addCaveat(type, config); | ||
| // Type assertion: addCaveat validates at runtime and throws if enforcer doesn't exist | ||
| (scopeCaveatBuilder.addCaveat as any)(type, config); |
There was a problem hiding this comment.
I was going to suggest scopeCaveatBuilder.addCaveat(type as CaveatType, config); but this is going to be a problem for callers as well - the CoreCaveatBuilder is exported publicly, and the caller expects to be able to pass either the enum or the string as the first parameter.
This problem is my fault, because I suggested using the enum as the key to the CoreCaveatMap, which makes the type parameter of the CoreCaveatBuilder constrained to just the enum.
If we revert the key of CoreCaveatMap back to the string, I guess it won't allow the caveat to be passed, so we need CoreCaveatBuilder's generic parameter of CaveatBuilder to be keyed on CaveatType | ${CaveatType}`
There was a problem hiding this comment.
We have some good coverage for adding caveats with the CaveatType enum, but none for addCaveat(string, config)
There was a problem hiding this comment.
I left a comment further down indicating that we don't cover addCaveat(string, config) - I guess each of these individual caveat type tests should cover both?


📝 Description
Apply the ScopeType enum pattern from PR #133 to caveat type parameters, allowing both enum references and string literals when defining caveats.
🔄 What Changed?
CaveatTypeenum toconstants.tswith all 26 caveat enforcer function namesConvertCaveatConfigsToInputsgeneric type to support flexible caveat type specification (enum or string)CaveatConfigurationtype to use the flexible type conversion patternCaveatBuilder.addCaveat()to normalize and accept both enum and string caveat typesresolveCaveats()function to handle flexible caveat type normalizationCaveatType,CoreCaveatConfiguration,ConvertCaveatConfigsToInputs) in public API🚀 Why?
🧪 How to Test?
CaveatType.AllowedMethodsenum reference incaveatBuilder.addCaveat()'allowedMethods'createDelegationcaveats array📋 Checklist
🔗 Related Issues
Related to #145
Related to PR #133 (ScopeType enum pattern)
Related to PR #137 (enum flexibility pattern)
📚 Additional Notes
This implementation follows the same flexible type pattern established in PR #133 for ScopeType. Both enum references and string literals are supported for maximum flexibility:
Note
Medium Risk
Moderate risk: adds runtime validation of caveat
typestrings inresolveCaveats, which can turn previously generic errors into earlier failures and may reject non-coretypeconfigs passed in caveat arrays.Overview
Introduces a new
CaveatTypeenum for all core caveat/enforcer names and updates the core caveat map/types so caveat configs can be specified with either enum members or their string values.resolveCaveatsnow validates config-style caveats ({ type, ... }) againstCaveatTypeand improves error messages by reporting the failing array index; the enum is re-exported fromcaveatBuilderand the package root, and a new test suite exercises enum/string interchangeability acrossaddCaveat,createDelegation, andresolveCaveats.Written by Cursor Bugbot for commit 755b3e0. This will update automatically on new commits. Configure here.