pulse-js-framework 1.0.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.
- package/LICENSE +21 -0
- package/README.md +182 -0
- package/cli/build.js +199 -0
- package/cli/dev.js +225 -0
- package/cli/index.js +324 -0
- package/compiler/index.js +65 -0
- package/compiler/lexer.js +581 -0
- package/compiler/parser.js +900 -0
- package/compiler/transformer.js +552 -0
- package/index.js +19 -0
- package/loader/vite-plugin.js +160 -0
- package/package.json +58 -0
- package/runtime/dom.js +484 -0
- package/runtime/index.js +13 -0
- package/runtime/pulse.js +339 -0
- package/runtime/router.js +392 -0
- package/runtime/store.js +301 -0
package/runtime/store.js
ADDED
|
@@ -0,0 +1,301 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pulse Store - Global state management
|
|
3
|
+
*
|
|
4
|
+
* A simple but powerful store that integrates with Pulse reactivity
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { pulse, computed, effect, batch } from './pulse.js';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Create a global store
|
|
11
|
+
*/
|
|
12
|
+
export function createStore(initialState = {}, options = {}) {
|
|
13
|
+
const { persist = false, storageKey = 'pulse-store' } = options;
|
|
14
|
+
|
|
15
|
+
// Load persisted state if enabled
|
|
16
|
+
let state = initialState;
|
|
17
|
+
if (persist && typeof localStorage !== 'undefined') {
|
|
18
|
+
try {
|
|
19
|
+
const saved = localStorage.getItem(storageKey);
|
|
20
|
+
if (saved) {
|
|
21
|
+
state = { ...initialState, ...JSON.parse(saved) };
|
|
22
|
+
}
|
|
23
|
+
} catch (e) {
|
|
24
|
+
console.warn('Failed to load persisted state:', e);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// Create pulses for each state property
|
|
29
|
+
const pulses = {};
|
|
30
|
+
const store = {};
|
|
31
|
+
|
|
32
|
+
function createPulse(key, value) {
|
|
33
|
+
if (typeof value === 'object' && value !== null && !Array.isArray(value)) {
|
|
34
|
+
// Nested object - create nested store
|
|
35
|
+
const nested = {};
|
|
36
|
+
for (const [k, v] of Object.entries(value)) {
|
|
37
|
+
nested[k] = createPulse(`${key}.${k}`, v);
|
|
38
|
+
}
|
|
39
|
+
return nested;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const p = pulse(value);
|
|
43
|
+
pulses[key] = p;
|
|
44
|
+
return p;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Initialize state
|
|
48
|
+
for (const [key, value] of Object.entries(state)) {
|
|
49
|
+
store[key] = createPulse(key, value);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Persist state changes
|
|
53
|
+
if (persist) {
|
|
54
|
+
effect(() => {
|
|
55
|
+
const snapshot = {};
|
|
56
|
+
for (const [key, p] of Object.entries(pulses)) {
|
|
57
|
+
snapshot[key] = p.get();
|
|
58
|
+
}
|
|
59
|
+
try {
|
|
60
|
+
localStorage.setItem(storageKey, JSON.stringify(snapshot));
|
|
61
|
+
} catch (e) {
|
|
62
|
+
console.warn('Failed to persist state:', e);
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Get a snapshot of the current state
|
|
69
|
+
*/
|
|
70
|
+
function getState() {
|
|
71
|
+
const snapshot = {};
|
|
72
|
+
for (const [key, p] of Object.entries(pulses)) {
|
|
73
|
+
snapshot[key] = p.peek();
|
|
74
|
+
}
|
|
75
|
+
return snapshot;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Set multiple values at once
|
|
80
|
+
*/
|
|
81
|
+
function setState(updates) {
|
|
82
|
+
batch(() => {
|
|
83
|
+
for (const [key, value] of Object.entries(updates)) {
|
|
84
|
+
if (pulses[key]) {
|
|
85
|
+
pulses[key].set(value);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Reset state to initial values
|
|
93
|
+
*/
|
|
94
|
+
function reset() {
|
|
95
|
+
batch(() => {
|
|
96
|
+
for (const [key, value] of Object.entries(initialState)) {
|
|
97
|
+
if (pulses[key]) {
|
|
98
|
+
pulses[key].set(value);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Subscribe to all state changes
|
|
106
|
+
*/
|
|
107
|
+
function subscribe(callback) {
|
|
108
|
+
return effect(() => {
|
|
109
|
+
const state = getState();
|
|
110
|
+
callback(state);
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// Attach methods to store
|
|
115
|
+
store.$getState = getState;
|
|
116
|
+
store.$setState = setState;
|
|
117
|
+
store.$reset = reset;
|
|
118
|
+
store.$subscribe = subscribe;
|
|
119
|
+
store.$pulses = pulses;
|
|
120
|
+
|
|
121
|
+
return store;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Create actions that can modify the store
|
|
126
|
+
*/
|
|
127
|
+
export function createActions(store, actions) {
|
|
128
|
+
const boundActions = {};
|
|
129
|
+
|
|
130
|
+
for (const [name, action] of Object.entries(actions)) {
|
|
131
|
+
boundActions[name] = (...args) => {
|
|
132
|
+
return action(store, ...args);
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
return boundActions;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Create getters (computed values) for the store
|
|
141
|
+
*/
|
|
142
|
+
export function createGetters(store, getters) {
|
|
143
|
+
const boundGetters = {};
|
|
144
|
+
|
|
145
|
+
for (const [name, getter] of Object.entries(getters)) {
|
|
146
|
+
boundGetters[name] = computed(() => getter(store));
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
return boundGetters;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Combine multiple stores
|
|
154
|
+
*/
|
|
155
|
+
export function combineStores(stores) {
|
|
156
|
+
const combined = {};
|
|
157
|
+
|
|
158
|
+
for (const [namespace, store] of Object.entries(stores)) {
|
|
159
|
+
combined[namespace] = store;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
return combined;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Create a module-based store (like Vuex modules)
|
|
167
|
+
*/
|
|
168
|
+
export function createModuleStore(modules) {
|
|
169
|
+
const stores = {};
|
|
170
|
+
const rootStore = {};
|
|
171
|
+
|
|
172
|
+
for (const [name, module] of Object.entries(modules)) {
|
|
173
|
+
const { state = {}, actions = {}, getters = {} } = module;
|
|
174
|
+
|
|
175
|
+
const store = createStore(state);
|
|
176
|
+
const boundActions = createActions(store, actions);
|
|
177
|
+
const boundGetters = createGetters(store, getters);
|
|
178
|
+
|
|
179
|
+
stores[name] = {
|
|
180
|
+
...store,
|
|
181
|
+
...boundActions,
|
|
182
|
+
...boundGetters
|
|
183
|
+
};
|
|
184
|
+
|
|
185
|
+
// Also expose at root level
|
|
186
|
+
rootStore[name] = stores[name];
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// Root methods
|
|
190
|
+
rootStore.$getState = () => {
|
|
191
|
+
const state = {};
|
|
192
|
+
for (const [name, store] of Object.entries(stores)) {
|
|
193
|
+
state[name] = store.$getState();
|
|
194
|
+
}
|
|
195
|
+
return state;
|
|
196
|
+
};
|
|
197
|
+
|
|
198
|
+
rootStore.$reset = () => {
|
|
199
|
+
for (const store of Object.values(stores)) {
|
|
200
|
+
store.$reset();
|
|
201
|
+
}
|
|
202
|
+
};
|
|
203
|
+
|
|
204
|
+
return rootStore;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Plugin system for store
|
|
209
|
+
*/
|
|
210
|
+
export function usePlugin(store, plugin) {
|
|
211
|
+
return plugin(store);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* Logger plugin - logs all state changes
|
|
216
|
+
*/
|
|
217
|
+
export function loggerPlugin(store) {
|
|
218
|
+
const originalSetState = store.$setState;
|
|
219
|
+
|
|
220
|
+
store.$setState = (updates) => {
|
|
221
|
+
console.group('Store Update');
|
|
222
|
+
console.log('Previous:', store.$getState());
|
|
223
|
+
console.log('Updates:', updates);
|
|
224
|
+
originalSetState(updates);
|
|
225
|
+
console.log('Next:', store.$getState());
|
|
226
|
+
console.groupEnd();
|
|
227
|
+
};
|
|
228
|
+
|
|
229
|
+
return store;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* History plugin - enables undo/redo
|
|
234
|
+
*/
|
|
235
|
+
export function historyPlugin(store, maxHistory = 50) {
|
|
236
|
+
const history = [store.$getState()];
|
|
237
|
+
let currentIndex = 0;
|
|
238
|
+
|
|
239
|
+
const originalSetState = store.$setState;
|
|
240
|
+
|
|
241
|
+
store.$setState = (updates) => {
|
|
242
|
+
// Remove any future states if we're not at the end
|
|
243
|
+
if (currentIndex < history.length - 1) {
|
|
244
|
+
history.splice(currentIndex + 1);
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
originalSetState(updates);
|
|
248
|
+
|
|
249
|
+
// Add new state to history
|
|
250
|
+
history.push(store.$getState());
|
|
251
|
+
if (history.length > maxHistory) {
|
|
252
|
+
history.shift();
|
|
253
|
+
} else {
|
|
254
|
+
currentIndex++;
|
|
255
|
+
}
|
|
256
|
+
};
|
|
257
|
+
|
|
258
|
+
store.$undo = () => {
|
|
259
|
+
if (currentIndex > 0) {
|
|
260
|
+
currentIndex--;
|
|
261
|
+
const state = history[currentIndex];
|
|
262
|
+
batch(() => {
|
|
263
|
+
for (const [key, value] of Object.entries(state)) {
|
|
264
|
+
if (store.$pulses[key]) {
|
|
265
|
+
store.$pulses[key].set(value);
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
};
|
|
271
|
+
|
|
272
|
+
store.$redo = () => {
|
|
273
|
+
if (currentIndex < history.length - 1) {
|
|
274
|
+
currentIndex++;
|
|
275
|
+
const state = history[currentIndex];
|
|
276
|
+
batch(() => {
|
|
277
|
+
for (const [key, value] of Object.entries(state)) {
|
|
278
|
+
if (store.$pulses[key]) {
|
|
279
|
+
store.$pulses[key].set(value);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
});
|
|
283
|
+
}
|
|
284
|
+
};
|
|
285
|
+
|
|
286
|
+
store.$canUndo = () => currentIndex > 0;
|
|
287
|
+
store.$canRedo = () => currentIndex < history.length - 1;
|
|
288
|
+
|
|
289
|
+
return store;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
export default {
|
|
293
|
+
createStore,
|
|
294
|
+
createActions,
|
|
295
|
+
createGetters,
|
|
296
|
+
combineStores,
|
|
297
|
+
createModuleStore,
|
|
298
|
+
usePlugin,
|
|
299
|
+
loggerPlugin,
|
|
300
|
+
historyPlugin
|
|
301
|
+
};
|