@uistate/core 5.2.0 → 5.4.0

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.
Files changed (142) hide show
  1. package/index.js +2 -9
  2. package/package.json +4 -10
  3. package/queryClient.js +55 -0
  4. package/cssState.js +0 -212
  5. package/examples/001-counter/README.md +0 -44
  6. package/examples/001-counter/index.html +0 -33
  7. package/examples/002-counter-improved/README.md +0 -44
  8. package/examples/002-counter-improved/index.html +0 -47
  9. package/examples/003-input-reactive/README.md +0 -44
  10. package/examples/003-input-reactive/index.html +0 -33
  11. package/examples/004-computed-state/README.md +0 -45
  12. package/examples/004-computed-state/index.html +0 -65
  13. package/examples/005-conditional-rendering/README.md +0 -42
  14. package/examples/005-conditional-rendering/index.html +0 -39
  15. package/examples/006-list-rendering/README.md +0 -49
  16. package/examples/006-list-rendering/index.html +0 -63
  17. package/examples/007-form-validation/README.md +0 -52
  18. package/examples/007-form-validation/index.html +0 -102
  19. package/examples/008-undo-redo/README.md +0 -70
  20. package/examples/008-undo-redo/index.html +0 -108
  21. package/examples/009-localStorage-side-effects/README.md +0 -72
  22. package/examples/009-localStorage-side-effects/index.html +0 -57
  23. package/examples/010-decoupled-components/README.md +0 -74
  24. package/examples/010-decoupled-components/index.html +0 -93
  25. package/examples/011-async-patterns/README.md +0 -98
  26. package/examples/011-async-patterns/index.html +0 -132
  27. package/examples/028-counter-improved-eventTest/LICENSE +0 -55
  28. package/examples/028-counter-improved-eventTest/README.md +0 -131
  29. package/examples/028-counter-improved-eventTest/app/store.js +0 -9
  30. package/examples/028-counter-improved-eventTest/index.html +0 -49
  31. package/examples/028-counter-improved-eventTest/runtime/core/behaviors.runtime.js +0 -282
  32. package/examples/028-counter-improved-eventTest/runtime/core/eventState.js +0 -100
  33. package/examples/028-counter-improved-eventTest/runtime/core/eventStateNew.js +0 -149
  34. package/examples/028-counter-improved-eventTest/runtime/core/helpers.js +0 -212
  35. package/examples/028-counter-improved-eventTest/runtime/core/router.js +0 -271
  36. package/examples/028-counter-improved-eventTest/store.d.ts +0 -8
  37. package/examples/028-counter-improved-eventTest/style.css +0 -170
  38. package/examples/028-counter-improved-eventTest/tests/README.md +0 -208
  39. package/examples/028-counter-improved-eventTest/tests/counter.test.js +0 -116
  40. package/examples/028-counter-improved-eventTest/tests/eventTest.js +0 -176
  41. package/examples/028-counter-improved-eventTest/tests/generateTypes.js +0 -168
  42. package/examples/028-counter-improved-eventTest/tests/run.js +0 -20
  43. package/examples/030-todo-app-with-eventTest/LICENSE +0 -55
  44. package/examples/030-todo-app-with-eventTest/README.md +0 -121
  45. package/examples/030-todo-app-with-eventTest/app/router.js +0 -25
  46. package/examples/030-todo-app-with-eventTest/app/store.js +0 -16
  47. package/examples/030-todo-app-with-eventTest/app/views/home.js +0 -11
  48. package/examples/030-todo-app-with-eventTest/app/views/todoDemo.js +0 -88
  49. package/examples/030-todo-app-with-eventTest/index.html +0 -65
  50. package/examples/030-todo-app-with-eventTest/runtime/core/behaviors.runtime.js +0 -282
  51. package/examples/030-todo-app-with-eventTest/runtime/core/eventState.js +0 -100
  52. package/examples/030-todo-app-with-eventTest/runtime/core/eventStateNew.js +0 -149
  53. package/examples/030-todo-app-with-eventTest/runtime/core/helpers.js +0 -212
  54. package/examples/030-todo-app-with-eventTest/runtime/core/router.js +0 -271
  55. package/examples/030-todo-app-with-eventTest/store.d.ts +0 -18
  56. package/examples/030-todo-app-with-eventTest/style.css +0 -170
  57. package/examples/030-todo-app-with-eventTest/tests/README.md +0 -208
  58. package/examples/030-todo-app-with-eventTest/tests/eventTest.js +0 -176
  59. package/examples/030-todo-app-with-eventTest/tests/generateTypes.js +0 -189
  60. package/examples/030-todo-app-with-eventTest/tests/run.js +0 -20
  61. package/examples/030-todo-app-with-eventTest/tests/todos.test.js +0 -167
  62. package/examples/031-todo-app-with-eventTest/LICENSE +0 -55
  63. package/examples/031-todo-app-with-eventTest/README.md +0 -54
  64. package/examples/031-todo-app-with-eventTest/TUTORIAL.md +0 -390
  65. package/examples/031-todo-app-with-eventTest/WHY_EVENTSTATE.md +0 -777
  66. package/examples/031-todo-app-with-eventTest/app/bridges.js +0 -113
  67. package/examples/031-todo-app-with-eventTest/app/router.js +0 -26
  68. package/examples/031-todo-app-with-eventTest/app/store.js +0 -15
  69. package/examples/031-todo-app-with-eventTest/app/views/home.js +0 -46
  70. package/examples/031-todo-app-with-eventTest/app/views/todoDemo.js +0 -69
  71. package/examples/031-todo-app-with-eventTest/devtools/dock.js +0 -41
  72. package/examples/031-todo-app-with-eventTest/devtools/stateTracker.dock.js +0 -10
  73. package/examples/031-todo-app-with-eventTest/devtools/stateTracker.js +0 -246
  74. package/examples/031-todo-app-with-eventTest/devtools/telemetry.js +0 -104
  75. package/examples/031-todo-app-with-eventTest/devtools/typeGenerator.js +0 -339
  76. package/examples/031-todo-app-with-eventTest/index.html +0 -103
  77. package/examples/031-todo-app-with-eventTest/package-lock.json +0 -2184
  78. package/examples/031-todo-app-with-eventTest/package.json +0 -24
  79. package/examples/031-todo-app-with-eventTest/runtime/core/behaviors.runtime.js +0 -282
  80. package/examples/031-todo-app-with-eventTest/runtime/core/eventState.js +0 -100
  81. package/examples/031-todo-app-with-eventTest/runtime/core/eventStateNew.js +0 -149
  82. package/examples/031-todo-app-with-eventTest/runtime/core/helpers.js +0 -212
  83. package/examples/031-todo-app-with-eventTest/runtime/core/router.js +0 -271
  84. package/examples/031-todo-app-with-eventTest/runtime/extensions/boundary.js +0 -36
  85. package/examples/031-todo-app-with-eventTest/runtime/extensions/converge.js +0 -63
  86. package/examples/031-todo-app-with-eventTest/runtime/extensions/eventState.plus.js +0 -210
  87. package/examples/031-todo-app-with-eventTest/runtime/extensions/hydrate.js +0 -157
  88. package/examples/031-todo-app-with-eventTest/runtime/extensions/queryBinding.js +0 -69
  89. package/examples/031-todo-app-with-eventTest/runtime/forms/computed.js +0 -78
  90. package/examples/031-todo-app-with-eventTest/runtime/forms/meta.js +0 -51
  91. package/examples/031-todo-app-with-eventTest/runtime/forms/submitWithBoundary.js +0 -28
  92. package/examples/031-todo-app-with-eventTest/runtime/forms/validators.js +0 -55
  93. package/examples/031-todo-app-with-eventTest/store.d.ts +0 -23
  94. package/examples/031-todo-app-with-eventTest/style.css +0 -170
  95. package/examples/031-todo-app-with-eventTest/tests/README.md +0 -208
  96. package/examples/031-todo-app-with-eventTest/tests/eventTest.js +0 -176
  97. package/examples/031-todo-app-with-eventTest/tests/generateTypes.js +0 -191
  98. package/examples/031-todo-app-with-eventTest/tests/run.js +0 -20
  99. package/examples/031-todo-app-with-eventTest/tests/todos.test.js +0 -192
  100. package/examples/032-todo-app-with-eventTest/LICENSE +0 -55
  101. package/examples/032-todo-app-with-eventTest/README.md +0 -54
  102. package/examples/032-todo-app-with-eventTest/TUTORIAL.md +0 -390
  103. package/examples/032-todo-app-with-eventTest/WHY_EVENTSTATE.md +0 -777
  104. package/examples/032-todo-app-with-eventTest/app/actions/index.js +0 -153
  105. package/examples/032-todo-app-with-eventTest/app/bridges.js +0 -113
  106. package/examples/032-todo-app-with-eventTest/app/router.js +0 -26
  107. package/examples/032-todo-app-with-eventTest/app/store.js +0 -15
  108. package/examples/032-todo-app-with-eventTest/app/views/home.js +0 -46
  109. package/examples/032-todo-app-with-eventTest/app/views/todoDemo.js +0 -69
  110. package/examples/032-todo-app-with-eventTest/devtools/dock.js +0 -41
  111. package/examples/032-todo-app-with-eventTest/devtools/stateTracker.dock.js +0 -10
  112. package/examples/032-todo-app-with-eventTest/devtools/stateTracker.js +0 -246
  113. package/examples/032-todo-app-with-eventTest/devtools/telemetry.js +0 -104
  114. package/examples/032-todo-app-with-eventTest/devtools/typeGenerator.js +0 -339
  115. package/examples/032-todo-app-with-eventTest/index.html +0 -87
  116. package/examples/032-todo-app-with-eventTest/package-lock.json +0 -2184
  117. package/examples/032-todo-app-with-eventTest/package.json +0 -24
  118. package/examples/032-todo-app-with-eventTest/runtime/core/behaviors.runtime.js +0 -282
  119. package/examples/032-todo-app-with-eventTest/runtime/core/eventState.js +0 -100
  120. package/examples/032-todo-app-with-eventTest/runtime/core/eventStateNew.js +0 -149
  121. package/examples/032-todo-app-with-eventTest/runtime/core/helpers.js +0 -212
  122. package/examples/032-todo-app-with-eventTest/runtime/core/router.js +0 -271
  123. package/examples/032-todo-app-with-eventTest/runtime/extensions/boundary.js +0 -36
  124. package/examples/032-todo-app-with-eventTest/runtime/extensions/converge.js +0 -63
  125. package/examples/032-todo-app-with-eventTest/runtime/extensions/eventState.plus.js +0 -210
  126. package/examples/032-todo-app-with-eventTest/runtime/extensions/hydrate.js +0 -157
  127. package/examples/032-todo-app-with-eventTest/runtime/extensions/queryBinding.js +0 -69
  128. package/examples/032-todo-app-with-eventTest/runtime/forms/computed.js +0 -78
  129. package/examples/032-todo-app-with-eventTest/runtime/forms/meta.js +0 -51
  130. package/examples/032-todo-app-with-eventTest/runtime/forms/submitWithBoundary.js +0 -28
  131. package/examples/032-todo-app-with-eventTest/runtime/forms/validators.js +0 -55
  132. package/examples/032-todo-app-with-eventTest/store.d.ts +0 -23
  133. package/examples/032-todo-app-with-eventTest/style.css +0 -170
  134. package/examples/032-todo-app-with-eventTest/tests/README.md +0 -208
  135. package/examples/032-todo-app-with-eventTest/tests/eventTest.js +0 -176
  136. package/examples/032-todo-app-with-eventTest/tests/generateTypes.js +0 -191
  137. package/examples/032-todo-app-with-eventTest/tests/run.js +0 -20
  138. package/examples/032-todo-app-with-eventTest/tests/todos.test.js +0 -192
  139. package/playground/exercise001.html +0 -38
  140. package/playground/exercise002.html +0 -49
  141. package/stateSerializer.js +0 -267
  142. package/templateManager.js +0 -216
@@ -1,74 +0,0 @@
1
- # 010 Decoupled Components - Shared State
2
-
3
- Demonstrates how components can communicate through shared state without direct coupling.
4
-
5
- ## What's Here
6
-
7
- - **Component A (Writer):** Sends messages
8
- - **Component B (Reader):** Displays current message
9
- - **Component C (Logger):** Logs all messages with timestamps
10
- - **Zero coupling:** Components don't know about each other
11
-
12
- ## How It Works
13
-
14
- ```javascript
15
- // Shared store - the ONLY connection
16
- const store = createEventState({ message: '' });
17
-
18
- // Component A: Writes to state
19
- document.getElementById('sendBtn').onclick = () => {
20
- store.set('message', inputValue);
21
- };
22
-
23
- // Component B: Reads from state
24
- store.subscribe('message', (value) => {
25
- document.getElementById('display').textContent = value;
26
- });
27
-
28
- // Component C: Also reads from state
29
- store.subscribe('message', (value) => {
30
- logMessage(value);
31
- });
32
- ```
33
-
34
- ## Key Insight
35
-
36
- **State is the interface between components.**
37
-
38
- Components communicate by:
39
- 1. Writing to shared state
40
- 2. Subscribing to shared state
41
- 3. **Never** calling each other directly
42
-
43
- This is the **pub/sub pattern** in action:
44
- - ✅ Components are decoupled
45
- - ✅ Easy to add/remove components
46
- - ✅ No prop drilling
47
- - ✅ No callbacks passed down
48
-
49
- Other frameworks need:
50
- - React: Context API, prop drilling, or state management libraries
51
- - Vue: Provide/inject or Vuex
52
- - Svelte: Context API or stores
53
-
54
- **EventState:** Just share the store. That's it.
55
-
56
- ## Architecture Pattern
57
-
58
- This demonstrates the **mediator pattern**:
59
- - Components don't talk to each other
60
- - They talk to the store
61
- - The store mediates all communication
62
-
63
- This is how your full UIstate framework works:
64
- - Intents → State changes
65
- - State changes → UI updates
66
- - Everything is decoupled through events
67
-
68
- ## Run It
69
-
70
- Open `index.html`, type messages, and watch all three components react independently.
71
-
72
- ## Try This
73
-
74
- Add a fourth component that counts vowels in messages. It only needs to subscribe to `'message'` - no other code changes needed.
@@ -1,93 +0,0 @@
1
- <!DOCTYPE html>
2
- <html lang="en">
3
- <head>
4
- <meta charset="UTF-8">
5
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
- <title>010 Decoupled Components - EventState</title>
7
- <style>
8
- .component {
9
- border: 2px solid #333;
10
- padding: 20px;
11
- margin: 10px 0;
12
- }
13
- </style>
14
- <script type="importmap">
15
- { "imports": { "@uistate/core": "../../index.js" } }
16
- </script>
17
- </head>
18
- <body>
19
- <h1>Decoupled Components</h1>
20
- <p>Two independent "components" sharing state without knowing about each other.</p>
21
-
22
- <!-- Component A: Writer -->
23
- <div class="component">
24
- <h2>Component A: Writer</h2>
25
- <input type="text" id="messageInput" placeholder="Type a message...">
26
- <button id="sendBtn">Send</button>
27
- </div>
28
-
29
- <!-- Component B: Reader -->
30
- <div class="component">
31
- <h2>Component B: Reader</h2>
32
- <p><strong>Received:</strong> <span id="messageDisplay">(nothing yet)</span></p>
33
- <p><small>Message count: <span id="messageCount">0</span></small></p>
34
- </div>
35
-
36
- <!-- Component C: Logger -->
37
- <div class="component">
38
- <h2>Component C: Logger</h2>
39
- <ul id="messageLog"></ul>
40
- </div>
41
-
42
- <script type="module">
43
- import { createEventState } from '@uistate/core';
44
-
45
- // Shared store - the ONLY connection between components
46
- const store = createEventState({
47
- message: '',
48
- messageCount: 0
49
- });
50
-
51
- // ===== COMPONENT A: Writer =====
52
- // Knows nothing about B or C, just writes to state
53
- document.getElementById('sendBtn').onclick = () => {
54
- const input = document.getElementById('messageInput');
55
- const value = input.value.trim();
56
-
57
- if (value) {
58
- store.set('message', value);
59
- store.set('messageCount', store.get('messageCount') + 1);
60
- input.value = '';
61
- }
62
- };
63
-
64
- document.getElementById('messageInput').onkeypress = (e) => {
65
- if (e.key === 'Enter') {
66
- document.getElementById('sendBtn').click();
67
- }
68
- };
69
-
70
- // ===== COMPONENT B: Reader =====
71
- // Knows nothing about A or C, just reads from state
72
- store.subscribe('message', (value) => {
73
- document.getElementById('messageDisplay').textContent = value || '(nothing yet)';
74
- });
75
-
76
- store.subscribe('messageCount', (value) => {
77
- document.getElementById('messageCount').textContent = value;
78
- });
79
-
80
- // ===== COMPONENT C: Logger =====
81
- // Knows nothing about A or B, just logs all messages
82
- store.subscribe('message', (value) => {
83
- if (value) {
84
- const log = document.getElementById('messageLog');
85
- const timestamp = new Date().toLocaleTimeString();
86
- const li = document.createElement('li');
87
- li.textContent = `[${timestamp}] ${value}`;
88
- log.appendChild(li);
89
- }
90
- });
91
- </script>
92
- </body>
93
- </html>
@@ -1,98 +0,0 @@
1
- # 011 Async Patterns - Debouncing & Loading States
2
-
3
- Demonstrates how to handle async operations with EventState.
4
-
5
- ## What's Here
6
-
7
- - **Debounced search:** Wait for user to stop typing before searching
8
- - **Loading states:** Show loading indicator during async operations
9
- - **Error handling:** Graceful failure with try/catch
10
- - **No async library needed** - just Promises and state
11
-
12
- ## How It Works
13
-
14
- ### Debounced Search
15
-
16
- ```javascript
17
- let searchTimeout = null;
18
-
19
- store.subscribe('searchQuery', async (value) => {
20
- clearTimeout(searchTimeout);
21
-
22
- store.set('isSearching', true);
23
-
24
- // Wait 300ms before searching
25
- searchTimeout = setTimeout(async () => {
26
- const result = await performSearch(value);
27
- store.set('searchResult', result);
28
- store.set('isSearching', false);
29
- }, 300);
30
- });
31
- ```
32
-
33
- ### Loading States
34
-
35
- ```javascript
36
- document.getElementById('fetchBtn').onclick = async () => {
37
- store.set('isLoading', true);
38
-
39
- try {
40
- const data = await fetchData();
41
- store.set('apiData', data);
42
- } finally {
43
- store.set('isLoading', false);
44
- }
45
- };
46
- ```
47
-
48
- ## Key Insight
49
-
50
- **Async is just state changes over time.**
51
-
52
- Pattern for any async operation:
53
- 1. Set loading state to `true`
54
- 2. Perform async operation
55
- 3. Set result state
56
- 4. Set loading state to `false`
57
-
58
- Other frameworks need:
59
- - React: `useState` + `useEffect` + cleanup functions
60
- - Vue: `ref` + `watch` + async watchers
61
- - Svelte: Reactive statements + `#await` blocks
62
-
63
- **EventState:** Just use async/await in subscribers. That's it.
64
-
65
- ## Patterns Demonstrated
66
-
67
- ### 1. Debouncing
68
- Wait for user to stop typing before triggering expensive operations.
69
-
70
- ### 2. Loading Indicators
71
- Show feedback during async operations to improve UX.
72
-
73
- ### 3. Disabling Actions
74
- Prevent duplicate requests by disabling buttons during loading.
75
-
76
- ### 4. Cleanup
77
- Clear timeouts to prevent race conditions.
78
-
79
- ## Run It
80
-
81
- Open `index.html`:
82
- - Type in the search box (notice the 300ms delay)
83
- - Click "Fetch Data" (notice the loading state)
84
-
85
- ## Try This
86
-
87
- Open DevTools Network tab and throttle to "Slow 3G" to see loading states more clearly.
88
-
89
- ## Real-World Usage
90
-
91
- This pattern works for:
92
- - ✅ API calls
93
- - ✅ Autocomplete
94
- - ✅ Form submission
95
- - ✅ File uploads
96
- - ✅ Any async operation
97
-
98
- Just manage loading/error/success states in the store.
@@ -1,132 +0,0 @@
1
- <!DOCTYPE html>
2
- <html lang="en">
3
- <head>
4
- <meta charset="UTF-8">
5
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
- <title>011 Async Patterns - EventState</title>
7
- <script type="importmap">
8
- { "imports": { "@uistate/core": "../../index.js" } }
9
- </script>
10
- </head>
11
- <body>
12
- <h1>Async Patterns</h1>
13
-
14
- <h2>Debounced Search</h2>
15
- <input type="text" id="searchInput" placeholder="Type to search...">
16
- <p id="searchStatus"></p>
17
- <p><strong>Search result:</strong> <span id="searchResult">(type something)</span></p>
18
-
19
- <hr>
20
-
21
- <h2>Simulated API Call</h2>
22
- <button id="fetchBtn">Fetch Data</button>
23
- <p id="loadingStatus"></p>
24
- <p><strong>Data:</strong> <span id="apiData">(not loaded)</span></p>
25
-
26
- <script type="module">
27
- import { createEventState } from '@uistate/core';
28
-
29
- // Create store
30
- const store = createEventState({
31
- searchQuery: '',
32
- searchResult: '',
33
- isSearching: false,
34
- apiData: null,
35
- isLoading: false
36
- });
37
-
38
- // ===== DEBOUNCED SEARCH =====
39
- let searchTimeout = null;
40
-
41
- // Simulate search function
42
- const performSearch = (query) => {
43
- return new Promise((resolve) => {
44
- setTimeout(() => {
45
- resolve(`Results for "${query}": Found ${query.length} items`);
46
- }, 500);
47
- });
48
- };
49
-
50
- // Subscribe to search query changes
51
- store.subscribe('searchQuery', async (value) => {
52
- // Clear previous timeout
53
- clearTimeout(searchTimeout);
54
-
55
- if (!value) {
56
- store.set('searchResult', '');
57
- store.set('isSearching', false);
58
- return;
59
- }
60
-
61
- // Show searching indicator
62
- store.set('isSearching', true);
63
-
64
- // Debounce: wait 300ms before searching
65
- searchTimeout = setTimeout(async () => {
66
- const result = await performSearch(value);
67
- store.set('searchResult', result);
68
- store.set('isSearching', false);
69
- }, 300);
70
- });
71
-
72
- // Update UI for search status
73
- store.subscribe('isSearching', (value) => {
74
- document.getElementById('searchStatus').textContent =
75
- value ? 'Searching...' : '';
76
- });
77
-
78
- store.subscribe('searchResult', (value) => {
79
- document.getElementById('searchResult').textContent =
80
- value || '(type something)';
81
- });
82
-
83
- // Wire up search input
84
- document.getElementById('searchInput').oninput = (e) => {
85
- store.set('searchQuery', e.target.value);
86
- };
87
-
88
- // ===== SIMULATED API CALL =====
89
-
90
- // Simulate API fetch
91
- const fetchData = () => {
92
- return new Promise((resolve) => {
93
- setTimeout(() => {
94
- resolve({
95
- id: Math.floor(Math.random() * 1000),
96
- timestamp: new Date().toLocaleTimeString()
97
- });
98
- }, 1000);
99
- });
100
- };
101
-
102
- // Subscribe to loading state
103
- store.subscribe('isLoading', (value) => {
104
- document.getElementById('loadingStatus').textContent =
105
- value ? '⏳ Loading...' : '';
106
- document.getElementById('fetchBtn').disabled = value;
107
- });
108
-
109
- // Subscribe to API data
110
- store.subscribe('apiData', (value) => {
111
- if (value) {
112
- document.getElementById('apiData').textContent =
113
- `ID: ${value.id}, Time: ${value.timestamp}`;
114
- }
115
- });
116
-
117
- // Fetch button
118
- document.getElementById('fetchBtn').onclick = async () => {
119
- store.set('isLoading', true);
120
-
121
- try {
122
- const data = await fetchData();
123
- store.set('apiData', data);
124
- } catch (error) {
125
- console.error('Fetch failed:', error);
126
- } finally {
127
- store.set('isLoading', false);
128
- }
129
- };
130
- </script>
131
- </body>
132
- </html>
@@ -1,55 +0,0 @@
1
- Apache License
2
- Version 2.0, January 2004
3
- http://www.apache.org/licenses/
4
-
5
- TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6
-
7
- 1. Definitions.
8
-
9
- "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document.
10
-
11
- "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License.
12
-
13
- "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.
14
-
15
- "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License.
16
-
17
- "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files.
18
-
19
- "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types.
20
-
21
- "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work.
22
-
23
- "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof.
24
-
25
- "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution."
26
-
27
- "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work.
28
-
29
- 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form.
30
-
31
- 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed.
32
-
33
- 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions:
34
-
35
- (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and
36
-
37
- (b) You must cause any modified files to carry prominent notices stating that You changed the files; and
38
-
39
- (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and
40
-
41
- (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License.
42
-
43
- You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License.
44
-
45
- 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions.
46
-
47
- 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file.
48
-
49
- 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License.
50
-
51
- 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages.
52
-
53
- 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability.
54
-
55
- END OF TERMS AND CONDITIONS
@@ -1,131 +0,0 @@
1
- # 028 Counter with eventTest
2
-
3
- A simple counter app demonstrating event-sequence testing and type generation with the EventState primitive.
4
-
5
- ## What This Example Shows
6
-
7
- This example demonstrates **TDD with event-sequence testing** for a simple counter app:
8
-
9
- ✅ **EventState primitive** - `runtime/core/eventState.js` (~80 lines)
10
- ✅ **Event-sequence testing** - `eventTest.js` for testing state changes
11
- ✅ **Type generation** - Auto-generate `.d.ts` from test assertions
12
- ✅ **Direct state manipulation** - Simple `store.get()` and `store.set()`
13
-
14
- ## Structure
15
-
16
- ```
17
- app/
18
- └── store.js → Store with counter state
19
-
20
- runtime/core/
21
- ├── eventState.js → The 80-line reactive primitive
22
- └── helpers.js → Utility functions
23
-
24
- tests/
25
- ├── eventTest.js → Event-sequence testing framework
26
- ├── counter.test.js → Tests for counter operations
27
- ├── generateTypes.js → Type generator
28
- └── run.js → Test runner
29
-
30
- index.html → Simple counter UI
31
- store.d.ts → Auto-generated types
32
- ```
33
-
34
- ## Counter Operations
35
-
36
- The app has three simple operations:
37
-
38
- ```javascript
39
- // Increment
40
- const current = store.get('count');
41
- store.set('count', current + 1);
42
-
43
- // Decrement
44
- const current = store.get('count');
45
- store.set('count', current - 1);
46
-
47
- // Double
48
- const current = store.get('count');
49
- store.set('count', current * 2);
50
- ```
51
-
52
- ## Event-Sequence Testing
53
-
54
- Tests verify state changes as event sequences:
55
-
56
- ```javascript
57
- import { createEventTest } from './eventTest.js';
58
-
59
- const tests = {
60
- 'increment increases count by 1': () => {
61
- const t = createEventTest({ count: 0 });
62
-
63
- // Perform operation
64
- const current = t.store.get('count');
65
- t.store.set('count', current + 1);
66
-
67
- // Assert type
68
- t.assertType('count', 'number');
69
-
70
- // Assert value
71
- const count = t.store.get('count');
72
- if (count !== 1) {
73
- throw new Error(`Expected count to be 1, got ${count}`);
74
- }
75
- }
76
- };
77
- ```
78
-
79
- ## Type Generation
80
-
81
- Run tests to generate TypeScript definitions:
82
-
83
- ```bash
84
- # Run tests
85
- node tests/counter.test.js
86
-
87
- # Generate types from test assertions
88
- node tests/generateTypes.js
89
- ```
90
-
91
- This creates `store.d.ts`:
92
-
93
- ```typescript
94
- export interface StoreState {
95
- count: number;
96
- }
97
- ```
98
-
99
- ## Run It
100
-
101
- ```bash
102
- # Open in browser
103
- open index.html
104
-
105
- # Run tests
106
- node tests/counter.test.js
107
-
108
- # Generate types
109
- node tests/generateTypes.js
110
-
111
- # Run all tests
112
- node tests/run.js
113
- ```
114
-
115
- ## What Makes This Different
116
-
117
- **Traditional testing:**
118
- - Mock DOM interactions
119
- - Test implementation details
120
- - Separate type definitions
121
-
122
- **Event-sequence testing:**
123
- - Test state changes directly
124
- - Verify behavior, not implementation
125
- - Types generated from tests
126
-
127
- ## Learn More
128
-
129
- - See `tests/counter.test.js` for test examples
130
- - See `tests/eventTest.js` for the testing framework
131
- - See `runtime/core/eventState.js` for the core primitive
@@ -1,9 +0,0 @@
1
- // store.js — singleton eventState store for counter app
2
- import { createEventState } from '../runtime/core/eventStateNew.js';
3
-
4
- const initial = {
5
- count: 0
6
- };
7
-
8
- const store = createEventState(initial);
9
- export default store;
@@ -1,49 +0,0 @@
1
- <!doctype html>
2
- <html lang="en">
3
- <head>
4
- <meta charset="utf-8" />
5
- <meta name="viewport" content="width=device-width, initial-scale=1" />
6
- <title>028 Counter with eventTest</title>
7
- <link rel="stylesheet" href="style.css">
8
- </head>
9
- <body>
10
- <main style="max-width: 600px; margin: 2rem auto; padding: 0 1rem; text-align: center;">
11
- <h1>Counter: <span id="count">0</span></h1>
12
- <div style="display: flex; gap: 1rem; justify-content: center; margin-top: 2rem;">
13
- <button id="increment" class="btn">+</button>
14
- <button id="decrement" class="btn">-</button>
15
- <button id="double" class="btn">x2</button>
16
- </div>
17
- </main>
18
-
19
- <script type="module">
20
- import store from './app/store.js';
21
-
22
- // Subscribe to count changes
23
- store.subscribe('count', (value) => {
24
- document.getElementById('count').textContent = value;
25
- });
26
-
27
- // Set initial value
28
- document.getElementById('count').textContent = store.get('count');
29
-
30
- // Increment
31
- document.getElementById('increment').onclick = () => {
32
- const current = store.get('count');
33
- store.set('count', current + 1);
34
- };
35
-
36
- // Decrement
37
- document.getElementById('decrement').onclick = () => {
38
- const current = store.get('count');
39
- store.set('count', current - 1);
40
- };
41
-
42
- // Double
43
- document.getElementById('double').onclick = () => {
44
- const current = store.get('count');
45
- store.set('count', current * 2);
46
- };
47
- </script>
48
- </body>
49
- </html>