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.
Files changed (82) hide show
  1. package/.github/workflows/deploy-docs.yml +57 -0
  2. package/LICENSE.md +1 -1
  3. package/README.md +2 -28
  4. package/TODO.md +8 -1
  5. package/bun.lock +3 -0
  6. package/config/upload.config.ts +135 -0
  7. package/core/App.ts +168 -4
  8. package/core/ArcheType.ts +122 -0
  9. package/core/BatchLoader.ts +100 -0
  10. package/core/ComponentRegistry.ts +4 -3
  11. package/core/Components.ts +2 -2
  12. package/core/Decorators.ts +15 -8
  13. package/core/Entity.ts +193 -14
  14. package/core/EntityCache.ts +15 -0
  15. package/core/EntityHookManager.ts +855 -0
  16. package/core/EntityManager.ts +12 -2
  17. package/core/ErrorHandler.ts +64 -7
  18. package/core/FileValidator.ts +284 -0
  19. package/core/Query.ts +503 -85
  20. package/core/RequestContext.ts +24 -0
  21. package/core/RequestLoaders.ts +89 -0
  22. package/core/SchedulerManager.ts +710 -0
  23. package/core/UploadManager.ts +261 -0
  24. package/core/components/UploadComponent.ts +206 -0
  25. package/core/decorators/EntityHooks.ts +190 -0
  26. package/core/decorators/ScheduledTask.ts +83 -0
  27. package/core/events/EntityLifecycleEvents.ts +177 -0
  28. package/core/processors/ImageProcessor.ts +423 -0
  29. package/core/storage/LocalStorageProvider.ts +290 -0
  30. package/core/storage/StorageProvider.ts +112 -0
  31. package/database/DatabaseHelper.ts +183 -58
  32. package/database/index.ts +5 -5
  33. package/database/sqlHelpers.ts +7 -0
  34. package/docs/README.md +149 -0
  35. package/docs/_coverpage.md +36 -0
  36. package/docs/_sidebar.md +23 -0
  37. package/docs/api/core.md +568 -0
  38. package/docs/api/hooks.md +554 -0
  39. package/docs/api/index.md +222 -0
  40. package/docs/api/query.md +678 -0
  41. package/docs/api/service.md +744 -0
  42. package/docs/core-concepts/archetypes.md +512 -0
  43. package/docs/core-concepts/components.md +498 -0
  44. package/docs/core-concepts/entity.md +314 -0
  45. package/docs/core-concepts/hooks.md +683 -0
  46. package/docs/core-concepts/query.md +588 -0
  47. package/docs/core-concepts/services.md +647 -0
  48. package/docs/examples/code-examples.md +425 -0
  49. package/docs/getting-started.md +337 -0
  50. package/docs/index.html +97 -0
  51. package/gql/Generator.ts +58 -35
  52. package/gql/decorators/Upload.ts +176 -0
  53. package/gql/helpers.ts +67 -0
  54. package/gql/index.ts +65 -31
  55. package/gql/types.ts +1 -1
  56. package/index.ts +79 -11
  57. package/package.json +19 -10
  58. package/rest/Generator.ts +3 -0
  59. package/rest/index.ts +22 -0
  60. package/service/Service.ts +1 -1
  61. package/service/ServiceRegistry.ts +10 -6
  62. package/service/index.ts +12 -1
  63. package/tests/bench/insert.bench.ts +59 -0
  64. package/tests/bench/relations.bench.ts +269 -0
  65. package/tests/bench/sorting.bench.ts +415 -0
  66. package/tests/component-hooks.test.ts +1409 -0
  67. package/tests/component.test.ts +338 -0
  68. package/tests/errorHandling.test.ts +155 -0
  69. package/tests/hooks.test.ts +666 -0
  70. package/tests/query-sorting.test.ts +101 -0
  71. package/tests/relations.test.ts +169 -0
  72. package/tests/scheduler.test.ts +724 -0
  73. package/tsconfig.json +35 -34
  74. package/types/graphql.types.ts +87 -0
  75. package/types/hooks.types.ts +141 -0
  76. package/types/scheduler.types.ts +165 -0
  77. package/types/upload.types.ts +184 -0
  78. package/upload/index.ts +140 -0
  79. package/utils/UploadHelper.ts +305 -0
  80. package/utils/cronParser.ts +366 -0
  81. package/utils/errorMessages.ts +151 -0
  82. 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!* 🚀