@blac/core 2.0.0-rc-14 → 2.0.0-rc.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 (162) hide show
  1. package/dist/.tsbuildinfo +1 -0
  2. package/dist/Blac.d.ts +246 -0
  3. package/dist/Blac.d.ts.map +1 -0
  4. package/dist/Blac.js +625 -0
  5. package/dist/Blac.js.map +1 -0
  6. package/dist/Bloc.d.ts +41 -0
  7. package/dist/Bloc.d.ts.map +1 -0
  8. package/dist/Bloc.js +140 -0
  9. package/dist/Bloc.js.map +1 -0
  10. package/dist/BlocBase.d.ts +110 -0
  11. package/dist/BlocBase.d.ts.map +1 -0
  12. package/dist/BlocBase.js +287 -0
  13. package/dist/BlocBase.js.map +1 -0
  14. package/dist/Cubit.d.ts +25 -0
  15. package/dist/Cubit.d.ts.map +1 -0
  16. package/dist/Cubit.js +64 -0
  17. package/dist/Cubit.js.map +1 -0
  18. package/dist/adapter/BlacAdapter.d.ts +71 -0
  19. package/dist/adapter/BlacAdapter.d.ts.map +1 -0
  20. package/dist/adapter/BlacAdapter.js +373 -0
  21. package/dist/adapter/BlacAdapter.js.map +1 -0
  22. package/dist/adapter/ProxyFactory.d.ts +50 -0
  23. package/dist/adapter/ProxyFactory.d.ts.map +1 -0
  24. package/dist/adapter/ProxyFactory.js +150 -0
  25. package/dist/adapter/ProxyFactory.js.map +1 -0
  26. package/{src/adapter/index.ts → dist/adapter/index.d.ts} +1 -0
  27. package/dist/adapter/index.d.ts.map +1 -0
  28. package/dist/adapter/index.js +19 -0
  29. package/dist/adapter/index.js.map +1 -0
  30. package/dist/errors/BlacError.d.ts +38 -0
  31. package/dist/errors/BlacError.d.ts.map +1 -0
  32. package/dist/errors/BlacError.js +55 -0
  33. package/dist/errors/BlacError.js.map +1 -0
  34. package/dist/errors/ErrorManager.d.ts +39 -0
  35. package/dist/errors/ErrorManager.d.ts.map +1 -0
  36. package/dist/errors/ErrorManager.js +91 -0
  37. package/dist/errors/ErrorManager.js.map +1 -0
  38. package/dist/errors/handleError.d.ts +24 -0
  39. package/dist/errors/handleError.d.ts.map +1 -0
  40. package/dist/errors/handleError.js +62 -0
  41. package/dist/errors/handleError.js.map +1 -0
  42. package/dist/errors/index.d.ts +5 -0
  43. package/dist/errors/index.d.ts.map +1 -0
  44. package/dist/errors/index.js +13 -0
  45. package/dist/errors/index.js.map +1 -0
  46. package/{src/events.ts → dist/events.d.ts} +4 -2
  47. package/dist/events.d.ts.map +1 -0
  48. package/dist/events.js +17 -0
  49. package/dist/events.js.map +1 -0
  50. package/dist/index.cjs +38 -0
  51. package/dist/index.d.ts +17 -1303
  52. package/dist/index.d.ts.map +1 -0
  53. package/dist/index.js +38 -0
  54. package/dist/index.js.map +1 -0
  55. package/dist/lifecycle/BlocLifecycle.d.ts +51 -0
  56. package/dist/lifecycle/BlocLifecycle.d.ts.map +1 -0
  57. package/dist/lifecycle/BlocLifecycle.js +123 -0
  58. package/dist/lifecycle/BlocLifecycle.js.map +1 -0
  59. package/dist/lifecycle/index.d.ts +2 -0
  60. package/dist/lifecycle/index.d.ts.map +1 -0
  61. package/dist/lifecycle/index.js +18 -0
  62. package/dist/lifecycle/index.js.map +1 -0
  63. package/dist/plugins/BlocPluginRegistry.d.ts +58 -0
  64. package/dist/plugins/BlocPluginRegistry.d.ts.map +1 -0
  65. package/dist/plugins/BlocPluginRegistry.js +201 -0
  66. package/dist/plugins/BlocPluginRegistry.js.map +1 -0
  67. package/dist/plugins/SystemPluginRegistry.d.ts +79 -0
  68. package/dist/plugins/SystemPluginRegistry.d.ts.map +1 -0
  69. package/dist/plugins/SystemPluginRegistry.js +184 -0
  70. package/dist/plugins/SystemPluginRegistry.js.map +1 -0
  71. package/{src/plugins/index.ts → dist/plugins/index.d.ts} +1 -5
  72. package/dist/plugins/index.d.ts.map +1 -0
  73. package/dist/plugins/index.js +26 -0
  74. package/dist/plugins/index.js.map +1 -0
  75. package/dist/plugins/types.d.ts +103 -0
  76. package/dist/plugins/types.d.ts.map +1 -0
  77. package/dist/plugins/types.js +3 -0
  78. package/dist/plugins/types.js.map +1 -0
  79. package/dist/subscription/SubscriptionManager.d.ts +67 -0
  80. package/dist/subscription/SubscriptionManager.d.ts.map +1 -0
  81. package/dist/subscription/SubscriptionManager.js +317 -0
  82. package/dist/subscription/SubscriptionManager.js.map +1 -0
  83. package/{src/subscription/index.ts → dist/subscription/index.d.ts} +1 -0
  84. package/dist/subscription/index.d.ts.map +1 -0
  85. package/dist/subscription/index.js +19 -0
  86. package/dist/subscription/index.js.map +1 -0
  87. package/dist/subscription/types.d.ts +65 -0
  88. package/dist/subscription/types.d.ts.map +1 -0
  89. package/dist/subscription/types.js +3 -0
  90. package/dist/subscription/types.js.map +1 -0
  91. package/dist/testing.d.ts +85 -0
  92. package/dist/testing.d.ts.map +1 -0
  93. package/dist/testing.js +186 -0
  94. package/dist/testing.js.map +1 -0
  95. package/{src/types.ts → dist/types.d.ts} +11 -28
  96. package/dist/types.d.ts.map +1 -0
  97. package/dist/types.js +3 -0
  98. package/dist/types.js.map +1 -0
  99. package/dist/utils/BatchingManager.d.ts +28 -0
  100. package/dist/utils/BatchingManager.d.ts.map +1 -0
  101. package/dist/utils/BatchingManager.js +48 -0
  102. package/dist/utils/BatchingManager.js.map +1 -0
  103. package/dist/utils/RerenderLogger.d.ts +60 -0
  104. package/dist/utils/RerenderLogger.d.ts.map +1 -0
  105. package/dist/utils/RerenderLogger.js +205 -0
  106. package/dist/utils/RerenderLogger.js.map +1 -0
  107. package/dist/utils/generateInstanceId.d.ts +11 -0
  108. package/dist/utils/generateInstanceId.d.ts.map +1 -0
  109. package/dist/utils/generateInstanceId.js +50 -0
  110. package/dist/utils/generateInstanceId.js.map +1 -0
  111. package/dist/utils/shallowEqual.d.ts +9 -0
  112. package/dist/utils/shallowEqual.d.ts.map +1 -0
  113. package/dist/utils/shallowEqual.js +35 -0
  114. package/dist/utils/shallowEqual.js.map +1 -0
  115. package/dist/utils/uuid.d.ts +10 -0
  116. package/dist/utils/uuid.d.ts.map +1 -0
  117. package/dist/utils/uuid.js +24 -0
  118. package/dist/utils/uuid.js.map +1 -0
  119. package/package.json +17 -10
  120. package/dist/index.cjs.js +0 -11
  121. package/dist/index.cjs.js.map +0 -1
  122. package/dist/index.es.js +0 -2275
  123. package/dist/index.es.js.map +0 -1
  124. package/src/Blac.ts +0 -802
  125. package/src/Bloc.ts +0 -180
  126. package/src/BlocBase.ts +0 -398
  127. package/src/Cubit.ts +0 -68
  128. package/src/__tests__/Blac.config.test.ts +0 -79
  129. package/src/__tests__/Bloc.event.test.ts +0 -394
  130. package/src/__tests__/BlocBase.subscription.test.ts +0 -349
  131. package/src/__tests__/Cubit.test.ts +0 -462
  132. package/src/__tests__/memory-leaks.test.ts +0 -383
  133. package/src/__tests__/plugins.test.ts +0 -464
  134. package/src/adapter/BlacAdapter.ts +0 -467
  135. package/src/adapter/ProxyFactory.ts +0 -249
  136. package/src/adapter/__tests__/BlacAdapter.test.ts +0 -555
  137. package/src/adapter/__tests__/ProxyFactory.test.ts +0 -655
  138. package/src/errors/BlacError.ts +0 -69
  139. package/src/errors/ErrorManager.ts +0 -101
  140. package/src/errors/handleError.ts +0 -114
  141. package/src/errors/index.ts +0 -13
  142. package/src/index.ts +0 -26
  143. package/src/lifecycle/BlocLifecycle.ts +0 -156
  144. package/src/lifecycle/index.ts +0 -1
  145. package/src/plugins/BlocPluginRegistry.ts +0 -240
  146. package/src/plugins/SystemPluginRegistry.ts +0 -242
  147. package/src/plugins/__tests__/BlocPluginRegistry.test.ts +0 -450
  148. package/src/plugins/__tests__/SystemPluginRegistry.test.ts +0 -404
  149. package/src/plugins/types.ts +0 -129
  150. package/src/subscription/README.md +0 -132
  151. package/src/subscription/SubscriptionManager.ts +0 -395
  152. package/src/subscription/__tests__/SubscriptionManager.test.ts +0 -355
  153. package/src/subscription/types.ts +0 -90
  154. package/src/testing.ts +0 -246
  155. package/src/utils/BatchingManager.ts +0 -59
  156. package/src/utils/RerenderLogger.ts +0 -265
  157. package/src/utils/__tests__/generateInstanceId.test.ts +0 -104
  158. package/src/utils/__tests__/shallowEqual.test.ts +0 -171
  159. package/src/utils/__tests__/uuid.test.ts +0 -144
  160. package/src/utils/generateInstanceId.ts +0 -54
  161. package/src/utils/shallowEqual.ts +0 -40
  162. package/src/utils/uuid.ts +0 -22
@@ -1,59 +0,0 @@
1
- /**
2
- * Manages batching of state updates to optimize performance
3
- */
4
- export class BatchingManager<S> {
5
- private pendingUpdates: Array<{
6
- newState: S;
7
- oldState: S;
8
- action?: unknown;
9
- }> = [];
10
- private isBatching = false;
11
-
12
- /**
13
- * Execute a callback with batched updates
14
- */
15
- batchUpdates(
16
- callback: () => void,
17
- onFlush: (finalUpdate: {
18
- newState: S;
19
- oldState: S;
20
- action?: unknown;
21
- }) => void,
22
- ): void {
23
- if (this.isBatching) {
24
- callback();
25
- return;
26
- }
27
-
28
- this.isBatching = true;
29
- this.pendingUpdates = [];
30
-
31
- try {
32
- callback();
33
-
34
- if (this.pendingUpdates.length > 0) {
35
- const finalUpdate = this.pendingUpdates[this.pendingUpdates.length - 1];
36
- onFlush(finalUpdate);
37
- }
38
- } finally {
39
- this.isBatching = false;
40
- this.pendingUpdates = [];
41
- }
42
- }
43
-
44
- /**
45
- * Add an update to the batch
46
- */
47
- addUpdate(update: { newState: S; oldState: S; action?: unknown }): void {
48
- if (this.isBatching) {
49
- this.pendingUpdates.push(update);
50
- }
51
- }
52
-
53
- /**
54
- * Check if currently batching
55
- */
56
- get isCurrentlyBatching(): boolean {
57
- return this.isBatching;
58
- }
59
- }
@@ -1,265 +0,0 @@
1
- import { Blac } from '../Blac';
2
- import { BlocBase } from '../BlocBase';
3
-
4
- export interface RerenderInfo {
5
- componentName: string;
6
- blocName: string;
7
- blocId: string;
8
- renderCount: number;
9
- reason: RerenderReason;
10
- timestamp: number;
11
- duration?: number;
12
- stackTrace?: string;
13
- }
14
-
15
- export interface RerenderReason {
16
- type:
17
- | 'state-change'
18
- | 'dependency-change'
19
- | 'mount'
20
- | 'props-change'
21
- | 'unknown';
22
- changedPaths?: string[];
23
- oldValues?: Record<string, any>;
24
- newValues?: Record<string, any>;
25
- dependencies?: string[];
26
- description?: string;
27
- }
28
-
29
- /**
30
- * Utility class for logging component rerenders with detailed debugging information
31
- */
32
- export class RerenderLogger {
33
- private static componentRenderCounts = new Map<string, number>();
34
- private static lastRenderTimestamps = new Map<string, number>();
35
- private static groupedLogs = new Map<string, RerenderInfo[]>();
36
- private static groupTimeouts = new Map<string, NodeJS.Timeout>();
37
-
38
- /**
39
- * Log a component rerender with detailed information
40
- */
41
- static logRerender(info: RerenderInfo): void {
42
- // RerenderLogger is now controlled by the RenderLoggingPlugin
43
- // This method is called by the plugin which already handles configuration
44
- const config = (Blac as any)._config?.rerenderLogging;
45
- if (!config) return;
46
-
47
- // Parse config
48
- const loggingConfig = this.parseConfig(config);
49
- if (!loggingConfig.enabled) return;
50
-
51
- // Apply filter if provided
52
- if (
53
- loggingConfig.filter &&
54
- !loggingConfig.filter({
55
- componentName: info.componentName,
56
- blocName: info.blocName,
57
- })
58
- ) {
59
- return;
60
- }
61
-
62
- // Track render count
63
- const key = `${info.componentName}-${info.blocId}`;
64
- const currentCount = this.componentRenderCounts.get(key) || 0;
65
- this.componentRenderCounts.set(key, currentCount + 1);
66
- info.renderCount = currentCount + 1;
67
-
68
- // Calculate duration if possible
69
- const lastTimestamp = this.lastRenderTimestamps.get(key);
70
- if (lastTimestamp) {
71
- info.duration = info.timestamp - lastTimestamp;
72
- }
73
- this.lastRenderTimestamps.set(key, info.timestamp);
74
-
75
- // Add stack trace if requested
76
- if (loggingConfig.includeStackTrace && loggingConfig.level === 'detailed') {
77
- info.stackTrace = new Error().stack;
78
- }
79
-
80
- // Handle grouping
81
- if (loggingConfig.groupRerenders) {
82
- this.logGrouped(info, loggingConfig);
83
- } else {
84
- this.logImmediate(info, loggingConfig);
85
- }
86
- }
87
-
88
- /**
89
- * Parse the logging configuration
90
- */
91
- private static parseConfig(config: any) {
92
- if (config === true) {
93
- return { enabled: true, level: 'normal' as const };
94
- }
95
- if (typeof config === 'string') {
96
- return {
97
- enabled: true,
98
- level:
99
- config === 'minimal' ? ('minimal' as const) : ('detailed' as const),
100
- };
101
- }
102
- return {
103
- enabled: config.enabled,
104
- level: config.level || ('normal' as const),
105
- filter: config.filter,
106
- includeStackTrace: config.includeStackTrace,
107
- groupRerenders: config.groupRerenders,
108
- };
109
- }
110
-
111
- /**
112
- * Log immediately without grouping
113
- */
114
- private static logImmediate(
115
- info: RerenderInfo,
116
- config: ReturnType<typeof RerenderLogger.parseConfig>,
117
- ): void {
118
- const message = this.formatMessage(info, config.level!);
119
-
120
- if (config.level === 'detailed') {
121
- console.group(message.header);
122
- if (message.details) console.log(message.details);
123
- if (message.values) console.table(message.values);
124
- if (info.stackTrace) console.log('Stack trace:', info.stackTrace);
125
- console.groupEnd();
126
- } else {
127
- console.log(message.header, message.details || '');
128
- }
129
- }
130
-
131
- /**
132
- * Log with grouping (batch multiple rerenders)
133
- */
134
- private static logGrouped(
135
- info: RerenderInfo,
136
- config: ReturnType<typeof RerenderLogger.parseConfig>,
137
- ): void {
138
- const groupKey = `${info.componentName}-${info.blocId}`;
139
-
140
- // Clear existing timeout
141
- const existingTimeout = this.groupTimeouts.get(groupKey);
142
- if (existingTimeout) {
143
- clearTimeout(existingTimeout);
144
- }
145
-
146
- // Add to group
147
- const group = this.groupedLogs.get(groupKey) || [];
148
- group.push(info);
149
- this.groupedLogs.set(groupKey, group);
150
-
151
- // Set new timeout
152
- const timeout = setTimeout(() => {
153
- const logs = this.groupedLogs.get(groupKey) || [];
154
- this.groupedLogs.delete(groupKey);
155
- this.groupTimeouts.delete(groupKey);
156
-
157
- if (logs.length === 1) {
158
- this.logImmediate(logs[0], config);
159
- } else {
160
- console.group(
161
- `🔄 ${info.componentName} rendered ${logs.length} times (${info.blocName})`,
162
- );
163
- logs.forEach((log, index) => {
164
- const message = this.formatMessage(log, config.level!);
165
- console.log(` ${index + 1}. ${message.details || message.header}`);
166
- });
167
- console.groupEnd();
168
- }
169
- }, 50); // 50ms debounce
170
-
171
- this.groupTimeouts.set(groupKey, timeout);
172
- }
173
-
174
- /**
175
- * Format the log message based on level
176
- */
177
- private static formatMessage(
178
- info: RerenderInfo,
179
- level: 'minimal' | 'normal' | 'detailed',
180
- ) {
181
- const emoji = this.getReasonEmoji(info.reason.type);
182
-
183
- if (level === 'minimal') {
184
- return {
185
- header: `${emoji} ${info.componentName} #${info.renderCount}`,
186
- details: null,
187
- values: null,
188
- };
189
- }
190
-
191
- const header = `${emoji} ${info.componentName} rerender #${info.renderCount} (${info.blocName})`;
192
-
193
- let details = `Reason: ${info.reason.description || info.reason.type}`;
194
-
195
- if (info.duration !== undefined) {
196
- details += ` | Time since last: ${info.duration}ms`;
197
- }
198
-
199
- if (
200
- level === 'normal' &&
201
- info.reason.changedPaths &&
202
- info.reason.changedPaths.length > 0
203
- ) {
204
- details += ` | Changed: ${info.reason.changedPaths.join(', ')}`;
205
- }
206
-
207
- let values: Record<string, { old: any; new: any }> | null = null;
208
- if (
209
- level === 'detailed' &&
210
- info.reason.changedPaths &&
211
- info.reason.changedPaths.length > 0
212
- ) {
213
- values = {};
214
- info.reason.changedPaths.forEach((path) => {
215
- values![path] = {
216
- old: info.reason.oldValues?.[path],
217
- new: info.reason.newValues?.[path],
218
- };
219
- });
220
-
221
- // If we have values to show in detailed mode, ensure they're not empty
222
- if (Object.keys(values).length === 0) {
223
- values = null;
224
- }
225
- }
226
-
227
- return { header, details, values };
228
- }
229
-
230
- /**
231
- * Get emoji for reason type
232
- */
233
- private static getReasonEmoji(type: RerenderReason['type']): string {
234
- switch (type) {
235
- case 'state-change':
236
- return '📊';
237
- case 'dependency-change':
238
- return '🔗';
239
- case 'mount':
240
- return '🚀';
241
- case 'props-change':
242
- return '📦';
243
- default:
244
- return '🔄';
245
- }
246
- }
247
-
248
- /**
249
- * Clear all tracking data
250
- */
251
- static clear(): void {
252
- this.componentRenderCounts.clear();
253
- this.lastRenderTimestamps.clear();
254
- this.groupedLogs.clear();
255
- this.groupTimeouts.forEach((timeout) => clearTimeout(timeout));
256
- this.groupTimeouts.clear();
257
- }
258
-
259
- /**
260
- * Get render count for a component
261
- */
262
- static getRenderCount(componentName: string, blocId: string): number {
263
- return this.componentRenderCounts.get(`${componentName}-${blocId}`) || 0;
264
- }
265
- }
@@ -1,104 +0,0 @@
1
- import { describe, it, expect } from 'vitest';
2
- import { generateInstanceIdFromProps } from '../generateInstanceId';
3
-
4
- describe('generateInstanceIdFromProps', () => {
5
- it('returns undefined for non-object props', () => {
6
- expect(generateInstanceIdFromProps(null)).toBeUndefined();
7
- expect(generateInstanceIdFromProps(undefined)).toBeUndefined();
8
- expect(generateInstanceIdFromProps('string')).toBeUndefined();
9
- expect(generateInstanceIdFromProps(123)).toBeUndefined();
10
- expect(generateInstanceIdFromProps(true)).toBeUndefined();
11
- });
12
-
13
- it('returns undefined for objects with no primitive values', () => {
14
- expect(generateInstanceIdFromProps({})).toBeUndefined();
15
- expect(
16
- generateInstanceIdFromProps({ obj: {}, arr: [], fn: () => {} }),
17
- ).toBeUndefined();
18
- expect(
19
- generateInstanceIdFromProps({ date: new Date(), regex: /test/ }),
20
- ).toBeUndefined();
21
- });
22
-
23
- it('generates ID from string values', () => {
24
- expect(generateInstanceIdFromProps({ id: 'user123' })).toBe('id:user123');
25
- expect(generateInstanceIdFromProps({ name: 'John', role: 'admin' })).toBe(
26
- 'name:John|role:admin',
27
- );
28
- });
29
-
30
- it('generates ID from number values', () => {
31
- expect(generateInstanceIdFromProps({ page: 1 })).toBe('page:1');
32
- expect(generateInstanceIdFromProps({ x: 10, y: 20 })).toBe('x:10|y:20');
33
- expect(generateInstanceIdFromProps({ float: 3.14 })).toBe('float:3.14');
34
- });
35
-
36
- it('generates ID from boolean values', () => {
37
- expect(generateInstanceIdFromProps({ active: true })).toBe('active:true');
38
- expect(generateInstanceIdFromProps({ visible: false })).toBe(
39
- 'visible:false',
40
- );
41
- });
42
-
43
- it('generates ID from null and undefined values', () => {
44
- expect(generateInstanceIdFromProps({ val: null })).toBe('val:null');
45
- expect(generateInstanceIdFromProps({ val: undefined })).toBe(
46
- 'val:undefined',
47
- );
48
- });
49
-
50
- it('generates ID from mixed primitive types', () => {
51
- const props = {
52
- id: 'abc',
53
- page: 2,
54
- active: true,
55
- extra: null,
56
- missing: undefined,
57
- };
58
- expect(generateInstanceIdFromProps(props)).toBe(
59
- 'active:true|extra:null|id:abc|missing:undefined|page:2',
60
- );
61
- });
62
-
63
- it('ignores non-primitive values', () => {
64
- const props = {
65
- id: 'test',
66
- obj: { nested: true },
67
- arr: [1, 2, 3],
68
- fn: () => {},
69
- page: 5,
70
- };
71
- expect(generateInstanceIdFromProps(props)).toBe('id:test|page:5');
72
- });
73
-
74
- it('sorts keys alphabetically for deterministic output', () => {
75
- const props1 = { z: 1, a: 2, m: 3 };
76
- const props2 = { m: 3, z: 1, a: 2 };
77
- const props3 = { a: 2, m: 3, z: 1 };
78
-
79
- const expected = 'a:2|m:3|z:1';
80
- expect(generateInstanceIdFromProps(props1)).toBe(expected);
81
- expect(generateInstanceIdFromProps(props2)).toBe(expected);
82
- expect(generateInstanceIdFromProps(props3)).toBe(expected);
83
- });
84
-
85
- it('handles edge cases', () => {
86
- expect(generateInstanceIdFromProps({ '': 'empty' })).toBe(':empty');
87
- expect(generateInstanceIdFromProps({ 'key|with|pipes': 'value' })).toBe(
88
- 'key|with|pipes:value',
89
- );
90
- expect(generateInstanceIdFromProps({ 'key:with:colons': 'value' })).toBe(
91
- 'key:with:colons:value',
92
- );
93
- });
94
-
95
- it('handles special number values', () => {
96
- expect(generateInstanceIdFromProps({ inf: Infinity })).toBe('inf:Infinity');
97
- expect(generateInstanceIdFromProps({ ninf: -Infinity })).toBe(
98
- 'ninf:-Infinity',
99
- );
100
- expect(generateInstanceIdFromProps({ nan: NaN })).toBe('nan:NaN');
101
- expect(generateInstanceIdFromProps({ zero: 0 })).toBe('zero:0');
102
- expect(generateInstanceIdFromProps({ negZero: -0 })).toBe('negZero:0');
103
- });
104
- });
@@ -1,171 +0,0 @@
1
- import { describe, it, expect } from 'vitest';
2
- import { shallowEqual } from '../shallowEqual';
3
-
4
- describe('shallowEqual', () => {
5
- describe('Primitive values', () => {
6
- it('should return true for identical primitives', () => {
7
- expect(shallowEqual(1, 1)).toBe(true);
8
- expect(shallowEqual('hello', 'hello')).toBe(true);
9
- expect(shallowEqual(true, true)).toBe(true);
10
- expect(shallowEqual(null, null)).toBe(true);
11
- expect(shallowEqual(undefined, undefined)).toBe(true);
12
- });
13
-
14
- it('should return false for different primitives', () => {
15
- expect(shallowEqual(1, 2)).toBe(false);
16
- expect(shallowEqual('hello', 'world')).toBe(false);
17
- expect(shallowEqual(true, false)).toBe(false);
18
- expect(shallowEqual(null, undefined)).toBe(false);
19
- expect(shallowEqual(0, false)).toBe(false);
20
- });
21
-
22
- it('should handle special numeric values using Object.is', () => {
23
- expect(shallowEqual(NaN, NaN)).toBe(true); // Object.is(NaN, NaN) is true
24
- expect(shallowEqual(0, -0)).toBe(false); // Object.is can distinguish +0 and -0
25
- expect(shallowEqual(-0, -0)).toBe(true);
26
- expect(shallowEqual(Infinity, Infinity)).toBe(true);
27
- expect(shallowEqual(-Infinity, -Infinity)).toBe(true);
28
- expect(shallowEqual(Infinity, -Infinity)).toBe(false);
29
- });
30
- });
31
-
32
- describe('Object comparison', () => {
33
- it('should return true for objects with same properties', () => {
34
- expect(shallowEqual({ a: 1, b: 2 }, { a: 1, b: 2 })).toBe(true);
35
- expect(
36
- shallowEqual({ name: 'John', age: 30 }, { name: 'John', age: 30 }),
37
- ).toBe(true);
38
- expect(shallowEqual({}, {})).toBe(true);
39
- });
40
-
41
- it('should return false for objects with different values', () => {
42
- expect(shallowEqual({ a: 1 }, { a: 2 })).toBe(false);
43
- expect(shallowEqual({ name: 'John' }, { name: 'Jane' })).toBe(false);
44
- });
45
-
46
- it('should return false for objects with different keys', () => {
47
- expect(shallowEqual({ a: 1 }, { b: 1 })).toBe(false);
48
- expect(shallowEqual({ a: 1, b: 2 }, { a: 1 })).toBe(false);
49
- expect(shallowEqual({ a: 1 }, { a: 1, b: 2 })).toBe(false);
50
- });
51
-
52
- it('should return true for same object reference', () => {
53
- const obj = { a: 1, b: 2 };
54
- expect(shallowEqual(obj, obj)).toBe(true);
55
- });
56
-
57
- it('should only compare one level deep (shallow)', () => {
58
- const obj1 = { a: 1, b: { c: 2 } };
59
- const obj2 = { a: 1, b: { c: 2 } };
60
- expect(shallowEqual(obj1, obj2)).toBe(false); // Different object references for b
61
-
62
- const sharedNested = { c: 2 };
63
- const obj3 = { a: 1, b: sharedNested };
64
- const obj4 = { a: 1, b: sharedNested };
65
- expect(shallowEqual(obj3, obj4)).toBe(true); // Same reference for b
66
- });
67
-
68
- it('should handle objects with null/undefined values', () => {
69
- expect(shallowEqual({ a: null }, { a: null })).toBe(true);
70
- expect(shallowEqual({ a: undefined }, { a: undefined })).toBe(true);
71
- expect(shallowEqual({ a: null }, { a: undefined })).toBe(false);
72
- });
73
- });
74
-
75
- describe('Array comparison', () => {
76
- it('should compare arrays shallowly', () => {
77
- expect(shallowEqual([1, 2, 3], [1, 2, 3])).toBe(true);
78
- expect(shallowEqual([1, 2, 3], [1, 2, 4])).toBe(false);
79
- expect(shallowEqual([1, 2], [1, 2, 3])).toBe(false);
80
- expect(shallowEqual([], [])).toBe(true);
81
- });
82
-
83
- it('should return true for same array reference', () => {
84
- const arr = [1, 2, 3];
85
- expect(shallowEqual(arr, arr)).toBe(true);
86
- });
87
-
88
- it('should handle nested arrays shallowly', () => {
89
- const nested = [4, 5];
90
- expect(shallowEqual([1, [2, 3]], [1, [2, 3]])).toBe(false); // Different array references
91
- expect(shallowEqual([1, nested], [1, nested])).toBe(true); // Same reference
92
- });
93
- });
94
-
95
- describe('Mixed types', () => {
96
- it('should return false when comparing different types', () => {
97
- // Arrays and objects with same keys will be considered equal by shallowEqual
98
- // since it only checks enumerable keys
99
- const arr: any[] = [];
100
- const obj = {};
101
- expect(shallowEqual(obj, arr)).toBe(true); // Both have 0 enumerable keys
102
-
103
- const arr2 = [1, 2];
104
- const obj2 = { 0: 1, 1: 2 };
105
- expect(shallowEqual(obj2, arr2)).toBe(true); // Same enumerable keys and values
106
-
107
- // These should return false because of type differences
108
- expect(
109
- shallowEqual('hello', { 0: 'h', 1: 'e', 2: 'l', 3: 'l', 4: 'o' }),
110
- ).toBe(false);
111
- expect(shallowEqual(42, { valueOf: () => 42 })).toBe(false);
112
- });
113
-
114
- it('should return false when comparing objects with primitives', () => {
115
- expect(shallowEqual({}, null)).toBe(false);
116
- expect(shallowEqual({}, undefined)).toBe(false);
117
- expect(shallowEqual({}, 'object')).toBe(false);
118
- expect(shallowEqual({}, 123)).toBe(false);
119
- expect(shallowEqual({}, true)).toBe(false);
120
- });
121
- });
122
-
123
- describe('Edge cases', () => {
124
- it('should handle objects with prototype properties correctly', () => {
125
- const obj1 = Object.create({ inherited: true });
126
- obj1.own = 'property';
127
-
128
- const obj2 = { own: 'property' };
129
-
130
- expect(shallowEqual(obj1, obj2)).toBe(true); // Only compares own properties
131
- });
132
-
133
- it('should handle objects with Symbol properties', () => {
134
- const sym = Symbol('test');
135
- const obj1 = { [sym]: 'value', a: 1 };
136
- const obj2 = { [sym]: 'value', a: 1 };
137
-
138
- // Symbol properties are not enumerated by Object.keys
139
- expect(shallowEqual(obj1, obj2)).toBe(true);
140
- });
141
-
142
- it('should handle Date objects', () => {
143
- const date1 = new Date('2023-01-01');
144
- const date2 = new Date('2023-01-01');
145
- const sameDate = date1;
146
-
147
- // Date objects have no enumerable properties, so they're considered equal
148
- expect(shallowEqual(date1, date2)).toBe(true); // Both have 0 enumerable keys
149
- expect(shallowEqual(date1, sameDate)).toBe(true); // Same reference
150
- });
151
-
152
- it('should handle RegExp objects', () => {
153
- const regex1 = /test/g;
154
- const regex2 = /test/g;
155
- const sameRegex = regex1;
156
-
157
- // RegExp objects have no enumerable properties, so they're considered equal
158
- expect(shallowEqual(regex1, regex2)).toBe(true); // Both have 0 enumerable keys
159
- expect(shallowEqual(regex1, sameRegex)).toBe(true); // Same reference
160
- });
161
-
162
- it('should handle functions', () => {
163
- const fn1 = () => {};
164
- const fn2 = () => {};
165
- const sameFn = fn1;
166
-
167
- expect(shallowEqual(fn1, fn2)).toBe(false);
168
- expect(shallowEqual(fn1, sameFn)).toBe(true);
169
- });
170
- });
171
- });