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,498 @@
1
+ # Component System
2
+
3
+ Components are the fundamental building blocks of BunSane's Entity-Component-System (ECS) architecture. They define the data and behavior that can be attached to entities, providing unparalleled flexibility in data modeling.
4
+
5
+ ## 🎯 What is a Component?
6
+
7
+ A Component is a pure data structure that represents a specific aspect or capability of an entity. Unlike traditional object-oriented classes, components are:
8
+
9
+ - **Composable**: Can be mixed and matched on entities as needed
10
+ - **Type-Safe**: Full TypeScript support with compile-time guarantees
11
+ - **Database-Backed**: Automatically persisted to PostgreSQL
12
+ - **Decorator-Driven**: Use simple decorators to define data properties
13
+ - **Indexed**: Support for database indexing on frequently queried fields
14
+
15
+ ## 🏗️ Creating Components
16
+
17
+ ### Basic Component Structure
18
+
19
+ ```typescript
20
+ import { Component, CompData, BaseComponent } from 'bunsane';
21
+
22
+ @Component
23
+ export class UserProfile extends BaseComponent {
24
+ @CompData()
25
+ name: string = '';
26
+
27
+ @CompData()
28
+ email: string = '';
29
+
30
+ @CompData({ indexed: true })
31
+ username: string = '';
32
+ }
33
+ ```
34
+
35
+ ### Component Registration
36
+
37
+ Components must be decorated with `@Component` and extend `BaseComponent`. The framework automatically:
38
+
39
+ - Registers the component with the ComponentRegistry
40
+ - Generates a unique type ID for database storage
41
+ - Creates database tables for component data
42
+ - Sets up indexing for marked properties
43
+
44
+ ## 📊 Component Data Properties
45
+
46
+ ### Basic Data Properties
47
+
48
+ ```typescript
49
+ @Component
50
+ export class ProductInfo extends BaseComponent {
51
+ @CompData()
52
+ title: string = '';
53
+
54
+ @CompData()
55
+ description: string = '';
56
+
57
+ @CompData()
58
+ price: number = 0;
59
+
60
+ @CompData()
61
+ inStock: boolean = true;
62
+ }
63
+ ```
64
+
65
+ ### Indexed Properties
66
+
67
+ ```typescript
68
+ @Component
69
+ export class BlogPost extends BaseComponent {
70
+ @CompData()
71
+ title: string = '';
72
+
73
+ @CompData()
74
+ content: string = '';
75
+
76
+ @CompData({ indexed: true })
77
+ authorId: string = '';
78
+
79
+ @CompData({ indexed: true })
80
+ publishedAt: Date = new Date();
81
+
82
+ @CompData({ indexed: true })
83
+ tags: string[] = [];
84
+ }
85
+ ```
86
+
87
+ ### Complex Data Types
88
+
89
+ ```typescript
90
+ @Component
91
+ export class UserPreferences extends BaseComponent {
92
+ @CompData()
93
+ theme: 'light' | 'dark' | 'auto' = 'light';
94
+
95
+ @CompData()
96
+ notifications: {
97
+ email: boolean;
98
+ push: boolean;
99
+ sms: boolean;
100
+ } = {
101
+ email: true,
102
+ push: false,
103
+ sms: false
104
+ };
105
+
106
+ @CompData()
107
+ language: string = 'en';
108
+
109
+ @CompData()
110
+ timezone: string = 'UTC';
111
+ }
112
+ ```
113
+
114
+ ## 🔧 Component Methods
115
+
116
+ ### Custom Methods
117
+
118
+ Components can include methods for data manipulation and business logic:
119
+
120
+ ```typescript
121
+ @Component
122
+ export class ShoppingCart extends BaseComponent {
123
+ @CompData()
124
+ items: CartItem[] = [];
125
+
126
+ @CompData()
127
+ total: number = 0;
128
+
129
+ // Custom method to add items
130
+ addItem(productId: string, quantity: number, price: number) {
131
+ const existingItem = this.items.find(item => item.productId === productId);
132
+
133
+ if (existingItem) {
134
+ existingItem.quantity += quantity;
135
+ } else {
136
+ this.items.push({
137
+ productId,
138
+ quantity,
139
+ price,
140
+ addedAt: new Date()
141
+ });
142
+ }
143
+
144
+ this.recalculateTotal();
145
+ }
146
+
147
+ // Custom method to remove items
148
+ removeItem(productId: string) {
149
+ this.items = this.items.filter(item => item.productId !== productId);
150
+ this.recalculateTotal();
151
+ }
152
+
153
+ // Private helper method
154
+ private recalculateTotal() {
155
+ this.total = this.items.reduce((sum, item) => sum + (item.price * item.quantity), 0);
156
+ }
157
+ }
158
+
159
+ interface CartItem {
160
+ productId: string;
161
+ quantity: number;
162
+ price: number;
163
+ addedAt: Date;
164
+ }
165
+ ```
166
+
167
+ ## 💾 Component Persistence
168
+
169
+ ### Automatic Database Schema
170
+
171
+ When you create a component, BunSane automatically:
172
+
173
+ 1. **Creates Database Tables**:
174
+ - `components` - Stores component data as JSON
175
+ - `entity_components` - Links entities to their components
176
+ - `component_indexes` - Stores indexed field values
177
+
178
+ 2. **Handles Data Types**:
179
+ - Primitive types (string, number, boolean)
180
+ - Complex objects and arrays
181
+ - Date objects (stored as ISO strings)
182
+ - Custom classes (serialized as JSON)
183
+
184
+ ### Manual Persistence
185
+
186
+ ```typescript
187
+ // Create component instance
188
+ const profile = new UserProfile();
189
+ profile.name = 'John Doe';
190
+ profile.email = 'john@example.com';
191
+ profile.username = 'johndoe';
192
+
193
+ // Save to database (called automatically when entity is saved)
194
+ await profile.save(entityId);
195
+ ```
196
+
197
+ ## 🔍 Component Queries
198
+
199
+ ### Accessing Component Data
200
+
201
+ ```typescript
202
+ // Get component from entity
203
+ const userEntity = await Entity.FindById(userId);
204
+ const profile = userEntity.get(UserProfile);
205
+
206
+ // Access data properties
207
+ console.log(profile.name); // 'John Doe'
208
+ console.log(profile.email); // 'john@example.com'
209
+
210
+ // Get all data as object
211
+ const profileData = profile.data();
212
+ console.log(profileData); // { name: 'John Doe', email: 'john@example.com', username: 'johndoe' }
213
+ ```
214
+
215
+ ### Component Metadata
216
+
217
+ ```typescript
218
+ // Get component type ID
219
+ const typeId = profile.getTypeID();
220
+ console.log(typeId); // 'sha256_hash_of_UserProfile'
221
+
222
+ // Get component properties
223
+ const properties = profile.properties();
224
+ console.log(properties); // ['name', 'email', 'username']
225
+
226
+ // Get indexed properties
227
+ const indexedProps = profile.indexedProperties();
228
+ console.log(indexedProps); // ['username']
229
+ ```
230
+
231
+ ## 🔄 Component Updates
232
+
233
+ ### Updating Component Data
234
+
235
+ ```typescript
236
+ // Update existing component on entity
237
+ await userEntity.set(UserProfile, {
238
+ name: 'Jane Doe',
239
+ email: 'jane@example.com'
240
+ });
241
+
242
+ // Save changes
243
+ await userEntity.save();
244
+ ```
245
+
246
+ ### Partial Updates
247
+
248
+ ```typescript
249
+ // Update only specific fields
250
+ const profile = userEntity.get(UserProfile);
251
+ profile.name = 'Updated Name';
252
+
253
+ // Mark as dirty for saving
254
+ profile.setDirty(true);
255
+ await userEntity.save();
256
+ ```
257
+
258
+ ## 🏷️ Component Relationships
259
+
260
+ ### Reference Components
261
+
262
+ ```typescript
263
+ @Component
264
+ export class BlogPost extends BaseComponent {
265
+ @CompData()
266
+ title: string = '';
267
+
268
+ @CompData()
269
+ content: string = '';
270
+
271
+ @CompData({ indexed: true })
272
+ authorId: string = ''; // Reference to User entity
273
+
274
+ @CompData()
275
+ categoryIds: string[] = []; // References to Category entities
276
+ }
277
+
278
+ @Component
279
+ export class Comment extends BaseComponent {
280
+ @CompData()
281
+ content: string = '';
282
+
283
+ @CompData({ indexed: true })
284
+ postId: string = ''; // Reference to BlogPost entity
285
+
286
+ @CompData({ indexed: true })
287
+ authorId: string = ''; // Reference to User entity
288
+
289
+ @CompData()
290
+ parentId?: string; // Reference to parent Comment (for nested comments)
291
+ }
292
+ ```
293
+
294
+ ### Polymorphic References
295
+
296
+ ```typescript
297
+ @Component
298
+ export class Like extends BaseComponent {
299
+ @CompData({ indexed: true })
300
+ userId: string = '';
301
+
302
+ @CompData({ indexed: true })
303
+ targetType: 'post' | 'comment' | 'user' = 'post';
304
+
305
+ @CompData({ indexed: true })
306
+ targetId: string = '';
307
+
308
+ @CompData()
309
+ createdAt: Date = new Date();
310
+ }
311
+ ```
312
+
313
+ ## 🎨 Advanced Component Patterns
314
+
315
+ ### Validation Components
316
+
317
+ ```typescript
318
+ @Component
319
+ export class EmailValidation extends BaseComponent {
320
+ @CompData()
321
+ isValid: boolean = false;
322
+
323
+ @CompData()
324
+ validationErrors: string[] = [];
325
+
326
+ @CompData()
327
+ lastValidated: Date = new Date();
328
+
329
+ // Custom validation method
330
+ validateEmail(email: string): boolean {
331
+ const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
332
+ this.isValid = emailRegex.test(email);
333
+
334
+ if (!this.isValid) {
335
+ this.validationErrors = ['Invalid email format'];
336
+ } else {
337
+ this.validationErrors = [];
338
+ }
339
+
340
+ this.lastValidated = new Date();
341
+ return this.isValid;
342
+ }
343
+ }
344
+ ```
345
+
346
+ ### Computed Properties
347
+
348
+ ```typescript
349
+ @Component
350
+ export class ProductStats extends BaseComponent {
351
+ @CompData()
352
+ viewCount: number = 0;
353
+
354
+ @CompData()
355
+ purchaseCount: number = 0;
356
+
357
+ @CompData()
358
+ rating: number = 0;
359
+
360
+ @CompData()
361
+ reviewCount: number = 0;
362
+
363
+ // Computed property (not stored in DB)
364
+ get averageRating(): number {
365
+ return this.reviewCount > 0 ? this.rating / this.reviewCount : 0;
366
+ }
367
+
368
+ // Computed property for popularity score
369
+ get popularityScore(): number {
370
+ return (this.viewCount * 0.3) + (this.purchaseCount * 0.7);
371
+ }
372
+
373
+ // Method to update stats
374
+ recordView() {
375
+ this.viewCount++;
376
+ }
377
+
378
+ recordPurchase() {
379
+ this.purchaseCount++;
380
+ }
381
+
382
+ addRating(newRating: number) {
383
+ this.rating = ((this.rating * this.reviewCount) + newRating) / (this.reviewCount + 1);
384
+ this.reviewCount++;
385
+ }
386
+ }
387
+ ```
388
+
389
+ ### Event Components
390
+
391
+ ```typescript
392
+ @Component
393
+ export class AuditLog extends BaseComponent {
394
+ @CompData({ indexed: true })
395
+ entityId: string = '';
396
+
397
+ @CompData({ indexed: true })
398
+ action: 'create' | 'update' | 'delete' = 'create';
399
+
400
+ @CompData()
401
+ changes: Record<string, { old: any; new: any }> = {};
402
+
403
+ @CompData()
404
+ timestamp: Date = new Date();
405
+
406
+ @CompData()
407
+ userId?: string;
408
+
409
+ @CompData()
410
+ ipAddress?: string;
411
+
412
+ // Method to record changes
413
+ recordChanges(oldData: any, newData: any) {
414
+ const changes: Record<string, { old: any; new: any }> = {};
415
+
416
+ for (const key in newData) {
417
+ if (oldData[key] !== newData[key]) {
418
+ changes[key] = {
419
+ old: oldData[key],
420
+ new: newData[key]
421
+ };
422
+ }
423
+ }
424
+
425
+ this.changes = changes;
426
+ }
427
+ }
428
+ ```
429
+
430
+ ## 🔧 Component Registry
431
+
432
+ ### Component Management
433
+
434
+ ```typescript
435
+ import ComponentRegistry from 'bunsane/core/ComponentRegistry';
436
+
437
+ // Check if component is registered
438
+ const isRegistered = ComponentRegistry.isComponentReady('UserProfile');
439
+ console.log(isRegistered); // true
440
+
441
+ // Get component ID
442
+ const componentId = ComponentRegistry.getComponentId('UserProfile');
443
+ console.log(componentId); // 'sha256_hash'
444
+
445
+ // Get all registered components
446
+ const allComponents = ComponentRegistry.getAllComponents();
447
+ console.log(allComponents); // ['UserProfile', 'ProductInfo', ...]
448
+ ```
449
+
450
+ ## 📈 Best Practices
451
+
452
+ ### Component Design
453
+
454
+ - **Single Responsibility**: Each component should represent one clear concept
455
+ - **Minimal Data**: Keep components focused and avoid bloated data structures
456
+ - **Consistent Naming**: Use clear, descriptive names for components and properties
457
+ - **Index Strategically**: Only index fields that are frequently queried
458
+ - **Type Safety**: Leverage TypeScript for robust type checking
459
+
460
+ ### Performance Considerations
461
+
462
+ - **Lazy Loading**: Only load components when needed
463
+ - **Batch Operations**: Use entity batching for multiple component operations
464
+ - **Efficient Queries**: Leverage indexed fields for fast lookups
465
+ - **Memory Management**: Be mindful of component size and complexity
466
+
467
+ ### Error Handling
468
+
469
+ ```typescript
470
+ try {
471
+ const component = new UserProfile();
472
+ component.name = userData.name;
473
+ component.email = userData.email;
474
+
475
+ // Validate before saving
476
+ if (!component.email.includes('@')) {
477
+ throw new Error('Invalid email format');
478
+ }
479
+
480
+ await entity.add(component).save();
481
+ } catch (error) {
482
+ console.error('Failed to create user profile:', error);
483
+ // Handle error appropriately
484
+ }
485
+ ```
486
+
487
+ ## 🚀 What's Next?
488
+
489
+ Now that you understand Components, let's explore:
490
+
491
+ - **[ArcheTypes](archetypes.md)** - Reusable entity templates
492
+ - **[Entity System](entity.md)** - How entities use components
493
+ - **[Query System](query.md)** - Efficient data retrieval
494
+ - **[Services](services.md)** - Business logic and GraphQL integration
495
+
496
+ ---
497
+
498
+ *Ready to build with components? Let's look at [ArcheTypes](archetypes.md) next!* 🚀