bunsane 0.1.0 → 0.1.1
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 +119 -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 +159 -12
- 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 +453 -85
- package/core/RequestContext.ts +24 -0
- package/core/RequestLoaders.ts +65 -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 +1 -1
- 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/examples/hooks/README.md +228 -0
- package/examples/hooks/audit-logger.ts +495 -0
- package/gql/Generator.ts +56 -34
- package/gql/decorators/Upload.ts +176 -0
- package/gql/helpers.ts +67 -0
- package/gql/index.ts +55 -31
- package/gql/types.ts +1 -1
- package/index.ts +79 -11
- package/package.json +5 -4
- 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 +205 -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/validate-docs.sh +90 -0
- package/core/Events.ts +0 -0
|
@@ -0,0 +1,337 @@
|
|
|
1
|
+
# Getting Started with BunSane
|
|
2
|
+
|
|
3
|
+
This guide will walk you through installing and setting up BunSane for your first project.
|
|
4
|
+
|
|
5
|
+
## 📋 Prerequisites
|
|
6
|
+
|
|
7
|
+
Before you begin, ensure you have the following installed:
|
|
8
|
+
|
|
9
|
+
- **Bun Runtime**: Version 1.0 or later ([Download Bun](https://bun.sh/))
|
|
10
|
+
- **PostgreSQL**: Version 12 or later ([Download PostgreSQL](https://www.postgresql.org/download/))
|
|
11
|
+
- **Node.js**: Version 18+ (for some development tools, though Bun is the primary runtime)
|
|
12
|
+
|
|
13
|
+
## 🚀 Installation
|
|
14
|
+
|
|
15
|
+
### 1. Install BunSane
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
bun install bunsane
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
### 2. Verify Installation
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
bun run --version
|
|
25
|
+
# Should show Bun version 1.x.x
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## ⚙️ Configuration
|
|
29
|
+
|
|
30
|
+
### TypeScript Configuration
|
|
31
|
+
|
|
32
|
+
BunSane requires experimental decorators to be enabled. Update your `tsconfig.json`:
|
|
33
|
+
|
|
34
|
+
```json
|
|
35
|
+
{
|
|
36
|
+
"compilerOptions": {
|
|
37
|
+
"target": "ES2022",
|
|
38
|
+
"module": "ESNext",
|
|
39
|
+
"moduleResolution": "bundler",
|
|
40
|
+
"experimentalDecorators": true,
|
|
41
|
+
"emitDecoratorMetadata": true,
|
|
42
|
+
"strict": true,
|
|
43
|
+
"esModuleInterop": true,
|
|
44
|
+
"skipLibCheck": true,
|
|
45
|
+
"forceConsistentCasingInFileNames": true,
|
|
46
|
+
"allowSyntheticDefaultImports": true,
|
|
47
|
+
"resolveJsonModule": true,
|
|
48
|
+
"isolatedModules": true,
|
|
49
|
+
"noEmit": true,
|
|
50
|
+
"types": ["bun-types"]
|
|
51
|
+
},
|
|
52
|
+
"include": ["src/**/*", "index.ts"],
|
|
53
|
+
"exclude": ["node_modules"]
|
|
54
|
+
}
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### Database Setup
|
|
58
|
+
|
|
59
|
+
Create a PostgreSQL database for your application:
|
|
60
|
+
|
|
61
|
+
```sql
|
|
62
|
+
-- Create database
|
|
63
|
+
CREATE DATABASE bunsane_app;
|
|
64
|
+
|
|
65
|
+
-- Create user (optional)
|
|
66
|
+
CREATE USER bunsane_user WITH PASSWORD 'your_password';
|
|
67
|
+
|
|
68
|
+
-- Grant permissions
|
|
69
|
+
GRANT ALL PRIVILEGES ON DATABASE bunsane_app TO bunsane_user;
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### Environment Configuration
|
|
73
|
+
|
|
74
|
+
Create a `.env` file in your project root:
|
|
75
|
+
|
|
76
|
+
```env
|
|
77
|
+
# Database Configuration
|
|
78
|
+
DATABASE_URL=postgresql://username:password@localhost:5432/bunsane_app
|
|
79
|
+
|
|
80
|
+
# Application Configuration
|
|
81
|
+
NODE_ENV=development
|
|
82
|
+
PORT=3000
|
|
83
|
+
|
|
84
|
+
# Optional: Logging
|
|
85
|
+
LOG_LEVEL=info
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## 🏗️ Your First BunSane Application
|
|
89
|
+
|
|
90
|
+
### Project Structure
|
|
91
|
+
|
|
92
|
+
Create the following directory structure:
|
|
93
|
+
|
|
94
|
+
```
|
|
95
|
+
my-bunsane-app/
|
|
96
|
+
├── src/
|
|
97
|
+
│ ├── services/
|
|
98
|
+
│ └── helpers/
|
|
99
|
+
├── index.ts
|
|
100
|
+
├── package.json
|
|
101
|
+
├── tsconfig.json
|
|
102
|
+
└── .env
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
### 1. Create Your First Component
|
|
106
|
+
|
|
107
|
+
```typescript
|
|
108
|
+
// src/services/UserService.ts
|
|
109
|
+
import { Component, CompData, BaseComponent, ArcheType, Entity, GraphQLObjectType, GraphQLOperation, GraphQLFieldTypes, GraphQLField } from 'bunsane';
|
|
110
|
+
|
|
111
|
+
@Component
|
|
112
|
+
export class UserProfile extends BaseComponent {
|
|
113
|
+
@CompData()
|
|
114
|
+
name: string = '';
|
|
115
|
+
|
|
116
|
+
@CompData()
|
|
117
|
+
email: string = '';
|
|
118
|
+
|
|
119
|
+
@CompData({ indexed: true })
|
|
120
|
+
username: string = '';
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
@Component
|
|
124
|
+
export class UserPreferences extends BaseComponent {
|
|
125
|
+
@CompData()
|
|
126
|
+
theme: 'light' | 'dark' = 'light';
|
|
127
|
+
|
|
128
|
+
@CompData()
|
|
129
|
+
notifications: boolean = true;
|
|
130
|
+
}
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
### 2. Create an ArcheType
|
|
134
|
+
|
|
135
|
+
```typescript
|
|
136
|
+
// src/services/UserService.ts (continued)
|
|
137
|
+
import { ArcheType } from 'bunsane';
|
|
138
|
+
|
|
139
|
+
export const UserArcheType = new ArcheType([
|
|
140
|
+
UserProfile,
|
|
141
|
+
UserPreferences
|
|
142
|
+
]);
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
### 3. Create a Service
|
|
146
|
+
|
|
147
|
+
```typescript
|
|
148
|
+
// src/services/UserService.ts (continued)
|
|
149
|
+
import { BaseService, GraphQLObjectType, GraphQLOperation, GraphQLFieldTypes, GraphQLField } from 'bunsane';
|
|
150
|
+
|
|
151
|
+
const userFields = {
|
|
152
|
+
id: GraphQLFieldTypes.ID_REQUIRED,
|
|
153
|
+
name: GraphQLFieldTypes.STRING_OPTIONAL,
|
|
154
|
+
email: GraphQLFieldTypes.STRING_REQUIRED,
|
|
155
|
+
username: GraphQLFieldTypes.STRING_OPTIONAL
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
const userInputs = {
|
|
159
|
+
createUser: {
|
|
160
|
+
name: GraphQLFieldTypes.STRING_REQUIRED,
|
|
161
|
+
email: GraphQLFieldTypes.STRING_REQUIRED,
|
|
162
|
+
username: GraphQLFieldTypes.STRING_REQUIRED
|
|
163
|
+
},
|
|
164
|
+
getUser: {
|
|
165
|
+
id: GraphQLFieldTypes.ID_REQUIRED
|
|
166
|
+
}
|
|
167
|
+
};
|
|
168
|
+
|
|
169
|
+
@GraphQLObjectType({
|
|
170
|
+
name: "User",
|
|
171
|
+
fields: userFields
|
|
172
|
+
})
|
|
173
|
+
export default class UserService extends BaseService {
|
|
174
|
+
@GraphQLOperation({
|
|
175
|
+
type: "Mutation",
|
|
176
|
+
input: userInputs.createUser,
|
|
177
|
+
output: "User"
|
|
178
|
+
})
|
|
179
|
+
async createUser(args: { name: string; email: string; username: string }) {
|
|
180
|
+
const userEntity = UserArcheType.fill(args).createEntity();
|
|
181
|
+
await userEntity.save();
|
|
182
|
+
return await UserArcheType.Unwrap(userEntity);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
@GraphQLOperation({
|
|
186
|
+
type: "Query",
|
|
187
|
+
input: userInputs.getUser,
|
|
188
|
+
output: "User"
|
|
189
|
+
})
|
|
190
|
+
async getUser(args: { id: string }) {
|
|
191
|
+
const entity = await Entity.FindById(args.id);
|
|
192
|
+
if (!entity) return null;
|
|
193
|
+
return await UserArcheType.Unwrap(entity);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
@GraphQLField({ type: "User", field: "id" })
|
|
197
|
+
idResolver(parent: Entity) {
|
|
198
|
+
return parent.id;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
@GraphQLField({ type: "User", field: "name" })
|
|
202
|
+
async nameResolver(parent: Entity) {
|
|
203
|
+
const profile = await parent.get(UserProfile);
|
|
204
|
+
return profile?.name ?? "";
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
@GraphQLField({ type: "User", field: "email" })
|
|
208
|
+
async emailResolver(parent: Entity) {
|
|
209
|
+
const profile = await parent.get(UserProfile);
|
|
210
|
+
return profile?.email ?? "";
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
@GraphQLField({ type: "User", field: "username" })
|
|
214
|
+
async usernameResolver(parent: Entity) {
|
|
215
|
+
const profile = await parent.get(UserProfile);
|
|
216
|
+
return profile?.username ?? "";
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
### 4. Set Up the Application
|
|
222
|
+
|
|
223
|
+
```typescript
|
|
224
|
+
// index.ts
|
|
225
|
+
import { App } from 'bunsane';
|
|
226
|
+
import UserService from './src/services/UserService';
|
|
227
|
+
|
|
228
|
+
async function main() {
|
|
229
|
+
// Services are automatically registered when imported
|
|
230
|
+
// No manual registration needed
|
|
231
|
+
|
|
232
|
+
// Create and start the application
|
|
233
|
+
const app = new App({
|
|
234
|
+
port: 3000,
|
|
235
|
+
databaseUrl: process.env.DATABASE_URL
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
await app.start();
|
|
239
|
+
|
|
240
|
+
console.log('🚀 BunSane server running on http://localhost:3000');
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
main().catch(console.error);
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
### 5. Run Your Application
|
|
247
|
+
|
|
248
|
+
```bash
|
|
249
|
+
bun run index.ts
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
## 🧪 Testing Your Setup
|
|
253
|
+
|
|
254
|
+
### GraphQL API Testing
|
|
255
|
+
|
|
256
|
+
Your service now exposes GraphQL endpoints. Visit `http://localhost:3000/graphql` to access the GraphQL playground.
|
|
257
|
+
|
|
258
|
+
**Create User Mutation:**
|
|
259
|
+
```graphql
|
|
260
|
+
mutation CreateUser($input: CreateUserInput!) {
|
|
261
|
+
createUser(input: $input) {
|
|
262
|
+
id
|
|
263
|
+
name
|
|
264
|
+
email
|
|
265
|
+
username
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
With variables:
|
|
271
|
+
```json
|
|
272
|
+
{
|
|
273
|
+
"input": {
|
|
274
|
+
"name": "John Doe",
|
|
275
|
+
"email": "john.doe@example.com",
|
|
276
|
+
"username": "johndoe"
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
**Get User Query:**
|
|
282
|
+
```graphql
|
|
283
|
+
query GetUser($id: ID!) {
|
|
284
|
+
getUser(input: { id: $id }) {
|
|
285
|
+
id
|
|
286
|
+
name
|
|
287
|
+
email
|
|
288
|
+
username
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
### REST API Testing
|
|
294
|
+
|
|
295
|
+
You can also test REST endpoints using tools like curl or Postman:
|
|
296
|
+
|
|
297
|
+
```bash
|
|
298
|
+
# Example curl request
|
|
299
|
+
curl -X GET http://localhost:3000/api/users
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
## 🔍 What's Next?
|
|
303
|
+
|
|
304
|
+
Congratulations! You now have a working BunSane application. Here's what you can explore next:
|
|
305
|
+
|
|
306
|
+
- **[Entity System](core-concepts/entity.md)** - Deep dive into entity management
|
|
307
|
+
- **[Component Architecture](core-concepts/components.md)** - Advanced component patterns
|
|
308
|
+
- **[Query System](core-concepts/query.md)** - Efficient data retrieval
|
|
309
|
+
- **[Lifecycle Hooks](core-concepts/hooks.md)** - Business logic integration
|
|
310
|
+
- **[Real Examples](examples/)** - Complete application tutorials
|
|
311
|
+
|
|
312
|
+
## 🐛 Troubleshooting
|
|
313
|
+
|
|
314
|
+
### Common Issues
|
|
315
|
+
|
|
316
|
+
**"Component not registered" error**
|
|
317
|
+
- Ensure all components are properly decorated with `@Component`
|
|
318
|
+
- Check that components are imported before use
|
|
319
|
+
|
|
320
|
+
**Database connection failed**
|
|
321
|
+
- Verify PostgreSQL is running
|
|
322
|
+
- Check DATABASE_URL format and credentials
|
|
323
|
+
- Ensure database exists and user has permissions
|
|
324
|
+
|
|
325
|
+
**Service not found error**
|
|
326
|
+
- Verify services extend `BaseService`
|
|
327
|
+
- Check that services are registered with `ServiceRegistry`
|
|
328
|
+
|
|
329
|
+
### Getting Help
|
|
330
|
+
|
|
331
|
+
- [GitHub Issues](https://github.com/yaaruu/bunsane/issues) - Report bugs
|
|
332
|
+
- [GitHub Discussions](https://github.com/yaaruu/bunsane/discussions) - Ask questions
|
|
333
|
+
- [Documentation](https://yaaruu.github.io/bunsane/) - Complete reference
|
|
334
|
+
|
|
335
|
+
---
|
|
336
|
+
|
|
337
|
+
*Ready to build something amazing? Let's continue with the [Entity System](core-concepts/entity.md)!* 🚀
|
package/docs/index.html
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<title>BunSane Framework Documentation</title>
|
|
6
|
+
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
|
|
7
|
+
<meta name="description" content="Professional documentation for BunSane - A batteries-included TypeScript API framework for Bun">
|
|
8
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0">
|
|
9
|
+
<link rel="icon" href="https://raw.githubusercontent.com/yaaruu/bunsane/main/BunSane.jpg" type="image/x-icon">
|
|
10
|
+
<link rel="stylesheet" href="//cdn.jsdelivr.net/npm/docsify@4/lib/themes/vue.css">
|
|
11
|
+
<link rel="stylesheet" href="//cdn.jsdelivr.net/npm/docsify@4/lib/themes/dark.css" title="dark" disabled>
|
|
12
|
+
<link rel="stylesheet" href="//cdn.jsdelivr.net/npm/docsify@4/lib/themes/buble.css" title="buble" disabled>
|
|
13
|
+
<link rel="stylesheet" href="//cdn.jsdelivr.net/npm/docsify@4/lib/themes/pure.css" title="pure" disabled>
|
|
14
|
+
<style>
|
|
15
|
+
.sidebar-nav ul li a {
|
|
16
|
+
font-size: 14px;
|
|
17
|
+
padding: 8px 15px;
|
|
18
|
+
}
|
|
19
|
+
.sidebar-nav ul li.active > a {
|
|
20
|
+
background-color: #f0f8ff;
|
|
21
|
+
border-left: 4px solid #007acc;
|
|
22
|
+
}
|
|
23
|
+
.content {
|
|
24
|
+
max-width: 900px;
|
|
25
|
+
}
|
|
26
|
+
.markdown-section h1 {
|
|
27
|
+
border-bottom: 2px solid #007acc;
|
|
28
|
+
padding-bottom: 10px;
|
|
29
|
+
}
|
|
30
|
+
.markdown-section h2 {
|
|
31
|
+
border-bottom: 1px solid #ddd;
|
|
32
|
+
padding-bottom: 5px;
|
|
33
|
+
}
|
|
34
|
+
.markdown-section code {
|
|
35
|
+
background-color: #f6f8fa;
|
|
36
|
+
padding: 2px 6px;
|
|
37
|
+
border-radius: 3px;
|
|
38
|
+
font-size: 0.9em;
|
|
39
|
+
}
|
|
40
|
+
.markdown-section pre code {
|
|
41
|
+
background-color: transparent;
|
|
42
|
+
padding: 0;
|
|
43
|
+
}
|
|
44
|
+
.markdown-section blockquote {
|
|
45
|
+
border-left: 4px solid #007acc;
|
|
46
|
+
background-color: #f0f8ff;
|
|
47
|
+
margin: 20px 0;
|
|
48
|
+
padding: 15px 20px;
|
|
49
|
+
}
|
|
50
|
+
</style>
|
|
51
|
+
</head>
|
|
52
|
+
<body>
|
|
53
|
+
<div id="app"></div>
|
|
54
|
+
<script>
|
|
55
|
+
window.$docsify = {
|
|
56
|
+
name: 'BunSane Framework',
|
|
57
|
+
repo: 'https://github.com/yaaruu/bunsane',
|
|
58
|
+
loadSidebar: true,
|
|
59
|
+
loadNavbar: true,
|
|
60
|
+
coverpage: true,
|
|
61
|
+
themeColor: '#007acc',
|
|
62
|
+
auto2top: true,
|
|
63
|
+
search: {
|
|
64
|
+
paths: 'auto',
|
|
65
|
+
placeholder: 'Search documentation...',
|
|
66
|
+
noData: 'No results found',
|
|
67
|
+
depth: 3
|
|
68
|
+
},
|
|
69
|
+
pagination: {
|
|
70
|
+
previousText: 'Previous',
|
|
71
|
+
nextText: 'Next',
|
|
72
|
+
crossChapter: true,
|
|
73
|
+
crossChapterText: true
|
|
74
|
+
},
|
|
75
|
+
plugins: [
|
|
76
|
+
function(hook, vm) {
|
|
77
|
+
hook.beforeEach(function(html) {
|
|
78
|
+
return html + '\n\n---\n\n*Last updated: ' + new Date().toLocaleDateString() + '*';
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
]
|
|
82
|
+
}
|
|
83
|
+
</script>
|
|
84
|
+
<script src="//cdn.jsdelivr.net/npm/docsify@4/lib/docsify.min.js"></script>
|
|
85
|
+
<script src="//cdn.jsdelivr.net/npm/docsify@4/lib/plugins/search.min.js"></script>
|
|
86
|
+
<script src="//cdn.jsdelivr.net/npm/docsify@4/lib/plugins/emoji.min.js"></script>
|
|
87
|
+
<script src="//cdn.jsdelivr.net/npm/docsify@4/lib/plugins/zoom-image.min.js"></script>
|
|
88
|
+
<script src="//cdn.jsdelivr.net/npm/docsify@4/lib/plugins/external-script.min.js"></script>
|
|
89
|
+
<script src="//cdn.jsdelivr.net/npm/prismjs@1/components/prism-typescript.min.js"></script>
|
|
90
|
+
<script src="//cdn.jsdelivr.net/npm/prismjs@1/components/prism-bash.min.js"></script>
|
|
91
|
+
<script src="//cdn.jsdelivr.net/npm/prismjs@1/components/prism-json.min.js"></script>
|
|
92
|
+
<script src="//cdn.jsdelivr.net/npm/prismjs@1/components/prism-sql.min.js"></script>
|
|
93
|
+
<script src="//cdn.jsdelivr.net/npm/prismjs@1/components/prism-graphql.min.js"></script>
|
|
94
|
+
<script src="//cdn.jsdelivr.net/npm/prismjs@1/plugins/line-numbers/prism-line-numbers.min.js"></script>
|
|
95
|
+
<link rel="stylesheet" href="//cdn.jsdelivr.net/npm/prismjs@1/plugins/line-numbers/prism-line-numbers.css">
|
|
96
|
+
</body>
|
|
97
|
+
</html>
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
# Entity Lifecycle Hooks Examples
|
|
2
|
+
|
|
3
|
+
This directory contains example implementations of common use cases for the Entity Lifecycle Hooks system. Each example demonstrates best practices for hook implementation, error handling, and performance optimization.
|
|
4
|
+
|
|
5
|
+
## Examples Overview
|
|
6
|
+
|
|
7
|
+
### 1. Audit Logging (`audit-logger.ts`)
|
|
8
|
+
Demonstrates comprehensive audit logging for entity lifecycle events with different storage backends.
|
|
9
|
+
|
|
10
|
+
### 2. Cache Management (`cache-manager.ts`)
|
|
11
|
+
Shows how to implement intelligent cache invalidation and population using entity hooks.
|
|
12
|
+
|
|
13
|
+
### 3. Search Indexing (`search-indexer.ts`)
|
|
14
|
+
Illustrates real-time search index updates for entities and components.
|
|
15
|
+
|
|
16
|
+
### 4. Business Rules (`business-rules.ts`)
|
|
17
|
+
Examples of business rule validation and enforcement using component hooks.
|
|
18
|
+
|
|
19
|
+
### 5. Notifications (`notification-service.ts`)
|
|
20
|
+
Demonstrates event-driven notification systems for user actions.
|
|
21
|
+
|
|
22
|
+
### 6. Data Synchronization (`data-sync.ts`)
|
|
23
|
+
Shows cross-system data synchronization patterns.
|
|
24
|
+
|
|
25
|
+
### 7. Metrics Collection (`metrics-collector.ts`)
|
|
26
|
+
Performance monitoring and metrics collection for entity operations.
|
|
27
|
+
|
|
28
|
+
### 8. Security (`security-hooks.ts`)
|
|
29
|
+
Security-related hooks for access control and data protection.
|
|
30
|
+
|
|
31
|
+
## Usage
|
|
32
|
+
|
|
33
|
+
Each example can be used as a starting point for your own implementations. Import the example classes and register them with the hook system:
|
|
34
|
+
|
|
35
|
+
```typescript
|
|
36
|
+
import { AuditLogger } from "./examples/hooks/audit-logger";
|
|
37
|
+
import { registerDecoratedHooks } from "bunsane";
|
|
38
|
+
|
|
39
|
+
// Register example hooks
|
|
40
|
+
const auditLogger = new AuditLogger();
|
|
41
|
+
registerDecoratedHooks(auditLogger);
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Best Practices Demonstrated
|
|
45
|
+
|
|
46
|
+
- **Error Isolation**: Each hook handles its own errors without affecting others
|
|
47
|
+
- **Performance Optimization**: Use of filters, batching, and async operations
|
|
48
|
+
- **Resource Management**: Proper cleanup and resource handling
|
|
49
|
+
- **Type Safety**: Full TypeScript support with proper typing
|
|
50
|
+
- **Monitoring**: Built-in metrics and logging
|
|
51
|
+
- **Configuration**: Environment-based configuration
|
|
52
|
+
- **Testing**: Testable hook implementations
|
|
53
|
+
|
|
54
|
+
## Common Patterns
|
|
55
|
+
|
|
56
|
+
### Conditional Execution
|
|
57
|
+
```typescript
|
|
58
|
+
@EntityHook("entity.created")
|
|
59
|
+
async handleEntityCreated(event: EntityCreatedEvent) {
|
|
60
|
+
// Only process specific entity types
|
|
61
|
+
if (!event.getEntity().has(TargetComponent)) return;
|
|
62
|
+
|
|
63
|
+
// Process the entity
|
|
64
|
+
await this.processEntity(event.getEntity());
|
|
65
|
+
}
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### Batch Processing
|
|
69
|
+
```typescript
|
|
70
|
+
private pendingEvents: EntityCreatedEvent[] = [];
|
|
71
|
+
|
|
72
|
+
@EntityHook("entity.created")
|
|
73
|
+
handleEntityCreated(event: EntityCreatedEvent) {
|
|
74
|
+
this.pendingEvents.push(event);
|
|
75
|
+
|
|
76
|
+
// Process in batches to improve performance
|
|
77
|
+
if (this.pendingEvents.length >= 10) {
|
|
78
|
+
this.processBatch();
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### Error Handling
|
|
84
|
+
```typescript
|
|
85
|
+
@EntityHook("entity.created")
|
|
86
|
+
async handleEntityCreated(event: EntityCreatedEvent) {
|
|
87
|
+
try {
|
|
88
|
+
await this.unreliableOperation();
|
|
89
|
+
} catch (error) {
|
|
90
|
+
// Log error but don't throw - preserve other hooks
|
|
91
|
+
this.logger.error("Hook execution failed:", error);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
### Resource Cleanup
|
|
97
|
+
```typescript
|
|
98
|
+
@EntityHook("entity.deleted")
|
|
99
|
+
handleEntityDeleted(event: EntityDeletedEvent) {
|
|
100
|
+
// Clean up resources associated with the entity
|
|
101
|
+
this.cache.delete(event.getEntity().id);
|
|
102
|
+
this.pendingOperations.delete(event.getEntity().id);
|
|
103
|
+
}
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
## Configuration
|
|
107
|
+
|
|
108
|
+
Most examples support configuration through environment variables or constructor parameters:
|
|
109
|
+
|
|
110
|
+
```typescript
|
|
111
|
+
// Environment-based configuration
|
|
112
|
+
const auditLogger = new AuditLogger({
|
|
113
|
+
enabled: process.env.AUDIT_ENABLED === 'true',
|
|
114
|
+
level: process.env.AUDIT_LEVEL || 'info',
|
|
115
|
+
storage: process.env.AUDIT_STORAGE || 'database'
|
|
116
|
+
});
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
## Testing
|
|
120
|
+
|
|
121
|
+
Each example includes testing patterns:
|
|
122
|
+
|
|
123
|
+
```typescript
|
|
124
|
+
describe("AuditLogger", () => {
|
|
125
|
+
let auditLogger: AuditLogger;
|
|
126
|
+
|
|
127
|
+
beforeEach(() => {
|
|
128
|
+
auditLogger = new AuditLogger();
|
|
129
|
+
registerDecoratedHooks(auditLogger);
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
afterEach(() => {
|
|
133
|
+
EntityHookManager.clearAllHooks();
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
test("should log entity creation", async () => {
|
|
137
|
+
const entity = Entity.Create();
|
|
138
|
+
await entity.save();
|
|
139
|
+
|
|
140
|
+
// Verify audit log was created
|
|
141
|
+
expect(auditLogger.getLogs()).toContain(/* expected log entry */);
|
|
142
|
+
});
|
|
143
|
+
});
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
## Performance Considerations
|
|
147
|
+
|
|
148
|
+
Examples demonstrate performance optimization techniques:
|
|
149
|
+
|
|
150
|
+
- **Filtering**: Reduce unnecessary hook execution
|
|
151
|
+
- **Batching**: Group operations for efficiency
|
|
152
|
+
- **Async Processing**: Use async hooks for I/O operations
|
|
153
|
+
- **Caching**: Cache frequently accessed data
|
|
154
|
+
- **Timeouts**: Prevent hanging operations
|
|
155
|
+
|
|
156
|
+
## Integration
|
|
157
|
+
|
|
158
|
+
Examples show how to integrate with existing systems:
|
|
159
|
+
|
|
160
|
+
- **Database**: Entity persistence and queries
|
|
161
|
+
- **Cache**: Redis, Memcached, or in-memory caching
|
|
162
|
+
- **Search**: Elasticsearch, Algolia, or custom search
|
|
163
|
+
- **Queues**: Background job processing
|
|
164
|
+
- **Monitoring**: Metrics collection and alerting
|
|
165
|
+
- **Logging**: Structured logging with context
|
|
166
|
+
|
|
167
|
+
## Customization
|
|
168
|
+
|
|
169
|
+
Examples are designed to be easily customizable:
|
|
170
|
+
|
|
171
|
+
```typescript
|
|
172
|
+
// Extend base examples
|
|
173
|
+
class CustomAuditLogger extends AuditLogger {
|
|
174
|
+
@EntityHook("entity.updated")
|
|
175
|
+
async handleCustomUpdate(event: EntityUpdatedEvent) {
|
|
176
|
+
// Custom logic
|
|
177
|
+
await super.handleEntityUpdated(event);
|
|
178
|
+
|
|
179
|
+
// Additional processing
|
|
180
|
+
await this.customProcessing(event);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
## Monitoring
|
|
186
|
+
|
|
187
|
+
Examples include monitoring and health checks:
|
|
188
|
+
|
|
189
|
+
```typescript
|
|
190
|
+
// Health check endpoint
|
|
191
|
+
app.get('/health/hooks', async (req, res) => {
|
|
192
|
+
const metrics = EntityHookManager.getMetrics();
|
|
193
|
+
const health = {
|
|
194
|
+
status: metrics.errorCount > 10 ? 'unhealthy' : 'healthy',
|
|
195
|
+
metrics,
|
|
196
|
+
timestamp: new Date().toISOString()
|
|
197
|
+
};
|
|
198
|
+
|
|
199
|
+
res.json(health);
|
|
200
|
+
});
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
## Troubleshooting
|
|
204
|
+
|
|
205
|
+
Common issues and solutions:
|
|
206
|
+
|
|
207
|
+
1. **Hooks not executing**: Check `EntityHookManager.waitForReady()`
|
|
208
|
+
2. **Performance issues**: Use `EntityHookManager.getMetrics()` to identify bottlenecks
|
|
209
|
+
3. **Memory leaks**: Ensure proper cleanup in deletion hooks
|
|
210
|
+
4. **Error propagation**: Handle errors gracefully to prevent hook isolation issues
|
|
211
|
+
|
|
212
|
+
## Contributing
|
|
213
|
+
|
|
214
|
+
When adding new examples:
|
|
215
|
+
|
|
216
|
+
1. Follow the established patterns and best practices
|
|
217
|
+
2. Include comprehensive error handling
|
|
218
|
+
3. Add performance optimizations
|
|
219
|
+
4. Provide configuration options
|
|
220
|
+
5. Include tests and documentation
|
|
221
|
+
6. Demonstrate integration patterns
|
|
222
|
+
|
|
223
|
+
## Related Documentation
|
|
224
|
+
|
|
225
|
+
- [Hooks Documentation](../HOOKS_DOCUMENTATION.md)
|
|
226
|
+
- [Migration Guide](../HOOKS_MIGRATION_GUIDE.md)
|
|
227
|
+
- [Performance Guide](../HOOKS_PERFORMANCE_GUIDE.md)
|
|
228
|
+
- [API Reference](../HOOKS_DOCUMENTATION.md#api-reference)
|