phaser-hooks 0.1.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 (63) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +330 -0
  3. package/dist/hooks/batch-state-updates.d.ts +15 -0
  4. package/dist/hooks/batch-state-updates.d.ts.map +1 -0
  5. package/dist/hooks/batch-state-updates.js +20 -0
  6. package/dist/hooks/batch-state-updates.js.map +1 -0
  7. package/dist/hooks/index.d.ts +11 -0
  8. package/dist/hooks/index.d.ts.map +1 -0
  9. package/dist/hooks/index.js +11 -0
  10. package/dist/hooks/index.js.map +1 -0
  11. package/dist/hooks/type.d.ts +59 -0
  12. package/dist/hooks/type.d.ts.map +1 -0
  13. package/dist/hooks/type.js +2 -0
  14. package/dist/hooks/type.js.map +1 -0
  15. package/dist/hooks/validators.d.ts +26 -0
  16. package/dist/hooks/validators.d.ts.map +1 -0
  17. package/dist/hooks/validators.js +54 -0
  18. package/dist/hooks/validators.js.map +1 -0
  19. package/dist/hooks/with-computed-state.d.ts +24 -0
  20. package/dist/hooks/with-computed-state.d.ts.map +1 -0
  21. package/dist/hooks/with-computed-state.js +40 -0
  22. package/dist/hooks/with-computed-state.js.map +1 -0
  23. package/dist/hooks/with-debounced-state.d.ts +22 -0
  24. package/dist/hooks/with-debounced-state.d.ts.map +1 -0
  25. package/dist/hooks/with-debounced-state.js +39 -0
  26. package/dist/hooks/with-debounced-state.js.map +1 -0
  27. package/dist/hooks/with-global-state.d.ts +150 -0
  28. package/dist/hooks/with-global-state.d.ts.map +1 -0
  29. package/dist/hooks/with-global-state.js +240 -0
  30. package/dist/hooks/with-global-state.js.map +1 -0
  31. package/dist/hooks/with-local-state.d.ts +69 -0
  32. package/dist/hooks/with-local-state.d.ts.map +1 -0
  33. package/dist/hooks/with-local-state.js +78 -0
  34. package/dist/hooks/with-local-state.js.map +1 -0
  35. package/dist/hooks/with-persistent-state.d.ts +19 -0
  36. package/dist/hooks/with-persistent-state.d.ts.map +1 -0
  37. package/dist/hooks/with-persistent-state.js +45 -0
  38. package/dist/hooks/with-persistent-state.js.map +1 -0
  39. package/dist/hooks/with-state-def.d.ts +75 -0
  40. package/dist/hooks/with-state-def.d.ts.map +1 -0
  41. package/dist/hooks/with-state-def.js +148 -0
  42. package/dist/hooks/with-state-def.js.map +1 -0
  43. package/dist/hooks/with-undoable-state.d.ts +32 -0
  44. package/dist/hooks/with-undoable-state.d.ts.map +1 -0
  45. package/dist/hooks/with-undoable-state.js +96 -0
  46. package/dist/hooks/with-undoable-state.js.map +1 -0
  47. package/dist/index.d.ts +3 -0
  48. package/dist/index.d.ts.map +1 -0
  49. package/dist/index.js +3 -0
  50. package/dist/index.js.map +1 -0
  51. package/dist/utils/index.d.ts +2 -0
  52. package/dist/utils/index.d.ts.map +1 -0
  53. package/dist/utils/index.js +2 -0
  54. package/dist/utils/index.js.map +1 -0
  55. package/dist/utils/is-valid-scene.d.ts +7 -0
  56. package/dist/utils/is-valid-scene.d.ts.map +1 -0
  57. package/dist/utils/is-valid-scene.js +12 -0
  58. package/dist/utils/is-valid-scene.js.map +1 -0
  59. package/dist/utils/is-valid-scene.spec.d.ts +2 -0
  60. package/dist/utils/is-valid-scene.spec.d.ts.map +1 -0
  61. package/dist/utils/is-valid-scene.spec.js +19 -0
  62. package/dist/utils/is-valid-scene.spec.js.map +1 -0
  63. package/package.json +69 -0
@@ -0,0 +1,22 @@
1
+ import { type HookState } from './type';
2
+ /**
3
+ * Creates a state hook with debounced updates
4
+ * @template T The type of the state value
5
+ * @param scene The Phaser scene instance
6
+ * @param key Unique identifier for the state
7
+ * @param initialValue Initial value for the state
8
+ * @param debounceMs Debounce delay in milliseconds
9
+ * @returns HookState with debounced set operations
10
+ *
11
+ * @example
12
+ * ```typescript
13
+ * const debouncedSearch = withDebouncedState<string>(scene, 'search', '', 300);
14
+ *
15
+ * // These rapid calls will be debounced
16
+ * debouncedSearch.set('a');
17
+ * debouncedSearch.set('ab');
18
+ * debouncedSearch.set('abc'); // Only this final value will be set after 300ms
19
+ * ```
20
+ */
21
+ export declare const withDebouncedState: <T>(scene: Phaser.Scene, key: string, initialValue: T, debounceMs: number) => HookState<T>;
22
+ //# sourceMappingURL=with-debounced-state.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"with-debounced-state.d.ts","sourceRoot":"","sources":["../../src/hooks/with-debounced-state.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,SAAS,EAAE,MAAM,QAAQ,CAAC;AAGxC;;;;;;;;;;;;;;;;;;GAkBG;AACH,eAAO,MAAM,kBAAkB,GAAI,CAAC,EAClC,OAAO,MAAM,CAAC,KAAK,EACnB,KAAK,MAAM,EACX,cAAc,CAAC,EACf,YAAY,MAAM,KACjB,SAAS,CAAC,CAAC,CAoBb,CAAC"}
@@ -0,0 +1,39 @@
1
+ import { withLocalState } from './with-local-state';
2
+ /**
3
+ * Creates a state hook with debounced updates
4
+ * @template T The type of the state value
5
+ * @param scene The Phaser scene instance
6
+ * @param key Unique identifier for the state
7
+ * @param initialValue Initial value for the state
8
+ * @param debounceMs Debounce delay in milliseconds
9
+ * @returns HookState with debounced set operations
10
+ *
11
+ * @example
12
+ * ```typescript
13
+ * const debouncedSearch = withDebouncedState<string>(scene, 'search', '', 300);
14
+ *
15
+ * // These rapid calls will be debounced
16
+ * debouncedSearch.set('a');
17
+ * debouncedSearch.set('ab');
18
+ * debouncedSearch.set('abc'); // Only this final value will be set after 300ms
19
+ * ```
20
+ */
21
+ export const withDebouncedState = (scene, key, initialValue, debounceMs) => {
22
+ const actualState = withLocalState(scene, key, initialValue);
23
+ let timeoutId = null;
24
+ const debouncedSet = (value) => {
25
+ if (timeoutId) {
26
+ clearTimeout(timeoutId);
27
+ }
28
+ timeoutId = setTimeout(() => {
29
+ actualState.set(value);
30
+ timeoutId = null;
31
+ }, debounceMs);
32
+ };
33
+ return {
34
+ get: actualState.get,
35
+ set: debouncedSet,
36
+ onChange: actualState.onChange,
37
+ };
38
+ };
39
+ //# sourceMappingURL=with-debounced-state.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"with-debounced-state.js","sourceRoot":"","sources":["../../src/hooks/with-debounced-state.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAEpD;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAChC,KAAmB,EACnB,GAAW,EACX,YAAe,EACf,UAAkB,EACJ,EAAE;IAChB,MAAM,WAAW,GAAG,cAAc,CAAI,KAAK,EAAE,GAAG,EAAE,YAAY,CAAC,CAAC;IAChE,IAAI,SAAS,GAAyC,IAAI,CAAC;IAE3D,MAAM,YAAY,GAAG,CAAC,KAAQ,EAAQ,EAAE;QACtC,IAAI,SAAS,EAAE,CAAC;YACd,YAAY,CAAC,SAAS,CAAC,CAAC;QAC1B,CAAC;QAED,SAAS,GAAG,UAAU,CAAC,GAAG,EAAE;YAC1B,WAAW,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;YACvB,SAAS,GAAG,IAAI,CAAC;QACnB,CAAC,EAAE,UAAU,CAAC,CAAC;IACjB,CAAC,CAAC;IAEF,OAAO;QACL,GAAG,EAAE,WAAW,CAAC,GAAG;QACpB,GAAG,EAAE,YAAY;QACjB,QAAQ,EAAE,WAAW,CAAC,QAAQ;KAC/B,CAAC;AACJ,CAAC,CAAC"}
@@ -0,0 +1,150 @@
1
+ import { type HookState, type StateChangeCallback } from './type';
2
+ /**
3
+ * Global state manager singleton.
4
+ * Manages application-wide state without dependency on specific scenes.
5
+ */
6
+ declare class GlobalState {
7
+ private static registry;
8
+ private static listeners;
9
+ /**
10
+ * Set a value in the global state
11
+ */
12
+ static set<T>(key: string, value: T): void;
13
+ /**
14
+ * Get a value from the global state
15
+ */
16
+ static get<T>(key: string, defaultValue?: T): T | undefined;
17
+ /**
18
+ * Check if a key exists in the global state
19
+ */
20
+ static has(key: string): boolean;
21
+ /**
22
+ * Register a callback for when a specific key changes
23
+ */
24
+ static onChange<T>(key: string, callback: StateChangeCallback<T>): void;
25
+ /**
26
+ * Remove a callback for a specific key
27
+ */
28
+ static offChange<T>(key: string, callback: StateChangeCallback<T>): void;
29
+ /**
30
+ * Clear all global state (useful for testing or resetting game)
31
+ */
32
+ static clear(): void;
33
+ /**
34
+ * Get all keys in the global state
35
+ */
36
+ static keys(): string[];
37
+ /**
38
+ * Get debug information about the global state
39
+ */
40
+ static debug(): {
41
+ registry: Record<string, unknown>;
42
+ listeners: Record<string, number>;
43
+ };
44
+ }
45
+ /**
46
+ * Configuration for global state management
47
+ */
48
+ export type GlobalStateOptions = {
49
+ /**
50
+ * Validator function to validate state values
51
+ */
52
+ validator?: (value: unknown) => boolean | string;
53
+ /**
54
+ * Enable debug logging
55
+ */
56
+ debug?: boolean;
57
+ };
58
+ /**
59
+ * Creates a global state hook that persists across all scenes in the game.
60
+ * Global state is managed by a singleton registry, removing dependency on specific scenes.
61
+ * Perfect for user settings, game progress, authentication state, or any data that
62
+ * should persist throughout the game session.
63
+ *
64
+ * @template T The type of the state value
65
+ * @param scene Phaser scene instance (kept for API compatibility, not used internally)
66
+ * @param key Unique identifier for the global state
67
+ * @param initialValue Optional initial value to set if state doesn't exist
68
+ * @param options Optional configuration for validation and debugging
69
+ * @returns HookState interface for managing the global state
70
+ *
71
+ * @throws {Error} When key is invalid
72
+ * @throws {Error} When validator rejects the initial value
73
+ *
74
+ * @example
75
+ * ```typescript
76
+ * // User settings that persist across scenes
77
+ * interface UserSettings {
78
+ * soundVolume: number;
79
+ * musicEnabled: boolean;
80
+ * difficulty: 'easy' | 'normal' | 'hard';
81
+ * }
82
+ *
83
+ * const settingsState = withGlobalState<UserSettings>(scene, 'settings', {
84
+ * soundVolume: 0.8,
85
+ * musicEnabled: true,
86
+ * difficulty: 'normal'
87
+ * });
88
+ *
89
+ * // Game progress
90
+ * interface GameProgress {
91
+ * currentLevel: number;
92
+ * unlockedLevels: number[];
93
+ * totalScore: number;
94
+ * }
95
+ *
96
+ * const progressState = withGlobalState<GameProgress>(scene, 'progress', {
97
+ * currentLevel: 1,
98
+ * unlockedLevels: [1],
99
+ * totalScore: 0
100
+ * });
101
+ *
102
+ * // Listen to changes globally
103
+ * progressState.onChange((newProgress, oldProgress) => {
104
+ * console.log('Game progress updated:', newProgress);
105
+ * // Could trigger achievements, save to localStorage, etc.
106
+ * });
107
+ * ```
108
+ *
109
+ * @example
110
+ * ```typescript
111
+ * // With validation
112
+ * const scoreState = withGlobalState<number>(scene, 'score', 0, {
113
+ * validator: (value) => {
114
+ * if (typeof value !== 'number' || value < 0) {
115
+ * return 'Score must be a positive number';
116
+ * }
117
+ * return true;
118
+ * },
119
+ * debug: true
120
+ * });
121
+ *
122
+ * // Authentication state example
123
+ * interface AuthState {
124
+ * isLoggedIn: boolean;
125
+ * username: string | null;
126
+ * token: string | null;
127
+ * }
128
+ *
129
+ * const authState = withGlobalState<AuthState>(scene, 'auth', {
130
+ * isLoggedIn: false,
131
+ * username: null,
132
+ * token: null
133
+ * });
134
+ *
135
+ * // Use from any scene - no dependency on Boot scene!
136
+ * if (authState.get().isLoggedIn) {
137
+ * // Show authenticated content
138
+ * }
139
+ * ```
140
+ *
141
+ * @see {@link withLocalState} For scene-specific state
142
+ * @see {@link withStateDef} For low-level state management
143
+ */
144
+ export declare const withGlobalState: <T = unknown>(key: string, initialValue?: T, options?: GlobalStateOptions) => HookState<T>;
145
+ /**
146
+ * Export the GlobalState class for advanced use cases
147
+ * (testing, debugging, manual state management)
148
+ */
149
+ export { GlobalState };
150
+ //# sourceMappingURL=with-global-state.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"with-global-state.d.ts","sourceRoot":"","sources":["../../src/hooks/with-global-state.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,SAAS,EAAE,KAAK,mBAAmB,EAAE,MAAM,QAAQ,CAAC;AAElE;;;GAGG;AACH,cAAM,WAAW;IACf,OAAO,CAAC,MAAM,CAAC,QAAQ,CAA8B;IACrD,OAAO,CAAC,MAAM,CAAC,SAAS,CAGpB;IAEJ;;OAEG;IACH,MAAM,CAAC,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,GAAG,IAAI;IAqB1C;;OAEG;IACH,MAAM,CAAC,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,YAAY,CAAC,EAAE,CAAC,GAAG,CAAC,GAAG,SAAS;IAM3D;;OAEG;IACH,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO;IAIhC;;OAEG;IACH,MAAM,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,mBAAmB,CAAC,CAAC,CAAC,GAAG,IAAI;IAOvE;;OAEG;IACH,MAAM,CAAC,SAAS,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,mBAAmB,CAAC,CAAC,CAAC,GAAG,IAAI;IAUxE;;OAEG;IACH,MAAM,CAAC,KAAK,IAAI,IAAI;IAKpB;;OAEG;IACH,MAAM,CAAC,IAAI,IAAI,MAAM,EAAE;IAIvB;;OAEG;IACH,MAAM,CAAC,KAAK,IAAI;QACd,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QAClC,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;KACnC;CAcF;AAED;;GAEG;AACH,MAAM,MAAM,kBAAkB,GAAG;IAC/B;;OAEG;IACH,SAAS,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,OAAO,GAAG,MAAM,CAAC;IAEjD;;OAEG;IACH,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqFG;AAIH,eAAO,MAAM,eAAe,GAAI,CAAC,GAAG,OAAO,EACzC,KAAK,MAAM,EACX,eAAe,CAAC,EAChB,UAAS,kBAAuB,KAC/B,SAAS,CAAC,CAAC,CA4Eb,CAAC;AAEF;;;GAGG;AACH,OAAO,EAAE,WAAW,EAAE,CAAC"}
@@ -0,0 +1,240 @@
1
+ /**
2
+ * Global state manager singleton.
3
+ * Manages application-wide state without dependency on specific scenes.
4
+ */
5
+ class GlobalState {
6
+ static registry = new Map();
7
+ static listeners = new Map();
8
+ /**
9
+ * Set a value in the global state
10
+ */
11
+ static set(key, value) {
12
+ const oldValue = this.registry.get(key);
13
+ this.registry.set(key, value);
14
+ // Trigger callbacks
15
+ const callbacks = this.listeners.get(key);
16
+ if (callbacks) {
17
+ callbacks.forEach(callback => {
18
+ try {
19
+ callback(value, oldValue);
20
+ }
21
+ catch (error) {
22
+ // eslint-disable-next-line no-console
23
+ console.error(`[GlobalState] Error in onChange callback for "${key}":`, error);
24
+ }
25
+ });
26
+ }
27
+ }
28
+ /**
29
+ * Get a value from the global state
30
+ */
31
+ static get(key, defaultValue) {
32
+ return this.registry.has(key)
33
+ ? this.registry.get(key)
34
+ : defaultValue;
35
+ }
36
+ /**
37
+ * Check if a key exists in the global state
38
+ */
39
+ static has(key) {
40
+ return this.registry.has(key);
41
+ }
42
+ /**
43
+ * Register a callback for when a specific key changes
44
+ */
45
+ static onChange(key, callback) {
46
+ if (!this.listeners.has(key)) {
47
+ this.listeners.set(key, new Set());
48
+ }
49
+ this.listeners.get(key)?.add(callback);
50
+ }
51
+ /**
52
+ * Remove a callback for a specific key
53
+ */
54
+ static offChange(key, callback) {
55
+ const callbacks = this.listeners.get(key);
56
+ if (callbacks) {
57
+ callbacks.delete(callback);
58
+ if (callbacks.size === 0) {
59
+ this.listeners.delete(key);
60
+ }
61
+ }
62
+ }
63
+ /**
64
+ * Clear all global state (useful for testing or resetting game)
65
+ */
66
+ static clear() {
67
+ this.registry.clear();
68
+ this.listeners.clear();
69
+ }
70
+ /**
71
+ * Get all keys in the global state
72
+ */
73
+ static keys() {
74
+ return Array.from(this.registry.keys());
75
+ }
76
+ /**
77
+ * Get debug information about the global state
78
+ */
79
+ static debug() {
80
+ const debugRegistry = {};
81
+ const debugListeners = {};
82
+ this.registry.forEach((value, key) => {
83
+ debugRegistry[key] = value;
84
+ });
85
+ this.listeners.forEach((callbacks, key) => {
86
+ debugListeners[key] = callbacks.size;
87
+ });
88
+ return { registry: debugRegistry, listeners: debugListeners };
89
+ }
90
+ }
91
+ /**
92
+ * Creates a global state hook that persists across all scenes in the game.
93
+ * Global state is managed by a singleton registry, removing dependency on specific scenes.
94
+ * Perfect for user settings, game progress, authentication state, or any data that
95
+ * should persist throughout the game session.
96
+ *
97
+ * @template T The type of the state value
98
+ * @param scene Phaser scene instance (kept for API compatibility, not used internally)
99
+ * @param key Unique identifier for the global state
100
+ * @param initialValue Optional initial value to set if state doesn't exist
101
+ * @param options Optional configuration for validation and debugging
102
+ * @returns HookState interface for managing the global state
103
+ *
104
+ * @throws {Error} When key is invalid
105
+ * @throws {Error} When validator rejects the initial value
106
+ *
107
+ * @example
108
+ * ```typescript
109
+ * // User settings that persist across scenes
110
+ * interface UserSettings {
111
+ * soundVolume: number;
112
+ * musicEnabled: boolean;
113
+ * difficulty: 'easy' | 'normal' | 'hard';
114
+ * }
115
+ *
116
+ * const settingsState = withGlobalState<UserSettings>(scene, 'settings', {
117
+ * soundVolume: 0.8,
118
+ * musicEnabled: true,
119
+ * difficulty: 'normal'
120
+ * });
121
+ *
122
+ * // Game progress
123
+ * interface GameProgress {
124
+ * currentLevel: number;
125
+ * unlockedLevels: number[];
126
+ * totalScore: number;
127
+ * }
128
+ *
129
+ * const progressState = withGlobalState<GameProgress>(scene, 'progress', {
130
+ * currentLevel: 1,
131
+ * unlockedLevels: [1],
132
+ * totalScore: 0
133
+ * });
134
+ *
135
+ * // Listen to changes globally
136
+ * progressState.onChange((newProgress, oldProgress) => {
137
+ * console.log('Game progress updated:', newProgress);
138
+ * // Could trigger achievements, save to localStorage, etc.
139
+ * });
140
+ * ```
141
+ *
142
+ * @example
143
+ * ```typescript
144
+ * // With validation
145
+ * const scoreState = withGlobalState<number>(scene, 'score', 0, {
146
+ * validator: (value) => {
147
+ * if (typeof value !== 'number' || value < 0) {
148
+ * return 'Score must be a positive number';
149
+ * }
150
+ * return true;
151
+ * },
152
+ * debug: true
153
+ * });
154
+ *
155
+ * // Authentication state example
156
+ * interface AuthState {
157
+ * isLoggedIn: boolean;
158
+ * username: string | null;
159
+ * token: string | null;
160
+ * }
161
+ *
162
+ * const authState = withGlobalState<AuthState>(scene, 'auth', {
163
+ * isLoggedIn: false,
164
+ * username: null,
165
+ * token: null
166
+ * });
167
+ *
168
+ * // Use from any scene - no dependency on Boot scene!
169
+ * if (authState.get().isLoggedIn) {
170
+ * // Show authenticated content
171
+ * }
172
+ * ```
173
+ *
174
+ * @see {@link withLocalState} For scene-specific state
175
+ * @see {@link withStateDef} For low-level state management
176
+ */
177
+ /* eslint-disable max-lines-per-function */
178
+ /* eslint-disable complexity */
179
+ /* eslint-disable sonarjs/cognitive-complexity */
180
+ export const withGlobalState = (key, initialValue, options = {}) => {
181
+ if (!key || typeof key !== 'string' || key.trim().length === 0) {
182
+ throw new Error('[withGlobalState] Key must be a non-empty string');
183
+ }
184
+ const { validator, debug } = options;
185
+ // Prefix the key to indicate it's global state
186
+ const globalKey = `global:${key}`;
187
+ // Initialize value if it doesn't exist
188
+ if (!GlobalState.has(globalKey) && initialValue !== undefined) {
189
+ if (validator) {
190
+ const validationResult = validator(initialValue);
191
+ if (validationResult !== true) {
192
+ throw new Error(`[withGlobalState] ${validationResult || 'Invalid initial value'}`);
193
+ }
194
+ }
195
+ GlobalState.set(globalKey, initialValue);
196
+ if (debug) {
197
+ // eslint-disable-next-line no-console
198
+ console.debug(`[withGlobalState] Initialized "${key}" with value:`, initialValue);
199
+ }
200
+ }
201
+ return {
202
+ get: () => {
203
+ const value = GlobalState.get(globalKey);
204
+ if (debug) {
205
+ // eslint-disable-next-line no-console
206
+ console.debug(`[withGlobalState] Getting "${key}":`, value);
207
+ }
208
+ return value ?? initialValue;
209
+ },
210
+ set: (value) => {
211
+ if (validator) {
212
+ const validationResult = validator(value);
213
+ if (validationResult !== true) {
214
+ throw new Error(`[withGlobalState] Invalid value for key "${key}": ${validationResult}`);
215
+ }
216
+ }
217
+ if (debug) {
218
+ const oldValue = GlobalState.get(globalKey);
219
+ // eslint-disable-next-line no-console
220
+ console.debug(`[withGlobalState] Setting "${key}":`, {
221
+ oldValue,
222
+ newValue: value,
223
+ });
224
+ }
225
+ GlobalState.set(globalKey, value);
226
+ },
227
+ onChange: (callback) => {
228
+ if (typeof callback !== 'function') {
229
+ throw new Error('[withGlobalState] onChange callback must be a function');
230
+ }
231
+ GlobalState.onChange(globalKey, callback);
232
+ },
233
+ };
234
+ };
235
+ /**
236
+ * Export the GlobalState class for advanced use cases
237
+ * (testing, debugging, manual state management)
238
+ */
239
+ export { GlobalState };
240
+ //# sourceMappingURL=with-global-state.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"with-global-state.js","sourceRoot":"","sources":["../../src/hooks/with-global-state.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,69 @@
1
+ import * as Phaser from 'phaser';
2
+ import { type HookState } from './type';
3
+ import { type StateDefOptions } from './with-state-def';
4
+ /**
5
+ * Creates a local state hook scoped to a specific Phaser scene.
6
+ * Local state is isolated to the scene instance and doesn't persist across scene changes.
7
+ * This is ideal for UI state, temporary game state, or scene-specific data.
8
+ *
9
+ * @template T The type of the state value
10
+ * @param scene The Phaser scene instance that owns this state
11
+ * @param key Unique identifier for the state within this scene
12
+ * @param initialValue Optional initial value to set if state doesn't exist
13
+ * @param options Optional configuration for state behavior
14
+ * @returns HookState interface for managing the local state
15
+ *
16
+ * @throws {Error} When scene is not available or key is invalid
17
+ *
18
+ * @example
19
+ * ```typescript
20
+ * // Simple counter state
21
+ * const counterState = withLocalState<number>(scene, 'counter', 0);
22
+ *
23
+ * // Complex object state
24
+ * interface GameUI {
25
+ * isMenuOpen: boolean;
26
+ * selectedTab: string;
27
+ * }
28
+ *
29
+ * const uiState = withLocalState<GameUI>(scene, 'ui', {
30
+ * isMenuOpen: false,
31
+ * selectedTab: 'main'
32
+ * });
33
+ *
34
+ * // Listen to changes
35
+ * uiState.onChange((newUI, oldUI) => {
36
+ * if (newUI.isMenuOpen !== oldUI.isMenuOpen) {
37
+ * console.log('Menu visibility changed');
38
+ * }
39
+ * });
40
+ *
41
+ * // Update state
42
+ * uiState.set({ ...uiState.get(), isMenuOpen: true });
43
+ *
44
+ * // Array state example
45
+ * const inventoryState = withLocalState<string[]>(scene, 'inventory', []);
46
+ * inventoryState.set([...inventoryState.get(), 'new-item']);
47
+ * ```
48
+ *
49
+ * @example
50
+ * ```typescript
51
+ * // With validation
52
+ * const playerHealthState = withLocalState<number>(
53
+ * scene,
54
+ * 'health',
55
+ * 100,
56
+ * {
57
+ * validator: (value) => {
58
+ * const health = value as number;
59
+ * return health >= 0 && health <= 100 ? true : 'Health must be 0-100';
60
+ * }
61
+ * }
62
+ * );
63
+ * ```
64
+ *
65
+ * @see {@link withGlobalState} For state that persists across scenes
66
+ * @see {@link withStateDef} For low-level state management
67
+ */
68
+ export declare const withLocalState: <T>(scene: Phaser.Scene, key: string, initialValue?: T, options?: StateDefOptions) => HookState<T>;
69
+ //# sourceMappingURL=with-local-state.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"with-local-state.d.ts","sourceRoot":"","sources":["../../src/hooks/with-local-state.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,MAAM,QAAQ,CAAC;AAEjC,OAAO,EAAE,KAAK,SAAS,EAAE,MAAM,QAAQ,CAAC;AACxC,OAAO,EAAgB,KAAK,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAEtE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+DG;AACH,eAAO,MAAM,cAAc,GAAI,CAAC,EAC9B,OAAO,MAAM,CAAC,KAAK,EACnB,KAAK,MAAM,EACX,eAAe,CAAC,EAChB,UAAU,eAAe,KACxB,SAAS,CAAC,CAAC,CAab,CAAC"}
@@ -0,0 +1,78 @@
1
+ import { withStateDef } from './with-state-def';
2
+ /**
3
+ * Creates a local state hook scoped to a specific Phaser scene.
4
+ * Local state is isolated to the scene instance and doesn't persist across scene changes.
5
+ * This is ideal for UI state, temporary game state, or scene-specific data.
6
+ *
7
+ * @template T The type of the state value
8
+ * @param scene The Phaser scene instance that owns this state
9
+ * @param key Unique identifier for the state within this scene
10
+ * @param initialValue Optional initial value to set if state doesn't exist
11
+ * @param options Optional configuration for state behavior
12
+ * @returns HookState interface for managing the local state
13
+ *
14
+ * @throws {Error} When scene is not available or key is invalid
15
+ *
16
+ * @example
17
+ * ```typescript
18
+ * // Simple counter state
19
+ * const counterState = withLocalState<number>(scene, 'counter', 0);
20
+ *
21
+ * // Complex object state
22
+ * interface GameUI {
23
+ * isMenuOpen: boolean;
24
+ * selectedTab: string;
25
+ * }
26
+ *
27
+ * const uiState = withLocalState<GameUI>(scene, 'ui', {
28
+ * isMenuOpen: false,
29
+ * selectedTab: 'main'
30
+ * });
31
+ *
32
+ * // Listen to changes
33
+ * uiState.onChange((newUI, oldUI) => {
34
+ * if (newUI.isMenuOpen !== oldUI.isMenuOpen) {
35
+ * console.log('Menu visibility changed');
36
+ * }
37
+ * });
38
+ *
39
+ * // Update state
40
+ * uiState.set({ ...uiState.get(), isMenuOpen: true });
41
+ *
42
+ * // Array state example
43
+ * const inventoryState = withLocalState<string[]>(scene, 'inventory', []);
44
+ * inventoryState.set([...inventoryState.get(), 'new-item']);
45
+ * ```
46
+ *
47
+ * @example
48
+ * ```typescript
49
+ * // With validation
50
+ * const playerHealthState = withLocalState<number>(
51
+ * scene,
52
+ * 'health',
53
+ * 100,
54
+ * {
55
+ * validator: (value) => {
56
+ * const health = value as number;
57
+ * return health >= 0 && health <= 100 ? true : 'Health must be 0-100';
58
+ * }
59
+ * }
60
+ * );
61
+ * ```
62
+ *
63
+ * @see {@link withGlobalState} For state that persists across scenes
64
+ * @see {@link withStateDef} For low-level state management
65
+ */
66
+ export const withLocalState = (scene, key, initialValue, options) => {
67
+ if (!scene) {
68
+ throw new Error('[withLocalState] Scene parameter is required');
69
+ }
70
+ // Prefix the key with scene key to ensure locality
71
+ const sceneKey = scene.scene?.key || 'unknown-scene';
72
+ const localKey = `local:${sceneKey}:${key}`;
73
+ return withStateDef(scene, localKey, initialValue, {
74
+ ...options,
75
+ persistent: false, // Local state shouldn't persist across scene changes
76
+ });
77
+ };
78
+ //# sourceMappingURL=with-local-state.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"with-local-state.js","sourceRoot":"","sources":["../../src/hooks/with-local-state.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,YAAY,EAAwB,MAAM,kBAAkB,CAAC;AAEtE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+DG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,CAC5B,KAAmB,EACnB,GAAW,EACX,YAAgB,EAChB,OAAyB,EACX,EAAE;IAChB,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,MAAM,IAAI,KAAK,CAAC,8CAA8C,CAAC,CAAC;IAClE,CAAC;IAED,mDAAmD;IACnD,MAAM,QAAQ,GAAG,KAAK,CAAC,KAAK,EAAE,GAAG,IAAI,eAAe,CAAC;IACrD,MAAM,QAAQ,GAAG,SAAS,QAAQ,IAAI,GAAG,EAAE,CAAC;IAE5C,OAAO,YAAY,CAAC,KAAK,EAAE,QAAQ,EAAE,YAAY,EAAE;QACjD,GAAG,OAAO;QACV,UAAU,EAAE,KAAK,EAAE,qDAAqD;KACzE,CAAC,CAAC;AACL,CAAC,CAAC"}
@@ -0,0 +1,19 @@
1
+ import { type HookState } from './type';
2
+ /**
3
+ * Creates a state hook with automatic localStorage persistence
4
+ * @template T The type of the state value
5
+ * @param key Unique identifier for the state
6
+ * @param initialValue Initial value to use if no stored value exists
7
+ * @param storageKey Optional custom localStorage key (defaults to the state key)
8
+ * @returns HookState with automatic persistence
9
+ *
10
+ * @example
11
+ * ```typescript
12
+ * const persistentSettings = withPersistentState<UserSettings>(
13
+ * 'settings',
14
+ * { volume: 0.8, difficulty: 'normal' }
15
+ * );
16
+ * ```
17
+ */
18
+ export declare const withPersistentState: <T>(key: string, initialValue: T, storageKey?: string) => HookState<T>;
19
+ //# sourceMappingURL=with-persistent-state.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"with-persistent-state.d.ts","sourceRoot":"","sources":["../../src/hooks/with-persistent-state.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,SAAS,EAAE,MAAM,QAAQ,CAAC;AAGxC;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,mBAAmB,GAAI,CAAC,EACnC,KAAK,MAAM,EACX,cAAc,CAAC,EACf,aAAa,MAAM,KAClB,SAAS,CAAC,CAAC,CAkCb,CAAC"}