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
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 CassinoDev
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,330 @@
1
+ # Phaser Hooks (like "use" hooks in React)
2
+
3
+ A comprehensive state management library for Phaser games with React-like hooks pattern.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install phaser-hooks
9
+ # or
10
+ pnpm add phaser-hooks
11
+ # or
12
+ yarn add phaser-hooks
13
+ ```
14
+
15
+ ## Why "with" instead of "use"?
16
+
17
+ While React hooks traditionally use the "use" prefix (e.g., useState, useEffect), this library intentionally uses "with" to avoid linting issues. Many linting configurations, including ESLint's built-in hooks rules, expect functions starting with "use" to be used only within React components and in .jsx/.tsx files.
18
+
19
+ Since this library is designed to work with Phaser games, which typically use plain TypeScript/JavaScript files (.ts/.js), using the "with" prefix helps avoid false positives from linters while maintaining a clear and consistent naming convention that indicates the hook-like pattern these functions follow.
20
+
21
+ This approach allows you to use these state management utilities in your Phaser games without having to modify your linting configuration or suppress warnings.
22
+
23
+ ## Available Hooks
24
+
25
+ ### Core Hooks
26
+
27
+ #### `withLocalState`
28
+
29
+ Scene-specific state management that gets cleaned up when the scene is destroyed.
30
+
31
+ ```typescript
32
+ const playerState = withLocalState<PlayerData>(scene, 'player', {
33
+ hp: 100,
34
+ level: 1,
35
+ exp: 0,
36
+ });
37
+ ```
38
+
39
+ #### `withGlobalState`
40
+
41
+ Application-wide state that persists across all scenes.
42
+
43
+ ```typescript
44
+ const settingsState = withGlobalState<GameSettings>('settings', {
45
+ soundVolume: 0.8,
46
+ musicEnabled: true,
47
+ });
48
+ ```
49
+
50
+ #### `withStateDef`
51
+
52
+ Low-level state definition with custom behaviors and validation.
53
+
54
+ ```typescript
55
+ const customState = withStateDef<number>(scene, 'score', {
56
+ initialValue: 0,
57
+ validator: value => value >= 0,
58
+ onChange: (newValue, oldValue) => console.log('Score changed!'),
59
+ });
60
+ ```
61
+
62
+ ### Enhanced Hooks
63
+
64
+ #### `withPersistentState`
65
+
66
+ State with automatic localStorage persistence.
67
+
68
+ ```typescript
69
+ const persistentSettings = withPersistentState<UserSettings>('settings', {
70
+ volume: 0.8,
71
+ difficulty: 'normal',
72
+ });
73
+ ```
74
+
75
+ #### `withComputedState`
76
+
77
+ Derived state that automatically updates when source state changes.
78
+
79
+ ```typescript
80
+ const healthPercentage = withComputedState(
81
+ scene,
82
+ 'healthPercent',
83
+ playerState,
84
+ player => (player.hp / player.maxHp) * 100
85
+ );
86
+ ```
87
+
88
+ #### `withUndoableState`
89
+
90
+ State with undo/redo functionality.
91
+
92
+ ```typescript
93
+ const undoableText = withUndoableState<string>(scene, 'text', 'initial', 10);
94
+
95
+ undoableText.set('first change');
96
+ undoableText.set('second change');
97
+ undoableText.undo(); // Back to 'first change'
98
+ undoableText.redo(); // Forward to 'second change'
99
+ ```
100
+
101
+ #### `withDebouncedState`
102
+
103
+ State with debounced updates to prevent rapid successive changes.
104
+
105
+ ```typescript
106
+ const debouncedSearch = withDebouncedState<string>(scene, 'search', '', 300);
107
+
108
+ // These rapid calls will be debounced
109
+ debouncedSearch.set('a');
110
+ debouncedSearch.set('ab');
111
+ debouncedSearch.set('abc'); // Only this final value will be set after 300ms
112
+ ```
113
+
114
+ ### Utilities
115
+
116
+ #### `validators`
117
+
118
+ Pre-built validation functions for common patterns.
119
+
120
+ ```typescript
121
+ import { validators } from 'phaser-hooks';
122
+
123
+ const scoreState = withGlobalState<number>('score', 0, {
124
+ validator: validators.numberRange(0, 1000),
125
+ });
126
+
127
+ const nameState = withGlobalState<string>('name', '', {
128
+ validator: validators.nonEmptyString,
129
+ });
130
+ ```
131
+
132
+ #### `batchStateUpdates`
133
+
134
+ Utility for batching multiple state updates.
135
+
136
+ ```typescript
137
+ batchStateUpdates(() => {
138
+ playerState.set({ ...playerState.get(), hp: 90 });
139
+ inventoryState.set([...inventoryState.get(), 'new-item']);
140
+ scoreState.set(scoreState.get() + 100);
141
+ });
142
+ ```
143
+
144
+ ## Basic Usage Example
145
+
146
+ ```typescript
147
+ import { withLocalState, withGlobalState } from 'phaser-hooks';
148
+
149
+ export class GameScene extends Phaser.Scene {
150
+ create() {
151
+ // Local state - specific to this scene
152
+ const playerState = withLocalState<{ hp: number; mp: number }>(
153
+ this,
154
+ 'player',
155
+ {
156
+ hp: 100,
157
+ mp: 50,
158
+ }
159
+ );
160
+
161
+ // Global state - persists across scenes
162
+ const gameState = withGlobalState<{ score: number; level: number }>(
163
+ 'game',
164
+ {
165
+ score: 0,
166
+ level: 1,
167
+ }
168
+ );
169
+
170
+ // Listen to changes
171
+ playerState.onChange((newPlayer, oldPlayer) => {
172
+ console.log('Player health changed:', newPlayer.hp);
173
+ });
174
+
175
+ // Update state
176
+ playerState.set({
177
+ ...playerState.get(),
178
+ hp: playerState.get().hp - 10,
179
+ });
180
+ }
181
+ }
182
+ ```
183
+
184
+ ## Advanced Example
185
+
186
+ ```typescript
187
+ import {
188
+ withPersistentState,
189
+ withComputedState,
190
+ withUndoableState,
191
+ validators,
192
+ } from 'phaser-hooks';
193
+
194
+ export class AdvancedGameScene extends Phaser.Scene {
195
+ create() {
196
+ // Persistent settings
197
+ const settings = withPersistentState<GameSettings>('settings', {
198
+ soundVolume: 0.8,
199
+ musicVolume: 0.6,
200
+ difficulty: 'normal',
201
+ });
202
+
203
+ // Player state with validation
204
+ const player = withLocalState<PlayerData>(
205
+ this,
206
+ 'player',
207
+ {
208
+ hp: 100,
209
+ maxHp: 100,
210
+ level: 1,
211
+ },
212
+ {
213
+ validator: validators.oneOf(['easy', 'normal', 'hard']),
214
+ }
215
+ );
216
+
217
+ // Computed health percentage
218
+ const healthPercent = withComputedState(this, 'healthPercent', player, p =>
219
+ Math.round((p.hp / p.maxHp) * 100)
220
+ );
221
+
222
+ // Undoable action system
223
+ const actionHistory = withUndoableState<string>(this, 'actions', 'start');
224
+
225
+ // Use the states
226
+ console.log('Health:', healthPercent.get() + '%');
227
+
228
+ if (healthPercent.get() < 20) {
229
+ console.log('Low health warning!');
230
+ }
231
+ }
232
+ }
233
+ ```
234
+
235
+ ### Composing Hooks
236
+
237
+ You can compose your own hooks using other with\* hooks — similar to how custom React hooks are built. This is a powerful way to isolate logic, reuse behavior, and keep your scenes clean and focused.
238
+
239
+ Example: Extracting a withPlayerEnergy hook from withPlayerState
240
+
241
+ Imagine you have a local player state like this:
242
+
243
+ ```ts
244
+ interface PlayerAttributes {
245
+ energy: number;
246
+ stamina: number;
247
+ strength: number;
248
+ agility: number;
249
+ }
250
+
251
+ const playerState = withLocalState<PlayerAttributes>(scene, 'player', {
252
+ energy: 100,
253
+ stamina: 80,
254
+ strength: 50,
255
+ agility: 40,
256
+ });
257
+ ```
258
+
259
+ You can now create a custom hook focused only on energy:
260
+
261
+ ```ts
262
+ function withPlayerEnergy(scene: Phaser.Scene) {
263
+ const player = withLocalState<PlayerAttributes>(scene, 'player', {
264
+ energy: 100,
265
+ stamina: 80,
266
+ strength: 50,
267
+ agility: 40,
268
+ });
269
+
270
+ return {
271
+ get: () => player.get().energy,
272
+ set: (value: number) => player.set({ ...player.get(), energy: value }),
273
+ onChange: (fn: (energy: number) => void) =>
274
+ player.onChange(newVal => fn(newVal.energy)),
275
+ };
276
+ }
277
+ ```
278
+
279
+ Usage in a scene
280
+
281
+ ```ts
282
+ const energy = withPlayerEnergy(this);
283
+
284
+ console.log('Current energy:', energy.get());
285
+
286
+ energy.set(energy.get() - 10);
287
+
288
+ energy.onChange(newEnergy => {
289
+ if (newEnergy <= 0) {
290
+ console.warn('You are out of energy!');
291
+ }
292
+ });
293
+ ```
294
+
295
+ ### Why use this pattern?
296
+
297
+ ✅ Keeps your scene code focused on intent (e.g., energy.get()) rather than structure (player.get().energy)
298
+
299
+ ✅ Allows centralized validation, side effects, or formatting for specific state slices
300
+
301
+ ✅ Makes it easier to refactor or share logic across scenes and systems
302
+
303
+ You can extend this idea to compose computed hooks, persistent hooks, undoable hooks, and more — everything works with the same API.
304
+
305
+ ## TypeScript Support
306
+
307
+ All hooks are fully typed and provide excellent TypeScript support:
308
+
309
+ ```typescript
310
+ interface PlayerData {
311
+ hp: number;
312
+ maxHp: number;
313
+ level: number;
314
+ inventory: string[];
315
+ }
316
+
317
+ const playerState = withLocalState<PlayerData>(scene, 'player', {
318
+ hp: 100,
319
+ maxHp: 100,
320
+ level: 1,
321
+ inventory: [],
322
+ });
323
+
324
+ // TypeScript knows the exact type
325
+ const currentPlayer: PlayerData = playerState.get();
326
+ ```
327
+
328
+ ## License
329
+
330
+ MIT
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Utility to batch multiple state updates
3
+ * @param updateFn Function that performs multiple state updates
4
+ *
5
+ * @example
6
+ * ```typescript
7
+ * batchStateUpdates(() => {
8
+ * playerState.set({...playerState.get(), hp: 90});
9
+ * inventoryState.set([...inventoryState.get(), 'new-item']);
10
+ * scoreState.set(scoreState.get() + 100);
11
+ * });
12
+ * ```
13
+ */
14
+ export declare const batchStateUpdates: (updateFn: () => void) => void;
15
+ //# sourceMappingURL=batch-state-updates.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"batch-state-updates.d.ts","sourceRoot":"","sources":["../../src/hooks/batch-state-updates.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,iBAAiB,GAAI,UAAU,MAAM,IAAI,KAAG,IAKxD,CAAC"}
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Utility to batch multiple state updates
3
+ * @param updateFn Function that performs multiple state updates
4
+ *
5
+ * @example
6
+ * ```typescript
7
+ * batchStateUpdates(() => {
8
+ * playerState.set({...playerState.get(), hp: 90});
9
+ * inventoryState.set([...inventoryState.get(), 'new-item']);
10
+ * scoreState.set(scoreState.get() + 100);
11
+ * });
12
+ * ```
13
+ */
14
+ export const batchStateUpdates = (updateFn) => {
15
+ // Note: This is a placeholder for potential batching optimization
16
+ // In a more advanced implementation, you might collect all updates
17
+ // and apply them in a single registry update
18
+ updateFn();
19
+ };
20
+ //# sourceMappingURL=batch-state-updates.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"batch-state-updates.js","sourceRoot":"","sources":["../../src/hooks/batch-state-updates.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,QAAoB,EAAQ,EAAE;IAC9D,kEAAkE;IAClE,mEAAmE;IACnE,6CAA6C;IAC7C,QAAQ,EAAE,CAAC;AACb,CAAC,CAAC"}
@@ -0,0 +1,11 @@
1
+ export * from './batch-state-updates';
2
+ export * from './type';
3
+ export * from './validators';
4
+ export * from './with-computed-state';
5
+ export * from './with-debounced-state';
6
+ export * from './with-global-state';
7
+ export * from './with-local-state';
8
+ export * from './with-persistent-state';
9
+ export * from './with-state-def';
10
+ export * from './with-undoable-state';
11
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/hooks/index.ts"],"names":[],"mappings":"AAAA,cAAc,uBAAuB,CAAC;AACtC,cAAc,QAAQ,CAAC;AACvB,cAAc,cAAc,CAAC;AAC7B,cAAc,uBAAuB,CAAC;AACtC,cAAc,wBAAwB,CAAC;AACvC,cAAc,qBAAqB,CAAC;AACpC,cAAc,oBAAoB,CAAC;AACnC,cAAc,yBAAyB,CAAC;AACxC,cAAc,kBAAkB,CAAC;AACjC,cAAc,uBAAuB,CAAC"}
@@ -0,0 +1,11 @@
1
+ export * from './batch-state-updates';
2
+ export * from './type';
3
+ export * from './validators';
4
+ export * from './with-computed-state';
5
+ export * from './with-debounced-state';
6
+ export * from './with-global-state';
7
+ export * from './with-local-state';
8
+ export * from './with-persistent-state';
9
+ export * from './with-state-def';
10
+ export * from './with-undoable-state';
11
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/hooks/index.ts"],"names":[],"mappings":"AAAA,cAAc,uBAAuB,CAAC;AACtC,cAAc,QAAQ,CAAC;AACvB,cAAc,cAAc,CAAC;AAC7B,cAAc,uBAAuB,CAAC;AACtC,cAAc,wBAAwB,CAAC;AACvC,cAAc,qBAAqB,CAAC;AACpC,cAAc,oBAAoB,CAAC;AACnC,cAAc,yBAAyB,CAAC;AACxC,cAAc,kBAAkB,CAAC;AACjC,cAAc,uBAAuB,CAAC"}
@@ -0,0 +1,59 @@
1
+ /**
2
+ * Callback function type for state changes
3
+ * @template T The type of the state value
4
+ * @param newValue The new state value after the change
5
+ * @param oldValue The previous state value before the change
6
+ */
7
+ export type StateChangeCallback<T> = (newValue: T, oldValue: T) => void;
8
+ /**
9
+ * Utility type for state selectors
10
+ * @template T The input state type
11
+ * @template U The computed result type
12
+ */
13
+ export type StateSelector<T, U> = (state: T) => U;
14
+ /**
15
+ * Utility type for state updaters
16
+ * @template T The state type
17
+ */
18
+ export type StateUpdater<T> = (currentState: T) => T;
19
+ /**
20
+ * Core interface for all state management hooks in Phaser games.
21
+ * Provides a React-like state management API with get/set/onChange functionality.
22
+ *
23
+ * @template T The type of the state value being managed
24
+ *
25
+ * @example
26
+ * ```typescript
27
+ * // Basic usage
28
+ * const scoreState: HookState<number> = withLocalState(scene, 'score', 0);
29
+ *
30
+ * // Getting current value
31
+ * const currentScore = scoreState.get();
32
+ *
33
+ * // Setting new value
34
+ * scoreState.set(100);
35
+ *
36
+ * // Listening to changes
37
+ * scoreState.onChange((newScore, oldScore) => {
38
+ * console.log(`Score changed from ${oldScore} to ${newScore}`);
39
+ * });
40
+ * ```
41
+ */
42
+ export type HookState<T> = {
43
+ /**
44
+ * Gets the current state value
45
+ * @returns The current state value
46
+ */
47
+ get: () => T;
48
+ /**
49
+ * Sets a new state value and triggers change listeners
50
+ * @param value The new value to set
51
+ */
52
+ set: (value: T) => void;
53
+ /**
54
+ * Registers a callback to be called whenever the state changes
55
+ * @param callback Function to call when state changes
56
+ */
57
+ onChange: (callback: StateChangeCallback<T>) => void;
58
+ };
59
+ //# sourceMappingURL=type.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"type.d.ts","sourceRoot":"","sources":["../../src/hooks/type.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,MAAM,MAAM,mBAAmB,CAAC,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC,EAAE,QAAQ,EAAE,CAAC,KAAK,IAAI,CAAC;AAExE;;;;GAIG;AACH,MAAM,MAAM,aAAa,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC,KAAK,CAAC,CAAC;AAElD;;;GAGG;AACH,MAAM,MAAM,YAAY,CAAC,CAAC,IAAI,CAAC,YAAY,EAAE,CAAC,KAAK,CAAC,CAAC;AAErD;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,MAAM,MAAM,SAAS,CAAC,CAAC,IAAI;IACzB;;;OAGG;IACH,GAAG,EAAE,MAAM,CAAC,CAAC;IAEb;;;OAGG;IACH,GAAG,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,IAAI,CAAC;IAExB;;;OAGG;IACH,QAAQ,EAAE,CAAC,QAAQ,EAAE,mBAAmB,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC;CACtD,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=type.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"type.js","sourceRoot":"","sources":["../../src/hooks/type.ts"],"names":[],"mappings":""}
@@ -0,0 +1,26 @@
1
+ /**
2
+ * State validator function type
3
+ */
4
+ export type StateValidator = (value: unknown) => boolean | string;
5
+ /**
6
+ * Creates a state validator function for common patterns
7
+ */
8
+ export declare const validators: {
9
+ /**
10
+ * Validates that a number is within a range
11
+ */
12
+ numberRange: (min: number, max: number) => StateValidator;
13
+ /**
14
+ * Validates that a string is not empty
15
+ */
16
+ nonEmptyString: (value: unknown) => boolean | string;
17
+ /**
18
+ * Validates that an array has a specific length range
19
+ */
20
+ arrayLength: (min: number, max?: number) => StateValidator;
21
+ /**
22
+ * Validates that a value is one of the allowed options
23
+ */
24
+ oneOf: <T>(allowedValues: T[]) => StateValidator;
25
+ };
26
+ //# sourceMappingURL=validators.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validators.d.ts","sourceRoot":"","sources":["../../src/hooks/validators.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,MAAM,cAAc,GAAG,CAAC,KAAK,EAAE,OAAO,KAAK,OAAO,GAAG,MAAM,CAAC;AAElE;;GAEG;AACH,eAAO,MAAM,UAAU;IACrB;;OAEG;uBAEK,MAAM,OAAO,MAAM,KAAG,cAAc;IAY5C;;OAEG;4BACqB,OAAO,KAAG,OAAO,GAAG,MAAM;IAQlD;;OAEG;uBAEK,MAAM,QAAQ,MAAM,KAAG,cAAc;IAe7C;;OAEG;YAEA,CAAC,iBAAiB,CAAC,EAAE,KAAG,cAAc;CAO1C,CAAC"}
@@ -0,0 +1,54 @@
1
+ /**
2
+ * Creates a state validator function for common patterns
3
+ */
4
+ export const validators = {
5
+ /**
6
+ * Validates that a number is within a range
7
+ */
8
+ numberRange: (min, max) => (value) => {
9
+ const num = value;
10
+ if (typeof num !== 'number' || Number.isNaN(num)) {
11
+ return 'Value must be a number';
12
+ }
13
+ if (num < min || num > max) {
14
+ return `Value must be between ${min} and ${max}`;
15
+ }
16
+ return true;
17
+ },
18
+ /**
19
+ * Validates that a string is not empty
20
+ */
21
+ nonEmptyString: (value) => {
22
+ const str = value;
23
+ if (typeof str !== 'string' || str.trim().length === 0) {
24
+ return 'Value must be a non-empty string';
25
+ }
26
+ return true;
27
+ },
28
+ /**
29
+ * Validates that an array has a specific length range
30
+ */
31
+ arrayLength: (min, max) => (value) => {
32
+ const arr = value;
33
+ if (!Array.isArray(arr)) {
34
+ return 'Value must be an array';
35
+ }
36
+ if (arr.length < min) {
37
+ return `Array must have at least ${min} items`;
38
+ }
39
+ if (max !== undefined && arr.length > max) {
40
+ return `Array must have at most ${max} items`;
41
+ }
42
+ return true;
43
+ },
44
+ /**
45
+ * Validates that a value is one of the allowed options
46
+ */
47
+ oneOf: (allowedValues) => (value) => {
48
+ if (!allowedValues.includes(value)) {
49
+ return `Value must be one of: ${allowedValues.join(', ')}`;
50
+ }
51
+ return true;
52
+ },
53
+ };
54
+ //# sourceMappingURL=validators.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validators.js","sourceRoot":"","sources":["../../src/hooks/validators.ts"],"names":[],"mappings":"AAKA;;GAEG;AACH,MAAM,CAAC,MAAM,UAAU,GAAG;IACxB;;OAEG;IACH,WAAW,EACT,CAAC,GAAW,EAAE,GAAW,EAAkB,EAAE,CAC7C,CAAC,KAAc,EAAoB,EAAE;QACnC,MAAM,GAAG,GAAG,KAAe,CAAC;QAC5B,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC;YACjD,OAAO,wBAAwB,CAAC;QAClC,CAAC;QACD,IAAI,GAAG,GAAG,GAAG,IAAI,GAAG,GAAG,GAAG,EAAE,CAAC;YAC3B,OAAO,yBAAyB,GAAG,QAAQ,GAAG,EAAE,CAAC;QACnD,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;IAEH;;OAEG;IACH,cAAc,EAAE,CAAC,KAAc,EAAoB,EAAE;QACnD,MAAM,GAAG,GAAG,KAAe,CAAC;QAC5B,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,CAAC,IAAI,EAAE,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACvD,OAAO,kCAAkC,CAAC;QAC5C,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;IAED;;OAEG;IACH,WAAW,EACT,CAAC,GAAW,EAAE,GAAY,EAAkB,EAAE,CAC9C,CAAC,KAAc,EAAoB,EAAE;QACnC,MAAM,GAAG,GAAG,KAAkB,CAAC;QAC/B,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;YACxB,OAAO,wBAAwB,CAAC;QAClC,CAAC;QACD,IAAI,GAAG,CAAC,MAAM,GAAG,GAAG,EAAE,CAAC;YACrB,OAAO,4BAA4B,GAAG,QAAQ,CAAC;QACjD,CAAC;QACD,IAAI,GAAG,KAAK,SAAS,IAAI,GAAG,CAAC,MAAM,GAAG,GAAG,EAAE,CAAC;YAC1C,OAAO,2BAA2B,GAAG,QAAQ,CAAC;QAChD,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;IAEH;;OAEG;IACH,KAAK,EACH,CAAI,aAAkB,EAAkB,EAAE,CAC1C,CAAC,KAAc,EAAoB,EAAE;QACnC,IAAI,CAAC,aAAa,CAAC,QAAQ,CAAC,KAAU,CAAC,EAAE,CAAC;YACxC,OAAO,yBAAyB,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QAC7D,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;CACJ,CAAC"}
@@ -0,0 +1,24 @@
1
+ import { type HookState, type StateSelector } from './type';
2
+ /**
3
+ * Creates a computed state that derives its value from other states
4
+ * @template T The input state type
5
+ * @template U The computed result type
6
+ * @param scene The Phaser scene instance
7
+ * @param key Unique identifier for the computed state
8
+ * @param sourceState The source state to derive from
9
+ * @param selector Function to compute the derived value
10
+ * @returns HookState with computed value
11
+ *
12
+ * @example
13
+ * ```typescript
14
+ * const playerState = withLocalState<{hp: number, maxHp: number}>(scene, 'player', {...});
15
+ * const healthPercentage = withComputedState(
16
+ * scene,
17
+ * 'healthPercent',
18
+ * playerState,
19
+ * (player) => (player.hp / player.maxHp) * 100
20
+ * );
21
+ * ```
22
+ */
23
+ export declare const withComputedState: <T, U>(scene: Phaser.Scene, key: string, sourceState: HookState<T>, selector: StateSelector<T, U>) => HookState<U>;
24
+ //# sourceMappingURL=with-computed-state.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"with-computed-state.d.ts","sourceRoot":"","sources":["../../src/hooks/with-computed-state.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,SAAS,EAAE,KAAK,aAAa,EAAE,MAAM,QAAQ,CAAC;AAG5D;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,eAAO,MAAM,iBAAiB,GAAI,CAAC,EAAE,CAAC,EACpC,OAAO,MAAM,CAAC,KAAK,EACnB,KAAK,MAAM,EACX,aAAa,SAAS,CAAC,CAAC,CAAC,EACzB,UAAU,aAAa,CAAC,CAAC,EAAE,CAAC,CAAC,KAC5B,SAAS,CAAC,CAAC,CAoBb,CAAC"}
@@ -0,0 +1,40 @@
1
+ import { withLocalState } from './with-local-state';
2
+ /**
3
+ * Creates a computed state that derives its value from other states
4
+ * @template T The input state type
5
+ * @template U The computed result type
6
+ * @param scene The Phaser scene instance
7
+ * @param key Unique identifier for the computed state
8
+ * @param sourceState The source state to derive from
9
+ * @param selector Function to compute the derived value
10
+ * @returns HookState with computed value
11
+ *
12
+ * @example
13
+ * ```typescript
14
+ * const playerState = withLocalState<{hp: number, maxHp: number}>(scene, 'player', {...});
15
+ * const healthPercentage = withComputedState(
16
+ * scene,
17
+ * 'healthPercent',
18
+ * playerState,
19
+ * (player) => (player.hp / player.maxHp) * 100
20
+ * );
21
+ * ```
22
+ */
23
+ export const withComputedState = (scene, key, sourceState, selector) => {
24
+ // Initialize with computed value
25
+ const initialValue = selector(sourceState.get());
26
+ const computedState = withLocalState(scene, key, initialValue);
27
+ // Update computed state when source changes
28
+ sourceState.onChange(newSourceValue => {
29
+ const newComputedValue = selector(newSourceValue);
30
+ computedState.set(newComputedValue);
31
+ });
32
+ return {
33
+ get: computedState.get,
34
+ set: () => {
35
+ throw new Error(`[withComputedState] Cannot directly set computed state "${key}". Update the source state instead.`);
36
+ },
37
+ onChange: computedState.onChange,
38
+ };
39
+ };
40
+ //# sourceMappingURL=with-computed-state.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"with-computed-state.js","sourceRoot":"","sources":["../../src/hooks/with-computed-state.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAEpD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAC/B,KAAmB,EACnB,GAAW,EACX,WAAyB,EACzB,QAA6B,EACf,EAAE;IAChB,iCAAiC;IACjC,MAAM,YAAY,GAAG,QAAQ,CAAC,WAAW,CAAC,GAAG,EAAE,CAAC,CAAC;IACjD,MAAM,aAAa,GAAG,cAAc,CAAI,KAAK,EAAE,GAAG,EAAE,YAAY,CAAC,CAAC;IAElE,4CAA4C;IAC5C,WAAW,CAAC,QAAQ,CAAC,cAAc,CAAC,EAAE;QACpC,MAAM,gBAAgB,GAAG,QAAQ,CAAC,cAAc,CAAC,CAAC;QAClD,aAAa,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC;IACtC,CAAC,CAAC,CAAC;IAEH,OAAO;QACL,GAAG,EAAE,aAAa,CAAC,GAAG;QACtB,GAAG,EAAE,GAAS,EAAE;YACd,MAAM,IAAI,KAAK,CACb,2DAA2D,GAAG,qCAAqC,CACpG,CAAC;QACJ,CAAC;QACD,QAAQ,EAAE,aAAa,CAAC,QAAQ;KACjC,CAAC;AACJ,CAAC,CAAC"}