@blac/core 2.0.0-rc-16 → 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.
- package/dist/.tsbuildinfo +1 -0
- package/dist/Blac.d.ts +246 -0
- package/dist/Blac.d.ts.map +1 -0
- package/dist/Blac.js +625 -0
- package/dist/Blac.js.map +1 -0
- package/dist/Bloc.d.ts +41 -0
- package/dist/Bloc.d.ts.map +1 -0
- package/dist/Bloc.js +140 -0
- package/dist/Bloc.js.map +1 -0
- package/dist/BlocBase.d.ts +110 -0
- package/dist/BlocBase.d.ts.map +1 -0
- package/dist/BlocBase.js +287 -0
- package/dist/BlocBase.js.map +1 -0
- package/dist/Cubit.d.ts +25 -0
- package/dist/Cubit.d.ts.map +1 -0
- package/dist/Cubit.js +64 -0
- package/dist/Cubit.js.map +1 -0
- package/dist/adapter/BlacAdapter.d.ts +71 -0
- package/dist/adapter/BlacAdapter.d.ts.map +1 -0
- package/dist/adapter/BlacAdapter.js +373 -0
- package/dist/adapter/BlacAdapter.js.map +1 -0
- package/dist/adapter/ProxyFactory.d.ts +50 -0
- package/dist/adapter/ProxyFactory.d.ts.map +1 -0
- package/dist/adapter/ProxyFactory.js +150 -0
- package/dist/adapter/ProxyFactory.js.map +1 -0
- package/{src/adapter/index.ts → dist/adapter/index.d.ts} +1 -0
- package/dist/adapter/index.d.ts.map +1 -0
- package/dist/adapter/index.js +19 -0
- package/dist/adapter/index.js.map +1 -0
- package/dist/errors/BlacError.d.ts +38 -0
- package/dist/errors/BlacError.d.ts.map +1 -0
- package/dist/errors/BlacError.js +55 -0
- package/dist/errors/BlacError.js.map +1 -0
- package/dist/errors/ErrorManager.d.ts +39 -0
- package/dist/errors/ErrorManager.d.ts.map +1 -0
- package/dist/errors/ErrorManager.js +91 -0
- package/dist/errors/ErrorManager.js.map +1 -0
- package/dist/errors/handleError.d.ts +24 -0
- package/dist/errors/handleError.d.ts.map +1 -0
- package/dist/errors/handleError.js +62 -0
- package/dist/errors/handleError.js.map +1 -0
- package/dist/errors/index.d.ts +5 -0
- package/dist/errors/index.d.ts.map +1 -0
- package/dist/errors/index.js +13 -0
- package/dist/errors/index.js.map +1 -0
- package/{src/events.ts → dist/events.d.ts} +4 -2
- package/dist/events.d.ts.map +1 -0
- package/dist/events.js +17 -0
- package/dist/events.js.map +1 -0
- package/dist/index.cjs +38 -0
- package/{src/index.ts → dist/index.d.ts} +1 -10
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +38 -0
- package/dist/index.js.map +1 -0
- package/dist/lifecycle/BlocLifecycle.d.ts +51 -0
- package/dist/lifecycle/BlocLifecycle.d.ts.map +1 -0
- package/dist/lifecycle/BlocLifecycle.js +123 -0
- package/dist/lifecycle/BlocLifecycle.js.map +1 -0
- package/dist/lifecycle/index.d.ts +2 -0
- package/dist/lifecycle/index.d.ts.map +1 -0
- package/dist/lifecycle/index.js +18 -0
- package/dist/lifecycle/index.js.map +1 -0
- package/dist/plugins/BlocPluginRegistry.d.ts +58 -0
- package/dist/plugins/BlocPluginRegistry.d.ts.map +1 -0
- package/dist/plugins/BlocPluginRegistry.js +201 -0
- package/dist/plugins/BlocPluginRegistry.js.map +1 -0
- package/dist/plugins/SystemPluginRegistry.d.ts +79 -0
- package/dist/plugins/SystemPluginRegistry.d.ts.map +1 -0
- package/dist/plugins/SystemPluginRegistry.js +184 -0
- package/dist/plugins/SystemPluginRegistry.js.map +1 -0
- package/{src/plugins/index.ts → dist/plugins/index.d.ts} +1 -5
- package/dist/plugins/index.d.ts.map +1 -0
- package/dist/plugins/index.js +26 -0
- package/dist/plugins/index.js.map +1 -0
- package/dist/plugins/types.d.ts +103 -0
- package/dist/plugins/types.d.ts.map +1 -0
- package/dist/plugins/types.js +3 -0
- package/dist/plugins/types.js.map +1 -0
- package/dist/subscription/SubscriptionManager.d.ts +67 -0
- package/dist/subscription/SubscriptionManager.d.ts.map +1 -0
- package/dist/subscription/SubscriptionManager.js +317 -0
- package/dist/subscription/SubscriptionManager.js.map +1 -0
- package/{src/subscription/index.ts → dist/subscription/index.d.ts} +1 -0
- package/dist/subscription/index.d.ts.map +1 -0
- package/dist/subscription/index.js +19 -0
- package/dist/subscription/index.js.map +1 -0
- package/dist/subscription/types.d.ts +65 -0
- package/dist/subscription/types.d.ts.map +1 -0
- package/dist/subscription/types.js +3 -0
- package/dist/subscription/types.js.map +1 -0
- package/dist/testing.d.ts +85 -0
- package/dist/testing.d.ts.map +1 -0
- package/dist/testing.js +186 -0
- package/dist/testing.js.map +1 -0
- package/{src/types.ts → dist/types.d.ts} +11 -28
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +3 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/BatchingManager.d.ts +28 -0
- package/dist/utils/BatchingManager.d.ts.map +1 -0
- package/dist/utils/BatchingManager.js +48 -0
- package/dist/utils/BatchingManager.js.map +1 -0
- package/dist/utils/RerenderLogger.d.ts +60 -0
- package/dist/utils/RerenderLogger.d.ts.map +1 -0
- package/dist/utils/RerenderLogger.js +205 -0
- package/dist/utils/RerenderLogger.js.map +1 -0
- package/dist/utils/generateInstanceId.d.ts +11 -0
- package/dist/utils/generateInstanceId.d.ts.map +1 -0
- package/dist/utils/generateInstanceId.js +50 -0
- package/dist/utils/generateInstanceId.js.map +1 -0
- package/dist/utils/shallowEqual.d.ts +9 -0
- package/dist/utils/shallowEqual.d.ts.map +1 -0
- package/dist/utils/shallowEqual.js +35 -0
- package/dist/utils/shallowEqual.js.map +1 -0
- package/dist/utils/uuid.d.ts +10 -0
- package/dist/utils/uuid.d.ts.map +1 -0
- package/dist/utils/uuid.js +24 -0
- package/dist/utils/uuid.js.map +1 -0
- package/package.json +17 -6
- package/src/Blac.ts +0 -802
- package/src/Bloc.ts +0 -180
- package/src/BlocBase.ts +0 -398
- package/src/Cubit.ts +0 -68
- package/src/__tests__/Blac.config.test.ts +0 -79
- package/src/__tests__/Bloc.event.test.ts +0 -394
- package/src/__tests__/BlocBase.subscription.test.ts +0 -349
- package/src/__tests__/Cubit.test.ts +0 -462
- package/src/__tests__/memory-leaks.test.ts +0 -383
- package/src/__tests__/plugins.test.ts +0 -464
- package/src/adapter/BlacAdapter.ts +0 -467
- package/src/adapter/ProxyFactory.ts +0 -249
- package/src/adapter/__tests__/BlacAdapter.test.ts +0 -555
- package/src/adapter/__tests__/ProxyFactory.test.ts +0 -655
- package/src/errors/BlacError.ts +0 -69
- package/src/errors/ErrorManager.ts +0 -101
- package/src/errors/handleError.ts +0 -114
- package/src/errors/index.ts +0 -13
- package/src/lifecycle/BlocLifecycle.ts +0 -156
- package/src/lifecycle/index.ts +0 -1
- package/src/plugins/BlocPluginRegistry.ts +0 -240
- package/src/plugins/SystemPluginRegistry.ts +0 -242
- package/src/plugins/__tests__/BlocPluginRegistry.test.ts +0 -450
- package/src/plugins/__tests__/SystemPluginRegistry.test.ts +0 -404
- package/src/plugins/types.ts +0 -129
- package/src/subscription/README.md +0 -132
- package/src/subscription/SubscriptionManager.ts +0 -395
- package/src/subscription/__tests__/SubscriptionManager.test.ts +0 -355
- package/src/subscription/types.ts +0 -90
- package/src/testing.ts +0 -246
- package/src/utils/BatchingManager.ts +0 -59
- package/src/utils/RerenderLogger.ts +0 -265
- package/src/utils/__tests__/generateInstanceId.test.ts +0 -104
- package/src/utils/__tests__/shallowEqual.test.ts +0 -171
- package/src/utils/__tests__/uuid.test.ts +0 -144
- package/src/utils/generateInstanceId.ts +0 -54
- package/src/utils/shallowEqual.ts +0 -40
- 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
|
-
});
|