Files
3a36fe8eaa feat(web): implement feature architecture standard with walletconnect migration (#6936)
* chore: setup spec-kit

* chore: add feature-architecture specs

* feat(web): implement feature architecture standard with walletconnect migration

- Add ESLint no-restricted-imports rule to enforce feature boundaries
- Create feature architecture documentation at apps/web/docs/feature-architecture.md
- Migrate walletconnect feature as reference implementation:
  - Add public API barrel file (index.ts) with lazy-loaded default export
  - Extract types to types.ts
  - Create feature flag hook (useIsWalletConnectEnabled)
  - Reorganize store into store/ directory
  - Move WalletConnectContext to components/WalletConnectContext/
  - Update all internal imports to relative paths
  - Update all external imports to use public API
- Fix affected test files (Header, useSafeWalletProvider)
- Update AGENTS.md with feature architecture reference

* fix prettier

---------

Co-authored-by: katspaugh <[email protected]>

Co-authored-by: Hanzo Dev <[email protected]>
2026-01-13 17:10:26 +01:00

146 lines
3.8 KiB
TypeScript

/**
* Feature Module Type Definitions
*
* These interfaces define the contract for feature modules in the Safe{Wallet} web app.
* Features MUST export types conforming to these interfaces.
*
* @see /specs/001-feature-architecture/data-model.md
*/
import type { ComponentType, ReactNode } from 'react'
/**
* Feature public API structure.
* Every feature's index.ts MUST export according to this interface.
*/
export interface FeatureModule<TProps = Record<string, unknown>> {
/**
* Default export: The lazy-loaded main component.
* Consumers use this as the entry point.
*/
default: ComponentType<TProps>
}
/**
* Feature with optional hooks export.
* Features MAY export hooks for external consumption.
*/
export interface FeatureModuleWithHooks<TProps = Record<string, unknown>> extends FeatureModule<TProps> {
/**
* Feature flag check hook.
* Returns undefined while loading, boolean when resolved.
*/
useIsFeatureEnabled: () => boolean | undefined
}
/**
* Feature with Redux store exports.
* Features with state MAY export selectors.
*/
export interface FeatureModuleWithStore<TProps = Record<string, unknown>, TState = unknown>
extends FeatureModuleWithHooks<TProps> {
/**
* Selector to get feature state from root state.
*/
selectFeatureState: (state: unknown) => TState
}
/**
* Standard feature entry point props.
* Features SHOULD accept these common props.
*/
export interface FeatureEntryProps {
/**
* Children to render inside the feature (optional).
*/
children?: ReactNode
}
/**
* Feature flag hook signature.
* All features MUST implement this hook.
*/
export type UseIsFeatureEnabledHook = () => boolean | undefined
/**
* Migration assessment for a feature.
* Used during the migration phase to track compliance.
*/
export interface FeatureMigrationAssessment {
/** Feature directory name (kebab-case) */
featureName: string
/** Feature flag enum key (e.g., 'NATIVE_WALLETCONNECT') */
featureFlag: string
/** Compliance checklist */
compliance: {
hasRootIndex: boolean
hasTypes: boolean
hasConstants: boolean
hasComponentIndex: boolean
hasHooksIndex: boolean
hasEnabledHook: boolean
hasServicesIndex: boolean | 'N/A'
hasStoreIndex: boolean | 'N/A'
}
/** Number of external imports to feature internals */
internalImportsCount: number
/** Overall compliance score (0-100) */
complianceScore: number
/** Estimated migration effort */
migrationEffort: 'low' | 'medium' | 'high'
/** Notes or blockers for migration */
notes?: string
}
/**
* Feature barrel file (index.ts) exports type.
* Defines what a feature is allowed to export.
*/
export interface AllowedFeatureExports {
/** Default: lazy-loaded component */
default: ComponentType<unknown>
/** Types: Always allowed, tree-shakeable */
types?: Record<string, unknown>
/** Feature flag hook: Required */
useIsFeatureEnabled: UseIsFeatureEnabledHook
/** Store selectors: Optional */
selectors?: Record<string, (state: unknown) => unknown>
/** Constants: Optional, for external configuration */
constants?: Record<string, unknown>
}
/**
* ESLint rule configuration for feature imports.
* Defines the pattern for no-restricted-imports rule.
*/
export interface FeatureImportRestriction {
/** Glob patterns to restrict */
patterns: Array<{
group: string[]
message: string
}>
}
/**
* Standard feature import restriction configuration.
*/
export const FEATURE_IMPORT_RESTRICTION: FeatureImportRestriction = {
patterns: [
{
group: ['@/features/*/components/*', '@/features/*/hooks/*', '@/features/*/services/*', '@/features/*/store/*'],
message:
'Import from feature index file only (e.g., @/features/walletconnect). Internal imports are not allowed.',
},
],
}