Skip to content

feat(kernel): Add @wordpress/data store integration (A3)#17

Merged
pipewrk merged 2 commits intomainfrom
sprint-1/a3-store-factory
Sep 30, 2025
Merged

feat(kernel): Add @wordpress/data store integration (A3)#17
pipewrk merged 2 commits intomainfrom
sprint-1/a3-store-factory

Conversation

@pipewrk
Copy link
Contributor

@pipewrk pipewrk commented Sep 30, 2025

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 definitions
  • Automatic registration - Stores auto-register with window.wp.data on first access via lazy getter
  • Complete Redux-like API - Reducer, actions, selectors, and resolvers following @wordpress/data patterns

Resource Integration

  • Lazy store property - Added to ResourceObject for on-demand store creation
  • Zero configuration - Stores automatically use resource name, routes, and cache keys
  • Seamless WordPress integration - Works with @wordpress/data select/dispatch/useSelect APIs

TypeScript Support

Full type safety with:

  • ResourceState<T> - Normalized state shape
  • ResourceActions<T> - Action creators
  • ResourceSelectors<T, TQuery> - Data extraction functions
  • ResourceResolvers<T, TQuery> - Async data fetching
  • ResourceStoreConfig<T, TQuery> - Store configuration
  • ResourceStore<T, TQuery> - Complete store descriptor

Store Features

  • Selectors: getItem(), getItems(), getList(), getError(), plus resolution helpers
  • Resolvers: Automatic data fetching with error handling when selectors are used
  • Actions: receiveItem(), receiveItems(), receiveError(), invalidate(), invalidateAll()
  • Reducer: Immutable state updates with type guards for all action types
  • Customization: Custom getId and getQueryKey functions, initial state support

Testing

New Tests

  • 42 new test cases total
    • 36 tests for createStore (all store functionality)
    • 6 tests for defineResource lazy store initialization

Coverage Improvements

  • Overall coverage: 91.6% → 94.8%
  • Store module: 93.82% coverage
  • defineResource: 80% → 92.53% (fixed coverage drop)
  • Total tests: 195 passing

Test Coverage

  • Store creation with custom config
  • Reducer handling all action types (RECEIVE_ITEM, RECEIVE_ITEMS, RECEIVE_ERROR, INVALIDATE, INVALIDATE_ALL)
  • All action creators
  • All selectors (getItem, getItems, getList, getError)
  • Resolvers with error handling and NotImplementedError
  • Custom getId/getQueryKey functions
  • Lazy store initialization and singleton pattern
  • window.wp.data registration scenarios

Documentation

Added comprehensive @wordpress/data store integration section to docs/guide/resources.md:

  • Accessing stores from resources
  • Complete selector documentation with examples
  • Resolver auto-fetch patterns
  • Action usage for manual state updates
  • Complete working examples with useSelect hooks
  • Custom store configuration
  • Best practices (4 key patterns)

Version Changes

  • @geekist/wp-kernel: 0.1.0 → 0.1.1
  • Root package.json: 0.1.0 → 0.1.1
  • CHANGELOG.md: Added detailed A3 section

Quality Checks ✅

  • ✅ TypeScript compilation (all 4 packages)
  • ✅ All tests passing (195 tests, 7 suites)
  • ✅ Linting passing (0 errors, 0 warnings)
  • ✅ Coverage maintained above 90% threshold
  • ✅ Pre-commit hooks passing (typecheck:tests, docs:build)

Files Changed

New Files

  • packages/kernel/src/resource/store/types.ts (315 lines) - Store type definitions
  • packages/kernel/src/resource/store/createStore.ts (352 lines) - Store factory implementation
  • packages/kernel/src/resource/store/index.ts (16 lines) - Clean exports
  • packages/kernel/src/resource/store/__tests__/createStore.test.ts (690 lines) - Comprehensive tests

Modified Files

  • packages/kernel/src/resource/defineResource.ts - Added lazy store getter
  • packages/kernel/src/resource/types.ts - Added store property to ResourceObject
  • packages/kernel/src/resource/__tests__/defineResource.test.ts - Added 6 store tests
  • packages/kernel/src/index.ts - Exported createStore and store types
  • docs/guide/resources.md - Added comprehensive store integration guide
  • package.json - Version bump to 0.1.1
  • packages/kernel/package.json - Version bump to 0.1.1
  • CHANGELOG.md - Added A3 section

Implementation Notes

  • Store creation is lazy - only initialized on first resource.store access
  • Stores automatically register with window.wp.data.register() when available (browser environment)
  • Resolvers use async functions (not generators) for simpler implementation
  • Normalized state structure: items by ID, lists as ID arrays, separate metadata
  • Default getId assumes item.id property
  • Default getQueryKey uses JSON.stringify

Closes #3

- 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
Copilot AI review requested due to automatic review settings September 30, 2025 22:32
Copy link
Contributor

Copilot AI left a comment

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

Comment on lines +102 to +107
const typedAction = action as {
type: string;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
[key: string]: any;
};

Copy link

Copilot AI Sep 30, 2025

Choose a reason for hiding this comment

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

[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.

Suggested change
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;

Copilot uses AI. Check for mistakes.
const globalWp =
typeof window !== 'undefined'
? // eslint-disable-next-line @typescript-eslint/no-explicit-any
(window as any).wp
Copy link

Copilot AI Sep 30, 2025

Choose a reason for hiding this comment

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

[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.

Copilot uses AI. Check for mistakes.
});

it('should handle invalid action objects gracefully', () => {
const invalidAction = 'not an object' as any;
Copy link

Copilot AI Sep 30, 2025

Choose a reason for hiding this comment

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

[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.

Suggested change
const invalidAction = 'not an object' as any;
const invalidAction = 'not an object' as unknown;

Copilot uses AI. Check for mistakes.
},
});

const store = resource.store as any;
Copy link

Copilot AI Sep 30, 2025

Choose a reason for hiding this comment

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

[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.

Copilot uses AI. Check for mistakes.
- 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.
@pipewrk pipewrk merged commit 5f7b912 into main Sep 30, 2025
7 checks passed
@pipewrk pipewrk deleted the sprint-1/a3-store-factory branch October 5, 2025 01:48
pipewrk added a commit that referenced this pull request Nov 8, 2025
feat(kernel): Add @wordpress/data store integration (A3)
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.

A3: Store Factory

2 participants