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,45 @@
1
+ import { withGlobalState } from './with-global-state';
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 const withPersistentState = (key, initialValue, storageKey) => {
19
+ const actualStorageKey = storageKey ?? `phaser-state-${key}`;
20
+ // Load from localStorage if available
21
+ let storedValue = initialValue;
22
+ try {
23
+ const stored = localStorage.getItem(actualStorageKey);
24
+ if (stored) {
25
+ storedValue = JSON.parse(stored);
26
+ }
27
+ }
28
+ catch (error) {
29
+ // eslint-disable-next-line no-console
30
+ console.warn(`[withPersistentState] Failed to load stored state for "${key}":`, error);
31
+ }
32
+ const state = withGlobalState(key, storedValue);
33
+ // Save to localStorage on changes
34
+ state.onChange(newValue => {
35
+ try {
36
+ localStorage.setItem(actualStorageKey, JSON.stringify(newValue));
37
+ }
38
+ catch (error) {
39
+ // eslint-disable-next-line no-console
40
+ console.warn(`[withPersistentState] Failed to save state for "${key}":`, error);
41
+ }
42
+ });
43
+ return state;
44
+ };
45
+ //# sourceMappingURL=with-persistent-state.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"with-persistent-state.js","sourceRoot":"","sources":["../../src/hooks/with-persistent-state.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAEtD;;;;;;;;;;;;;;;GAeG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG,CACjC,GAAW,EACX,YAAe,EACf,UAAmB,EACL,EAAE;IAChB,MAAM,gBAAgB,GAAG,UAAU,IAAI,gBAAgB,GAAG,EAAE,CAAC;IAE7D,sCAAsC;IACtC,IAAI,WAAW,GAAG,YAAY,CAAC;IAC/B,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,YAAY,CAAC,OAAO,CAAC,gBAAgB,CAAC,CAAC;QACtD,IAAI,MAAM,EAAE,CAAC;YACX,WAAW,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;QACnC,CAAC;IACH,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,sCAAsC;QACtC,OAAO,CAAC,IAAI,CACV,0DAA0D,GAAG,IAAI,EACjE,KAAK,CACN,CAAC;IACJ,CAAC;IAED,MAAM,KAAK,GAAG,eAAe,CAAI,GAAG,EAAE,WAAW,CAAC,CAAC;IAEnD,kCAAkC;IAClC,KAAK,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE;QACxB,IAAI,CAAC;YACH,YAAY,CAAC,OAAO,CAAC,gBAAgB,EAAE,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC,CAAC;QACnE,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,sCAAsC;YACtC,OAAO,CAAC,IAAI,CACV,mDAAmD,GAAG,IAAI,EAC1D,KAAK,CACN,CAAC;QACJ,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,OAAO,KAAK,CAAC;AACf,CAAC,CAAC"}
@@ -0,0 +1,75 @@
1
+ import * as Phaser from 'phaser';
2
+ import { type HookState } from './type';
3
+ /**
4
+ * Configuration options for state definition
5
+ */
6
+ export type StateDefOptions = {
7
+ /**
8
+ * Whether to persist state across scene changes
9
+ * @default true
10
+ */
11
+ persistent?: boolean;
12
+ /**
13
+ * Custom validator function for state values
14
+ * @param value The value to validate
15
+ * @returns true if valid, false or error message if invalid
16
+ */
17
+ validator?: (value: unknown) => boolean | string;
18
+ /**
19
+ * Whether to enable debug logging for this state
20
+ * @default false
21
+ */
22
+ debug?: boolean;
23
+ };
24
+ /**
25
+ * Low-level state management hook that directly interfaces with Phaser's registry system.
26
+ * This is the foundation for all other state hooks and provides direct access to
27
+ * Phaser's scene registry with additional safety and TypeScript support.
28
+ *
29
+ * ⚠️ **Note**: This is a low-level hook. Consider using `withLocalState` or `withGlobalState`
30
+ * for most use cases unless you need specific registry control.
31
+ *
32
+ * @template T The type of the state value
33
+ * @param scene The Phaser scene instance that owns this state
34
+ * @param key Unique identifier for the state in the registry
35
+ * @param initialValue Optional initial value to set if key doesn't exist
36
+ * @param options Optional configuration for state behavior
37
+ * @returns HookState interface for managing the state
38
+ *
39
+ * @throws {Error} When scene or registry is not available
40
+ * @throws {Error} When key is invalid (empty or non-string)
41
+ * @throws {Error} When validator rejects the initial value
42
+ *
43
+ * @example
44
+ * ```typescript
45
+ * // Basic usage
46
+ * const playerState = withStateDef<{name: string, level: number}>(
47
+ * scene,
48
+ * 'player',
49
+ * { name: 'Player1', level: 1 }
50
+ * );
51
+ *
52
+ * // With validation
53
+ * const healthState = withStateDef<number>(
54
+ * scene,
55
+ * 'health',
56
+ * 100,
57
+ * {
58
+ * validator: (value) => {
59
+ * const health = value as number;
60
+ * return health >= 0 && health <= 100 ? true : 'Health must be between 0-100';
61
+ * }
62
+ * }
63
+ * );
64
+ *
65
+ * // With debug logging
66
+ * const debugState = withStateDef<string>(
67
+ * scene,
68
+ * 'debug-info',
69
+ * 'initial',
70
+ * { debug: true }
71
+ * );
72
+ * ```
73
+ */
74
+ export declare const withStateDef: <T>(scene: Phaser.Scene, key: string, initialValue?: T, options?: StateDefOptions) => HookState<T>;
75
+ //# sourceMappingURL=with-state-def.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"with-state-def.d.ts","sourceRoot":"","sources":["../../src/hooks/with-state-def.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,MAAM,QAAQ,CAAC;AAEjC,OAAO,EAAE,KAAK,SAAS,EAA4B,MAAM,QAAQ,CAAC;AAElE;;GAEG;AACH,MAAM,MAAM,eAAe,GAAG;IAC5B;;;OAGG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;IAErB;;;;OAIG;IACH,SAAS,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,OAAO,GAAG,MAAM,CAAC;IAEjD;;;OAGG;IACH,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiDG;AAIH,eAAO,MAAM,YAAY,GAAI,CAAC,EAC5B,OAAO,MAAM,CAAC,KAAK,EACnB,KAAK,MAAM,EACX,eAAe,CAAC,EAChB,UAAS,eAAoB,KAC5B,SAAS,CAAC,CAAC,CAyHb,CAAC"}
@@ -0,0 +1,148 @@
1
+ /**
2
+ * Low-level state management hook that directly interfaces with Phaser's registry system.
3
+ * This is the foundation for all other state hooks and provides direct access to
4
+ * Phaser's scene registry with additional safety and TypeScript support.
5
+ *
6
+ * ⚠️ **Note**: This is a low-level hook. Consider using `withLocalState` or `withGlobalState`
7
+ * for most use cases unless you need specific registry control.
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 in the registry
12
+ * @param initialValue Optional initial value to set if key doesn't exist
13
+ * @param options Optional configuration for state behavior
14
+ * @returns HookState interface for managing the state
15
+ *
16
+ * @throws {Error} When scene or registry is not available
17
+ * @throws {Error} When key is invalid (empty or non-string)
18
+ * @throws {Error} When validator rejects the initial value
19
+ *
20
+ * @example
21
+ * ```typescript
22
+ * // Basic usage
23
+ * const playerState = withStateDef<{name: string, level: number}>(
24
+ * scene,
25
+ * 'player',
26
+ * { name: 'Player1', level: 1 }
27
+ * );
28
+ *
29
+ * // With validation
30
+ * const healthState = withStateDef<number>(
31
+ * scene,
32
+ * 'health',
33
+ * 100,
34
+ * {
35
+ * validator: (value) => {
36
+ * const health = value as number;
37
+ * return health >= 0 && health <= 100 ? true : 'Health must be between 0-100';
38
+ * }
39
+ * }
40
+ * );
41
+ *
42
+ * // With debug logging
43
+ * const debugState = withStateDef<string>(
44
+ * scene,
45
+ * 'debug-info',
46
+ * 'initial',
47
+ * { debug: true }
48
+ * );
49
+ * ```
50
+ */
51
+ /* eslint-disable max-lines-per-function */
52
+ /* eslint-disable complexity */
53
+ /* eslint-disable sonarjs/cognitive-complexity */
54
+ export const withStateDef = (scene, key, initialValue, options = {}) => {
55
+ if (!scene) {
56
+ throw new Error('[withStateDef] Scene parameter is required');
57
+ }
58
+ if (!scene.registry) {
59
+ throw new Error('[withStateDef] Scene registry is not available. Ensure the scene is properly initialized.');
60
+ }
61
+ if (!key || typeof key !== 'string' || key.trim().length === 0) {
62
+ throw new Error('[withStateDef] Key must be a non-empty string');
63
+ }
64
+ const { validator, debug = false } = options;
65
+ const registry = scene.registry;
66
+ const eventKey = `changedata-${key}`;
67
+ // Validate and set initial value if provided
68
+ if (!registry.has(key) && initialValue !== undefined) {
69
+ if (validator) {
70
+ const validationResult = validator(initialValue);
71
+ if (validationResult !== true) {
72
+ const message = typeof validationResult === 'string'
73
+ ? validationResult
74
+ : `Invalid initial value for key "${key}"`;
75
+ throw new Error(`[withStateDef] ${message}`);
76
+ }
77
+ }
78
+ registry.set(key, initialValue);
79
+ if (debug) {
80
+ // eslint-disable-next-line no-console
81
+ console.debug(`[withStateDef] Initialized "${key}" with value:`, initialValue);
82
+ }
83
+ }
84
+ /**
85
+ * Gets the current state value from the registry
86
+ */
87
+ const get = () => {
88
+ const value = registry.get(key);
89
+ if (debug) {
90
+ // eslint-disable-next-line no-console
91
+ console.debug(`[withStateDef] Getting "${key}":`, value);
92
+ }
93
+ return value;
94
+ };
95
+ /**
96
+ * Sets a new state value in the registry
97
+ */
98
+ const set = (value) => {
99
+ if (validator) {
100
+ const validationResult = validator(value);
101
+ if (validationResult !== true) {
102
+ const message = typeof validationResult === 'string'
103
+ ? validationResult
104
+ : `Invalid value for key "${key}"`;
105
+ throw new Error(`[withStateDef] ${message}`);
106
+ }
107
+ }
108
+ const oldValue = registry.get(key);
109
+ registry.set(key, value);
110
+ if (debug) {
111
+ // eslint-disable-next-line no-console
112
+ console.debug(`[withStateDef] Setting "${key}":`, {
113
+ oldValue,
114
+ newValue: value,
115
+ });
116
+ }
117
+ };
118
+ /**
119
+ * Registers a change listener for this state
120
+ */
121
+ const onChange = (callback) => {
122
+ if (!callback || typeof callback !== 'function') {
123
+ throw new Error('[withStateDef] onChange callback must be a function');
124
+ }
125
+ registry.events.on(eventKey, (_parent, key, value, previousValue) => {
126
+ if (debug) {
127
+ // eslint-disable-next-line no-console
128
+ console.debug(`[withStateDef] Change detected for "${key}":`, {
129
+ previousValue,
130
+ value,
131
+ });
132
+ }
133
+ try {
134
+ callback(value, previousValue);
135
+ }
136
+ catch (error) {
137
+ // eslint-disable-next-line no-console
138
+ console.error(`[withStateDef] Error in onChange callback for "${key}":`, error);
139
+ }
140
+ });
141
+ };
142
+ return {
143
+ get,
144
+ set,
145
+ onChange,
146
+ };
147
+ };
148
+ //# sourceMappingURL=with-state-def.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"with-state-def.js","sourceRoot":"","sources":["../../src/hooks/with-state-def.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,32 @@
1
+ import { type HookState } from './type';
2
+ /**
3
+ * Enhanced HookState interface with undo/redo capabilities
4
+ */
5
+ export type UndoableHookState<T> = {
6
+ undo(): boolean;
7
+ redo(): boolean;
8
+ canUndo(): boolean;
9
+ canRedo(): boolean;
10
+ clearHistory(): void;
11
+ } & HookState<T>;
12
+ /**
13
+ * Creates a state hook with undo/redo functionality
14
+ * @template T The type of the state value
15
+ * @param scene The Phaser scene instance
16
+ * @param key Unique identifier for the state
17
+ * @param initialValue Initial value for the state
18
+ * @param maxHistorySize Maximum number of history entries to keep
19
+ * @returns Enhanced HookState with undo/redo capabilities
20
+ *
21
+ * @example
22
+ * ```typescript
23
+ * const undoableState = withUndoableState<string>(scene, 'text', 'initial', 10);
24
+ *
25
+ * undoableState.set('first change');
26
+ * undoableState.set('second change');
27
+ * undoableState.undo(); // Back to 'first change'
28
+ * undoableState.redo(); // Forward to 'second change'
29
+ * ```
30
+ */
31
+ export declare const withUndoableState: <T>(scene: Phaser.Scene, key: string, initialValue: T, maxHistorySize: number) => UndoableHookState<T>;
32
+ //# sourceMappingURL=with-undoable-state.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"with-undoable-state.d.ts","sourceRoot":"","sources":["../../src/hooks/with-undoable-state.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,SAAS,EAAE,MAAM,QAAQ,CAAC;AAGxC;;GAEG;AACH,MAAM,MAAM,iBAAiB,CAAC,CAAC,IAAI;IACjC,IAAI,IAAI,OAAO,CAAC;IAChB,IAAI,IAAI,OAAO,CAAC;IAChB,OAAO,IAAI,OAAO,CAAC;IACnB,OAAO,IAAI,OAAO,CAAC;IACnB,YAAY,IAAI,IAAI,CAAC;CACtB,GAAG,SAAS,CAAC,CAAC,CAAC,CAAC;AAEjB;;;;;;;;;;;;;;;;;;GAkBG;AAIH,eAAO,MAAM,iBAAiB,GAAI,CAAC,EACjC,OAAO,MAAM,CAAC,KAAK,EACnB,KAAK,MAAM,EACX,cAAc,CAAC,EACf,gBAAgB,MAAM,KACrB,iBAAiB,CAAC,CAAC,CAsFrB,CAAC"}
@@ -0,0 +1,96 @@
1
+ import { withLocalState } from './with-local-state';
2
+ /**
3
+ * Creates a state hook with undo/redo functionality
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 maxHistorySize Maximum number of history entries to keep
9
+ * @returns Enhanced HookState with undo/redo capabilities
10
+ *
11
+ * @example
12
+ * ```typescript
13
+ * const undoableState = withUndoableState<string>(scene, 'text', 'initial', 10);
14
+ *
15
+ * undoableState.set('first change');
16
+ * undoableState.set('second change');
17
+ * undoableState.undo(); // Back to 'first change'
18
+ * undoableState.redo(); // Forward to 'second change'
19
+ * ```
20
+ */
21
+ /* eslint-disable max-lines-per-function */
22
+ /* eslint-disable complexity */
23
+ /* eslint-disable sonarjs/cognitive-complexity */
24
+ export const withUndoableState = (scene, key, initialValue, maxHistorySize) => {
25
+ const currentState = withLocalState(scene, key, initialValue);
26
+ const historyState = withLocalState(scene, `${key}:history`, [
27
+ initialValue,
28
+ ]);
29
+ const historyIndexState = withLocalState(scene, `${key}:historyIndex`, 0);
30
+ const addToHistory = (value) => {
31
+ const history = historyState.get();
32
+ const currentIndex = historyIndexState.get();
33
+ // Remove any "future" history if we're not at the end
34
+ const newHistory = history.slice(0, currentIndex + 1);
35
+ // Add new value
36
+ newHistory.push(value);
37
+ // Limit history size
38
+ if (newHistory.length > maxHistorySize) {
39
+ newHistory.shift();
40
+ }
41
+ else {
42
+ historyIndexState.set(currentIndex + 1);
43
+ }
44
+ historyState.set(newHistory);
45
+ };
46
+ const set = (value) => {
47
+ addToHistory(value);
48
+ currentState.set(value);
49
+ };
50
+ const undo = () => {
51
+ const currentIndex = historyIndexState.get();
52
+ if (currentIndex > 0) {
53
+ const newIndex = currentIndex - 1;
54
+ historyIndexState.set(newIndex);
55
+ const history = historyState.get();
56
+ const previousValue = history[newIndex];
57
+ if (previousValue !== undefined) {
58
+ currentState.set(previousValue);
59
+ return true;
60
+ }
61
+ }
62
+ return false;
63
+ };
64
+ const redo = () => {
65
+ const currentIndex = historyIndexState.get();
66
+ const history = historyState.get();
67
+ if (currentIndex < history.length - 1) {
68
+ const newIndex = currentIndex + 1;
69
+ historyIndexState.set(newIndex);
70
+ const nextValue = history[newIndex];
71
+ if (nextValue !== undefined) {
72
+ currentState.set(nextValue);
73
+ return true;
74
+ }
75
+ }
76
+ return false;
77
+ };
78
+ const canUndo = () => historyIndexState.get() > 0;
79
+ const canRedo = () => historyIndexState.get() < historyState.get().length - 1;
80
+ const clearHistory = () => {
81
+ const current = currentState.get();
82
+ historyState.set([current]);
83
+ historyIndexState.set(0);
84
+ };
85
+ return {
86
+ get: currentState.get,
87
+ set,
88
+ onChange: currentState.onChange,
89
+ undo,
90
+ redo,
91
+ canUndo,
92
+ canRedo,
93
+ clearHistory,
94
+ };
95
+ };
96
+ //# sourceMappingURL=with-undoable-state.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"with-undoable-state.js","sourceRoot":"","sources":["../../src/hooks/with-undoable-state.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ export * from './hooks';
2
+ export * from './utils';
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,SAAS,CAAC;AACxB,cAAc,SAAS,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,3 @@
1
+ export * from './hooks';
2
+ export * from './utils';
3
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,SAAS,CAAC;AACxB,cAAc,SAAS,CAAC"}
@@ -0,0 +1,2 @@
1
+ export * from './is-valid-scene';
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA,cAAc,kBAAkB,CAAC"}
@@ -0,0 +1,2 @@
1
+ export * from './is-valid-scene';
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA,cAAc,kBAAkB,CAAC"}
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Utility function to check if a scene is valid for state management
3
+ * @param scene The scene to validate
4
+ * @returns true if scene is valid, false otherwise
5
+ */
6
+ export declare const isValidScene: (scene: unknown) => scene is Phaser.Scene;
7
+ //# sourceMappingURL=is-valid-scene.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"is-valid-scene.d.ts","sourceRoot":"","sources":["../../src/utils/is-valid-scene.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,eAAO,MAAM,YAAY,GAAI,OAAO,OAAO,KAAG,KAAK,IAAI,MAAM,CAAC,KAO7D,CAAC"}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Utility function to check if a scene is valid for state management
3
+ * @param scene The scene to validate
4
+ * @returns true if scene is valid, false otherwise
5
+ */
6
+ export const isValidScene = (scene) => {
7
+ return (scene != null &&
8
+ typeof scene === 'object' &&
9
+ 'registry' in scene &&
10
+ 'scene' in scene);
11
+ };
12
+ //# sourceMappingURL=is-valid-scene.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"is-valid-scene.js","sourceRoot":"","sources":["../../src/utils/is-valid-scene.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,KAAc,EAAyB,EAAE;IACpE,OAAO,CACL,KAAK,IAAI,IAAI;QACb,OAAO,KAAK,KAAK,QAAQ;QACzB,UAAU,IAAI,KAAK;QACnB,OAAO,IAAI,KAAK,CACjB,CAAC;AACJ,CAAC,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=is-valid-scene.spec.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"is-valid-scene.spec.d.ts","sourceRoot":"","sources":["../../src/utils/is-valid-scene.spec.ts"],"names":[],"mappings":""}
@@ -0,0 +1,19 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { isValidScene } from './is-valid-scene';
3
+ describe('isValidScene', () => {
4
+ it('should return true for valid Phaser scene', () => {
5
+ const mockScene = {
6
+ registry: {},
7
+ scene: {},
8
+ };
9
+ expect(isValidScene(mockScene)).toBe(true);
10
+ });
11
+ it('should return false for invalid scene', () => {
12
+ expect(isValidScene(null)).toBe(false);
13
+ expect(isValidScene(undefined)).toBe(false);
14
+ expect(isValidScene({})).toBe(false);
15
+ expect(isValidScene({ registry: {} })).toBe(false);
16
+ expect(isValidScene({ scene: {} })).toBe(false);
17
+ });
18
+ });
19
+ //# sourceMappingURL=is-valid-scene.spec.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"is-valid-scene.spec.js","sourceRoot":"","sources":["../../src/utils/is-valid-scene.spec.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAE9C,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAEhD,QAAQ,CAAC,cAAc,EAAE,GAAG,EAAE;IAC5B,EAAE,CAAC,2CAA2C,EAAE,GAAG,EAAE;QACnD,MAAM,SAAS,GAAG;YAChB,QAAQ,EAAE,EAAE;YACZ,KAAK,EAAE,EAAE;SACV,CAAC;QAEF,MAAM,CAAC,YAAY,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC7C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,uCAAuC,EAAE,GAAG,EAAE;QAC/C,MAAM,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACvC,MAAM,CAAC,YAAY,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAC5C,MAAM,CAAC,YAAY,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACrC,MAAM,CAAC,YAAY,CAAC,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACnD,MAAM,CAAC,YAAY,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAClD,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
package/package.json ADDED
@@ -0,0 +1,69 @@
1
+ {
2
+ "name": "phaser-hooks",
3
+ "version": "0.1.0",
4
+ "description": "Hooks in react-style for Phaser games",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "module": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "import": "./dist/index.js",
12
+ "types": "./dist/index.d.ts"
13
+ }
14
+ },
15
+ "files": [
16
+ "dist"
17
+ ],
18
+ "keywords": [
19
+ "phaser",
20
+ "game-development",
21
+ "hooks",
22
+ "typescript"
23
+ ],
24
+ "author": "CassinoDev",
25
+ "license": "MIT",
26
+ "repository": {
27
+ "type": "git",
28
+ "url": "https://github.com/renatocassino/phaser-toolkit.git",
29
+ "directory": "packages/phaser-hooks"
30
+ },
31
+ "bugs": {
32
+ "url": "https://github.com/renatocassino/phaser-toolkit/issues"
33
+ },
34
+ "homepage": "https://github.com/renatocassino/phaser-toolkit/tree/main/packages/phaser-hooks#readme",
35
+ "devDependencies": {
36
+ "@eslint-recommended/eslint-config": "^28.0.0",
37
+ "@types/node": "^24.1.0",
38
+ "@typescript-eslint/eslint-plugin": "^7.0.0",
39
+ "@typescript-eslint/parser": "^6.15.0",
40
+ "eslint": "^8.55.0",
41
+ "eslint-config-prettier": "^9.1.0",
42
+ "eslint-plugin-import": "^2.29.1",
43
+ "eslint-plugin-node": "^11.1.0",
44
+ "eslint-plugin-prefer-arrow": "^1.2.3",
45
+ "eslint-plugin-promise": "^6.1.1",
46
+ "eslint-plugin-security": "^1.7.1",
47
+ "eslint-plugin-sonarjs": "^0.23.0",
48
+ "eslint-plugin-unicorn": "^50.0.1",
49
+ "typescript": "^5.3.0",
50
+ "vitest": "^3.2.4"
51
+ },
52
+ "peerDependencies": {
53
+ "phaser": "^3.70.0"
54
+ },
55
+ "publishConfig": {
56
+ "access": "public"
57
+ },
58
+ "scripts": {
59
+ "build": "tsc --build",
60
+ "dev": "tsc --build --watch",
61
+ "clean": "rm -rf dist",
62
+ "test": "vitest run",
63
+ "test:watch": "vitest",
64
+ "typecheck": "tsc --noEmit",
65
+ "lint": "eslint src --ext .ts,.tsx --max-warnings 0",
66
+ "lint:fix": "eslint src --ext .ts,.tsx --fix",
67
+ "lint:report": "eslint src --ext .ts,.tsx --format html --output-file eslint-report.html"
68
+ }
69
+ }