@hkdigital/lib-core 0.5.88 → 0.5.90
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/dist/state/classes/reactive-data-store/ReactiveDataStore.svelte.d.ts +152 -0
- package/dist/state/classes/reactive-data-store/ReactiveDataStore.svelte.js +300 -0
- package/dist/state/classes.d.ts +1 -0
- package/dist/state/classes.js +1 -0
- package/dist/state/machines/page-machine/PageMachine.svelte.d.ts +45 -232
- package/dist/state/machines/page-machine/PageMachine.svelte.js +61 -308
- package/dist/state/machines/page-machine/README.md +8 -0
- package/package.json +1 -1
- package/dist/state/classes/subscribers-count/index.d.ts +0 -1
- package/dist/state/classes/subscribers-count/index.js +0 -1
|
@@ -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
|
+
}
|
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reactive key-value data store with fine-grained reactivity
|
|
3
|
+
*
|
|
4
|
+
* Built on SvelteMap for fine-grained reactivity where effects only re-run
|
|
5
|
+
* when the specific keys they access change.
|
|
6
|
+
*
|
|
7
|
+
* Features:
|
|
8
|
+
* - Fine-grained reactivity using SvelteMap
|
|
9
|
+
* - Strict mode: throws on access to uninitialized keys
|
|
10
|
+
* - Production guard: dev-only data throws on read in production
|
|
11
|
+
* - Initialization from plain object
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```javascript
|
|
15
|
+
* // Regular data store
|
|
16
|
+
* const store = new ReactiveDataStore({
|
|
17
|
+
* initialData: { score: 0, level: 1 }
|
|
18
|
+
* });
|
|
19
|
+
*
|
|
20
|
+
* store.set('score', 100);
|
|
21
|
+
* const score = store.get('score'); // Reactive access
|
|
22
|
+
*
|
|
23
|
+
* // Dev-only data store
|
|
24
|
+
* const devStore = new ReactiveDataStore({
|
|
25
|
+
* initialData: { autoNav: false },
|
|
26
|
+
* productionGuard: true,
|
|
27
|
+
* errorPrefix: 'Dev data key'
|
|
28
|
+
* });
|
|
29
|
+
*
|
|
30
|
+
* devStore.set('autoNav', true); // No-op in production
|
|
31
|
+
* devStore.get('autoNav'); // Throws in production
|
|
32
|
+
* ```
|
|
33
|
+
*/
|
|
34
|
+
|
|
35
|
+
import { SvelteMap } from 'svelte/reactivity';
|
|
36
|
+
import { dev } from '$app/environment';
|
|
37
|
+
|
|
38
|
+
export default class ReactiveDataStore {
|
|
39
|
+
/**
|
|
40
|
+
* Internal reactive map
|
|
41
|
+
* @type {SvelteMap<string, any>}
|
|
42
|
+
*/
|
|
43
|
+
#data;
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Throw on access to uninitialized keys
|
|
47
|
+
* @type {boolean}
|
|
48
|
+
*/
|
|
49
|
+
#strictMode;
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Guard against production access (for dev-only data)
|
|
53
|
+
* @type {boolean}
|
|
54
|
+
*/
|
|
55
|
+
#productionGuard;
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Prefix for error messages
|
|
59
|
+
* @type {string}
|
|
60
|
+
*/
|
|
61
|
+
#errorPrefix;
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Constructor
|
|
65
|
+
*
|
|
66
|
+
* @param {Object} [options]
|
|
67
|
+
* @param {Record<string, any>} [options.initialData={}]
|
|
68
|
+
* Initial key-value pairs
|
|
69
|
+
* @param {boolean} [options.strictMode=true]
|
|
70
|
+
* Throw error when accessing uninitialized keys
|
|
71
|
+
* @param {boolean} [options.productionGuard=false]
|
|
72
|
+
* Dev-only mode: no-op on SET, throw on GET in production
|
|
73
|
+
* @param {string} [options.errorPrefix='Data key']
|
|
74
|
+
* Prefix for error messages
|
|
75
|
+
*/
|
|
76
|
+
constructor({
|
|
77
|
+
initialData = {},
|
|
78
|
+
strictMode = true,
|
|
79
|
+
productionGuard = false,
|
|
80
|
+
errorPrefix = 'Data key'
|
|
81
|
+
} = {}) {
|
|
82
|
+
this.#strictMode = strictMode;
|
|
83
|
+
this.#productionGuard = productionGuard;
|
|
84
|
+
this.#errorPrefix = errorPrefix;
|
|
85
|
+
|
|
86
|
+
// Initialize map
|
|
87
|
+
this.#data = new SvelteMap();
|
|
88
|
+
|
|
89
|
+
// Only populate initial data in dev mode if guard is enabled
|
|
90
|
+
if (!productionGuard || dev) {
|
|
91
|
+
for (const [key, value] of Object.entries(initialData)) {
|
|
92
|
+
this.#data.set(key, value);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Set a data property value
|
|
99
|
+
*
|
|
100
|
+
* Automatically reactive - effects watching this key will re-run.
|
|
101
|
+
* Uses fine-grained reactivity via SvelteMap.
|
|
102
|
+
*
|
|
103
|
+
* With productionGuard: silent no-op in production (safe to call)
|
|
104
|
+
*
|
|
105
|
+
* @param {string} key - Property key
|
|
106
|
+
* @param {any} value - Property value
|
|
107
|
+
*
|
|
108
|
+
* @example
|
|
109
|
+
* ```javascript
|
|
110
|
+
* store.set('score', 100);
|
|
111
|
+
* store.set('playerName', 'Alice');
|
|
112
|
+
* ```
|
|
113
|
+
*/
|
|
114
|
+
set(key, value) {
|
|
115
|
+
// Production guard: no-op silently (safe to call conditionally)
|
|
116
|
+
if (this.#productionGuard && !dev) {
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
this.#data.set(key, value);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Get a data property value
|
|
125
|
+
*
|
|
126
|
+
* Automatically reactive - creates a dependency on this specific key.
|
|
127
|
+
* The effect will only re-run when THIS key changes.
|
|
128
|
+
*
|
|
129
|
+
* With strictMode: throws if key is not initialized
|
|
130
|
+
* With productionGuard: throws in production (programming error)
|
|
131
|
+
*
|
|
132
|
+
* @param {string} key - Property key
|
|
133
|
+
*
|
|
134
|
+
* @returns {any} Property value
|
|
135
|
+
*
|
|
136
|
+
* @throws {Error} If key not initialized (strictMode)
|
|
137
|
+
* @throws {Error} If accessed in production (productionGuard)
|
|
138
|
+
*
|
|
139
|
+
* @example
|
|
140
|
+
* ```javascript
|
|
141
|
+
* // Reactive - re-runs only when 'score' changes
|
|
142
|
+
* $effect(() => {
|
|
143
|
+
* const score = store.get('score');
|
|
144
|
+
* console.log('Score:', score);
|
|
145
|
+
* });
|
|
146
|
+
* ```
|
|
147
|
+
*/
|
|
148
|
+
get(key) {
|
|
149
|
+
// Production guard: THROW on read in production
|
|
150
|
+
if (this.#productionGuard && !dev) {
|
|
151
|
+
throw new Error(
|
|
152
|
+
`${this.#errorPrefix} "${key}" accessed in production. ` +
|
|
153
|
+
`This data is only available in development mode.`
|
|
154
|
+
);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// Strict mode: validate key exists
|
|
158
|
+
if (this.#strictMode && !this.#data.has(key)) {
|
|
159
|
+
throw new Error(
|
|
160
|
+
`${this.#errorPrefix} "${key}" is not initialized.`
|
|
161
|
+
);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
return this.#data.get(key);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Get all data properties as plain object
|
|
169
|
+
*
|
|
170
|
+
* Note: Returns a snapshot (plain object), not reactive.
|
|
171
|
+
* Use for serialization or inspection, not for reactive tracking.
|
|
172
|
+
*
|
|
173
|
+
* @returns {Record<string, any>} Plain object with all data
|
|
174
|
+
*
|
|
175
|
+
* @example
|
|
176
|
+
* ```javascript
|
|
177
|
+
* const allData = store.getAll();
|
|
178
|
+
* await saveToServer(allData);
|
|
179
|
+
* ```
|
|
180
|
+
*/
|
|
181
|
+
getAll() {
|
|
182
|
+
if (this.#productionGuard && !dev) {
|
|
183
|
+
return {};
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
return Object.fromEntries(this.#data);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Update multiple data properties at once
|
|
191
|
+
*
|
|
192
|
+
* Each property update triggers fine-grained reactivity.
|
|
193
|
+
* Only effects watching the specific changed keys will re-run.
|
|
194
|
+
*
|
|
195
|
+
* @param {Record<string, any>} updates
|
|
196
|
+
* Object with key-value pairs to update
|
|
197
|
+
*
|
|
198
|
+
* @example
|
|
199
|
+
* ```javascript
|
|
200
|
+
* store.update({
|
|
201
|
+
* score: 100,
|
|
202
|
+
* level: 5,
|
|
203
|
+
* completed: true
|
|
204
|
+
* });
|
|
205
|
+
* ```
|
|
206
|
+
*/
|
|
207
|
+
update(updates) {
|
|
208
|
+
if (this.#productionGuard && !dev) {
|
|
209
|
+
return;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
for (const [key, value] of Object.entries(updates)) {
|
|
213
|
+
this.#data.set(key, value);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Delete a data property
|
|
219
|
+
*
|
|
220
|
+
* @param {string} key - Property key to delete
|
|
221
|
+
*
|
|
222
|
+
* @returns {boolean} True if the key existed and was deleted
|
|
223
|
+
*
|
|
224
|
+
* @example
|
|
225
|
+
* ```javascript
|
|
226
|
+
* store.delete('temporaryFlag');
|
|
227
|
+
* ```
|
|
228
|
+
*/
|
|
229
|
+
delete(key) {
|
|
230
|
+
if (this.#productionGuard && !dev) {
|
|
231
|
+
return false;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
return this.#data.delete(key);
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* Check if data property exists
|
|
239
|
+
*
|
|
240
|
+
* With productionGuard: throws in production (same as get)
|
|
241
|
+
*
|
|
242
|
+
* @param {string} key - Property key to check
|
|
243
|
+
*
|
|
244
|
+
* @returns {boolean} True if the key exists
|
|
245
|
+
*
|
|
246
|
+
* @throws {Error} If accessed in production (productionGuard)
|
|
247
|
+
*
|
|
248
|
+
* @example
|
|
249
|
+
* ```javascript
|
|
250
|
+
* if (store.has('tutorialSeen')) {
|
|
251
|
+
* // Skip tutorial
|
|
252
|
+
* }
|
|
253
|
+
* ```
|
|
254
|
+
*/
|
|
255
|
+
has(key) {
|
|
256
|
+
// Production guard: THROW on read in production
|
|
257
|
+
if (this.#productionGuard && !dev) {
|
|
258
|
+
throw new Error(
|
|
259
|
+
`${this.#errorPrefix} "${key}" existence check in production. ` +
|
|
260
|
+
`This data is only available in development mode.`
|
|
261
|
+
);
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
return this.#data.has(key);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* Clear all data properties
|
|
269
|
+
*
|
|
270
|
+
* @example
|
|
271
|
+
* ```javascript
|
|
272
|
+
* store.clear(); // Reset all data
|
|
273
|
+
* ```
|
|
274
|
+
*/
|
|
275
|
+
clear() {
|
|
276
|
+
if (this.#productionGuard && !dev) {
|
|
277
|
+
return;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
this.#data.clear();
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
/**
|
|
284
|
+
* Get number of data properties
|
|
285
|
+
*
|
|
286
|
+
* @returns {number} Number of data entries
|
|
287
|
+
*
|
|
288
|
+
* @example
|
|
289
|
+
* ```javascript
|
|
290
|
+
* console.log(`Store has ${store.size} entries`);
|
|
291
|
+
* ```
|
|
292
|
+
*/
|
|
293
|
+
get size() {
|
|
294
|
+
if (this.#productionGuard && !dev) {
|
|
295
|
+
return 0;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
return this.#data.size;
|
|
299
|
+
}
|
|
300
|
+
}
|
package/dist/state/classes.d.ts
CHANGED
package/dist/state/classes.js
CHANGED
|
@@ -9,6 +9,8 @@ export default class PageMachine {
|
|
|
9
9
|
* Optional list of valid routes (for validation/dev tools)
|
|
10
10
|
* @param {Record<string, any>} [config.initialData={}]
|
|
11
11
|
* Initial data properties (use KEY_ constants for keys)
|
|
12
|
+
* @param {Record<string, any>} [config.initialDevData={}]
|
|
13
|
+
* Initial dev data properties (use KEY_DEV_ constants for keys)
|
|
12
14
|
* @param {import('../../../logging/client.js').Logger} [config.logger]
|
|
13
15
|
* Logger instance (optional)
|
|
14
16
|
*
|
|
@@ -17,6 +19,7 @@ export default class PageMachine {
|
|
|
17
19
|
* const ROUTE_INTRO = '/intro/start';
|
|
18
20
|
* const KEY_INTRO_COMPLETED = 'intro-completed';
|
|
19
21
|
* const KEY_SCORE = 'score';
|
|
22
|
+
* const KEY_DEV_AUTO_NAVIGATION = 'dev-auto-navigation';
|
|
20
23
|
*
|
|
21
24
|
* const machine = new PageMachine({
|
|
22
25
|
* startPath: ROUTE_INTRO,
|
|
@@ -24,14 +27,18 @@ export default class PageMachine {
|
|
|
24
27
|
* initialData: {
|
|
25
28
|
* [KEY_INTRO_COMPLETED]: false,
|
|
26
29
|
* [KEY_SCORE]: 0
|
|
30
|
+
* },
|
|
31
|
+
* initialDevData: {
|
|
32
|
+
* [KEY_DEV_AUTO_NAVIGATION]: false
|
|
27
33
|
* }
|
|
28
34
|
* });
|
|
29
35
|
* ```
|
|
30
36
|
*/
|
|
31
|
-
constructor({ startPath, routes, initialData, logger }: {
|
|
37
|
+
constructor({ startPath, routes, initialData, initialDevData, logger }: {
|
|
32
38
|
startPath: string;
|
|
33
39
|
routes?: string[] | undefined;
|
|
34
40
|
initialData?: Record<string, any> | undefined;
|
|
41
|
+
initialDevData?: Record<string, any> | undefined;
|
|
35
42
|
logger?: import("../../../logging/client.js").Logger | undefined;
|
|
36
43
|
});
|
|
37
44
|
/**
|
|
@@ -63,264 +70,69 @@ export default class PageMachine {
|
|
|
63
70
|
*/
|
|
64
71
|
get routes(): string[];
|
|
65
72
|
/**
|
|
66
|
-
*
|
|
73
|
+
* Get the reactive data store
|
|
67
74
|
*
|
|
68
|
-
*
|
|
69
|
-
*
|
|
70
|
-
* key will be triggered.
|
|
75
|
+
* Provides read-only access to the data store instance.
|
|
76
|
+
* Access all data methods through this property.
|
|
71
77
|
*
|
|
72
|
-
* @
|
|
73
|
-
* @param {any} value - Property value
|
|
74
|
-
*
|
|
75
|
-
* @example
|
|
76
|
-
* ```javascript
|
|
77
|
-
* const KEY_HAS_STRONG_PROFILE = 'has-strong-profile';
|
|
78
|
-
* const KEY_PROFILE_SCORE = 'profile-score';
|
|
79
|
-
*
|
|
80
|
-
* machine.setData(KEY_HAS_STRONG_PROFILE, true);
|
|
81
|
-
* machine.setData(KEY_PROFILE_SCORE, 85);
|
|
82
|
-
* ```
|
|
83
|
-
*/
|
|
84
|
-
setData(key: string, value: any): void;
|
|
85
|
-
/**
|
|
86
|
-
* Get a data property value
|
|
87
|
-
*
|
|
88
|
-
* Automatically reactive - creates a dependency on this specific key.
|
|
89
|
-
* The effect will only re-run when THIS key changes, not when other
|
|
90
|
-
* keys change.
|
|
91
|
-
*
|
|
92
|
-
* @param {string} key - Property key (use KEY_ constant)
|
|
93
|
-
*
|
|
94
|
-
* @returns {any} Property value or undefined
|
|
78
|
+
* @returns {ReactiveDataStore} The data store
|
|
95
79
|
*
|
|
96
80
|
* @example
|
|
97
81
|
* ```javascript
|
|
98
82
|
* const KEY_SCORE = 'score';
|
|
99
83
|
*
|
|
100
|
-
* //
|
|
84
|
+
* // Set data
|
|
85
|
+
* machine.data.set(KEY_SCORE, 100);
|
|
86
|
+
*
|
|
87
|
+
* // Get data (reactive)
|
|
101
88
|
* $effect(() => {
|
|
102
|
-
* const score = machine.
|
|
89
|
+
* const score = machine.data.get(KEY_SCORE);
|
|
103
90
|
* console.log('Score:', score);
|
|
104
91
|
* });
|
|
105
|
-
* ```
|
|
106
|
-
*/
|
|
107
|
-
getData(key: string): any;
|
|
108
|
-
/**
|
|
109
|
-
* Get all data properties as plain object
|
|
110
92
|
*
|
|
111
|
-
*
|
|
112
|
-
*
|
|
113
|
-
*
|
|
114
|
-
*
|
|
115
|
-
*
|
|
116
|
-
*
|
|
117
|
-
* ```javascript
|
|
118
|
-
* const allData = machine.getAllData();
|
|
119
|
-
* await playerService.saveData(allData);
|
|
93
|
+
* // Other operations
|
|
94
|
+
* machine.data.update({ [KEY_SCORE]: 200 });
|
|
95
|
+
* machine.data.has(KEY_SCORE);
|
|
96
|
+
* machine.data.delete(KEY_SCORE);
|
|
97
|
+
* machine.data.clear();
|
|
98
|
+
* console.log(machine.data.size);
|
|
120
99
|
* ```
|
|
121
100
|
*/
|
|
122
|
-
|
|
101
|
+
get data(): ReactiveDataStore;
|
|
123
102
|
/**
|
|
124
|
-
*
|
|
103
|
+
* Get the reactive dev data store
|
|
125
104
|
*
|
|
126
|
-
*
|
|
105
|
+
* Provides read-only access to the dev data store instance.
|
|
106
|
+
* Access all dev data methods through this property.
|
|
127
107
|
*
|
|
128
|
-
*
|
|
129
|
-
*
|
|
108
|
+
* Dev data is only available in development mode. In production:
|
|
109
|
+
* - SET operations are silent no-ops
|
|
110
|
+
* - GET/HAS operations throw errors (programming errors)
|
|
130
111
|
*
|
|
131
|
-
* @
|
|
132
|
-
* ```javascript
|
|
133
|
-
* const KEY_HAS_STRONG_PROFILE = 'has-strong-profile';
|
|
134
|
-
* const KEY_PROFILE_SCORE = 'profile-score';
|
|
135
|
-
* const KEY_MATCHED_SECTOR = 'matched-sector';
|
|
136
|
-
*
|
|
137
|
-
* machine.updateData({
|
|
138
|
-
* [KEY_HAS_STRONG_PROFILE]: true,
|
|
139
|
-
* [KEY_PROFILE_SCORE]: 85,
|
|
140
|
-
* [KEY_MATCHED_SECTOR]: 'technology'
|
|
141
|
-
* });
|
|
142
|
-
* ```
|
|
143
|
-
*/
|
|
144
|
-
updateData(dataUpdates: Record<string, any>): void;
|
|
145
|
-
/**
|
|
146
|
-
* Delete a data property
|
|
147
|
-
*
|
|
148
|
-
* @param {string} key - Property key to delete (use KEY_ constant)
|
|
149
|
-
*
|
|
150
|
-
* @returns {boolean} True if the key existed and was deleted
|
|
112
|
+
* @returns {ReactiveDataStore} The dev data store
|
|
151
113
|
*
|
|
152
114
|
* @example
|
|
153
115
|
* ```javascript
|
|
154
|
-
* const
|
|
116
|
+
* const KEY_DEV_AUTO_NAV = 'dev-auto-navigation';
|
|
155
117
|
*
|
|
156
|
-
*
|
|
157
|
-
*
|
|
158
|
-
*/
|
|
159
|
-
deleteData(key: string): boolean;
|
|
160
|
-
/**
|
|
161
|
-
* Check if data property exists
|
|
162
|
-
*
|
|
163
|
-
* @param {string} key - Property key to check (use KEY_ constant)
|
|
164
|
-
*
|
|
165
|
-
* @returns {boolean} True if the key exists
|
|
166
|
-
*
|
|
167
|
-
* @example
|
|
168
|
-
* ```javascript
|
|
169
|
-
* const KEY_TUTORIAL_SEEN = 'tutorial-seen';
|
|
170
|
-
*
|
|
171
|
-
* if (machine.hasData(KEY_TUTORIAL_SEEN)) {
|
|
172
|
-
* // Skip tutorial
|
|
173
|
-
* }
|
|
174
|
-
* ```
|
|
175
|
-
*/
|
|
176
|
-
hasData(key: string): boolean;
|
|
177
|
-
/**
|
|
178
|
-
* Clear all data properties
|
|
179
|
-
*
|
|
180
|
-
* @example
|
|
181
|
-
* ```javascript
|
|
182
|
-
* machine.clearData(); // Reset all game data
|
|
183
|
-
* ```
|
|
184
|
-
*/
|
|
185
|
-
clearData(): void;
|
|
186
|
-
/**
|
|
187
|
-
* Get number of data properties
|
|
188
|
-
*
|
|
189
|
-
* @returns {number} Number of data entries
|
|
190
|
-
*/
|
|
191
|
-
get dataSize(): number;
|
|
192
|
-
/**
|
|
193
|
-
* Set a dev data property value
|
|
194
|
-
*
|
|
195
|
-
* Automatically reactive - effects watching this key will re-run.
|
|
196
|
-
* Only available in dev mode - no-op in production.
|
|
197
|
-
*
|
|
198
|
-
* @param {string} key - Property key (use KEY_DEV_ constant)
|
|
199
|
-
* @param {any} value - Property value
|
|
200
|
-
*
|
|
201
|
-
* @example
|
|
202
|
-
* ```javascript
|
|
203
|
-
* const KEY_DEV_AUTO_NAVIGATION = 'dev-auto-navigation';
|
|
204
|
-
* const KEY_DEV_SKIP_ANIMATIONS = 'dev-skip-animations';
|
|
205
|
-
*
|
|
206
|
-
* machine.setDevData(KEY_DEV_AUTO_NAVIGATION, true);
|
|
207
|
-
* machine.setDevData(KEY_DEV_SKIP_ANIMATIONS, false);
|
|
208
|
-
* ```
|
|
209
|
-
*/
|
|
210
|
-
setDevData(key: string, value: any): void;
|
|
211
|
-
/**
|
|
212
|
-
* Get a dev data property value
|
|
118
|
+
* // Set dev data (no-op in production)
|
|
119
|
+
* machine.devData.set(KEY_DEV_AUTO_NAV, true);
|
|
213
120
|
*
|
|
214
|
-
*
|
|
215
|
-
* Only available in dev mode - returns undefined in production.
|
|
216
|
-
*
|
|
217
|
-
* @param {string} key - Property key (use KEY_DEV_ constant)
|
|
218
|
-
*
|
|
219
|
-
* @returns {any} Property value or undefined
|
|
220
|
-
*
|
|
221
|
-
* @example
|
|
222
|
-
* ```javascript
|
|
223
|
-
* const KEY_DEV_AUTO_NAVIGATION = 'dev-auto-navigation';
|
|
224
|
-
*
|
|
225
|
-
* // Reactive - re-runs only when KEY_DEV_AUTO_NAVIGATION changes
|
|
121
|
+
* // Get dev data (throws in production)
|
|
226
122
|
* $effect(() => {
|
|
227
|
-
* const autoNav = machine.
|
|
228
|
-
* console.log('Auto-
|
|
123
|
+
* const autoNav = machine.devData.get(KEY_DEV_AUTO_NAV);
|
|
124
|
+
* console.log('Auto-nav:', autoNav);
|
|
229
125
|
* });
|
|
230
|
-
* ```
|
|
231
|
-
*/
|
|
232
|
-
getDevData(key: string): any;
|
|
233
|
-
/**
|
|
234
|
-
* Get all dev data properties as plain object
|
|
235
|
-
*
|
|
236
|
-
* Note: Returns a snapshot (plain object), not reactive.
|
|
237
|
-
* Only available in dev mode - returns empty object in production.
|
|
238
|
-
*
|
|
239
|
-
* @returns {Record<string, any>} Plain object with all dev data
|
|
240
|
-
*
|
|
241
|
-
* @example
|
|
242
|
-
* ```javascript
|
|
243
|
-
* const allDevData = machine.getAllDevData();
|
|
244
|
-
* console.log('Dev settings:', allDevData);
|
|
245
|
-
* ```
|
|
246
|
-
*/
|
|
247
|
-
getAllDevData(): Record<string, any>;
|
|
248
|
-
/**
|
|
249
|
-
* Update multiple dev data properties at once
|
|
250
|
-
*
|
|
251
|
-
* Each property update triggers fine-grained reactivity.
|
|
252
|
-
* Only available in dev mode - no-op in production.
|
|
253
|
-
*
|
|
254
|
-
* @param {Record<string, any>} dataUpdates
|
|
255
|
-
* Object with key-value pairs (use KEY_DEV_ constants for keys)
|
|
256
|
-
*
|
|
257
|
-
* @example
|
|
258
|
-
* ```javascript
|
|
259
|
-
* const KEY_DEV_AUTO_NAVIGATION = 'dev-auto-navigation';
|
|
260
|
-
* const KEY_DEV_SKIP_ANIMATIONS = 'dev-skip-animations';
|
|
261
|
-
*
|
|
262
|
-
* machine.updateDevData({
|
|
263
|
-
* [KEY_DEV_AUTO_NAVIGATION]: true,
|
|
264
|
-
* [KEY_DEV_SKIP_ANIMATIONS]: false
|
|
265
|
-
* });
|
|
266
|
-
* ```
|
|
267
|
-
*/
|
|
268
|
-
updateDevData(dataUpdates: Record<string, any>): void;
|
|
269
|
-
/**
|
|
270
|
-
* Delete a dev data property
|
|
271
126
|
*
|
|
272
|
-
*
|
|
273
|
-
*
|
|
274
|
-
*
|
|
275
|
-
*
|
|
276
|
-
*
|
|
277
|
-
*
|
|
278
|
-
* @example
|
|
279
|
-
* ```javascript
|
|
280
|
-
* const KEY_DEV_TEMP_FLAG = 'dev-temp-flag';
|
|
281
|
-
*
|
|
282
|
-
* machine.deleteDevData(KEY_DEV_TEMP_FLAG);
|
|
127
|
+
* // Other operations
|
|
128
|
+
* machine.devData.update({ [KEY_DEV_AUTO_NAV]: false });
|
|
129
|
+
* machine.devData.has(KEY_DEV_AUTO_NAV);
|
|
130
|
+
* machine.devData.delete(KEY_DEV_AUTO_NAV);
|
|
131
|
+
* machine.devData.clear();
|
|
132
|
+
* console.log(machine.devData.size);
|
|
283
133
|
* ```
|
|
284
134
|
*/
|
|
285
|
-
|
|
286
|
-
/**
|
|
287
|
-
* Check if dev data property exists
|
|
288
|
-
*
|
|
289
|
-
* Only available in dev mode - returns false in production.
|
|
290
|
-
*
|
|
291
|
-
* @param {string} key - Property key to check (use KEY_DEV_ constant)
|
|
292
|
-
*
|
|
293
|
-
* @returns {boolean} True if the key exists
|
|
294
|
-
*
|
|
295
|
-
* @example
|
|
296
|
-
* ```javascript
|
|
297
|
-
* const KEY_DEV_AUTO_NAVIGATION = 'dev-auto-navigation';
|
|
298
|
-
*
|
|
299
|
-
* if (machine.hasDevData(KEY_DEV_AUTO_NAVIGATION)) {
|
|
300
|
-
* // Dev setting exists
|
|
301
|
-
* }
|
|
302
|
-
* ```
|
|
303
|
-
*/
|
|
304
|
-
hasDevData(key: string): boolean;
|
|
305
|
-
/**
|
|
306
|
-
* Clear all dev data properties
|
|
307
|
-
*
|
|
308
|
-
* Only available in dev mode - no-op in production.
|
|
309
|
-
*
|
|
310
|
-
* @example
|
|
311
|
-
* ```javascript
|
|
312
|
-
* machine.clearDevData(); // Reset all dev settings
|
|
313
|
-
* ```
|
|
314
|
-
*/
|
|
315
|
-
clearDevData(): void;
|
|
316
|
-
/**
|
|
317
|
-
* Get number of dev data properties
|
|
318
|
-
*
|
|
319
|
-
* Only available in dev mode - returns 0 in production.
|
|
320
|
-
*
|
|
321
|
-
* @returns {number} Number of dev data entries
|
|
322
|
-
*/
|
|
323
|
-
get devDataSize(): number;
|
|
135
|
+
get devData(): ReactiveDataStore;
|
|
324
136
|
/**
|
|
325
137
|
* Check if a route has been visited
|
|
326
138
|
*
|
|
@@ -425,3 +237,4 @@ export default class PageMachine {
|
|
|
425
237
|
redirectToStartPath(): void;
|
|
426
238
|
#private;
|
|
427
239
|
}
|
|
240
|
+
import { ReactiveDataStore } from '../../classes.js';
|
|
@@ -69,8 +69,8 @@
|
|
|
69
69
|
* });
|
|
70
70
|
* ```
|
|
71
71
|
*/
|
|
72
|
-
import {
|
|
73
|
-
import {
|
|
72
|
+
import { SvelteSet } from 'svelte/reactivity';
|
|
73
|
+
import { ReactiveDataStore } from '../../classes.js';
|
|
74
74
|
|
|
75
75
|
export default class PageMachine {
|
|
76
76
|
/**
|
|
@@ -99,17 +99,14 @@ export default class PageMachine {
|
|
|
99
99
|
#routes = [];
|
|
100
100
|
|
|
101
101
|
/**
|
|
102
|
-
* Reactive
|
|
103
|
-
*
|
|
104
|
-
* @type {SvelteMap<string, any>}
|
|
102
|
+
* Reactive data store for business/domain data
|
|
103
|
+
* @type {ReactiveDataStore}
|
|
105
104
|
*/
|
|
106
105
|
#data;
|
|
107
106
|
|
|
108
107
|
/**
|
|
109
|
-
* Reactive
|
|
110
|
-
*
|
|
111
|
-
* Only available in dev mode
|
|
112
|
-
* @type {SvelteMap<string, any>|null}
|
|
108
|
+
* Reactive data store for dev-mode helper data
|
|
109
|
+
* @type {ReactiveDataStore}
|
|
113
110
|
*/
|
|
114
111
|
#devData;
|
|
115
112
|
|
|
@@ -130,6 +127,8 @@ export default class PageMachine {
|
|
|
130
127
|
* Optional list of valid routes (for validation/dev tools)
|
|
131
128
|
* @param {Record<string, any>} [config.initialData={}]
|
|
132
129
|
* Initial data properties (use KEY_ constants for keys)
|
|
130
|
+
* @param {Record<string, any>} [config.initialDevData={}]
|
|
131
|
+
* Initial dev data properties (use KEY_DEV_ constants for keys)
|
|
133
132
|
* @param {import('../../../logging/client.js').Logger} [config.logger]
|
|
134
133
|
* Logger instance (optional)
|
|
135
134
|
*
|
|
@@ -138,6 +137,7 @@ export default class PageMachine {
|
|
|
138
137
|
* const ROUTE_INTRO = '/intro/start';
|
|
139
138
|
* const KEY_INTRO_COMPLETED = 'intro-completed';
|
|
140
139
|
* const KEY_SCORE = 'score';
|
|
140
|
+
* const KEY_DEV_AUTO_NAVIGATION = 'dev-auto-navigation';
|
|
141
141
|
*
|
|
142
142
|
* const machine = new PageMachine({
|
|
143
143
|
* startPath: ROUTE_INTRO,
|
|
@@ -145,11 +145,14 @@ export default class PageMachine {
|
|
|
145
145
|
* initialData: {
|
|
146
146
|
* [KEY_INTRO_COMPLETED]: false,
|
|
147
147
|
* [KEY_SCORE]: 0
|
|
148
|
+
* },
|
|
149
|
+
* initialDevData: {
|
|
150
|
+
* [KEY_DEV_AUTO_NAVIGATION]: false
|
|
148
151
|
* }
|
|
149
152
|
* });
|
|
150
153
|
* ```
|
|
151
154
|
*/
|
|
152
|
-
constructor({ startPath, routes = [], initialData = {}, logger = null }) {
|
|
155
|
+
constructor({ startPath, routes = [], initialData = {}, initialDevData = {}, logger = null }) {
|
|
153
156
|
if (!startPath) {
|
|
154
157
|
throw new Error('PageMachine requires startPath parameter');
|
|
155
158
|
}
|
|
@@ -160,18 +163,17 @@ export default class PageMachine {
|
|
|
160
163
|
this.#current = startPath;
|
|
161
164
|
|
|
162
165
|
// Initialize reactive data structures
|
|
163
|
-
this.#data = new
|
|
164
|
-
|
|
166
|
+
this.#data = new ReactiveDataStore({
|
|
167
|
+
initialData
|
|
168
|
+
});
|
|
165
169
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
+
this.#devData = new ReactiveDataStore({
|
|
171
|
+
initialData: initialDevData,
|
|
172
|
+
productionGuard: true,
|
|
173
|
+
errorPrefix: 'Dev data key'
|
|
174
|
+
});
|
|
170
175
|
|
|
171
|
-
|
|
172
|
-
for (const [key, value] of Object.entries(initialData)) {
|
|
173
|
-
this.#data.set(key, value);
|
|
174
|
-
}
|
|
176
|
+
this.#visitedRoutes = new SvelteSet();
|
|
175
177
|
|
|
176
178
|
// Mark start path as visited
|
|
177
179
|
this.#visitedRoutes.add(startPath);
|
|
@@ -256,324 +258,75 @@ export default class PageMachine {
|
|
|
256
258
|
/* ===== Data Properties (Business/Domain State) ===== */
|
|
257
259
|
|
|
258
260
|
/**
|
|
259
|
-
*
|
|
260
|
-
*
|
|
261
|
-
* Automatically reactive - effects watching this key will re-run.
|
|
262
|
-
* Uses fine-grained reactivity, so only effects watching this specific
|
|
263
|
-
* key will be triggered.
|
|
264
|
-
*
|
|
265
|
-
* @param {string} key - Property key (use KEY_ constant)
|
|
266
|
-
* @param {any} value - Property value
|
|
267
|
-
*
|
|
268
|
-
* @example
|
|
269
|
-
* ```javascript
|
|
270
|
-
* const KEY_HAS_STRONG_PROFILE = 'has-strong-profile';
|
|
271
|
-
* const KEY_PROFILE_SCORE = 'profile-score';
|
|
272
|
-
*
|
|
273
|
-
* machine.setData(KEY_HAS_STRONG_PROFILE, true);
|
|
274
|
-
* machine.setData(KEY_PROFILE_SCORE, 85);
|
|
275
|
-
* ```
|
|
276
|
-
*/
|
|
277
|
-
setData(key, value) {
|
|
278
|
-
this.#data.set(key, value);
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
/**
|
|
282
|
-
* Get a data property value
|
|
261
|
+
* Get the reactive data store
|
|
283
262
|
*
|
|
284
|
-
*
|
|
285
|
-
*
|
|
286
|
-
* keys change.
|
|
263
|
+
* Provides read-only access to the data store instance.
|
|
264
|
+
* Access all data methods through this property.
|
|
287
265
|
*
|
|
288
|
-
* @
|
|
289
|
-
*
|
|
290
|
-
* @returns {any} Property value or undefined
|
|
266
|
+
* @returns {ReactiveDataStore} The data store
|
|
291
267
|
*
|
|
292
268
|
* @example
|
|
293
269
|
* ```javascript
|
|
294
270
|
* const KEY_SCORE = 'score';
|
|
295
271
|
*
|
|
296
|
-
* //
|
|
272
|
+
* // Set data
|
|
273
|
+
* machine.data.set(KEY_SCORE, 100);
|
|
274
|
+
*
|
|
275
|
+
* // Get data (reactive)
|
|
297
276
|
* $effect(() => {
|
|
298
|
-
* const score = machine.
|
|
277
|
+
* const score = machine.data.get(KEY_SCORE);
|
|
299
278
|
* console.log('Score:', score);
|
|
300
279
|
* });
|
|
301
|
-
* ```
|
|
302
|
-
*/
|
|
303
|
-
getData(key) {
|
|
304
|
-
return this.#data.get(key);
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
/**
|
|
308
|
-
* Get all data properties as plain object
|
|
309
|
-
*
|
|
310
|
-
* Note: This returns a snapshot (plain object), not a reactive map.
|
|
311
|
-
* Use this for serialization or server sync, not for reactive tracking.
|
|
312
|
-
*
|
|
313
|
-
* @returns {Record<string, any>} Plain object with all data
|
|
314
|
-
*
|
|
315
|
-
* @example
|
|
316
|
-
* ```javascript
|
|
317
|
-
* const allData = machine.getAllData();
|
|
318
|
-
* await playerService.saveData(allData);
|
|
319
|
-
* ```
|
|
320
|
-
*/
|
|
321
|
-
getAllData() {
|
|
322
|
-
return Object.fromEntries(this.#data);
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
/**
|
|
326
|
-
* Update multiple data properties at once
|
|
327
|
-
*
|
|
328
|
-
* Each property update triggers fine-grained reactivity.
|
|
329
|
-
*
|
|
330
|
-
* @param {Record<string, any>} dataUpdates
|
|
331
|
-
* Object with key-value pairs (use KEY_ constants for keys)
|
|
332
|
-
*
|
|
333
|
-
* @example
|
|
334
|
-
* ```javascript
|
|
335
|
-
* const KEY_HAS_STRONG_PROFILE = 'has-strong-profile';
|
|
336
|
-
* const KEY_PROFILE_SCORE = 'profile-score';
|
|
337
|
-
* const KEY_MATCHED_SECTOR = 'matched-sector';
|
|
338
|
-
*
|
|
339
|
-
* machine.updateData({
|
|
340
|
-
* [KEY_HAS_STRONG_PROFILE]: true,
|
|
341
|
-
* [KEY_PROFILE_SCORE]: 85,
|
|
342
|
-
* [KEY_MATCHED_SECTOR]: 'technology'
|
|
343
|
-
* });
|
|
344
|
-
* ```
|
|
345
|
-
*/
|
|
346
|
-
updateData(dataUpdates) {
|
|
347
|
-
for (const [key, value] of Object.entries(dataUpdates)) {
|
|
348
|
-
this.#data.set(key, value);
|
|
349
|
-
}
|
|
350
|
-
}
|
|
351
|
-
|
|
352
|
-
/**
|
|
353
|
-
* Delete a data property
|
|
354
|
-
*
|
|
355
|
-
* @param {string} key - Property key to delete (use KEY_ constant)
|
|
356
|
-
*
|
|
357
|
-
* @returns {boolean} True if the key existed and was deleted
|
|
358
280
|
*
|
|
359
|
-
*
|
|
360
|
-
*
|
|
361
|
-
*
|
|
362
|
-
*
|
|
363
|
-
* machine.
|
|
281
|
+
* // Other operations
|
|
282
|
+
* machine.data.update({ [KEY_SCORE]: 200 });
|
|
283
|
+
* machine.data.has(KEY_SCORE);
|
|
284
|
+
* machine.data.delete(KEY_SCORE);
|
|
285
|
+
* machine.data.clear();
|
|
286
|
+
* console.log(machine.data.size);
|
|
364
287
|
* ```
|
|
365
288
|
*/
|
|
366
|
-
|
|
367
|
-
return this.#data
|
|
368
|
-
}
|
|
369
|
-
|
|
370
|
-
/**
|
|
371
|
-
* Check if data property exists
|
|
372
|
-
*
|
|
373
|
-
* @param {string} key - Property key to check (use KEY_ constant)
|
|
374
|
-
*
|
|
375
|
-
* @returns {boolean} True if the key exists
|
|
376
|
-
*
|
|
377
|
-
* @example
|
|
378
|
-
* ```javascript
|
|
379
|
-
* const KEY_TUTORIAL_SEEN = 'tutorial-seen';
|
|
380
|
-
*
|
|
381
|
-
* if (machine.hasData(KEY_TUTORIAL_SEEN)) {
|
|
382
|
-
* // Skip tutorial
|
|
383
|
-
* }
|
|
384
|
-
* ```
|
|
385
|
-
*/
|
|
386
|
-
hasData(key) {
|
|
387
|
-
return this.#data.has(key);
|
|
388
|
-
}
|
|
389
|
-
|
|
390
|
-
/**
|
|
391
|
-
* Clear all data properties
|
|
392
|
-
*
|
|
393
|
-
* @example
|
|
394
|
-
* ```javascript
|
|
395
|
-
* machine.clearData(); // Reset all game data
|
|
396
|
-
* ```
|
|
397
|
-
*/
|
|
398
|
-
clearData() {
|
|
399
|
-
this.#data.clear();
|
|
400
|
-
}
|
|
401
|
-
|
|
402
|
-
/**
|
|
403
|
-
* Get number of data properties
|
|
404
|
-
*
|
|
405
|
-
* @returns {number} Number of data entries
|
|
406
|
-
*/
|
|
407
|
-
get dataSize() {
|
|
408
|
-
return this.#data.size;
|
|
289
|
+
get data() {
|
|
290
|
+
return this.#data;
|
|
409
291
|
}
|
|
410
292
|
|
|
411
293
|
/* ===== Dev Data Properties (Dev-Mode Helpers) ===== */
|
|
412
294
|
|
|
413
295
|
/**
|
|
414
|
-
*
|
|
296
|
+
* Get the reactive dev data store
|
|
415
297
|
*
|
|
416
|
-
*
|
|
417
|
-
*
|
|
298
|
+
* Provides read-only access to the dev data store instance.
|
|
299
|
+
* Access all dev data methods through this property.
|
|
418
300
|
*
|
|
419
|
-
*
|
|
420
|
-
*
|
|
301
|
+
* Dev data is only available in development mode. In production:
|
|
302
|
+
* - SET operations are silent no-ops
|
|
303
|
+
* - GET/HAS operations throw errors (programming errors)
|
|
421
304
|
*
|
|
422
|
-
* @
|
|
423
|
-
* ```javascript
|
|
424
|
-
* const KEY_DEV_AUTO_NAVIGATION = 'dev-auto-navigation';
|
|
425
|
-
* const KEY_DEV_SKIP_ANIMATIONS = 'dev-skip-animations';
|
|
426
|
-
*
|
|
427
|
-
* machine.setDevData(KEY_DEV_AUTO_NAVIGATION, true);
|
|
428
|
-
* machine.setDevData(KEY_DEV_SKIP_ANIMATIONS, false);
|
|
429
|
-
* ```
|
|
430
|
-
*/
|
|
431
|
-
setDevData(key, value) {
|
|
432
|
-
if (!dev) return;
|
|
433
|
-
this.#devData.set(key, value);
|
|
434
|
-
}
|
|
435
|
-
|
|
436
|
-
/**
|
|
437
|
-
* Get a dev data property value
|
|
438
|
-
*
|
|
439
|
-
* Automatically reactive - creates a dependency on this specific key.
|
|
440
|
-
* Only available in dev mode - returns undefined in production.
|
|
441
|
-
*
|
|
442
|
-
* @param {string} key - Property key (use KEY_DEV_ constant)
|
|
443
|
-
*
|
|
444
|
-
* @returns {any} Property value or undefined
|
|
305
|
+
* @returns {ReactiveDataStore} The dev data store
|
|
445
306
|
*
|
|
446
307
|
* @example
|
|
447
308
|
* ```javascript
|
|
448
|
-
* const
|
|
309
|
+
* const KEY_DEV_AUTO_NAV = 'dev-auto-navigation';
|
|
310
|
+
*
|
|
311
|
+
* // Set dev data (no-op in production)
|
|
312
|
+
* machine.devData.set(KEY_DEV_AUTO_NAV, true);
|
|
449
313
|
*
|
|
450
|
-
* //
|
|
314
|
+
* // Get dev data (throws in production)
|
|
451
315
|
* $effect(() => {
|
|
452
|
-
* const autoNav = machine.
|
|
453
|
-
* console.log('Auto-
|
|
316
|
+
* const autoNav = machine.devData.get(KEY_DEV_AUTO_NAV);
|
|
317
|
+
* console.log('Auto-nav:', autoNav);
|
|
454
318
|
* });
|
|
455
|
-
* ```
|
|
456
|
-
*/
|
|
457
|
-
getDevData(key) {
|
|
458
|
-
if (!dev) return undefined;
|
|
459
|
-
return this.#devData.get(key);
|
|
460
|
-
}
|
|
461
|
-
|
|
462
|
-
/**
|
|
463
|
-
* Get all dev data properties as plain object
|
|
464
|
-
*
|
|
465
|
-
* Note: Returns a snapshot (plain object), not reactive.
|
|
466
|
-
* Only available in dev mode - returns empty object in production.
|
|
467
|
-
*
|
|
468
|
-
* @returns {Record<string, any>} Plain object with all dev data
|
|
469
319
|
*
|
|
470
|
-
*
|
|
471
|
-
*
|
|
472
|
-
*
|
|
473
|
-
*
|
|
320
|
+
* // Other operations
|
|
321
|
+
* machine.devData.update({ [KEY_DEV_AUTO_NAV]: false });
|
|
322
|
+
* machine.devData.has(KEY_DEV_AUTO_NAV);
|
|
323
|
+
* machine.devData.delete(KEY_DEV_AUTO_NAV);
|
|
324
|
+
* machine.devData.clear();
|
|
325
|
+
* console.log(machine.devData.size);
|
|
474
326
|
* ```
|
|
475
327
|
*/
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
return Object.fromEntries(this.#devData);
|
|
479
|
-
}
|
|
480
|
-
|
|
481
|
-
/**
|
|
482
|
-
* Update multiple dev data properties at once
|
|
483
|
-
*
|
|
484
|
-
* Each property update triggers fine-grained reactivity.
|
|
485
|
-
* Only available in dev mode - no-op in production.
|
|
486
|
-
*
|
|
487
|
-
* @param {Record<string, any>} dataUpdates
|
|
488
|
-
* Object with key-value pairs (use KEY_DEV_ constants for keys)
|
|
489
|
-
*
|
|
490
|
-
* @example
|
|
491
|
-
* ```javascript
|
|
492
|
-
* const KEY_DEV_AUTO_NAVIGATION = 'dev-auto-navigation';
|
|
493
|
-
* const KEY_DEV_SKIP_ANIMATIONS = 'dev-skip-animations';
|
|
494
|
-
*
|
|
495
|
-
* machine.updateDevData({
|
|
496
|
-
* [KEY_DEV_AUTO_NAVIGATION]: true,
|
|
497
|
-
* [KEY_DEV_SKIP_ANIMATIONS]: false
|
|
498
|
-
* });
|
|
499
|
-
* ```
|
|
500
|
-
*/
|
|
501
|
-
updateDevData(dataUpdates) {
|
|
502
|
-
if (!dev) return;
|
|
503
|
-
for (const [key, value] of Object.entries(dataUpdates)) {
|
|
504
|
-
this.#devData.set(key, value);
|
|
505
|
-
}
|
|
506
|
-
}
|
|
507
|
-
|
|
508
|
-
/**
|
|
509
|
-
* Delete a dev data property
|
|
510
|
-
*
|
|
511
|
-
* Only available in dev mode - no-op in production.
|
|
512
|
-
*
|
|
513
|
-
* @param {string} key - Property key to delete (use KEY_DEV_ constant)
|
|
514
|
-
*
|
|
515
|
-
* @returns {boolean} True if the key existed and was deleted
|
|
516
|
-
*
|
|
517
|
-
* @example
|
|
518
|
-
* ```javascript
|
|
519
|
-
* const KEY_DEV_TEMP_FLAG = 'dev-temp-flag';
|
|
520
|
-
*
|
|
521
|
-
* machine.deleteDevData(KEY_DEV_TEMP_FLAG);
|
|
522
|
-
* ```
|
|
523
|
-
*/
|
|
524
|
-
deleteDevData(key) {
|
|
525
|
-
if (!dev) return false;
|
|
526
|
-
return this.#devData.delete(key);
|
|
527
|
-
}
|
|
528
|
-
|
|
529
|
-
/**
|
|
530
|
-
* Check if dev data property exists
|
|
531
|
-
*
|
|
532
|
-
* Only available in dev mode - returns false in production.
|
|
533
|
-
*
|
|
534
|
-
* @param {string} key - Property key to check (use KEY_DEV_ constant)
|
|
535
|
-
*
|
|
536
|
-
* @returns {boolean} True if the key exists
|
|
537
|
-
*
|
|
538
|
-
* @example
|
|
539
|
-
* ```javascript
|
|
540
|
-
* const KEY_DEV_AUTO_NAVIGATION = 'dev-auto-navigation';
|
|
541
|
-
*
|
|
542
|
-
* if (machine.hasDevData(KEY_DEV_AUTO_NAVIGATION)) {
|
|
543
|
-
* // Dev setting exists
|
|
544
|
-
* }
|
|
545
|
-
* ```
|
|
546
|
-
*/
|
|
547
|
-
hasDevData(key) {
|
|
548
|
-
if (!dev) return false;
|
|
549
|
-
return this.#devData.has(key);
|
|
550
|
-
}
|
|
551
|
-
|
|
552
|
-
/**
|
|
553
|
-
* Clear all dev data properties
|
|
554
|
-
*
|
|
555
|
-
* Only available in dev mode - no-op in production.
|
|
556
|
-
*
|
|
557
|
-
* @example
|
|
558
|
-
* ```javascript
|
|
559
|
-
* machine.clearDevData(); // Reset all dev settings
|
|
560
|
-
* ```
|
|
561
|
-
*/
|
|
562
|
-
clearDevData() {
|
|
563
|
-
if (!dev) return;
|
|
564
|
-
this.#devData.clear();
|
|
565
|
-
}
|
|
566
|
-
|
|
567
|
-
/**
|
|
568
|
-
* Get number of dev data properties
|
|
569
|
-
*
|
|
570
|
-
* Only available in dev mode - returns 0 in production.
|
|
571
|
-
*
|
|
572
|
-
* @returns {number} Number of dev data entries
|
|
573
|
-
*/
|
|
574
|
-
get devDataSize() {
|
|
575
|
-
if (!dev) return 0;
|
|
576
|
-
return this.#devData.size;
|
|
328
|
+
get devData() {
|
|
329
|
+
return this.#devData;
|
|
577
330
|
}
|
|
578
331
|
|
|
579
332
|
/* ===== Visited Routes Tracking ===== */
|
|
@@ -78,6 +78,10 @@ const KEY_TUTORIAL_SEEN = 'tutorial-seen';
|
|
|
78
78
|
const KEY_HIGHEST_LEVEL = 'highest-level';
|
|
79
79
|
const KEY_DIFFICULTY = 'difficulty';
|
|
80
80
|
|
|
81
|
+
// Dev data keys (use KEY_DEV_ prefix)
|
|
82
|
+
const KEY_DEV_AUTO_NAVIGATION = 'dev-auto-navigation';
|
|
83
|
+
const KEY_DEV_SKIP_ANIMATIONS = 'dev-skip-animations';
|
|
84
|
+
|
|
81
85
|
export class PuzzleState extends PageMachine {
|
|
82
86
|
#logic;
|
|
83
87
|
|
|
@@ -96,6 +100,10 @@ export class PuzzleState extends PageMachine {
|
|
|
96
100
|
[KEY_TUTORIAL_SEEN]: false,
|
|
97
101
|
[KEY_HIGHEST_LEVEL]: 1,
|
|
98
102
|
[KEY_DIFFICULTY]: 'normal'
|
|
103
|
+
},
|
|
104
|
+
initialDevData: {
|
|
105
|
+
[KEY_DEV_AUTO_NAVIGATION]: false,
|
|
106
|
+
[KEY_DEV_SKIP_ANIMATIONS]: false
|
|
99
107
|
}
|
|
100
108
|
});
|
|
101
109
|
|
package/package.json
CHANGED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { default as SubScribersCount } from "./SubscribersCount.js";
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { default as SubScribersCount } from './SubscribersCount.js';
|