@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,208 +0,0 @@
1
- # EventTest - Event-Sequence Testing
2
-
3
- Event-driven TDD for EventState applications.
4
-
5
- ## Features
6
-
7
- - āœ… **Event-sequence testing** - Test state changes through event flows
8
- - āœ… **Type extraction** - Generate `.d.ts` files from test assertions
9
- - āœ… **No DOM required** - Tests run in Node.js
10
- - āœ… **Fluent API** - Chainable assertions
11
- - āœ… **Type-safe** - Tests define types, not manual definitions
12
-
13
- ## Usage
14
-
15
- ### Writing Tests
16
-
17
- ```javascript
18
- import { createEventTest, test } from './eventTest.js';
19
-
20
- test('add todo', () => {
21
- const t = createEventTest({
22
- domain: { todos: { items: [] } }
23
- });
24
-
25
- // Trigger intent
26
- t.trigger('intent.todo.add', { text: 'Buy milk' });
27
-
28
- // Assert types (for .d.ts generation)
29
- t.assertArrayOf('domain.todos.items', {
30
- id: 'number',
31
- text: 'string',
32
- done: 'boolean'
33
- });
34
-
35
- // Assert values
36
- t.assertArrayLength('domain.todos.items', 1);
37
- });
38
- ```
39
-
40
- ### Running Tests
41
-
42
- ```bash
43
- # Run all tests
44
- node tests/run.js
45
-
46
- # Or run specific test file
47
- node tests/todos.test.js
48
- ```
49
-
50
- ### Generating Types
51
-
52
- ```bash
53
- # Generate store.d.ts from test assertions
54
- node tests/generateTypes.js
55
- ```
56
-
57
- ## API Reference
58
-
59
- ### `createEventTest(initialState)`
60
-
61
- Creates a test instance with isolated store.
62
-
63
- **Returns:** Test API object
64
-
65
- ### Test API
66
-
67
- #### `.trigger(path, value)`
68
-
69
- Set a value in the store (trigger state change).
70
-
71
- ```javascript
72
- t.trigger('intent.todo.add', { text: 'Buy milk' });
73
- ```
74
-
75
- #### `.assertPath(path, expected)`
76
-
77
- Assert exact value at path.
78
-
79
- ```javascript
80
- t.assertPath('ui.theme', 'dark');
81
- ```
82
-
83
- #### `.assertType(path, type)`
84
-
85
- Assert primitive type. Stores type info for `.d.ts` generation.
86
-
87
- ```javascript
88
- t.assertType('ui.theme', 'string');
89
- ```
90
-
91
- #### `.assertArrayOf(path, elementShape)`
92
-
93
- Assert array with element shape. Stores type info for `.d.ts` generation.
94
-
95
- ```javascript
96
- t.assertArrayOf('domain.todos.items', {
97
- id: 'number',
98
- text: 'string',
99
- done: 'boolean'
100
- });
101
- ```
102
-
103
- #### `.assertShape(path, objectShape)`
104
-
105
- Assert object shape. Stores type info for `.d.ts` generation.
106
-
107
- ```javascript
108
- t.assertShape('ui.route', {
109
- path: 'string',
110
- view: 'string'
111
- });
112
- ```
113
-
114
- #### `.assertArrayLength(path, length)`
115
-
116
- Assert array length.
117
-
118
- ```javascript
119
- t.assertArrayLength('domain.todos.items', 3);
120
- ```
121
-
122
- #### `.assertEventFired(path, times)`
123
-
124
- Assert event fired N times.
125
-
126
- ```javascript
127
- t.assertEventFired('domain.todos.items', 1);
128
- ```
129
-
130
- #### `.getEventLog()`
131
-
132
- Get all captured events.
133
-
134
- ```javascript
135
- const log = t.getEventLog();
136
- // [{ timestamp: 123, path: 'domain.todos.items', value: [...] }]
137
- ```
138
-
139
- #### `.getTypeAssertions()`
140
-
141
- Get all type assertions (for type generation).
142
-
143
- ```javascript
144
- const assertions = t.getTypeAssertions();
145
- // [{ path: 'domain.todos.items', type: 'array', elementShape: {...} }]
146
- ```
147
-
148
- ## Workflow
149
-
150
- ### 1. Write Tests (TDD)
151
-
152
- ```javascript
153
- // tests/todos.test.js
154
- test('add todo', () => {
155
- const t = createEventTest({ domain: { todos: { items: [] } } });
156
- t.trigger('intent.todo.add', { text: 'Buy milk' });
157
- t.assertArrayOf('domain.todos.items', {
158
- id: 'number',
159
- text: 'string',
160
- done: 'boolean'
161
- });
162
- });
163
- ```
164
-
165
- ### 2. Implement Bridges
166
-
167
- ```javascript
168
- // app/bridges.js
169
- store.subscribe('intent.todo.add', ({ text }) => {
170
- const items = store.get('domain.todos.items') || [];
171
- const id = items.length + 1;
172
- store.set('domain.todos.items', [...items, { id, text, done: false }]);
173
- });
174
- ```
175
-
176
- ### 3. Generate Types
177
-
178
- ```bash
179
- node tests/generateTypes.js
180
- # Creates store.d.ts with perfect types!
181
- ```
182
-
183
- ### 4. Build UI
184
-
185
- ```javascript
186
- // Now you have autocomplete!
187
- const items = store.get('domain.todos.items');
188
- // TypeScript knows: Array<{ id: number, text: string, done: boolean }>
189
- ```
190
-
191
- ## Benefits
192
-
193
- - **Tests define behavior** - TDD workflow
194
- - **Types derived from tests** - No manual type definitions
195
- - **Perfect coverage** - Every test adds type information
196
- - **No DOM needed** - Fast feedback loop
197
- - **Deterministic** - Same tests = same types
198
- - **Watcher-friendly** - Auto-regenerate on test changes
199
-
200
- ## Philosophy
201
-
202
- EventTest follows EventState's core principles:
203
-
204
- 1. **State-first** - Tests work with state paths
205
- 2. **Dot-paths everywhere** - No special syntax
206
- 3. **Types are optional** - Tests work without TypeScript
207
- 4. **Types are derived** - Not hand-written
208
- 5. **JS-first** - Clean, readable code
@@ -1,116 +0,0 @@
1
- /**
2
- * counter.test.js - Event-sequence tests for counter functionality
3
- *
4
- * These tests verify direct state manipulation for counter operations
5
- */
6
-
7
- import { createEventTest, test, runTests } from './eventTest.js';
8
-
9
- // Helper functions that match the view logic
10
- function increment(store) {
11
- const current = store.get('count');
12
- store.set('count', current + 1);
13
- }
14
-
15
- function decrement(store) {
16
- const current = store.get('count');
17
- store.set('count', current - 1);
18
- }
19
-
20
- function double(store) {
21
- const current = store.get('count');
22
- store.set('count', current * 2);
23
- }
24
-
25
- // Test suite
26
- const tests = {
27
- 'increment increases count by 1': () => {
28
- const t = createEventTest({ count: 0 });
29
-
30
- increment(t.store);
31
-
32
- // Assert type
33
- t.assertType('count', 'number');
34
-
35
- // Assert value
36
- const count = t.store.get('count');
37
- if (count !== 1) {
38
- throw new Error(`Expected count to be 1, got ${count}`);
39
- }
40
- },
41
-
42
- 'decrement decreases count by 1': () => {
43
- const t = createEventTest({ count: 5 });
44
-
45
- decrement(t.store);
46
-
47
- // Assert type
48
- t.assertType('count', 'number');
49
-
50
- // Assert value
51
- const count = t.store.get('count');
52
- if (count !== 4) {
53
- throw new Error(`Expected count to be 4, got ${count}`);
54
- }
55
- },
56
-
57
- 'double multiplies count by 2': () => {
58
- const t = createEventTest({ count: 3 });
59
-
60
- double(t.store);
61
-
62
- // Assert type
63
- t.assertType('count', 'number');
64
-
65
- // Assert value
66
- const count = t.store.get('count');
67
- if (count !== 6) {
68
- throw new Error(`Expected count to be 6, got ${count}`);
69
- }
70
- },
71
-
72
- 'multiple increments work correctly': () => {
73
- const t = createEventTest({ count: 0 });
74
-
75
- increment(t.store);
76
- increment(t.store);
77
- increment(t.store);
78
-
79
- const count = t.store.get('count');
80
- if (count !== 3) {
81
- throw new Error(`Expected count to be 3, got ${count}`);
82
- }
83
- },
84
-
85
- 'mixed operations work correctly': () => {
86
- const t = createEventTest({ count: 10 });
87
-
88
- increment(t.store); // 11
89
- double(t.store); // 22
90
- decrement(t.store); // 21
91
-
92
- const count = t.store.get('count');
93
- if (count !== 21) {
94
- throw new Error(`Expected count to be 21, got ${count}`);
95
- }
96
- },
97
-
98
- 'count can go negative': () => {
99
- const t = createEventTest({ count: 0 });
100
-
101
- decrement(t.store);
102
- decrement(t.store);
103
-
104
- const count = t.store.get('count');
105
- if (count !== -2) {
106
- throw new Error(`Expected count to be -2, got ${count}`);
107
- }
108
- }
109
- };
110
-
111
- // Run tests if executed directly
112
- if (import.meta.url === `file://${process.argv[1]}`) {
113
- runTests(tests);
114
- }
115
-
116
- export default tests;
@@ -1,176 +0,0 @@
1
- /**
2
- * eventTest.js - Event-Sequence Testing for EventState
3
- *
4
- * Provides TDD-style testing with type extraction capabilities
5
- */
6
-
7
- import { createEventState } from '../runtime/core/eventStateNew.js';
8
-
9
- export function createEventTest(initialState = {}) {
10
- const store = createEventState(initialState);
11
- const eventLog = [];
12
- const typeAssertions = [];
13
-
14
- // Spy on all events
15
- store.subscribe('*', (detail) => {
16
- const { path, value } = detail;
17
- eventLog.push({ timestamp: Date.now(), path, value });
18
- });
19
-
20
- const api = {
21
- store,
22
-
23
- // Trigger a state change
24
- trigger(path, value) {
25
- store.set(path, value);
26
- return this;
27
- },
28
-
29
- // Assert exact value
30
- assertPath(path, expected) {
31
- const actual = store.get(path);
32
- if (JSON.stringify(actual) !== JSON.stringify(expected)) {
33
- throw new Error(`Expected ${path} to be ${JSON.stringify(expected)}, got ${JSON.stringify(actual)}`);
34
- }
35
- return this;
36
- },
37
-
38
- // Assert type (for type generation)
39
- assertType(path, expectedType) {
40
- const actual = store.get(path);
41
- const actualType = typeof actual;
42
-
43
- if (actualType !== expectedType) {
44
- throw new Error(`Expected ${path} to be type ${expectedType}, got ${actualType}`);
45
- }
46
-
47
- // Store for type generation
48
- typeAssertions.push({ path, type: expectedType });
49
- return this;
50
- },
51
-
52
- // Assert array with element shape (for type generation)
53
- assertArrayOf(path, elementShape) {
54
- const actual = store.get(path);
55
-
56
- if (!Array.isArray(actual)) {
57
- throw new Error(`Expected ${path} to be an array, got ${typeof actual}`);
58
- }
59
-
60
- // Validate first element matches shape (if array not empty)
61
- if (actual.length > 0) {
62
- validateShape(actual[0], elementShape, path);
63
- }
64
-
65
- // Store for type generation
66
- typeAssertions.push({ path, type: 'array', elementShape });
67
- return this;
68
- },
69
-
70
- // Assert object shape (for type generation)
71
- assertShape(path, objectShape) {
72
- const actual = store.get(path);
73
-
74
- if (typeof actual !== 'object' || actual === null || Array.isArray(actual)) {
75
- throw new Error(`Expected ${path} to be an object, got ${typeof actual}`);
76
- }
77
-
78
- validateShape(actual, objectShape, path);
79
-
80
- // Store for type generation
81
- typeAssertions.push({ path, type: 'object', shape: objectShape });
82
- return this;
83
- },
84
-
85
- // Assert array length
86
- assertArrayLength(path, expectedLength) {
87
- const actual = store.get(path);
88
-
89
- if (!Array.isArray(actual)) {
90
- throw new Error(`Expected ${path} to be an array`);
91
- }
92
-
93
- if (actual.length !== expectedLength) {
94
- throw new Error(`Expected ${path} to have length ${expectedLength}, got ${actual.length}`);
95
- }
96
-
97
- return this;
98
- },
99
-
100
- // Assert event fired N times
101
- assertEventFired(path, times) {
102
- const count = eventLog.filter(e => e.path === path).length;
103
- if (times !== undefined && count !== times) {
104
- throw new Error(`Expected ${path} to fire ${times} times, fired ${count}`);
105
- }
106
- return this;
107
- },
108
-
109
- // Get event log
110
- getEventLog() {
111
- return [...eventLog];
112
- },
113
-
114
- // Get type assertions (for type generation)
115
- getTypeAssertions() {
116
- return [...typeAssertions];
117
- }
118
- };
119
-
120
- return api;
121
- }
122
-
123
- // Helper to validate object shape
124
- function validateShape(actual, shape, path) {
125
- for (const [key, expectedType] of Object.entries(shape)) {
126
- if (!(key in actual)) {
127
- throw new Error(`Expected ${path} to have property ${key}`);
128
- }
129
-
130
- const actualValue = actual[key];
131
-
132
- // Handle nested objects
133
- if (typeof expectedType === 'object' && !Array.isArray(expectedType)) {
134
- validateShape(actualValue, expectedType, `${path}.${key}`);
135
- } else {
136
- // Primitive type check
137
- const actualType = typeof actualValue;
138
- if (actualType !== expectedType) {
139
- throw new Error(`Expected ${path}.${key} to be type ${expectedType}, got ${actualType}`);
140
- }
141
- }
142
- }
143
- }
144
-
145
- // Simple test runner
146
- export function test(name, fn) {
147
- try {
148
- fn();
149
- console.log(`āœ“ ${name}`);
150
- return true;
151
- } catch (error) {
152
- console.error(`āœ— ${name}`);
153
- console.error(` ${error.message}`);
154
- return false;
155
- }
156
- }
157
-
158
- // Run multiple tests
159
- export function runTests(tests) {
160
- console.log('\n🧪 Running tests...\n');
161
-
162
- let passed = 0;
163
- let failed = 0;
164
-
165
- for (const [name, fn] of Object.entries(tests)) {
166
- if (test(name, fn)) {
167
- passed++;
168
- } else {
169
- failed++;
170
- }
171
- }
172
-
173
- console.log(`\nšŸ“Š Results: ${passed} passed, ${failed} failed\n`);
174
-
175
- return { passed, failed };
176
- }
@@ -1,168 +0,0 @@
1
- #!/usr/bin/env node
2
- /**
3
- * generateTypes.js - Generate TypeScript definitions from test assertions
4
- *
5
- * Usage: node tests/generateTypes.js
6
- */
7
-
8
- import { createEventTest } from './eventTest.js';
9
- import todosTests from './todos.test.js';
10
- import { writeFileSync } from 'fs';
11
- import { fileURLToPath } from 'url';
12
- import { dirname, join } from 'path';
13
-
14
- const __filename = fileURLToPath(import.meta.url);
15
- const __dirname = dirname(__filename);
16
-
17
- // Collect all type assertions from tests
18
- const allAssertions = [];
19
-
20
- console.log('šŸ” Extracting types from tests...\n');
21
-
22
- for (const [name, testFn] of Object.entries(todosTests)) {
23
- try {
24
- // Run test to collect assertions
25
- testFn();
26
- console.log(`āœ“ ${name}`);
27
- } catch (error) {
28
- // Test might fail, but we still want type assertions
29
- console.log(`⚠ ${name} (failed but types extracted)`);
30
- }
31
- }
32
-
33
- // Simple type generator (minimal version for Node.js)
34
- class SimpleTypeGenerator {
35
- constructor() {
36
- this.pathTypes = new Map();
37
- }
38
-
39
- parseAssertions(assertions) {
40
- for (const assertion of assertions) {
41
- const { path, type, elementShape, shape } = assertion;
42
-
43
- if (type === 'array' && elementShape) {
44
- this.pathTypes.set(path, {
45
- type: 'array',
46
- element: elementShape
47
- });
48
- } else if (type === 'object' && shape) {
49
- this.pathTypes.set(path, {
50
- type: 'object',
51
- shape: shape
52
- });
53
- } else if (type) {
54
- this.pathTypes.set(path, { type });
55
- }
56
- }
57
- }
58
-
59
- buildTree() {
60
- const tree = {};
61
-
62
- for (const [path, typeInfo] of this.pathTypes) {
63
- const parts = path.split('.');
64
- let current = tree;
65
-
66
- for (let i = 0; i < parts.length; i++) {
67
- const part = parts[i];
68
-
69
- if (i === parts.length - 1) {
70
- current[part] = { __typeInfo: typeInfo };
71
- } else {
72
- if (!current[part]) {
73
- current[part] = {};
74
- }
75
- current = current[part];
76
- }
77
- }
78
- }
79
-
80
- return tree;
81
- }
82
-
83
- formatType(typeInfo) {
84
- if (typeof typeInfo === 'string') return typeInfo;
85
-
86
- if (typeInfo.type === 'array' && typeInfo.element) {
87
- const elementType = this.formatShape(typeInfo.element);
88
- return `Array<${elementType}>`;
89
- }
90
-
91
- if (typeInfo.type === 'object' && typeInfo.shape) {
92
- return this.formatShape(typeInfo.shape);
93
- }
94
-
95
- return typeInfo.type || 'unknown';
96
- }
97
-
98
- formatShape(shape) {
99
- if (typeof shape === 'string') return shape;
100
-
101
- const props = Object.entries(shape)
102
- .map(([k, v]) => {
103
- const type = typeof v === 'string' ? v : this.formatShape(v);
104
- return `${k}: ${type}`;
105
- })
106
- .join('; ');
107
-
108
- return `{ ${props} }`;
109
- }
110
-
111
- generateInterface(obj, indent = 0) {
112
- const lines = [];
113
- const spaces = ' '.repeat(indent);
114
-
115
- for (const [key, value] of Object.entries(obj)) {
116
- if (value.__typeInfo) {
117
- const type = this.formatType(value.__typeInfo);
118
- lines.push(`${spaces}${key}: ${type};`);
119
- } else {
120
- lines.push(`${spaces}${key}: {`);
121
- lines.push(this.generateInterface(value, indent + 1));
122
- lines.push(`${spaces}};`);
123
- }
124
- }
125
-
126
- return lines.join('\n');
127
- }
128
-
129
- generateDTS() {
130
- const lines = [
131
- '// Auto-generated from test assertions',
132
- '// DO NOT EDIT - regenerate by running: node tests/generateTypes.js',
133
- '',
134
- 'export interface StoreState {',
135
- ];
136
-
137
- const tree = this.buildTree();
138
- lines.push(this.generateInterface(tree, 1));
139
- lines.push('}');
140
- lines.push('');
141
- lines.push('export default StoreState;');
142
- lines.push('');
143
-
144
- return lines.join('\n');
145
- }
146
- }
147
-
148
- // Note: We need to actually collect assertions from test execution
149
- // For now, manually define expected types based on test assertions
150
- const manualAssertions = [
151
- {
152
- path: 'count',
153
- type: 'number'
154
- }
155
- ];
156
-
157
- const generator = new SimpleTypeGenerator();
158
- generator.parseAssertions(manualAssertions);
159
-
160
- const dts = generator.generateDTS();
161
-
162
- // Write to file
163
- const outputPath = join(__dirname, '..', 'store.d.ts');
164
- writeFileSync(outputPath, dts, 'utf-8');
165
-
166
- console.log(`\nāœ… Generated ${outputPath}`);
167
- console.log('\nšŸ“ Type definitions:\n');
168
- console.log(dts);
@@ -1,20 +0,0 @@
1
- #!/usr/bin/env node
2
- /**
3
- * run.js - Test runner for Node.js
4
- *
5
- * Usage: node tests/run.js
6
- */
7
-
8
- import { runTests } from './eventTest.js';
9
- import counterTests from './counter.test.js';
10
-
11
- // Combine all test suites
12
- const allTests = {
13
- ...counterTests
14
- };
15
-
16
- // Run tests
17
- const results = runTests(allTests);
18
-
19
- // Exit with appropriate code
20
- process.exit(results.failed > 0 ? 1 : 0);