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,314 @@
|
|
|
1
|
+
# Entity System
|
|
2
|
+
|
|
3
|
+
The Entity system is the foundation of BunSane's architecture. Unlike traditional objects or database rows, Entities in BunSane are dynamic containers composed of multiple Components, providing unparalleled flexibility in data modeling.
|
|
4
|
+
|
|
5
|
+
## 🎯 What is an Entity?
|
|
6
|
+
|
|
7
|
+
An Entity represents a single "thing" in your application - a user, a product, a blog post, etc. However, unlike traditional models, Entities don't have a fixed structure. Instead, they're composed of Components that define their properties and behavior.
|
|
8
|
+
|
|
9
|
+
### Key Characteristics
|
|
10
|
+
|
|
11
|
+
- **Dynamic Composition**: Entities can have any combination of components
|
|
12
|
+
- **Type-Safe**: Full TypeScript support with compile-time guarantees
|
|
13
|
+
- **Database-Backed**: Automatically persisted to PostgreSQL
|
|
14
|
+
- **Event-Driven**: Supports lifecycle hooks for business logic
|
|
15
|
+
- **Relationship-Aware**: Can reference other entities through components
|
|
16
|
+
|
|
17
|
+
## 🏗️ Creating Entities
|
|
18
|
+
|
|
19
|
+
### Basic Entity Creation
|
|
20
|
+
|
|
21
|
+
```typescript
|
|
22
|
+
import { Entity } from 'bunsane';
|
|
23
|
+
|
|
24
|
+
// Create a new empty entity
|
|
25
|
+
const user = Entity.Create();
|
|
26
|
+
|
|
27
|
+
// The entity gets a unique ID automatically
|
|
28
|
+
console.log(user.id); // "01HXXXXXXXXXXXXXXXXXXXXX"
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
### Adding Components to Entities
|
|
32
|
+
|
|
33
|
+
```typescript
|
|
34
|
+
import { Entity, Component, CompData, BaseComponent } from 'bunsane';
|
|
35
|
+
|
|
36
|
+
@Component
|
|
37
|
+
class UserProfile extends BaseComponent {
|
|
38
|
+
@CompData()
|
|
39
|
+
name: string = '';
|
|
40
|
+
|
|
41
|
+
@CompData()
|
|
42
|
+
email: string = '';
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
@Component
|
|
46
|
+
class UserPreferences extends BaseComponent {
|
|
47
|
+
@CompData()
|
|
48
|
+
theme: 'light' | 'dark' = 'light';
|
|
49
|
+
|
|
50
|
+
@CompData()
|
|
51
|
+
notifications: boolean = true;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Create entity and add components
|
|
55
|
+
const user = Entity.Create();
|
|
56
|
+
user.add(UserProfile, {
|
|
57
|
+
name: 'John Doe',
|
|
58
|
+
email: 'john@example.com'
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
user.add(UserPreferences, {
|
|
62
|
+
theme: 'dark',
|
|
63
|
+
notifications: false
|
|
64
|
+
});
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## 💾 Persisting Entities
|
|
68
|
+
|
|
69
|
+
### Saving to Database
|
|
70
|
+
|
|
71
|
+
```typescript
|
|
72
|
+
// Save the entity to database
|
|
73
|
+
await user.save();
|
|
74
|
+
|
|
75
|
+
// The entity is now persisted
|
|
76
|
+
console.log(user._persisted); // true
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### Loading from Database
|
|
80
|
+
|
|
81
|
+
```typescript
|
|
82
|
+
// Load entity by ID
|
|
83
|
+
const loadedUser = await Entity.FindById(user.id);
|
|
84
|
+
|
|
85
|
+
// Load multiple entities
|
|
86
|
+
const userIds = ['01HXXX...', '01HYYY...'];
|
|
87
|
+
const users = await Entity.LoadMultiple(userIds);
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
## 🔍 Accessing Component Data
|
|
91
|
+
|
|
92
|
+
### Getting Component Data
|
|
93
|
+
|
|
94
|
+
```typescript
|
|
95
|
+
// Get component data
|
|
96
|
+
const profile = user.get(UserProfile);
|
|
97
|
+
console.log(profile.data()); // { name: 'John Doe', email: 'john@example.com' }
|
|
98
|
+
|
|
99
|
+
// Direct property access
|
|
100
|
+
const userName = profile.name; // 'John Doe'
|
|
101
|
+
const userEmail = profile.email; // 'john@example.com'
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
### Checking Component Existence
|
|
105
|
+
|
|
106
|
+
```typescript
|
|
107
|
+
// Check if entity has a component
|
|
108
|
+
if (user.has(UserProfile)) {
|
|
109
|
+
const profile = user.get(UserProfile);
|
|
110
|
+
console.log('User has profile:', profile.name);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Get all components
|
|
114
|
+
const allComponents = user.componentList();
|
|
115
|
+
console.log('Entity has', allComponents.length, 'components');
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
## 🔄 Updating Entities
|
|
119
|
+
|
|
120
|
+
### Updating Component Data
|
|
121
|
+
|
|
122
|
+
```typescript
|
|
123
|
+
// Update existing component
|
|
124
|
+
user.set(UserProfile, {
|
|
125
|
+
name: 'Jane Doe',
|
|
126
|
+
email: 'jane@example.com'
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
// Add new component
|
|
130
|
+
@Component
|
|
131
|
+
class UserStats extends BaseComponent {
|
|
132
|
+
@CompData()
|
|
133
|
+
loginCount: number = 0;
|
|
134
|
+
|
|
135
|
+
@CompData()
|
|
136
|
+
lastLogin: Date = new Date();
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
user.add(UserStats, {
|
|
140
|
+
loginCount: 1,
|
|
141
|
+
lastLogin: new Date()
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
// Save changes
|
|
145
|
+
await user.save();
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
### Bulk Updates
|
|
149
|
+
|
|
150
|
+
```typescript
|
|
151
|
+
// Update multiple components at once
|
|
152
|
+
await user.set(UserProfile, { name: 'Updated Name' });
|
|
153
|
+
await user.set(UserStats, { loginCount: 2 });
|
|
154
|
+
|
|
155
|
+
// Save all changes
|
|
156
|
+
await user.save();
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
## 🗑️ Deleting Entities
|
|
160
|
+
|
|
161
|
+
### Soft Delete
|
|
162
|
+
|
|
163
|
+
```typescript
|
|
164
|
+
// Mark entity as deleted (soft delete)
|
|
165
|
+
await user.delete();
|
|
166
|
+
|
|
167
|
+
// Entity is marked as deleted but still exists
|
|
168
|
+
console.log(user._persisted); // false
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
### Force Delete
|
|
172
|
+
|
|
173
|
+
```typescript
|
|
174
|
+
// Permanently delete entity and all its components
|
|
175
|
+
await user.delete(true);
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
## 🔗 Entity Relationships
|
|
179
|
+
|
|
180
|
+
### Referencing Other Entities
|
|
181
|
+
|
|
182
|
+
```typescript
|
|
183
|
+
@Component
|
|
184
|
+
class BlogPost extends BaseComponent {
|
|
185
|
+
@CompData()
|
|
186
|
+
title: string = '';
|
|
187
|
+
|
|
188
|
+
@CompData()
|
|
189
|
+
content: string = '';
|
|
190
|
+
|
|
191
|
+
@CompData()
|
|
192
|
+
authorId: string = ''; // Reference to User entity
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
@Component
|
|
196
|
+
class Comment extends BaseComponent {
|
|
197
|
+
@CompData()
|
|
198
|
+
content: string = '';
|
|
199
|
+
|
|
200
|
+
@CompData()
|
|
201
|
+
postId: string = ''; // Reference to BlogPost entity
|
|
202
|
+
|
|
203
|
+
@CompData()
|
|
204
|
+
authorId: string = ''; // Reference to User entity
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// Create related entities
|
|
208
|
+
const author = Entity.Create();
|
|
209
|
+
author.add(UserProfile, { name: 'Author Name', email: 'author@example.com' });
|
|
210
|
+
await author.save();
|
|
211
|
+
|
|
212
|
+
const post = Entity.Create();
|
|
213
|
+
post.add(BlogPost, {
|
|
214
|
+
title: 'My First Post',
|
|
215
|
+
content: 'Post content...',
|
|
216
|
+
authorId: author.id
|
|
217
|
+
});
|
|
218
|
+
await post.save();
|
|
219
|
+
|
|
220
|
+
const comment = Entity.Create();
|
|
221
|
+
comment.add(Comment, {
|
|
222
|
+
content: 'Great post!',
|
|
223
|
+
postId: post.id,
|
|
224
|
+
authorId: author.id
|
|
225
|
+
});
|
|
226
|
+
await comment.save();
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
## 📊 Advanced Entity Operations
|
|
230
|
+
|
|
231
|
+
### Entity Metadata
|
|
232
|
+
|
|
233
|
+
```typescript
|
|
234
|
+
// Check if entity is dirty (has unsaved changes)
|
|
235
|
+
console.log(user._dirty); // true/false
|
|
236
|
+
|
|
237
|
+
// Get entity creation timestamp (if available)
|
|
238
|
+
console.log(user.createdAt);
|
|
239
|
+
|
|
240
|
+
// Get list of dirty components
|
|
241
|
+
const dirtyComponents = user.getDirtyComponents();
|
|
242
|
+
console.log('Dirty components:', dirtyComponents);
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
### Batch Operations
|
|
246
|
+
|
|
247
|
+
```typescript
|
|
248
|
+
// Create multiple entities efficiently
|
|
249
|
+
const entities = [];
|
|
250
|
+
for (let i = 0; i < 100; i++) {
|
|
251
|
+
const entity = Entity.Create();
|
|
252
|
+
entity.add(UserProfile, {
|
|
253
|
+
name: `User ${i}`,
|
|
254
|
+
email: `user${i}@example.com`
|
|
255
|
+
});
|
|
256
|
+
entities.push(entity);
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
// Save all at once (more efficient)
|
|
260
|
+
await Promise.all(entities.map(entity => entity.save()));
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
## 🎣 Entity Lifecycle
|
|
264
|
+
|
|
265
|
+
Entities go through several lifecycle stages:
|
|
266
|
+
|
|
267
|
+
1. **Created**: Entity instantiated with `Entity.Create()`
|
|
268
|
+
2. **Composed**: Components added to entity
|
|
269
|
+
3. **Persisted**: Entity saved to database
|
|
270
|
+
4. **Loaded**: Entity retrieved from database
|
|
271
|
+
5. **Updated**: Entity modified and re-saved
|
|
272
|
+
6. **Deleted**: Entity marked as deleted or force-deleted
|
|
273
|
+
|
|
274
|
+
## 🔧 Best Practices
|
|
275
|
+
|
|
276
|
+
### Entity Design
|
|
277
|
+
|
|
278
|
+
- **Keep Entities Focused**: Each entity should represent one logical concept
|
|
279
|
+
- **Use Components Wisely**: Break down data into logical, reusable components
|
|
280
|
+
- **Plan Relationships**: Design entity references carefully to avoid circular dependencies
|
|
281
|
+
- **Index Strategically**: Use `@CompData({ indexed: true })` for frequently queried fields
|
|
282
|
+
|
|
283
|
+
### Performance Considerations
|
|
284
|
+
|
|
285
|
+
- **Batch Operations**: Use `Entity.LoadMultiple()` for loading multiple entities
|
|
286
|
+
- **Lazy Loading**: Only load components when needed
|
|
287
|
+
- **Efficient Queries**: Use the Query system for complex data retrieval
|
|
288
|
+
- **Monitor Entity Size**: Large entities with many components can impact performance
|
|
289
|
+
|
|
290
|
+
### Error Handling
|
|
291
|
+
|
|
292
|
+
```typescript
|
|
293
|
+
try {
|
|
294
|
+
const entity = Entity.Create();
|
|
295
|
+
entity.add(UserProfile, userData);
|
|
296
|
+
await entity.save();
|
|
297
|
+
} catch (error) {
|
|
298
|
+
console.error('Failed to create user:', error);
|
|
299
|
+
// Handle error appropriately
|
|
300
|
+
}
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
## 🚀 What's Next?
|
|
304
|
+
|
|
305
|
+
Now that you understand Entities, let's explore:
|
|
306
|
+
|
|
307
|
+
- **[Components](components.md)** - The building blocks of entities
|
|
308
|
+
- **[ArcheTypes](archetypes.md)** - Reusable entity templates
|
|
309
|
+
- **[Query System](query.md)** - Efficient data retrieval
|
|
310
|
+
- **[Services](services.md)** - Business logic and GraphQL integration
|
|
311
|
+
|
|
312
|
+
---
|
|
313
|
+
|
|
314
|
+
*Ready to dive deeper? Let's look at [Components](components.md) next!* 🚀
|