feat(kernel): Add @wordpress/data store integration (A3)#17
Conversation
- Implement createStore factory for generating typed stores from resources - Add lazy store loading to ResourceObject via getter property - Auto-register stores with @wordpress/data on first access - Include comprehensive test suite (42 new tests, 94.8% coverage) - Add complete @wordpress/data store integration documentation - Export createStore and all store types from kernel package Features: - Selectors: getItem, getItems, getList, getError, resolution helpers - Resolvers: Auto-fetch with error handling when selectors used - Actions: receiveItem, receiveItems, receiveError, invalidate, invalidateAll - Reducer: Immutable state updates with type guards - Customization: Custom getId/getQueryKey functions, initial state Testing: - 36 new tests for createStore covering all functionality - 6 new tests for defineResource lazy store initialization - Store module: 93.82% coverage - defineResource: 92.53% coverage (up from 80%) - Overall: 94.8% coverage (up from 91.6%) Documentation: - Comprehensive store integration guide in docs/guide/resources.md - Complete examples with useSelect hooks - Best practices for store usage Version: - Bump @geekist/wp-kernel to 0.1.1 - Bump root package.json to 0.1.1 - Update CHANGELOG.md with detailed A3 changes Closes #3
There was a problem hiding this comment.
Pull Request Overview
This PR implements A3 from Sprint 1: @wordpress/data store integration for resources. It adds a complete store factory system that generates typed Redux-like stores from resource definitions, with automatic registration to WordPress's data layer.
- Adds createStore factory for generating @wordpress/data stores from ResourceObject definitions
- Implements lazy store initialization with automatic window.wp.data registration
- Provides complete TypeScript support with resource-specific state, actions, selectors, and resolvers
Reviewed Changes
Copilot reviewed 12 out of 12 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| packages/kernel/src/resource/types.ts | Added store property to ResourceObject interface |
| packages/kernel/src/resource/store/types.ts | Comprehensive TypeScript definitions for store system |
| packages/kernel/src/resource/store/index.ts | Clean exports for store module |
| packages/kernel/src/resource/store/createStore.ts | Core store factory implementation with reducer, actions, selectors, resolvers |
| packages/kernel/src/resource/store/tests/createStore.test.ts | Comprehensive test suite for store functionality |
| packages/kernel/src/resource/defineResource.ts | Added lazy store getter to resource objects |
| packages/kernel/src/resource/tests/defineResource.test.ts | Tests for lazy store initialization |
| packages/kernel/src/index.ts | Export store factory and types |
| packages/kernel/package.json | Version bump to 0.1.1 |
| package.json | Version bump to 0.1.1 |
| docs/guide/resources.md | Comprehensive store integration documentation |
| CHANGELOG.md | Detailed A3 implementation notes |
| const typedAction = action as { | ||
| type: string; | ||
| // eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
| [key: string]: any; | ||
| }; | ||
|
|
There was a problem hiding this comment.
[nitpick] Consider using a more specific type instead of any. You could define a union type for known action properties or use unknown with proper type guards for better type safety.
| const typedAction = action as { | |
| type: string; | |
| // eslint-disable-next-line @typescript-eslint/no-explicit-any | |
| [key: string]: any; | |
| }; | |
| // Define a union type for known action shapes | |
| type ReceiveItemAction = { | |
| type: typeof ACTION_TYPES.RECEIVE_ITEM; | |
| item: T; | |
| }; | |
| type ReceiveItemsAction = { | |
| type: typeof ACTION_TYPES.RECEIVE_ITEMS; | |
| items: T[]; | |
| query?: TQuery; | |
| meta?: ListResponse<T>['meta']; | |
| }; | |
| type ReceiveErrorAction = { | |
| type: typeof ACTION_TYPES.RECEIVE_ERROR; | |
| error: unknown; | |
| query?: TQuery; | |
| }; | |
| type InvalidateAction = { | |
| type: typeof ACTION_TYPES.INVALIDATE; | |
| id: string | number; | |
| }; | |
| type InvalidateAllAction = { | |
| type: typeof ACTION_TYPES.INVALIDATE_ALL; | |
| }; | |
| type ResourceAction = | |
| | ReceiveItemAction | |
| | ReceiveItemsAction | |
| | ReceiveErrorAction | |
| | InvalidateAction | |
| | InvalidateAllAction; | |
| const typedAction = action as ResourceAction; |
| const globalWp = | ||
| typeof window !== 'undefined' | ||
| ? // eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
| (window as any).wp |
There was a problem hiding this comment.
[nitpick] Consider defining a proper interface for window.wp instead of using any. This would provide better type safety and IntelliSense support. You could create a global type definition file or extend the Window interface.
| }); | ||
|
|
||
| it('should handle invalid action objects gracefully', () => { | ||
| const invalidAction = 'not an object' as any; |
There was a problem hiding this comment.
[nitpick] Instead of using as any, consider using as unknown for better type safety practices, or create a proper test type that represents invalid input.
| const invalidAction = 'not an object' as any; | |
| const invalidAction = 'not an object' as unknown; |
| }, | ||
| }); | ||
|
|
||
| const store = resource.store as any; |
There was a problem hiding this comment.
[nitpick] Consider importing the proper store types and using type assertions with the actual expected type instead of any to maintain type safety in tests.
- Import ResourceStore type in defineResource tests - Add WindowWithWp interface for type-safe window mocking - Replace 'as any' with 'as ResourceStore<Thing, ThingQuery>' - Use 'as unknown as' pattern for intentional invalid type tests - Improve type safety without losing test coverage All tests passing with full type safety maintained.
feat(kernel): Add @wordpress/data store integration (A3)
Summary
Implements A3 from Sprint 1: @wordpress/data store integration for resources.
What Changed
Store Factory
createStore()factory - Generates typed @wordpress/data stores from ResourceObject definitionswindow.wp.dataon first access via lazy getterResource Integration
storeproperty - Added to ResourceObject for on-demand store creationTypeScript Support
Full type safety with:
ResourceState<T>- Normalized state shapeResourceActions<T>- Action creatorsResourceSelectors<T, TQuery>- Data extraction functionsResourceResolvers<T, TQuery>- Async data fetchingResourceStoreConfig<T, TQuery>- Store configurationResourceStore<T, TQuery>- Complete store descriptorStore Features
getItem(),getItems(),getList(),getError(), plus resolution helpersreceiveItem(),receiveItems(),receiveError(),invalidate(),invalidateAll()getIdandgetQueryKeyfunctions, initial state supportTesting
New Tests
Coverage Improvements
Test Coverage
Documentation
Added comprehensive @wordpress/data store integration section to
docs/guide/resources.md:Version Changes
Quality Checks ✅
Files Changed
New Files
packages/kernel/src/resource/store/types.ts(315 lines) - Store type definitionspackages/kernel/src/resource/store/createStore.ts(352 lines) - Store factory implementationpackages/kernel/src/resource/store/index.ts(16 lines) - Clean exportspackages/kernel/src/resource/store/__tests__/createStore.test.ts(690 lines) - Comprehensive testsModified Files
packages/kernel/src/resource/defineResource.ts- Added lazy store getterpackages/kernel/src/resource/types.ts- Added store property to ResourceObjectpackages/kernel/src/resource/__tests__/defineResource.test.ts- Added 6 store testspackages/kernel/src/index.ts- Exported createStore and store typesdocs/guide/resources.md- Added comprehensive store integration guidepackage.json- Version bump to 0.1.1packages/kernel/package.json- Version bump to 0.1.1CHANGELOG.md- Added A3 sectionImplementation Notes
resource.storeaccesswindow.wp.data.register()when available (browser environment)getIdassumesitem.idpropertygetQueryKeyusesJSON.stringifyCloses #3