bunsane 0.1.0 → 0.1.2
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/.github/workflows/deploy-docs.yml +57 -0
- package/LICENSE.md +1 -1
- package/README.md +2 -28
- package/TODO.md +8 -1
- package/bun.lock +3 -0
- package/config/upload.config.ts +135 -0
- package/core/App.ts +168 -4
- package/core/ArcheType.ts +122 -0
- package/core/BatchLoader.ts +100 -0
- package/core/ComponentRegistry.ts +4 -3
- package/core/Components.ts +2 -2
- package/core/Decorators.ts +15 -8
- package/core/Entity.ts +193 -14
- package/core/EntityCache.ts +15 -0
- package/core/EntityHookManager.ts +855 -0
- package/core/EntityManager.ts +12 -2
- package/core/ErrorHandler.ts +64 -7
- package/core/FileValidator.ts +284 -0
- package/core/Query.ts +503 -85
- package/core/RequestContext.ts +24 -0
- package/core/RequestLoaders.ts +89 -0
- package/core/SchedulerManager.ts +710 -0
- package/core/UploadManager.ts +261 -0
- package/core/components/UploadComponent.ts +206 -0
- package/core/decorators/EntityHooks.ts +190 -0
- package/core/decorators/ScheduledTask.ts +83 -0
- package/core/events/EntityLifecycleEvents.ts +177 -0
- package/core/processors/ImageProcessor.ts +423 -0
- package/core/storage/LocalStorageProvider.ts +290 -0
- package/core/storage/StorageProvider.ts +112 -0
- package/database/DatabaseHelper.ts +183 -58
- package/database/index.ts +5 -5
- package/database/sqlHelpers.ts +7 -0
- package/docs/README.md +149 -0
- package/docs/_coverpage.md +36 -0
- package/docs/_sidebar.md +23 -0
- package/docs/api/core.md +568 -0
- package/docs/api/hooks.md +554 -0
- package/docs/api/index.md +222 -0
- package/docs/api/query.md +678 -0
- package/docs/api/service.md +744 -0
- package/docs/core-concepts/archetypes.md +512 -0
- package/docs/core-concepts/components.md +498 -0
- package/docs/core-concepts/entity.md +314 -0
- package/docs/core-concepts/hooks.md +683 -0
- package/docs/core-concepts/query.md +588 -0
- package/docs/core-concepts/services.md +647 -0
- package/docs/examples/code-examples.md +425 -0
- package/docs/getting-started.md +337 -0
- package/docs/index.html +97 -0
- package/gql/Generator.ts +58 -35
- package/gql/decorators/Upload.ts +176 -0
- package/gql/helpers.ts +67 -0
- package/gql/index.ts +65 -31
- package/gql/types.ts +1 -1
- package/index.ts +79 -11
- package/package.json +19 -10
- package/rest/Generator.ts +3 -0
- package/rest/index.ts +22 -0
- package/service/Service.ts +1 -1
- package/service/ServiceRegistry.ts +10 -6
- package/service/index.ts +12 -1
- package/tests/bench/insert.bench.ts +59 -0
- package/tests/bench/relations.bench.ts +269 -0
- package/tests/bench/sorting.bench.ts +415 -0
- package/tests/component-hooks.test.ts +1409 -0
- package/tests/component.test.ts +338 -0
- package/tests/errorHandling.test.ts +155 -0
- package/tests/hooks.test.ts +666 -0
- package/tests/query-sorting.test.ts +101 -0
- package/tests/relations.test.ts +169 -0
- package/tests/scheduler.test.ts +724 -0
- package/tsconfig.json +35 -34
- package/types/graphql.types.ts +87 -0
- package/types/hooks.types.ts +141 -0
- package/types/scheduler.types.ts +165 -0
- package/types/upload.types.ts +184 -0
- package/upload/index.ts +140 -0
- package/utils/UploadHelper.ts +305 -0
- package/utils/cronParser.ts +366 -0
- package/utils/errorMessages.ts +151 -0
- package/core/Events.ts +0 -0
|
@@ -0,0 +1,554 @@
|
|
|
1
|
+
# Hooks API Reference
|
|
2
|
+
|
|
3
|
+
This page provides detailed API reference for BunSane's hook system and lifecycle events.
|
|
4
|
+
|
|
5
|
+
## 🪝 EntityHookManager Class
|
|
6
|
+
|
|
7
|
+
The `EntityHookManager` class manages lifecycle hooks for entities and components.
|
|
8
|
+
|
|
9
|
+
### Static Methods
|
|
10
|
+
|
|
11
|
+
#### `EntityHookManager.on(event, handler)`
|
|
12
|
+
|
|
13
|
+
Registers an event handler.
|
|
14
|
+
|
|
15
|
+
```typescript
|
|
16
|
+
static on(event: string, handler: HookHandler): void
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
**Parameters:**
|
|
20
|
+
- `event`: String - Event name
|
|
21
|
+
- `handler`: HookHandler - Event handler function
|
|
22
|
+
|
|
23
|
+
**Example:**
|
|
24
|
+
```typescript
|
|
25
|
+
EntityHookManager.on('entity:created', async (entity) => {
|
|
26
|
+
console.log('Entity created:', entity.id);
|
|
27
|
+
});
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
#### `EntityHookManager.off(event, handler)`
|
|
31
|
+
|
|
32
|
+
Removes an event handler.
|
|
33
|
+
|
|
34
|
+
```typescript
|
|
35
|
+
static off(event: string, handler: HookHandler): void
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
**Parameters:**
|
|
39
|
+
- `event`: String - Event name
|
|
40
|
+
- `handler`: HookHandler - Event handler function
|
|
41
|
+
|
|
42
|
+
**Example:**
|
|
43
|
+
```typescript
|
|
44
|
+
const handler = (entity) => console.log('Entity created');
|
|
45
|
+
EntityHookManager.on('entity:created', handler);
|
|
46
|
+
// Later...
|
|
47
|
+
EntityHookManager.off('entity:created', handler);
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
#### `EntityHookManager.emit(event, data)`
|
|
51
|
+
|
|
52
|
+
Emits an event to all registered handlers.
|
|
53
|
+
|
|
54
|
+
```typescript
|
|
55
|
+
static async emit(event: string, data: any): Promise<void>
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
**Parameters:**
|
|
59
|
+
- `event`: String - Event name
|
|
60
|
+
- `data`: Any - Event data
|
|
61
|
+
|
|
62
|
+
**Returns:** `Promise<void>`
|
|
63
|
+
|
|
64
|
+
**Example:**
|
|
65
|
+
```typescript
|
|
66
|
+
await EntityHookManager.emit('custom:event', { message: 'Hello' });
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
#### `EntityHookManager.clear(event)`
|
|
70
|
+
|
|
71
|
+
Clears all handlers for an event.
|
|
72
|
+
|
|
73
|
+
```typescript
|
|
74
|
+
static clear(event?: string): void
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
**Parameters:**
|
|
78
|
+
- `event` (optional): String - Event name, clears all if not specified
|
|
79
|
+
|
|
80
|
+
**Example:**
|
|
81
|
+
```typescript
|
|
82
|
+
// Clear all handlers for entity:created
|
|
83
|
+
EntityHookManager.clear('entity:created');
|
|
84
|
+
|
|
85
|
+
// Clear all handlers
|
|
86
|
+
EntityHookManager.clear();
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## 🎣 Hook Types
|
|
90
|
+
|
|
91
|
+
### HookHandler
|
|
92
|
+
|
|
93
|
+
Function signature for hook handlers.
|
|
94
|
+
|
|
95
|
+
```typescript
|
|
96
|
+
type HookHandler = (data: any, context?: HookContext) => Promise<void> | void;
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
### HookContext
|
|
100
|
+
|
|
101
|
+
Context information passed to hook handlers.
|
|
102
|
+
|
|
103
|
+
```typescript
|
|
104
|
+
interface HookContext {
|
|
105
|
+
userId?: string;
|
|
106
|
+
requestId?: string;
|
|
107
|
+
timestamp: Date;
|
|
108
|
+
source?: string;
|
|
109
|
+
}
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
## 📋 Entity Lifecycle Events
|
|
113
|
+
|
|
114
|
+
### Entity Events
|
|
115
|
+
|
|
116
|
+
#### `entity:creating`
|
|
117
|
+
|
|
118
|
+
Fired before an entity is created.
|
|
119
|
+
|
|
120
|
+
```typescript
|
|
121
|
+
EntityHookManager.on('entity:creating', async (entity: Entity) => {
|
|
122
|
+
// Validate entity data
|
|
123
|
+
// Set default values
|
|
124
|
+
// Generate additional data
|
|
125
|
+
});
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
#### `entity:created`
|
|
129
|
+
|
|
130
|
+
Fired after an entity is created.
|
|
131
|
+
|
|
132
|
+
```typescript
|
|
133
|
+
EntityHookManager.on('entity:created', async (entity: Entity) => {
|
|
134
|
+
// Send notifications
|
|
135
|
+
// Update caches
|
|
136
|
+
// Log creation
|
|
137
|
+
console.log(`Entity ${entity.id} created`);
|
|
138
|
+
});
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
#### `entity:saving`
|
|
142
|
+
|
|
143
|
+
Fired before an entity is saved.
|
|
144
|
+
|
|
145
|
+
```typescript
|
|
146
|
+
EntityHookManager.on('entity:saving', async (entity: Entity) => {
|
|
147
|
+
// Validate data
|
|
148
|
+
// Update timestamps
|
|
149
|
+
entity.set(TimestampComponent, { updatedAt: new Date() });
|
|
150
|
+
});
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
#### `entity:saved`
|
|
154
|
+
|
|
155
|
+
Fired after an entity is saved.
|
|
156
|
+
|
|
157
|
+
```typescript
|
|
158
|
+
EntityHookManager.on('entity:saved', async (entity: Entity) => {
|
|
159
|
+
// Update search indexes
|
|
160
|
+
// Send real-time updates
|
|
161
|
+
// Trigger background jobs
|
|
162
|
+
});
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
#### `entity:updating`
|
|
166
|
+
|
|
167
|
+
Fired before an entity is updated.
|
|
168
|
+
|
|
169
|
+
```typescript
|
|
170
|
+
EntityHookManager.on('entity:updating', async (entity: Entity) => {
|
|
171
|
+
// Validate update permissions
|
|
172
|
+
// Create audit trail
|
|
173
|
+
// Backup old data
|
|
174
|
+
});
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
#### `entity:updated`
|
|
178
|
+
|
|
179
|
+
Fired after an entity is updated.
|
|
180
|
+
|
|
181
|
+
```typescript
|
|
182
|
+
EntityHookManager.on('entity:updated', async (entity: Entity) => {
|
|
183
|
+
// Send notifications
|
|
184
|
+
// Update caches
|
|
185
|
+
// Log changes
|
|
186
|
+
});
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
#### `entity:deleting`
|
|
190
|
+
|
|
191
|
+
Fired before an entity is deleted.
|
|
192
|
+
|
|
193
|
+
```typescript
|
|
194
|
+
EntityHookManager.on('entity:deleting', async (entity: Entity) => {
|
|
195
|
+
// Check deletion permissions
|
|
196
|
+
// Create backup
|
|
197
|
+
// Cascade delete related data
|
|
198
|
+
});
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
#### `entity:deleted`
|
|
202
|
+
|
|
203
|
+
Fired after an entity is deleted.
|
|
204
|
+
|
|
205
|
+
```typescript
|
|
206
|
+
EntityHookManager.on('entity:deleted', async (entity: Entity) => {
|
|
207
|
+
// Clean up related data
|
|
208
|
+
// Update caches
|
|
209
|
+
// Send notifications
|
|
210
|
+
});
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
### Component Events
|
|
214
|
+
|
|
215
|
+
#### `component:attaching`
|
|
216
|
+
|
|
217
|
+
Fired before a component is attached to an entity.
|
|
218
|
+
|
|
219
|
+
```typescript
|
|
220
|
+
EntityHookManager.on('component:attaching', async (data: {
|
|
221
|
+
entity: Entity;
|
|
222
|
+
component: BaseComponent;
|
|
223
|
+
componentType: string;
|
|
224
|
+
}) => {
|
|
225
|
+
// Validate component compatibility
|
|
226
|
+
// Set up component relationships
|
|
227
|
+
});
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
#### `component:attached`
|
|
231
|
+
|
|
232
|
+
Fired after a component is attached to an entity.
|
|
233
|
+
|
|
234
|
+
```typescript
|
|
235
|
+
EntityHookManager.on('component:attached', async (data: {
|
|
236
|
+
entity: Entity;
|
|
237
|
+
component: BaseComponent;
|
|
238
|
+
componentType: string;
|
|
239
|
+
}) => {
|
|
240
|
+
// Initialize component data
|
|
241
|
+
// Update entity state
|
|
242
|
+
});
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
#### `component:detaching`
|
|
246
|
+
|
|
247
|
+
Fired before a component is detached from an entity.
|
|
248
|
+
|
|
249
|
+
```typescript
|
|
250
|
+
EntityHookManager.on('component:detaching', async (data: {
|
|
251
|
+
entity: Entity;
|
|
252
|
+
component: BaseComponent;
|
|
253
|
+
componentType: string;
|
|
254
|
+
}) => {
|
|
255
|
+
// Clean up component resources
|
|
256
|
+
// Update relationships
|
|
257
|
+
});
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
#### `component:detached`
|
|
261
|
+
|
|
262
|
+
Fired after a component is detached from an entity.
|
|
263
|
+
|
|
264
|
+
```typescript
|
|
265
|
+
EntityHookManager.on('component:detached', async (data: {
|
|
266
|
+
entity: Entity;
|
|
267
|
+
component: BaseComponent;
|
|
268
|
+
componentType: string;
|
|
269
|
+
}) => {
|
|
270
|
+
// Update caches
|
|
271
|
+
// Send notifications
|
|
272
|
+
});
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
## 🔧 Component Hooks
|
|
276
|
+
|
|
277
|
+
### @Hook Decorator
|
|
278
|
+
|
|
279
|
+
Marks a method as a component hook.
|
|
280
|
+
|
|
281
|
+
```typescript
|
|
282
|
+
@Hook(event: string): MethodDecorator
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
**Parameters:**
|
|
286
|
+
- `event`: String - Hook event name
|
|
287
|
+
|
|
288
|
+
**Example:**
|
|
289
|
+
```typescript
|
|
290
|
+
@Component
|
|
291
|
+
export class UserProfile extends BaseComponent {
|
|
292
|
+
@CompData()
|
|
293
|
+
name: string = '';
|
|
294
|
+
|
|
295
|
+
@CompData()
|
|
296
|
+
email: string = '';
|
|
297
|
+
|
|
298
|
+
@Hook('component:attached')
|
|
299
|
+
async onAttached(entity: Entity): Promise<void> {
|
|
300
|
+
console.log(`UserProfile attached to entity ${entity.id}`);
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
@Hook('component:detaching')
|
|
304
|
+
async onDetaching(entity: Entity): Promise<void> {
|
|
305
|
+
console.log(`UserProfile detaching from entity ${entity.id}`);
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
### Built-in Component Hooks
|
|
311
|
+
|
|
312
|
+
```typescript
|
|
313
|
+
@Component
|
|
314
|
+
export class AuditableComponent extends BaseComponent {
|
|
315
|
+
@CompData()
|
|
316
|
+
createdAt: Date = new Date();
|
|
317
|
+
|
|
318
|
+
@CompData()
|
|
319
|
+
updatedAt: Date = new Date();
|
|
320
|
+
|
|
321
|
+
@CompData()
|
|
322
|
+
createdBy: string = '';
|
|
323
|
+
|
|
324
|
+
@CompData()
|
|
325
|
+
updatedBy: string = '';
|
|
326
|
+
|
|
327
|
+
@Hook('component:attaching')
|
|
328
|
+
async setCreationData(entity: Entity): Promise<void> {
|
|
329
|
+
this.createdAt = new Date();
|
|
330
|
+
this.createdBy = RequestContext.getCurrentUser()?.id || 'system';
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
@Hook('component:saving')
|
|
334
|
+
async setUpdateData(entity: Entity): Promise<void> {
|
|
335
|
+
this.updatedAt = new Date();
|
|
336
|
+
this.updatedBy = RequestContext.getCurrentUser()?.id || 'system';
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
## 🎯 Custom Hooks
|
|
342
|
+
|
|
343
|
+
### Creating Custom Events
|
|
344
|
+
|
|
345
|
+
```typescript
|
|
346
|
+
// Define custom event types
|
|
347
|
+
export const CUSTOM_EVENTS = {
|
|
348
|
+
USER_REGISTERED: 'user:registered',
|
|
349
|
+
ORDER_COMPLETED: 'order:completed',
|
|
350
|
+
PAYMENT_FAILED: 'payment:failed'
|
|
351
|
+
} as const;
|
|
352
|
+
|
|
353
|
+
// Emit custom events
|
|
354
|
+
export class UserService extends BaseService {
|
|
355
|
+
async registerUser(userData: UserData): Promise<Entity> {
|
|
356
|
+
const user = Entity.Create();
|
|
357
|
+
user.add(UserProfile, userData);
|
|
358
|
+
await user.save();
|
|
359
|
+
|
|
360
|
+
// Emit custom event
|
|
361
|
+
await EntityHookManager.emit(CUSTOM_EVENTS.USER_REGISTERED, {
|
|
362
|
+
userId: user.id,
|
|
363
|
+
email: userData.email,
|
|
364
|
+
timestamp: new Date()
|
|
365
|
+
});
|
|
366
|
+
|
|
367
|
+
return user;
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
```
|
|
371
|
+
|
|
372
|
+
### Handling Custom Events
|
|
373
|
+
|
|
374
|
+
```typescript
|
|
375
|
+
// Handle custom events
|
|
376
|
+
EntityHookManager.on(CUSTOM_EVENTS.USER_REGISTERED, async (data) => {
|
|
377
|
+
console.log('New user registered:', data.email);
|
|
378
|
+
|
|
379
|
+
// Send welcome email
|
|
380
|
+
await emailService.sendWelcomeEmail(data.email);
|
|
381
|
+
|
|
382
|
+
// Create user preferences
|
|
383
|
+
const user = await Entity.FindById(data.userId);
|
|
384
|
+
if (user) {
|
|
385
|
+
user.add(UserPreferences, {
|
|
386
|
+
theme: 'light',
|
|
387
|
+
notifications: true
|
|
388
|
+
});
|
|
389
|
+
await user.save();
|
|
390
|
+
}
|
|
391
|
+
});
|
|
392
|
+
```
|
|
393
|
+
|
|
394
|
+
## 🔄 Async Hook Patterns
|
|
395
|
+
|
|
396
|
+
### Sequential Processing
|
|
397
|
+
|
|
398
|
+
```typescript
|
|
399
|
+
EntityHookManager.on('entity:saving', async (entity) => {
|
|
400
|
+
// Step 1: Validate data
|
|
401
|
+
await validateEntityData(entity);
|
|
402
|
+
|
|
403
|
+
// Step 2: Update timestamps
|
|
404
|
+
entity.set(TimestampComponent, { updatedAt: new Date() });
|
|
405
|
+
|
|
406
|
+
// Step 3: Generate audit trail
|
|
407
|
+
await createAuditEntry(entity);
|
|
408
|
+
});
|
|
409
|
+
```
|
|
410
|
+
|
|
411
|
+
### Parallel Processing
|
|
412
|
+
|
|
413
|
+
```typescript
|
|
414
|
+
EntityHookManager.on('entity:created', async (entity) => {
|
|
415
|
+
// Run multiple operations in parallel
|
|
416
|
+
await Promise.all([
|
|
417
|
+
sendWelcomeEmail(entity),
|
|
418
|
+
createUserStats(entity),
|
|
419
|
+
updateUserCount(),
|
|
420
|
+
logUserCreation(entity)
|
|
421
|
+
]);
|
|
422
|
+
});
|
|
423
|
+
```
|
|
424
|
+
|
|
425
|
+
### Conditional Hooks
|
|
426
|
+
|
|
427
|
+
```typescript
|
|
428
|
+
EntityHookManager.on('entity:updating', async (entity) => {
|
|
429
|
+
const profile = entity.get(UserProfile);
|
|
430
|
+
|
|
431
|
+
// Only run for premium users
|
|
432
|
+
if (profile.tier === 'premium') {
|
|
433
|
+
await sendPremiumNotification(entity);
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
// Only run if email changed
|
|
437
|
+
if (entity.hasChanged('email')) {
|
|
438
|
+
await sendEmailVerification(entity);
|
|
439
|
+
}
|
|
440
|
+
});
|
|
441
|
+
```
|
|
442
|
+
|
|
443
|
+
## 🛡️ Error Handling in Hooks
|
|
444
|
+
|
|
445
|
+
### Hook Error Handling
|
|
446
|
+
|
|
447
|
+
```typescript
|
|
448
|
+
EntityHookManager.on('entity:saving', async (entity) => {
|
|
449
|
+
try {
|
|
450
|
+
await validateEntityData(entity);
|
|
451
|
+
await updateTimestamps(entity);
|
|
452
|
+
} catch (error) {
|
|
453
|
+
console.error('Hook error:', error);
|
|
454
|
+
// Don't rethrow - hooks should not block entity operations
|
|
455
|
+
// Log error and continue
|
|
456
|
+
}
|
|
457
|
+
});
|
|
458
|
+
```
|
|
459
|
+
|
|
460
|
+
### Hook Error Propagation
|
|
461
|
+
|
|
462
|
+
```typescript
|
|
463
|
+
// For critical validation hooks
|
|
464
|
+
EntityHookManager.on('entity:saving', async (entity) => {
|
|
465
|
+
const validationErrors = await validateCriticalData(entity);
|
|
466
|
+
if (validationErrors.length > 0) {
|
|
467
|
+
throw new ValidationError('Critical validation failed', validationErrors);
|
|
468
|
+
}
|
|
469
|
+
});
|
|
470
|
+
```
|
|
471
|
+
|
|
472
|
+
## 🚀 Performance Optimization
|
|
473
|
+
|
|
474
|
+
### Hook Debouncing
|
|
475
|
+
|
|
476
|
+
```typescript
|
|
477
|
+
class HookDebouncer {
|
|
478
|
+
private timeouts = new Map<string, NodeJS.Timeout>();
|
|
479
|
+
|
|
480
|
+
debounce(key: string, fn: () => void, delay: number = 1000): void {
|
|
481
|
+
const existing = this.timeouts.get(key);
|
|
482
|
+
if (existing) {
|
|
483
|
+
clearTimeout(existing);
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
this.timeouts.set(key, setTimeout(() => {
|
|
487
|
+
fn();
|
|
488
|
+
this.timeouts.delete(key);
|
|
489
|
+
}, delay));
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
const debouncer = new HookDebouncer();
|
|
494
|
+
|
|
495
|
+
EntityHookManager.on('entity:updated', (entity) => {
|
|
496
|
+
debouncer.debounce(`update-cache-${entity.id}`, async () => {
|
|
497
|
+
await updateCache(entity);
|
|
498
|
+
});
|
|
499
|
+
});
|
|
500
|
+
```
|
|
501
|
+
|
|
502
|
+
### Hook Filtering
|
|
503
|
+
|
|
504
|
+
```typescript
|
|
505
|
+
// Only run hooks for specific component types
|
|
506
|
+
EntityHookManager.on('component:attached', async (data) => {
|
|
507
|
+
if (data.componentType === 'UserProfile') {
|
|
508
|
+
await handleUserProfileAttached(data.entity);
|
|
509
|
+
}
|
|
510
|
+
});
|
|
511
|
+
```
|
|
512
|
+
|
|
513
|
+
### Batch Hook Processing
|
|
514
|
+
|
|
515
|
+
```typescript
|
|
516
|
+
class BatchProcessor {
|
|
517
|
+
private queue: any[] = [];
|
|
518
|
+
private processing = false;
|
|
519
|
+
|
|
520
|
+
async add(item: any): Promise<void> {
|
|
521
|
+
this.queue.push(item);
|
|
522
|
+
if (!this.processing) {
|
|
523
|
+
await this.processQueue();
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
private async processQueue(): Promise<void> {
|
|
528
|
+
this.processing = true;
|
|
529
|
+
|
|
530
|
+
while (this.queue.length > 0) {
|
|
531
|
+
const batch = this.queue.splice(0, 10); // Process 10 at a time
|
|
532
|
+
await Promise.all(batch.map(item => processItem(item)));
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
this.processing = false;
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
const batchProcessor = new BatchProcessor();
|
|
540
|
+
|
|
541
|
+
EntityHookManager.on('entity:created', (entity) => {
|
|
542
|
+
batchProcessor.add(entity);
|
|
543
|
+
});
|
|
544
|
+
```
|
|
545
|
+
|
|
546
|
+
## 🔗 Related APIs
|
|
547
|
+
|
|
548
|
+
- **[Entity API](core.md)** - Entity operations
|
|
549
|
+
- **[Component API](core.md)** - Component management
|
|
550
|
+
- **[Service API](service.md)** - Business logic layer
|
|
551
|
+
|
|
552
|
+
---
|
|
553
|
+
|
|
554
|
+
*Need more details? Check the [Upload API](upload.md) for file handling operations!* 🚀
|