@hkdigital/lib-core 0.5.88 → 0.5.91

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 (27) hide show
  1. package/dist/state/classes/reactive-data-store/README.md +445 -0
  2. package/dist/state/classes/reactive-data-store/ReactiveDataStore.svelte.d.ts +152 -0
  3. package/dist/state/classes/reactive-data-store/ReactiveDataStore.svelte.js +300 -0
  4. package/dist/state/classes.d.ts +1 -0
  5. package/dist/state/classes.js +1 -0
  6. package/dist/state/machines/page-machine/PageMachine.svelte.d.ts +45 -232
  7. package/dist/state/machines/page-machine/PageMachine.svelte.js +61 -308
  8. package/dist/state/machines/page-machine/README.md +79 -20
  9. package/dist/ui/CHANGES-COMPONENT-PROPS.md +121 -0
  10. package/dist/ui/components/game-box/GameBox.svelte +10 -7
  11. package/dist/ui/components/game-box/GameBox.svelte.d.ts +2 -0
  12. package/dist/ui/components/game-box/ScaledContainer.svelte +14 -7
  13. package/dist/ui/components/grid-layers/GridLayers.svelte +9 -7
  14. package/dist/ui/components/grid-layers/GridLayers.svelte.d.ts +2 -0
  15. package/dist/ui/components/image-box/ImageBox.svelte +9 -7
  16. package/dist/ui/components/image-box/ImageBox.svelte.d.ts +2 -0
  17. package/dist/ui/components/presenter/Presenter.svelte +5 -3
  18. package/dist/ui/components/presenter/Presenter.svelte.d.ts +2 -0
  19. package/dist/ui/primitives/buttons/button/Button.svelte +9 -7
  20. package/dist/ui/primitives/buttons/button/Button.svelte.d.ts +2 -0
  21. package/dist/ui/primitives/icons/SteezeIcon.svelte +7 -5
  22. package/dist/ui/primitives/icons/SteezeIcon.svelte.d.ts +2 -0
  23. package/dist/ui/primitives/panels/panel/Panel.svelte +9 -7
  24. package/dist/ui/primitives/panels/panel/Panel.svelte.d.ts +2 -0
  25. package/package.json +1 -1
  26. package/dist/state/classes/subscribers-count/index.d.ts +0 -1
  27. package/dist/state/classes/subscribers-count/index.js +0 -1
@@ -0,0 +1,445 @@
1
+ # ReactiveDataStore
2
+
3
+ Reactive key-value data store with fine-grained reactivity built on Svelte 5's
4
+ `SvelteMap`.
5
+
6
+ ## Features
7
+
8
+ - **Fine-grained reactivity**: Effects only re-run when specific keys change
9
+ - **Strict mode**: Throws errors on uninitialized key access
10
+ - **Production guard**: Dev-only data mode for development helpers
11
+ - **Full CRUD API**: Set, get, update, delete, check existence, clear
12
+ - **Type-safe**: Use constants for keys to get autocomplete and typo prevention
13
+
14
+ ## Basic Usage
15
+
16
+ ```javascript
17
+ import { ReactiveDataStore } from '$lib/state/classes.js';
18
+
19
+ // Create a store
20
+ const store = new ReactiveDataStore({
21
+ initialData: {
22
+ score: 0,
23
+ level: 1,
24
+ playerName: 'Alice'
25
+ }
26
+ });
27
+
28
+ // Set data
29
+ store.set('score', 100);
30
+
31
+ // Get data (reactive)
32
+ $effect(() => {
33
+ const score = store.get('score');
34
+ console.log('Score changed:', score);
35
+ });
36
+
37
+ // Update multiple values
38
+ store.update({
39
+ score: 200,
40
+ level: 5
41
+ });
42
+
43
+ // Check existence
44
+ if (store.has('score')) {
45
+ // ...
46
+ }
47
+
48
+ // Get all data (snapshot)
49
+ const allData = store.getAll();
50
+
51
+ // Delete
52
+ store.delete('temporaryFlag');
53
+
54
+ // Clear all
55
+ store.clear();
56
+
57
+ // Get size
58
+ console.log(`Store has ${store.size} entries`);
59
+ ```
60
+
61
+ ## Options
62
+
63
+ ```javascript
64
+ const store = new ReactiveDataStore({
65
+ // Initial key-value pairs
66
+ initialData: {},
67
+
68
+ // Throw on uninitialized key access (default: true)
69
+ strictMode: true,
70
+
71
+ // Dev-only mode (default: false)
72
+ productionGuard: false,
73
+
74
+ // Custom error message prefix (default: 'Data key')
75
+ errorPrefix: 'Custom prefix'
76
+ });
77
+ ```
78
+
79
+ ## Fine-Grained Reactivity
80
+
81
+ ReactiveDataStore uses `SvelteMap` internally, providing fine-grained
82
+ reactivity where effects only re-run when the specific keys they access change.
83
+
84
+ ```javascript
85
+ const store = new ReactiveDataStore({
86
+ initialData: { score: 0, lives: 3 }
87
+ });
88
+
89
+ // Effect 1: Only re-runs when 'score' changes
90
+ $effect(() => {
91
+ const score = store.get('score');
92
+ console.log('Score:', score);
93
+ });
94
+
95
+ // Effect 2: Only re-runs when 'lives' changes
96
+ $effect(() => {
97
+ const lives = store.get('lives');
98
+ console.log('Lives:', lives);
99
+ });
100
+
101
+ // This only triggers Effect 1, not Effect 2
102
+ store.set('score', 100);
103
+
104
+ // This only triggers Effect 2, not Effect 1
105
+ store.set('lives', 2);
106
+ ```
107
+
108
+ ## Strict Mode (Default)
109
+
110
+ By default, ReactiveDataStore throws errors when accessing uninitialized keys.
111
+ This helps catch typos and ensures data is properly initialized.
112
+
113
+ ```javascript
114
+ const store = new ReactiveDataStore();
115
+
116
+ // ❌ Throws: "Data key "score" is not initialized."
117
+ store.get('score');
118
+
119
+ // ✅ Initialize first
120
+ store.set('score', 0);
121
+ store.get('score'); // Works
122
+
123
+ // Or initialize via constructor
124
+ const store2 = new ReactiveDataStore({
125
+ initialData: { score: 0 }
126
+ });
127
+ store2.get('score'); // Works
128
+ ```
129
+
130
+ ### Disabling Strict Mode
131
+
132
+ ```javascript
133
+ const store = new ReactiveDataStore({
134
+ strictMode: false
135
+ });
136
+
137
+ // Returns undefined instead of throwing
138
+ const score = store.get('nonexistent'); // undefined
139
+ ```
140
+
141
+ ## Production Guard (Dev-Only Data)
142
+
143
+ Use `productionGuard: true` for development-only data that should not be
144
+ accessed in production.
145
+
146
+ ```javascript
147
+ const devStore = new ReactiveDataStore({
148
+ initialData: {
149
+ autoNavigation: false,
150
+ skipAnimations: false,
151
+ mockApi: 'localhost'
152
+ },
153
+ productionGuard: true,
154
+ errorPrefix: 'Dev data key'
155
+ });
156
+
157
+ // In development mode:
158
+ devStore.set('autoNavigation', true); // ✅ Works
159
+ const value = devStore.get('autoNavigation'); // ✅ Works
160
+
161
+ // In production:
162
+ devStore.set('autoNavigation', true); // ✅ Silent no-op (safe)
163
+ devStore.get('autoNavigation'); // ❌ Throws error (programming bug!)
164
+ ```
165
+
166
+ **Why this design?**
167
+
168
+ - **SET operations**: Safe to call conditionally in production (no-op)
169
+ - **GET/HAS operations**: Reading dev data in production is a programming
170
+ error that should fail fast
171
+
172
+ ## Using with PageMachine
173
+
174
+ ReactiveDataStore is used internally by PageMachine for data management:
175
+
176
+ ```javascript
177
+ import { PageMachine } from '$lib/state/machines.js';
178
+
179
+ const machine = new PageMachine({
180
+ startPath: '/intro',
181
+ initialData: {
182
+ score: 0,
183
+ tutorialSeen: false
184
+ },
185
+ initialDevData: {
186
+ autoNav: false,
187
+ skipAnimations: false
188
+ }
189
+ });
190
+
191
+ // Access via read-only getters
192
+ machine.data.set('score', 100);
193
+ machine.data.get('score');
194
+
195
+ machine.devData.set('autoNav', true);
196
+ machine.devData.get('autoNav');
197
+ ```
198
+
199
+ ## Best Practices
200
+
201
+ ### 1. Use Constants for Keys
202
+
203
+ Avoid magic strings - use constants for autocomplete and typo prevention:
204
+
205
+ ```javascript
206
+ // ✅ Good - use constants
207
+ const KEY_SCORE = 'score';
208
+ const KEY_PLAYER_NAME = 'player-name';
209
+ const KEY_TUTORIAL_SEEN = 'tutorial-seen';
210
+
211
+ store.set(KEY_SCORE, 100);
212
+ const score = store.get(KEY_SCORE);
213
+
214
+ // ❌ Avoid - magic strings
215
+ store.set('score', 100);
216
+ const score = store.get('scroe'); // Typo! No error until runtime
217
+ ```
218
+
219
+ ### 2. Initialize Data in Constructor
220
+
221
+ Always initialize expected keys in the constructor for strict mode:
222
+
223
+ ```javascript
224
+ // ✅ Good
225
+ const store = new ReactiveDataStore({
226
+ initialData: {
227
+ score: 0,
228
+ lives: 3,
229
+ playerName: ''
230
+ }
231
+ });
232
+
233
+ // ❌ Avoid - uninitialized keys
234
+ const store = new ReactiveDataStore();
235
+ store.get('score'); // Throws error
236
+ ```
237
+
238
+ ### 3. Use Dev Data for Development Helpers
239
+
240
+ Keep development-only flags separate with production guard:
241
+
242
+ ```javascript
243
+ // Regular data
244
+ const data = new ReactiveDataStore({
245
+ initialData: {
246
+ score: 0,
247
+ level: 1
248
+ }
249
+ });
250
+
251
+ // Dev-only data
252
+ const devData = new ReactiveDataStore({
253
+ initialData: {
254
+ autoNavigation: false,
255
+ skipAnimations: false,
256
+ debugMode: false
257
+ },
258
+ productionGuard: true,
259
+ errorPrefix: 'Dev data key'
260
+ });
261
+ ```
262
+
263
+ ### 4. Use getAll() for Serialization Only
264
+
265
+ `getAll()` returns a snapshot (plain object), not a reactive value. Use it for
266
+ serialization or inspection, not for reactive tracking:
267
+
268
+ ```javascript
269
+ // ✅ Good - serialization
270
+ const snapshot = store.getAll();
271
+ await saveToServer(snapshot);
272
+
273
+ // ✅ Good - inspection
274
+ console.log('Current state:', store.getAll());
275
+
276
+ // ❌ Avoid - for reactivity
277
+ $effect(() => {
278
+ const data = store.getAll(); // Re-runs on ANY key change
279
+ console.log(data.score); // Use store.get('score') instead
280
+ });
281
+ ```
282
+
283
+ ## API Reference
284
+
285
+ ### Constructor
286
+
287
+ ```javascript
288
+ new ReactiveDataStore(options)
289
+ ```
290
+
291
+ **Options:**
292
+ - `initialData` (object): Initial key-value pairs
293
+ - `strictMode` (boolean): Throw on uninitialized keys (default: `true`)
294
+ - `productionGuard` (boolean): Dev-only mode (default: `false`)
295
+ - `errorPrefix` (string): Error message prefix (default: `'Data key'`)
296
+
297
+ ### Methods
298
+
299
+ #### `set(key, value)`
300
+
301
+ Set a data property value. Triggers reactivity for this key.
302
+
303
+ ```javascript
304
+ store.set('score', 100);
305
+ ```
306
+
307
+ #### `get(key)`
308
+
309
+ Get a data property value. Creates a reactive dependency on this key.
310
+
311
+ ```javascript
312
+ const score = store.get('score');
313
+ ```
314
+
315
+ **Throws:** Error if key not initialized (strict mode)
316
+
317
+ #### `getAll()`
318
+
319
+ Get all data as a plain object snapshot (not reactive).
320
+
321
+ ```javascript
322
+ const snapshot = store.getAll();
323
+ ```
324
+
325
+ #### `update(updates)`
326
+
327
+ Update multiple properties at once.
328
+
329
+ ```javascript
330
+ store.update({
331
+ score: 100,
332
+ level: 5,
333
+ lives: 2
334
+ });
335
+ ```
336
+
337
+ #### `has(key)`
338
+
339
+ Check if a key exists.
340
+
341
+ ```javascript
342
+ if (store.has('score')) {
343
+ // Key exists
344
+ }
345
+ ```
346
+
347
+ #### `delete(key)`
348
+
349
+ Delete a property.
350
+
351
+ ```javascript
352
+ const deleted = store.delete('temporaryFlag');
353
+ ```
354
+
355
+ **Returns:** `true` if key existed and was deleted
356
+
357
+ #### `clear()`
358
+
359
+ Clear all data properties.
360
+
361
+ ```javascript
362
+ store.clear();
363
+ ```
364
+
365
+ #### `size` (getter)
366
+
367
+ Get the number of data entries.
368
+
369
+ ```javascript
370
+ console.log(`Store has ${store.size} entries`);
371
+ ```
372
+
373
+ ## Common Patterns
374
+
375
+ ### Persisting to Server
376
+
377
+ ```javascript
378
+ const store = new ReactiveDataStore({
379
+ initialData: {
380
+ score: 0,
381
+ level: 1,
382
+ preferences: {}
383
+ }
384
+ });
385
+
386
+ // Save snapshot to server
387
+ async function saveProgress() {
388
+ const data = store.getAll();
389
+ await fetch('/api/save', {
390
+ method: 'POST',
391
+ body: JSON.stringify(data)
392
+ });
393
+ }
394
+
395
+ // Load from server
396
+ async function loadProgress() {
397
+ const response = await fetch('/api/load');
398
+ const data = await response.json();
399
+ store.update(data);
400
+ }
401
+ ```
402
+
403
+ ### Computed Values
404
+
405
+ ```javascript
406
+ const store = new ReactiveDataStore({
407
+ initialData: {
408
+ score: 0,
409
+ multiplier: 1
410
+ }
411
+ });
412
+
413
+ // Computed value based on store data
414
+ const totalScore = $derived(
415
+ store.get('score') * store.get('multiplier')
416
+ );
417
+ ```
418
+
419
+ ### Conditional Effects
420
+
421
+ ```javascript
422
+ const store = new ReactiveDataStore({
423
+ initialData: {
424
+ level: 1,
425
+ lives: 3
426
+ }
427
+ });
428
+
429
+ // Effect only runs when level changes and lives > 0
430
+ $effect(() => {
431
+ const level = store.get('level');
432
+ const lives = store.get('lives');
433
+
434
+ if (lives > 0) {
435
+ console.log(`Playing level ${level}`);
436
+ }
437
+ });
438
+ ```
439
+
440
+ ## See Also
441
+
442
+ - [PageMachine](../../machines/page-machine/README.md) - Uses ReactiveDataStore
443
+ for route-aware data management
444
+ - [SubscribersCount](../subscribers-count/SubscribersCount.js) - Another state
445
+ utility class
@@ -0,0 +1,152 @@
1
+ export default class ReactiveDataStore {
2
+ /**
3
+ * Constructor
4
+ *
5
+ * @param {Object} [options]
6
+ * @param {Record<string, any>} [options.initialData={}]
7
+ * Initial key-value pairs
8
+ * @param {boolean} [options.strictMode=true]
9
+ * Throw error when accessing uninitialized keys
10
+ * @param {boolean} [options.productionGuard=false]
11
+ * Dev-only mode: no-op on SET, throw on GET in production
12
+ * @param {string} [options.errorPrefix='Data key']
13
+ * Prefix for error messages
14
+ */
15
+ constructor({ initialData, strictMode, productionGuard, errorPrefix }?: {
16
+ initialData?: Record<string, any> | undefined;
17
+ strictMode?: boolean | undefined;
18
+ productionGuard?: boolean | undefined;
19
+ errorPrefix?: string | undefined;
20
+ });
21
+ /**
22
+ * Set a data property value
23
+ *
24
+ * Automatically reactive - effects watching this key will re-run.
25
+ * Uses fine-grained reactivity via SvelteMap.
26
+ *
27
+ * With productionGuard: silent no-op in production (safe to call)
28
+ *
29
+ * @param {string} key - Property key
30
+ * @param {any} value - Property value
31
+ *
32
+ * @example
33
+ * ```javascript
34
+ * store.set('score', 100);
35
+ * store.set('playerName', 'Alice');
36
+ * ```
37
+ */
38
+ set(key: string, value: any): void;
39
+ /**
40
+ * Get a data property value
41
+ *
42
+ * Automatically reactive - creates a dependency on this specific key.
43
+ * The effect will only re-run when THIS key changes.
44
+ *
45
+ * With strictMode: throws if key is not initialized
46
+ * With productionGuard: throws in production (programming error)
47
+ *
48
+ * @param {string} key - Property key
49
+ *
50
+ * @returns {any} Property value
51
+ *
52
+ * @throws {Error} If key not initialized (strictMode)
53
+ * @throws {Error} If accessed in production (productionGuard)
54
+ *
55
+ * @example
56
+ * ```javascript
57
+ * // Reactive - re-runs only when 'score' changes
58
+ * $effect(() => {
59
+ * const score = store.get('score');
60
+ * console.log('Score:', score);
61
+ * });
62
+ * ```
63
+ */
64
+ get(key: string): any;
65
+ /**
66
+ * Get all data properties as plain object
67
+ *
68
+ * Note: Returns a snapshot (plain object), not reactive.
69
+ * Use for serialization or inspection, not for reactive tracking.
70
+ *
71
+ * @returns {Record<string, any>} Plain object with all data
72
+ *
73
+ * @example
74
+ * ```javascript
75
+ * const allData = store.getAll();
76
+ * await saveToServer(allData);
77
+ * ```
78
+ */
79
+ getAll(): Record<string, any>;
80
+ /**
81
+ * Update multiple data properties at once
82
+ *
83
+ * Each property update triggers fine-grained reactivity.
84
+ * Only effects watching the specific changed keys will re-run.
85
+ *
86
+ * @param {Record<string, any>} updates
87
+ * Object with key-value pairs to update
88
+ *
89
+ * @example
90
+ * ```javascript
91
+ * store.update({
92
+ * score: 100,
93
+ * level: 5,
94
+ * completed: true
95
+ * });
96
+ * ```
97
+ */
98
+ update(updates: Record<string, any>): void;
99
+ /**
100
+ * Delete a data property
101
+ *
102
+ * @param {string} key - Property key to delete
103
+ *
104
+ * @returns {boolean} True if the key existed and was deleted
105
+ *
106
+ * @example
107
+ * ```javascript
108
+ * store.delete('temporaryFlag');
109
+ * ```
110
+ */
111
+ delete(key: string): boolean;
112
+ /**
113
+ * Check if data property exists
114
+ *
115
+ * With productionGuard: throws in production (same as get)
116
+ *
117
+ * @param {string} key - Property key to check
118
+ *
119
+ * @returns {boolean} True if the key exists
120
+ *
121
+ * @throws {Error} If accessed in production (productionGuard)
122
+ *
123
+ * @example
124
+ * ```javascript
125
+ * if (store.has('tutorialSeen')) {
126
+ * // Skip tutorial
127
+ * }
128
+ * ```
129
+ */
130
+ has(key: string): boolean;
131
+ /**
132
+ * Clear all data properties
133
+ *
134
+ * @example
135
+ * ```javascript
136
+ * store.clear(); // Reset all data
137
+ * ```
138
+ */
139
+ clear(): void;
140
+ /**
141
+ * Get number of data properties
142
+ *
143
+ * @returns {number} Number of data entries
144
+ *
145
+ * @example
146
+ * ```javascript
147
+ * console.log(`Store has ${store.size} entries`);
148
+ * ```
149
+ */
150
+ get size(): number;
151
+ #private;
152
+ }