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,744 @@
1
+ # Service API Reference
2
+
3
+ This page provides detailed API reference for BunSane's service layer and business logic components.
4
+
5
+ ## 🏢 BaseService Class
6
+
7
+ The `BaseService` class provides the foundation for all business logic services in BunSane.
8
+
9
+ ### Constructor
10
+
11
+ ```typescript
12
+ new BaseService()
13
+ ```
14
+
15
+ ### Instance Methods
16
+
17
+ #### `service.initialize()`
18
+
19
+ Initializes the service with dependencies.
20
+
21
+ ```typescript
22
+ async initialize(): Promise<void>
23
+ ```
24
+
25
+ **Returns:** `Promise<void>`
26
+
27
+ **Example:**
28
+ ```typescript
29
+ await userService.initialize();
30
+ // Service is now ready to use
31
+ ```
32
+
33
+ ## 👤 ServiceRegistry Class
34
+
35
+ Manages service registration and dependency injection. Services are automatically registered when they extend `BaseService` and are imported in your application.
36
+
37
+ ### Static Methods
38
+
39
+ #### `ServiceRegistry.register(serviceClass)`
40
+
41
+ Registers a service with the registry.
42
+
43
+ ```typescript
44
+ static register(serviceClass: new () => BaseService): void
45
+ ```
46
+
47
+ **Parameters:**
48
+ - `serviceClass`: Service constructor
49
+
50
+ **Example:**
51
+ ```typescript
52
+ export class UserService extends BaseService {
53
+ // Service implementation
54
+ }
55
+
56
+ // Automatic registration when imported
57
+ // No manual registration needed in most cases
58
+ ServiceRegistry.register(UserService);
59
+ ```
60
+
61
+ #### `ServiceRegistry.get(serviceClass)`
62
+
63
+ Gets a service instance by class.
64
+
65
+ ```typescript
66
+ static get<T extends BaseService>(serviceClass: new () => T): T
67
+ ```
68
+
69
+ **Type Parameters:**
70
+ - `T`: Service class
71
+
72
+ **Parameters:**
73
+ - `serviceClass`: Service constructor
74
+
75
+ **Returns:** `T` - Service instance
76
+
77
+ **Example:**
78
+ ```typescript
79
+ const userService = ServiceRegistry.get(UserService);
80
+ ```
81
+
82
+ ### Automatic Service Discovery
83
+
84
+ Services are automatically discovered and registered when:
85
+ 1. They extend `BaseService`
86
+ 2. They are imported in your application entry point
87
+ 3. The application starts via `App.start()`
88
+
89
+ ```typescript
90
+ // app.ts
91
+ import { App } from 'bunsane';
92
+ import { UserService } from './services/UserService';
93
+ import { OrderService } from './services/OrderService';
94
+
95
+ const app = new App();
96
+
97
+ // Services are automatically registered when imported
98
+ // No manual registration required
99
+ app.start();
100
+ ```
101
+
102
+ ### Service Dependencies
103
+
104
+ Services can access other services through the ServiceRegistry:
105
+
106
+ ```typescript
107
+ export class OrderService extends BaseService {
108
+ private userService: UserService;
109
+
110
+ async initialize(): Promise<void> {
111
+ await super.initialize();
112
+ // Get service instance from registry
113
+ this.userService = ServiceRegistry.get(UserService);
114
+ }
115
+
116
+ async createOrder(userId: string, orderData: any) {
117
+ // Validate user exists
118
+ const user = await this.userService.getUser({ id: userId });
119
+ if (!user) throw new Error('User not found');
120
+
121
+ // Create order logic
122
+ const order = Entity.Create();
123
+ // ... order creation logic
124
+ return order;
125
+ }
126
+ }
127
+ ```
128
+
129
+ ## 🎯 GraphQL Service Decorators
130
+
131
+ ### @GraphQLObjectType
132
+
133
+ Defines a GraphQL object type for the service.
134
+
135
+ ```typescript
136
+ @GraphQLObjectType(config: GraphQLObjectTypeConfig): ClassDecorator
137
+ ```
138
+
139
+ **Parameters:**
140
+ - `config`: GraphQLObjectTypeConfig - Type configuration
141
+
142
+ **Example:**
143
+ ```typescript
144
+ const userFields = {
145
+ id: GraphQLFieldTypes.ID_REQUIRED,
146
+ name: GraphQLFieldTypes.STRING_OPTIONAL,
147
+ email: GraphQLFieldTypes.STRING_REQUIRED
148
+ };
149
+
150
+ @GraphQLObjectType({
151
+ name: "User",
152
+ fields: userFields
153
+ })
154
+ export class UserService extends BaseService {
155
+ // Service implementation
156
+ }
157
+ ```
158
+
159
+ ### @GraphQLOperation
160
+
161
+ Defines a GraphQL operation (Query/Mutation).
162
+
163
+ ```typescript
164
+ @GraphQLOperation(config: GraphQLOperationConfig): MethodDecorator
165
+ ```
166
+
167
+ **Parameters:**
168
+ - `config`: GraphQLOperationConfig - Operation configuration
169
+
170
+ **Example:**
171
+ ```typescript
172
+ @GraphQLOperation({
173
+ type: "Query",
174
+ input: { id: GraphQLFieldTypes.ID_REQUIRED },
175
+ output: "User"
176
+ })
177
+ async getUser(args: { id: string }) {
178
+ // Implementation
179
+ }
180
+
181
+ @GraphQLOperation({
182
+ type: "Mutation",
183
+ input: {
184
+ name: GraphQLFieldTypes.STRING_REQUIRED,
185
+ email: GraphQLFieldTypes.STRING_REQUIRED
186
+ },
187
+ output: "User"
188
+ })
189
+ async createUser(args: { name: string; email: string }) {
190
+ // Implementation
191
+ }
192
+ ```
193
+
194
+ ### @GraphQLField
195
+
196
+ Defines a GraphQL field resolver.
197
+
198
+ ```typescript
199
+ @GraphQLField(config: GraphQLFieldConfig): MethodDecorator
200
+ ```
201
+
202
+ **Parameters:**
203
+ - `config`: GraphQLFieldConfig - Field configuration
204
+
205
+ **Example:**
206
+ ```typescript
207
+ @GraphQLField({ type: "User", field: "name" })
208
+ async nameResolver(parent: Entity) {
209
+ const profile = await parent.get(UserProfile);
210
+ return profile?.name ?? "";
211
+ }
212
+ ```
213
+
214
+ ## 🌐 REST Service Decorators
215
+
216
+ ### @Post
217
+
218
+ Defines a POST REST endpoint.
219
+
220
+ ```typescript
221
+ @Post(path: string): MethodDecorator
222
+ ```
223
+
224
+ **Parameters:**
225
+ - `path`: String - Endpoint path
226
+
227
+ **Example:**
228
+ ```typescript
229
+ @Post("/auth/login")
230
+ async userLogin(req: Request) {
231
+ // Handle login logic
232
+ return new Response(JSON.stringify({ token: "jwt-token" }));
233
+ }
234
+ ```
235
+
236
+ ## ⏰ Scheduled Task Decorators
237
+
238
+ ### @ScheduledTask
239
+
240
+ Defines a scheduled background task.
241
+
242
+ ```typescript
243
+ @ScheduledTask(config: ScheduledTaskConfig): MethodDecorator
244
+ ```
245
+
246
+ **Parameters:**
247
+ - `config`: ScheduledTaskConfig - Task configuration
248
+
249
+ **Example:**
250
+ ```typescript
251
+ @ScheduledTask({
252
+ interval: ScheduleInterval.MINUTE,
253
+ componentTarget: {
254
+ includeComponents: [UserTag],
255
+ }
256
+ })
257
+ async checkUserPerMinutes(entities: Entity[]) {
258
+ // Run every minute for user entities
259
+ }
260
+ ```
261
+
262
+ ## 🎣 Lifecycle Hook Decorators
263
+
264
+ ### @ComponentTargetHook
265
+
266
+ Defines a component lifecycle hook.
267
+
268
+ ```typescript
269
+ @ComponentTargetHook(event: string, config: HookConfig): MethodDecorator
270
+ ```
271
+
272
+ **Parameters:**
273
+ - `event`: String - Hook event name
274
+ - `config`: HookConfig - Hook configuration
275
+
276
+ **Example:**
277
+ ```typescript
278
+ @ComponentTargetHook("entity.created", {
279
+ includeComponents: [UserTag, EmailComponent]
280
+ })
281
+ async onUserCreate(event: EntityCreatedEvent) {
282
+ const emailComp = await event.entity.get(EmailComponent);
283
+ console.log(`New user: ${emailComp?.value}`);
284
+ }
285
+ ```
286
+
287
+ ## 📋 Service Patterns
288
+
289
+ ### GraphQL CRUD Service Pattern
290
+
291
+ ```typescript
292
+ import { GraphQLObjectType, GraphQLOperation, GraphQLField, GraphQLFieldTypes } from 'bunsane';
293
+
294
+ const userFields = {
295
+ id: GraphQLFieldTypes.ID_REQUIRED,
296
+ name: GraphQLFieldTypes.STRING_OPTIONAL,
297
+ email: GraphQLFieldTypes.STRING_REQUIRED,
298
+ username: GraphQLFieldTypes.STRING_OPTIONAL
299
+ };
300
+
301
+ const userInputs = {
302
+ createUser: {
303
+ name: GraphQLFieldTypes.STRING_REQUIRED,
304
+ email: GraphQLFieldTypes.STRING_REQUIRED,
305
+ username: GraphQLFieldTypes.STRING_REQUIRED
306
+ },
307
+ getUser: {
308
+ id: GraphQLFieldTypes.ID_REQUIRED
309
+ },
310
+ updateUser: {
311
+ id: GraphQLFieldTypes.ID_REQUIRED,
312
+ name: GraphQLFieldTypes.STRING_OPTIONAL,
313
+ email: GraphQLFieldTypes.STRING_OPTIONAL
314
+ }
315
+ };
316
+
317
+ @GraphQLObjectType({
318
+ name: "User",
319
+ fields: userFields
320
+ })
321
+ export class UserService extends BaseService {
322
+ @GraphQLOperation({
323
+ type: "Mutation",
324
+ input: userInputs.createUser,
325
+ output: "User"
326
+ })
327
+ async createUser(args: { name: string; email: string; username: string }) {
328
+ const userEntity = UserArcheType.fill(args).createEntity();
329
+ await userEntity.save();
330
+ return await UserArcheType.Unwrap(userEntity);
331
+ }
332
+
333
+ @GraphQLOperation({
334
+ type: "Query",
335
+ input: userInputs.getUser,
336
+ output: "User"
337
+ })
338
+ async getUser(args: { id: string }) {
339
+ const entity = await Entity.FindById(args.id);
340
+ if (!entity) return null;
341
+ return await UserArcheType.Unwrap(entity);
342
+ }
343
+
344
+ @GraphQLOperation({
345
+ type: "Mutation",
346
+ input: userInputs.updateUser,
347
+ output: "User"
348
+ })
349
+ async updateUser(args: { id: string; name?: string; email?: string }) {
350
+ const entity = await Entity.FindById(args.id);
351
+ if (!entity) throw new Error('User not found');
352
+
353
+ await UserArcheType.updateEntity(entity, args);
354
+ await entity.save();
355
+ return await UserArcheType.Unwrap(entity);
356
+ }
357
+
358
+ @GraphQLField({ type: "User", field: "id" })
359
+ idResolver(parent: Entity) {
360
+ return parent.id;
361
+ }
362
+
363
+ @GraphQLField({ type: "User", field: "name" })
364
+ async nameResolver(parent: Entity) {
365
+ const profile = await parent.get(UserProfile);
366
+ return profile?.name ?? "";
367
+ }
368
+
369
+ @GraphQLField({ type: "User", field: "email" })
370
+ async emailResolver(parent: Entity) {
371
+ const profile = await parent.get(UserProfile);
372
+ return profile?.email ?? "";
373
+ }
374
+
375
+ @GraphQLField({ type: "User", field: "username" })
376
+ async usernameResolver(parent: Entity) {
377
+ const profile = await parent.get(UserProfile);
378
+ return profile?.username ?? "";
379
+ }
380
+ }
381
+ ```
382
+
383
+ ### REST Service Pattern
384
+
385
+ ```typescript
386
+ import { Post } from 'bunsane';
387
+
388
+ export class AuthService extends BaseService {
389
+ @Post("/auth/login")
390
+ async userLogin(req: Request) {
391
+ const body = await req.json();
392
+ const { email, password } = body;
393
+
394
+ // Authentication logic
395
+ const user = await this.authenticateUser(email, password);
396
+ if (!user) {
397
+ return new Response(JSON.stringify({ error: "Invalid credentials" }), {
398
+ status: 401
399
+ });
400
+ }
401
+
402
+ const token = this.generateToken(user);
403
+ return new Response(JSON.stringify({ token, user }), { status: 200 });
404
+ }
405
+
406
+ @Post("/auth/register")
407
+ async userRegister(req: Request) {
408
+ try {
409
+ const body = await req.json();
410
+ const input = this.validateRegistrationData(body);
411
+
412
+ const existingUser = await Query.Find(UserTag)
413
+ .with(EmailComponent, Query.filters(
414
+ Query.filter("value", Query.filterOp.EQ, input.email)
415
+ ))
416
+ .exec();
417
+
418
+ if (existingUser.length > 0) {
419
+ return new Response(JSON.stringify({
420
+ error: "Email already in use"
421
+ }), { status: 400 });
422
+ }
423
+
424
+ const entity = UserArcheType.fill(input).createEntity();
425
+ await entity.save();
426
+
427
+ return new Response(JSON.stringify({
428
+ message: "User registered successfully",
429
+ user: await UserArcheType.Unwrap(entity, ['password'])
430
+ }), { status: 201 });
431
+ } catch (error) {
432
+ return new Response(JSON.stringify({
433
+ error: "Registration failed"
434
+ }), { status: 500 });
435
+ }
436
+ }
437
+
438
+ private async authenticateUser(email: string, password: string) {
439
+ // Authentication implementation
440
+ return null;
441
+ }
442
+
443
+ private generateToken(user: any) {
444
+ // JWT token generation
445
+ return "jwt-token";
446
+ }
447
+
448
+ private validateRegistrationData(data: any) {
449
+ // Validation logic
450
+ return data;
451
+ }
452
+ }
453
+ ```
454
+
455
+ ### Scheduled Task Service Pattern
456
+
457
+ ```typescript
458
+ import { ScheduledTask, ScheduleInterval, ComponentTargetHook } from 'bunsane';
459
+
460
+ export class MaintenanceService extends BaseService {
461
+ @ScheduledTask({
462
+ interval: ScheduleInterval.HOUR,
463
+ componentTarget: {
464
+ includeComponents: [UserTag],
465
+ }
466
+ })
467
+ async cleanupInactiveUsers(entities: Entity[]) {
468
+ const oneMonthAgo = new Date();
469
+ oneMonthAgo.setMonth(oneMonthAgo.getMonth() - 1);
470
+
471
+ for (const entity of entities) {
472
+ const lastLogin = await entity.get(LastLoginComponent);
473
+ if (lastLogin && lastLogin.value < oneMonthAgo) {
474
+ // Mark user as inactive or send reminder
475
+ await this.sendInactivityReminder(entity);
476
+ }
477
+ }
478
+ }
479
+
480
+ @ScheduledTask({
481
+ interval: ScheduleInterval.DAY,
482
+ componentTarget: {
483
+ includeComponents: [PostTag],
484
+ }
485
+ })
486
+ async cleanupOldPosts(entities: Entity[]) {
487
+ const thirtyDaysAgo = new Date();
488
+ thirtyDaysAgo.setDate(thirtyDaysAgo.getDate() - 30);
489
+
490
+ for (const entity of entities) {
491
+ const createdAt = await entity.get(DateComponent);
492
+ if (createdAt && createdAt.value < thirtyDaysAgo) {
493
+ // Archive or delete old posts
494
+ await entity.delete();
495
+ }
496
+ }
497
+ }
498
+
499
+ @ComponentTargetHook("entity.created", {
500
+ includeComponents: [UserTag, EmailComponent]
501
+ })
502
+ async onUserCreated(event: EntityCreatedEvent) {
503
+ const emailComp = await event.entity.get(EmailComponent);
504
+ if (emailComp) {
505
+ await this.sendWelcomeEmail(emailComp.value);
506
+ }
507
+ }
508
+
509
+ private async sendInactivityReminder(entity: Entity) {
510
+ // Send reminder email implementation
511
+ }
512
+
513
+ private async sendWelcomeEmail(email: string) {
514
+ // Send welcome email implementation
515
+ }
516
+ }
517
+ ```
518
+
519
+ ## 🔄 Service Communication
520
+
521
+ ### Service Dependencies
522
+
523
+ ```typescript
524
+ export class NotificationService extends BaseService {
525
+ private emailService: EmailService;
526
+ private smsService: SmsService;
527
+
528
+ async initialize(): Promise<void> {
529
+ await super.initialize();
530
+ this.emailService = ServiceRegistry.get(EmailService);
531
+ this.smsService = ServiceRegistry.get(SmsService);
532
+ }
533
+
534
+ async sendWelcomeMessage(userId: string): Promise<void> {
535
+ const user = await Entity.FindById(userId);
536
+ if (!user) return;
537
+
538
+ const profile = await user.get(UserProfile);
539
+
540
+ // Send both email and SMS
541
+ await Promise.all([
542
+ this.emailService.sendWelcomeEmail(profile.email, profile.name),
543
+ this.smsService.sendWelcomeSms(profile.phone, profile.name)
544
+ ]);
545
+ }
546
+ }
547
+ ```
548
+
549
+ ### Event-Driven Services
550
+
551
+ ```typescript
552
+ export class AuditService extends BaseService {
553
+ async initialize(): Promise<void> {
554
+ await super.initialize();
555
+
556
+ // Listen to entity events
557
+ EntityHookManager.on('entity:created', this.onEntityCreated.bind(this));
558
+ EntityHookManager.on('entity:updated', this.onEntityUpdated.bind(this));
559
+ EntityHookManager.on('entity:deleted', this.onEntityDeleted.bind(this));
560
+ }
561
+
562
+ private async onEntityCreated(entity: Entity): Promise<void> {
563
+ await this.logAuditEvent({
564
+ action: 'CREATE',
565
+ entityId: entity.id,
566
+ timestamp: new Date(),
567
+ userId: this.getCurrentUserId()
568
+ });
569
+ }
570
+
571
+ private async onEntityUpdated(entity: Entity): Promise<void> {
572
+ await this.logAuditEvent({
573
+ action: 'UPDATE',
574
+ entityId: entity.id,
575
+ timestamp: new Date(),
576
+ userId: this.getCurrentUserId()
577
+ });
578
+ }
579
+
580
+ private async onEntityDeleted(entity: Entity): Promise<void> {
581
+ await this.logAuditEvent({
582
+ action: 'DELETE',
583
+ entityId: entity.id,
584
+ timestamp: new Date(),
585
+ userId: this.getCurrentUserId()
586
+ });
587
+ }
588
+
589
+ private async logAuditEvent(event: AuditEvent): Promise<void> {
590
+ const auditEntity = Entity.Create();
591
+ await auditEntity.add(AuditLog, event);
592
+ await auditEntity.save();
593
+ }
594
+
595
+ private getCurrentUserId(): string {
596
+ // Get current user from context
597
+ return RequestContext.getCurrentUser()?.id || 'system';
598
+ }
599
+ }
600
+ ```
601
+
602
+ ## 🛡️ Error Handling
603
+
604
+ ### Service Error Types
605
+
606
+ ```typescript
607
+ export class ServiceError extends Error {
608
+ constructor(
609
+ message: string,
610
+ public code: string,
611
+ public statusCode: number = 500
612
+ ) {
613
+ super(message);
614
+ this.name = 'ServiceError';
615
+ }
616
+ }
617
+
618
+ export class ValidationError extends ServiceError {
619
+ constructor(message: string, public field?: string) {
620
+ super(message, 'VALIDATION_ERROR', 400);
621
+ this.name = 'ValidationError';
622
+ }
623
+ }
624
+
625
+ export class NotFoundError extends ServiceError {
626
+ constructor(resource: string) {
627
+ super(`${resource} not found`, 'NOT_FOUND', 404);
628
+ this.name = 'NotFoundError';
629
+ }
630
+ }
631
+ ```
632
+
633
+ ### Error Handling Patterns
634
+
635
+ ```typescript
636
+ export class UserService extends BaseService {
637
+ async createUser(userData: CreateUserData): Promise<Entity> {
638
+ try {
639
+ // Validate input
640
+ this.validateUserData(userData);
641
+
642
+ // Check for existing user
643
+ const existing = await Query.Find(UserProfile)
644
+ .where({ email: userData.email })
645
+ .first();
646
+
647
+ if (existing) {
648
+ throw new ValidationError('Email already exists', 'email');
649
+ }
650
+
651
+ // Create user
652
+ const user = Entity.Create();
653
+ await user.add(UserProfile, userData);
654
+ await user.save();
655
+
656
+ return user;
657
+ } catch (error) {
658
+ this.getLogger().error('Failed to create user', { error, userData });
659
+ throw error;
660
+ }
661
+ }
662
+
663
+ private validateUserData(data: CreateUserData): void {
664
+ if (!data.email || !data.email.includes('@')) {
665
+ throw new ValidationError('Invalid email address', 'email');
666
+ }
667
+
668
+ if (!data.name || data.name.length < 2) {
669
+ throw new ValidationError('Name must be at least 2 characters', 'name');
670
+ }
671
+ }
672
+ }
673
+ ```
674
+
675
+ ## 🚀 Performance Optimization
676
+
677
+ ### Service Caching
678
+
679
+ ```typescript
680
+ export class CacheService extends BaseService {
681
+ private cache = new Map<string, any>();
682
+
683
+ async get<T>(key: string, ttl: number = 300000): Promise<T | null> {
684
+ const cached = this.cache.get(key);
685
+ if (cached && cached.expires > Date.now()) {
686
+ return cached.value;
687
+ }
688
+ return null;
689
+ }
690
+
691
+ async set<T>(key: string, value: T, ttl: number = 300000): Promise<void> {
692
+ this.cache.set(key, {
693
+ value,
694
+ expires: Date.now() + ttl
695
+ });
696
+ }
697
+
698
+ clear(): void {
699
+ this.cache.clear();
700
+ }
701
+ }
702
+ ```
703
+
704
+ ### Batch Operations
705
+
706
+ ```typescript
707
+ export class BulkOperationService extends BaseService {
708
+ async bulkCreateUsers(userData: CreateUserData[]): Promise<Entity[]> {
709
+ const entities: Entity[] = [];
710
+
711
+ // Process in batches to avoid memory issues
712
+ const batchSize = 100;
713
+ for (let i = 0; i < userData.length; i += batchSize) {
714
+ const batch = userData.slice(i, i + batchSize);
715
+ const batchEntities = await this.createUserBatch(batch);
716
+ entities.push(...batchEntities);
717
+ }
718
+
719
+ return entities;
720
+ }
721
+
722
+ private async createUserBatch(batch: CreateUserData[]): Promise<Entity[]> {
723
+ const entities = batch.map(data => {
724
+ const entity = Entity.Create();
725
+ await entity.add(UserProfile, data);
726
+ return entity;
727
+ });
728
+
729
+ // Save all entities in parallel
730
+ await Promise.all(entities.map(entity => entity.save()));
731
+ return entities;
732
+ }
733
+ }
734
+ ```
735
+
736
+ ## 🔗 Related APIs
737
+
738
+ - **[Entity API](core.md)** - Entity operations
739
+ - **[Query API](query.md)** - Database querying
740
+ - **[Hooks API](hooks.md)** - Lifecycle events
741
+
742
+ ---
743
+
744
+ *Need more details? Check the [Hooks API](hooks.md) for lifecycle event handling!* 🚀