@plasius/react-state 1.0.4 → 1.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/cd.yml +18 -2
- package/CHANGELOG.md +28 -26
- package/README.md +69 -0
- package/docs/adrs/adr-0001: React store.md +42 -0
- package/package.json +2 -6
- package/playwright-report/data/109751d8d4b8e14d7b389909bd1a747627520094.webm +0 -0
- package/playwright-report/data/519874c90b026e864332369e27b133b93b2bbada.png +0 -0
- package/playwright-report/data/7a33d5db6370b6de345e990751aa1f1da65ad675.png +0 -0
- package/playwright-report/data/a4176307dad9d5a38218684c7d4830977909693d.webm +0 -0
- package/playwright-report/data/bc46c1bd87c84f3ee59e21ab267d63b2b3a299c3.webm +0 -0
- package/playwright-report/data/bf6639b594a098ffdea4f12a3d3fc4ad525c88ad.png +0 -0
- package/playwright-report/index.html +0 -76
package/.github/workflows/cd.yml
CHANGED
|
@@ -2,8 +2,6 @@ name: CD (Publish to npm)
|
|
|
2
2
|
|
|
3
3
|
on:
|
|
4
4
|
workflow_dispatch:
|
|
5
|
-
release:
|
|
6
|
-
types: [published]
|
|
7
5
|
|
|
8
6
|
permissions:
|
|
9
7
|
contents: write
|
|
@@ -40,6 +38,9 @@ jobs:
|
|
|
40
38
|
NEW_VER=$(npm version patch -m "chore: release v%s [skip ci]")
|
|
41
39
|
echo "New version: $NEW_VER"
|
|
42
40
|
git push --follow-tags
|
|
41
|
+
VER_NO_V=${NEW_VER#v}
|
|
42
|
+
echo "tag=$NEW_VER" >> "$GITHUB_OUTPUT"
|
|
43
|
+
echo "version=$VER_NO_V" >> "$GITHUB_OUTPUT"
|
|
43
44
|
|
|
44
45
|
NAME=$(node -p "require('./package.json').name")
|
|
45
46
|
echo "name=$NAME" >> "$GITHUB_OUTPUT"
|
|
@@ -49,6 +50,21 @@ jobs:
|
|
|
49
50
|
echo "flags=--access public" >> "$GITHUB_OUTPUT"
|
|
50
51
|
fi
|
|
51
52
|
|
|
53
|
+
- name: Create GitHub Release from tag (first-party)
|
|
54
|
+
env:
|
|
55
|
+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
56
|
+
run: |
|
|
57
|
+
set -euo pipefail
|
|
58
|
+
TAG="${{ steps.pkg.outputs.tag }}"
|
|
59
|
+
if gh release view "$TAG" >/dev/null 2>&1; then
|
|
60
|
+
echo "Release $TAG already exists; skipping creation."
|
|
61
|
+
else
|
|
62
|
+
gh release create "$TAG" \
|
|
63
|
+
--title "Release $TAG" \
|
|
64
|
+
--generate-notes \
|
|
65
|
+
--latest
|
|
66
|
+
fi
|
|
67
|
+
|
|
52
68
|
- name: Publish
|
|
53
69
|
env:
|
|
54
70
|
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
|
|
2
|
-
|
|
3
2
|
# Changelog
|
|
4
3
|
|
|
5
4
|
All notable changes to this project will be documented in this file.
|
|
@@ -9,41 +8,44 @@ The format is based on **[Keep a Changelog](https://keepachangelog.com/en/1.1.0/
|
|
|
9
8
|
---
|
|
10
9
|
|
|
11
10
|
## [Unreleased]
|
|
12
|
-
### Added
|
|
13
|
-
- (placeholder) Add new hooks, scoped store features, or context helpers here.
|
|
14
11
|
|
|
15
|
-
|
|
16
|
-
- (placeholder)
|
|
12
|
+
- **Added**
|
|
13
|
+
- (placeholder) Add new hooks, scoped store features, or context helpers here.
|
|
14
|
+
|
|
15
|
+
- **Changed**
|
|
16
|
+
- (placeholder)
|
|
17
17
|
|
|
18
|
-
|
|
19
|
-
- (placeholder)
|
|
18
|
+
- **Fixed**
|
|
19
|
+
- (placeholder)
|
|
20
20
|
|
|
21
|
-
|
|
22
|
-
- (placeholder)
|
|
21
|
+
- **Security**
|
|
22
|
+
- (placeholder)
|
|
23
23
|
|
|
24
24
|
---
|
|
25
25
|
|
|
26
|
-
## [
|
|
27
|
-
|
|
28
|
-
-
|
|
29
|
-
- `createStore` for basic state container functionality with `dispatch`, `getState`, and subscription API.
|
|
30
|
-
- `createScopedStoreContext` for React integration:
|
|
31
|
-
- `<Provider>` component wrapping React trees,
|
|
32
|
-
- `useStore()` to access state,
|
|
33
|
-
- `useDispatch()` to dispatch actions.
|
|
34
|
-
- Support for per-key subscriptions and selector-based subscriptions.
|
|
35
|
-
- Unit tests with Vitest and component tests with React Testing Library.
|
|
36
|
-
- Early Playwright integration tests for end-to-end validation of store-driven DOM.
|
|
26
|
+
## [1.0.0] - 2025-09-16
|
|
27
|
+
|
|
28
|
+
- **Added**
|
|
37
29
|
|
|
38
|
-
|
|
39
|
-
-
|
|
30
|
+
- Initial public release of `@plasius/react-state`.
|
|
31
|
+
- `createStore` for basic state container functionality with `dispatch`, `getState`, and subscription API.
|
|
32
|
+
- `createScopedStoreContext` for React integration:
|
|
33
|
+
- `<Provider>` component wrapping React trees,
|
|
34
|
+
- `useStore()` to access state,
|
|
35
|
+
- `useDispatch()` to dispatch actions.
|
|
36
|
+
- Support for per-key subscriptions and selector-based subscriptions.
|
|
37
|
+
- Unit tests with Vitest and component tests with React Testing Library.
|
|
40
38
|
|
|
41
|
-
|
|
42
|
-
- N/A (initial release)
|
|
39
|
+
- **Changed**
|
|
40
|
+
- N/A (initial release)
|
|
41
|
+
|
|
42
|
+
- **Fixed**
|
|
43
|
+
- N/A (initial release)
|
|
43
44
|
|
|
44
45
|
---
|
|
45
46
|
|
|
46
47
|
## Release process (maintainers)
|
|
48
|
+
|
|
47
49
|
1. Update `CHANGELOG.md` under **Unreleased** with user‑visible changes.
|
|
48
50
|
2. Bump version in `package.json` following SemVer (major/minor/patch).
|
|
49
51
|
3. Move entries from **Unreleased** to a new version section with the current date.
|
|
@@ -54,5 +56,5 @@ The format is based on **[Keep a Changelog](https://keepachangelog.com/en/1.1.0/
|
|
|
54
56
|
|
|
55
57
|
---
|
|
56
58
|
|
|
57
|
-
[Unreleased]: https://github.com/Plasius-LTD/react-state/compare/
|
|
58
|
-
[
|
|
59
|
+
[Unreleased]: https://github.com/Plasius-LTD/react-state/compare/v1.0.0...HEAD
|
|
60
|
+
[1.0.0]: https://github.com/Plasius-LTD/react-state/releases/tag/v1.0.0
|
package/README.md
CHANGED
|
@@ -25,7 +25,11 @@ npm install @plasius/react-state
|
|
|
25
25
|
|
|
26
26
|
## Usage Example
|
|
27
27
|
|
|
28
|
+
### Accessing the store
|
|
29
|
+
|
|
28
30
|
```tsx
|
|
31
|
+
import { createStore } from '@plasius/react-state'
|
|
32
|
+
|
|
29
33
|
type Action =
|
|
30
34
|
| { type: "INCREMENT_VALUE"; payload: number }
|
|
31
35
|
| { type: "SET_VALUE"; payload: number }
|
|
@@ -60,6 +64,71 @@ function doSomething() {
|
|
|
60
64
|
}
|
|
61
65
|
```
|
|
62
66
|
|
|
67
|
+
### Scoped react hooks
|
|
68
|
+
|
|
69
|
+
```tsx
|
|
70
|
+
import { createScopedStore } from '@plasius/react-state'
|
|
71
|
+
|
|
72
|
+
type Action =
|
|
73
|
+
| { type: "INCREMENT_VALUE"; payload: number }
|
|
74
|
+
| { type: "SET_VALUE"; payload: number }
|
|
75
|
+
| { type: "DECREMENT_VALUE"; payload: number };
|
|
76
|
+
|
|
77
|
+
interface State {
|
|
78
|
+
value: number;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const initialState: State = { value: 0 };
|
|
82
|
+
|
|
83
|
+
function reducer(state: State, action: Action): State {
|
|
84
|
+
switch (action.type) {
|
|
85
|
+
case "INCREMENT_VALUE":
|
|
86
|
+
return { ...state, value: state.value + action.payload };
|
|
87
|
+
case "DECREMENT_VALUE":
|
|
88
|
+
return { ...state, value: state.value - action.payload };
|
|
89
|
+
case "SET_VALUE":
|
|
90
|
+
// Distinct-until-changed: return the SAME reference if no change,
|
|
91
|
+
// so listeners relying on referential equality will not fire.
|
|
92
|
+
if (action.payload === state.value) return state;
|
|
93
|
+
return { ...state, value: action.payload };
|
|
94
|
+
default:
|
|
95
|
+
return state;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const store = createStore<State, Action>(reducer, initialState);
|
|
100
|
+
|
|
101
|
+
const Counter = () => {
|
|
102
|
+
const state = store.useStore();
|
|
103
|
+
const dispatch = store.useDispatch();
|
|
104
|
+
|
|
105
|
+
return (
|
|
106
|
+
<div>
|
|
107
|
+
<button id="counter-inc" onClick={() => dispatch({ type: "inc" })}>
|
|
108
|
+
+
|
|
109
|
+
</button>
|
|
110
|
+
<button id="counter-dec" onClick={() => dispatch({ type: "dec" })}>
|
|
111
|
+
-
|
|
112
|
+
</button>
|
|
113
|
+
<input
|
|
114
|
+
aria-label="Counter value"
|
|
115
|
+
title=""
|
|
116
|
+
id="counter-set"
|
|
117
|
+
type="number"
|
|
118
|
+
value={state.count}
|
|
119
|
+
onChange={(e) =>
|
|
120
|
+
dispatch({ type: "set", payload: Number(e.target.value) })
|
|
121
|
+
}
|
|
122
|
+
/>
|
|
123
|
+
</div>
|
|
124
|
+
);
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
function App() {
|
|
128
|
+
return (<><store.Provider><Counter /></store.Provider></>);
|
|
129
|
+
}
|
|
130
|
+
```
|
|
131
|
+
|
|
63
132
|
---
|
|
64
133
|
|
|
65
134
|
## Contributing
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# ADR-0001: React State Store Purpose and Scope
|
|
2
|
+
|
|
3
|
+
## Status
|
|
4
|
+
|
|
5
|
+
- Proposed → Accepted
|
|
6
|
+
- Date: 2025-09-12
|
|
7
|
+
- Version: 1.0
|
|
8
|
+
- Supersedes: N/A
|
|
9
|
+
- Superseded by: N/A
|
|
10
|
+
|
|
11
|
+
## Context
|
|
12
|
+
|
|
13
|
+
Managing React application state across multiple components is a common challenge. Existing solutions (Redux, Zustand, Jotai, etc.) offer different trade-offs in complexity, ergonomics, and bundle size. For the Plasius ecosystem, we want:
|
|
14
|
+
|
|
15
|
+
- A minimal, type-safe state container,
|
|
16
|
+
- Strong integration with React via Context and Hooks,
|
|
17
|
+
- Support for both global and scoped stores,
|
|
18
|
+
- Predictable subscription and unsubscribe behaviour,
|
|
19
|
+
- Alignment with SOLID principles and enterprise-grade maintainability.
|
|
20
|
+
|
|
21
|
+
## Decision
|
|
22
|
+
|
|
23
|
+
We will build a dedicated library, `@plasius/react-state`, that:
|
|
24
|
+
|
|
25
|
+
- Provides a `createStore` primitive for basic state containers,
|
|
26
|
+
- Provides `createScopedStoreContext` to integrate with React Context/Provider patterns,
|
|
27
|
+
- Exposes hooks (`useStore`, `useDispatch`) to read state and dispatch actions,
|
|
28
|
+
- Supports subscriptions: global, per-key, and selector-based,
|
|
29
|
+
- Ensures distinct-until-changed semantics to avoid unnecessary re-renders,
|
|
30
|
+
- Is published as an open source package under the Plasius-LTD organisation.
|
|
31
|
+
|
|
32
|
+
## Consequences
|
|
33
|
+
|
|
34
|
+
- **Positive:** Consistent and predictable state handling across Plasius projects, type-safe APIs, lighter alternative to Redux, improved testability, open source adoption possible.
|
|
35
|
+
- **Negative:** Adds maintenance burden to keep feature parity with evolving React ecosystem; community may default to established libraries.
|
|
36
|
+
- **Neutral:** Internal consumers may still choose other state management libs if required, but Plasius packages will standardise on this one.
|
|
37
|
+
|
|
38
|
+
## Alternatives Considered
|
|
39
|
+
|
|
40
|
+
- **Use Redux or Redux Toolkit:** Very mature, but verbose and heavier than desired.
|
|
41
|
+
- **Use Zustand or Jotai:** Good ergonomics, but external dependency risk and less control over PII handling and subscription semantics.
|
|
42
|
+
- **React Context alone:** Too minimal, lacking structured dispatch and subscribe/unsubscribe APIs.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@plasius/react-state",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.6",
|
|
4
4
|
"description": "Tiny, testable, typesafe React Scoped Store helper.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"react",
|
|
@@ -46,13 +46,9 @@
|
|
|
46
46
|
"test:watch": "vitest",
|
|
47
47
|
"lint": "eslint .",
|
|
48
48
|
"prepare": "npm run build",
|
|
49
|
-
"clean": "rimraf dist"
|
|
50
|
-
"test:e2e": "playwright test",
|
|
51
|
-
"test:e2e:headed": "playwright test --headed",
|
|
52
|
-
"e2e:report": "playwright show-report"
|
|
49
|
+
"clean": "rimraf dist"
|
|
53
50
|
},
|
|
54
51
|
"devDependencies": {
|
|
55
|
-
"@playwright/test": "^1.55.0",
|
|
56
52
|
"@testing-library/jest-dom": "^6.8.0",
|
|
57
53
|
"@testing-library/react": "^16.3.0",
|
|
58
54
|
"@types/node": "^24.3.1",
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|