bindra 2.0.0
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/CHANGELOG.md +56 -0
- package/LICENSE +21 -0
- package/README.md +229 -0
- package/dist/core/Container.d.ts +88 -0
- package/dist/core/Container.d.ts.map +1 -0
- package/dist/core/Container.js +185 -0
- package/dist/core/Container.js.map +1 -0
- package/dist/core/DataSource.d.ts +272 -0
- package/dist/core/DataSource.d.ts.map +1 -0
- package/dist/core/DataSource.js +903 -0
- package/dist/core/DataSource.js.map +1 -0
- package/dist/core/Dispatcher.d.ts +6 -0
- package/dist/core/Dispatcher.d.ts.map +1 -0
- package/dist/core/Dispatcher.js +44 -0
- package/dist/core/Dispatcher.js.map +1 -0
- package/dist/core/EventEmitter.d.ts +11 -0
- package/dist/core/EventEmitter.d.ts.map +1 -0
- package/dist/core/EventEmitter.js +34 -0
- package/dist/core/EventEmitter.js.map +1 -0
- package/dist/core/MiddlewareManager.d.ts +47 -0
- package/dist/core/MiddlewareManager.d.ts.map +1 -0
- package/dist/core/MiddlewareManager.js +86 -0
- package/dist/core/MiddlewareManager.js.map +1 -0
- package/dist/core/Observable.d.ts +12 -0
- package/dist/core/Observable.d.ts.map +1 -0
- package/dist/core/Observable.js +43 -0
- package/dist/core/Observable.js.map +1 -0
- package/dist/core/errors.d.ts +124 -0
- package/dist/core/errors.d.ts.map +1 -0
- package/dist/core/errors.js +149 -0
- package/dist/core/errors.js.map +1 -0
- package/dist/core/validation.d.ts +100 -0
- package/dist/core/validation.d.ts.map +1 -0
- package/dist/core/validation.js +217 -0
- package/dist/core/validation.js.map +1 -0
- package/dist/examples.d.ts +52 -0
- package/dist/examples.d.ts.map +1 -0
- package/dist/examples.js +242 -0
- package/dist/examples.js.map +1 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +14 -0
- package/dist/index.js.map +1 -0
- package/dist/utils/performance.d.ts +49 -0
- package/dist/utils/performance.d.ts.map +1 -0
- package/dist/utils/performance.js +94 -0
- package/dist/utils/performance.js.map +1 -0
- package/package.json +64 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [2.0.0] - 2025-12-24
|
|
9
|
+
|
|
10
|
+
### Added - Phase 1: Foundation
|
|
11
|
+
- **Generic Type Support**: Full TypeScript generics for type-safe DataSource operations
|
|
12
|
+
- **Error Handling System**: Custom error types (ValidationError, NetworkError, NotFoundError)
|
|
13
|
+
- **Container Async Initialization**: Proper async initialization with status tracking
|
|
14
|
+
- **Validation System**: Comprehensive validation with custom validators and error collection
|
|
15
|
+
- **Testing Infrastructure**: 155 tests with Vitest, 100% passing
|
|
16
|
+
|
|
17
|
+
### Added - Phase 2: Advanced Features
|
|
18
|
+
- **Caching System**: TTL-based cache with manual and automatic invalidation
|
|
19
|
+
- **Pagination**: Offset and cursor-based pagination with auto-loading
|
|
20
|
+
- **Batch Operations**: Efficient batch create, update, and delete operations
|
|
21
|
+
- **Optimistic Updates**: Immediate UI updates with automatic rollback on error
|
|
22
|
+
|
|
23
|
+
### Added - Phase 3: Production Features
|
|
24
|
+
- **WebSocket/Real-time Support**: Bidirectional WebSocket communication with auto-reconnect
|
|
25
|
+
- **Security Features**: XSS sanitization and CSRF token support
|
|
26
|
+
- **Performance Utilities**: debounce, throttle, and debounceWithMaxWait functions
|
|
27
|
+
|
|
28
|
+
### Added - Phase 4: Polish
|
|
29
|
+
- **CI/CD Pipeline**: GitHub Actions for automated testing and publishing
|
|
30
|
+
- **npm Publishing**: Proper package configuration and publishing workflow
|
|
31
|
+
- **Comprehensive Examples**: Real-world usage examples and integrations
|
|
32
|
+
- **Complete API Documentation**: Full API reference with examples
|
|
33
|
+
|
|
34
|
+
### Changed
|
|
35
|
+
- **BREAKING**: DataSource now requires generic type parameter for type safety
|
|
36
|
+
- **BREAKING**: Error handling now throws custom error types instead of generic Error
|
|
37
|
+
- Improved TypeScript strict mode compliance
|
|
38
|
+
- Enhanced JSDoc documentation across all public APIs
|
|
39
|
+
|
|
40
|
+
### Fixed
|
|
41
|
+
- Type inference issues in query operations
|
|
42
|
+
- Race conditions in async initialization
|
|
43
|
+
- Memory leaks in event listeners
|
|
44
|
+
- WebSocket connection cleanup
|
|
45
|
+
|
|
46
|
+
## [1.0.0] - Previous Release
|
|
47
|
+
|
|
48
|
+
Initial release with basic reactive data management features.
|
|
49
|
+
|
|
50
|
+
### Features
|
|
51
|
+
- Local and remote data source support
|
|
52
|
+
- CRUD operations
|
|
53
|
+
- Event system
|
|
54
|
+
- Middleware support
|
|
55
|
+
- Navigation (next/prev/goto)
|
|
56
|
+
- Basic querying
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Mohamad J.
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
# Bindra
|
|
2
|
+
|
|
3
|
+
[](https://github.com/mohamad-j/Bindra/actions)
|
|
4
|
+
[](https://www.npmjs.com/package/bindra)
|
|
5
|
+
[](https://www.typescriptlang.org/)
|
|
6
|
+
[](https://opensource.org/licenses/MIT)
|
|
7
|
+
|
|
8
|
+
**Bindra** is a lightweight, reactive data management library for TypeScript applications. It provides a unified API for managing both local and remote data with built-in reactivity, validation, caching, and real-time updates.
|
|
9
|
+
|
|
10
|
+
## ✨ Features
|
|
11
|
+
|
|
12
|
+
- 🎯 **Type-Safe**: Full TypeScript support with comprehensive type definitions
|
|
13
|
+
- 🔄 **Reactive**: Built-in reactivity with signals and reactive objects
|
|
14
|
+
- 🌐 **API Ready**: Seamless REST API integration with automatic retries
|
|
15
|
+
- ✅ **Validation**: Comprehensive validation system with custom rules
|
|
16
|
+
- 💾 **Caching**: Smart TTL-based caching to reduce API calls
|
|
17
|
+
- 📄 **Pagination**: Offset and cursor-based pagination strategies
|
|
18
|
+
- 🔌 **Real-time**: WebSocket support with auto-reconnect
|
|
19
|
+
- 📦 **Zero Dependencies**: Pure TypeScript, no external dependencies
|
|
20
|
+
- 🎨 **Framework Agnostic**: Works with React, Vue, Svelte, or vanilla JS
|
|
21
|
+
|
|
22
|
+
## 📦 Installation
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
npm install bindra
|
|
26
|
+
# or
|
|
27
|
+
pnpm add bindra
|
|
28
|
+
# or
|
|
29
|
+
yarn add bindra
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## 🚀 Quick Start
|
|
33
|
+
|
|
34
|
+
```typescript
|
|
35
|
+
import { DataSource } from 'bindra';
|
|
36
|
+
|
|
37
|
+
interface User {
|
|
38
|
+
id: number;
|
|
39
|
+
name: string;
|
|
40
|
+
email: string;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Create a DataSource
|
|
44
|
+
const users = new DataSource<User>({
|
|
45
|
+
url: '/api/users'
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
// Fetch data
|
|
49
|
+
await users.fetch();
|
|
50
|
+
|
|
51
|
+
// Create a record
|
|
52
|
+
const newUser = await users.create({
|
|
53
|
+
name: 'Alice',
|
|
54
|
+
email: 'alice@example.com'
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
// Update a record
|
|
58
|
+
await users.update(1, { name: 'Bob' });
|
|
59
|
+
|
|
60
|
+
// Delete a record
|
|
61
|
+
await users.delete(1);
|
|
62
|
+
|
|
63
|
+
// Subscribe to changes
|
|
64
|
+
users.on('created', (user) => {
|
|
65
|
+
console.log('User created:', user);
|
|
66
|
+
});
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## 📚 Documentation
|
|
70
|
+
|
|
71
|
+
For complete documentation, visit: [https://bindra-docs.web.app](https://bindra-docs.web.app)
|
|
72
|
+
|
|
73
|
+
- [Getting Started](https://bindra-docs.web.app/guide/getting-started)
|
|
74
|
+
- [API Reference](https://bindra-docs.web.app/api/)
|
|
75
|
+
- [Examples](https://bindra-docs.web.app/examples/)
|
|
76
|
+
- [Migration Guide](https://bindra-docs.web.app/guide/migration-guide)
|
|
77
|
+
|
|
78
|
+
## 💡 Key Concepts
|
|
79
|
+
|
|
80
|
+
### DataSource
|
|
81
|
+
|
|
82
|
+
The main class for managing data:
|
|
83
|
+
|
|
84
|
+
```typescript
|
|
85
|
+
const ds = new DataSource<Product>({
|
|
86
|
+
url: '/api/products',
|
|
87
|
+
cache: { enabled: true, ttl: 300000 },
|
|
88
|
+
pagination: { pageSize: 20 }
|
|
89
|
+
});
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### Validation
|
|
93
|
+
|
|
94
|
+
Built-in validation system:
|
|
95
|
+
|
|
96
|
+
```typescript
|
|
97
|
+
const users = new DataSource<User>({
|
|
98
|
+
url: '/api/users',
|
|
99
|
+
fields: [
|
|
100
|
+
{
|
|
101
|
+
name: 'email',
|
|
102
|
+
validation: {
|
|
103
|
+
required: true,
|
|
104
|
+
type: 'email'
|
|
105
|
+
}
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
name: 'age',
|
|
109
|
+
validation: {
|
|
110
|
+
type: 'number',
|
|
111
|
+
min: 18,
|
|
112
|
+
max: 120
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
]
|
|
116
|
+
});
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
### Real-time Updates
|
|
120
|
+
|
|
121
|
+
WebSocket support for live data:
|
|
122
|
+
|
|
123
|
+
```typescript
|
|
124
|
+
const messages = new DataSource<Message>({
|
|
125
|
+
url: '/api/messages',
|
|
126
|
+
realtime: {
|
|
127
|
+
enabled: true,
|
|
128
|
+
url: 'wss://api.example.com/ws',
|
|
129
|
+
reconnect: true
|
|
130
|
+
}
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
messages.on('realtime:message', (data) => {
|
|
134
|
+
console.log('New message:', data);
|
|
135
|
+
});
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
## 🎯 Use Cases
|
|
139
|
+
|
|
140
|
+
Bindra is perfect for:
|
|
141
|
+
|
|
142
|
+
- ✅ CRUD applications
|
|
143
|
+
- ✅ Data-heavy dashboards
|
|
144
|
+
- ✅ Real-time collaborative apps
|
|
145
|
+
- ✅ Forms with validation
|
|
146
|
+
- ✅ Paginated lists
|
|
147
|
+
- ✅ Infinite scroll
|
|
148
|
+
|
|
149
|
+
## 🧪 Testing
|
|
150
|
+
|
|
151
|
+
Bindra is thoroughly tested with 155+ tests:
|
|
152
|
+
|
|
153
|
+
```bash
|
|
154
|
+
cd package
|
|
155
|
+
pnpm test
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
## 📄 License
|
|
159
|
+
|
|
160
|
+
MIT License - see [LICENSE](LICENSE) for details
|
|
161
|
+
|
|
162
|
+
## 🤝 Contributing
|
|
163
|
+
|
|
164
|
+
See [CONTRIBUTING.md](../docs/CONTRIBUTING.md) for contribution guidelines.
|
|
165
|
+
|
|
166
|
+
## 🔗 Links
|
|
167
|
+
|
|
168
|
+
- [NPM Package](https://www.npmjs.com/package/bindra)
|
|
169
|
+
- [Documentation](https://bindra-docs.web.app)
|
|
170
|
+
- [GitHub Repository](https://github.com/mohamad-j/Bindra)
|
|
171
|
+
- [Issue Tracker](https://github.com/mohamad-j/Bindra/issues)
|
|
172
|
+
|
|
173
|
+
## 📦 What's Included
|
|
174
|
+
|
|
175
|
+
```
|
|
176
|
+
bindra@2.0.0
|
|
177
|
+
├── dist/
|
|
178
|
+
│ ├── index.js # Main entry point
|
|
179
|
+
│ ├── index.d.ts # TypeScript definitions
|
|
180
|
+
│ ├── core/ # Core modules
|
|
181
|
+
│ └── utils/ # Utility functions
|
|
182
|
+
├── README.md
|
|
183
|
+
├── LICENSE
|
|
184
|
+
└── CHANGELOG.md
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
## 🚀 Quick Examples
|
|
188
|
+
|
|
189
|
+
### Local Data
|
|
190
|
+
|
|
191
|
+
```typescript
|
|
192
|
+
const todos = new DataSource<Todo>({
|
|
193
|
+
data: [
|
|
194
|
+
{ id: 1, title: 'Learn Bindra', completed: false },
|
|
195
|
+
{ id: 2, title: 'Build app', completed: false }
|
|
196
|
+
]
|
|
197
|
+
});
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
### Remote API
|
|
201
|
+
|
|
202
|
+
```typescript
|
|
203
|
+
const users = new DataSource<User>({
|
|
204
|
+
url: 'https://api.example.com/users',
|
|
205
|
+
cache: { enabled: true, ttl: 300000 }
|
|
206
|
+
});
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
### With Validation
|
|
210
|
+
|
|
211
|
+
```typescript
|
|
212
|
+
const products = new DataSource<Product>({
|
|
213
|
+
url: '/api/products',
|
|
214
|
+
fields: [
|
|
215
|
+
{
|
|
216
|
+
name: 'name',
|
|
217
|
+
validation: { required: true, type: 'string', min: 3 }
|
|
218
|
+
},
|
|
219
|
+
{
|
|
220
|
+
name: 'price',
|
|
221
|
+
validation: { required: true, type: 'number', min: 0 }
|
|
222
|
+
}
|
|
223
|
+
]
|
|
224
|
+
});
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
---
|
|
228
|
+
|
|
229
|
+
**Built with ❤️ for the TypeScript community**
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { DataSource, type DataSourceConfig } from "./DataSource";
|
|
2
|
+
/**
|
|
3
|
+
* Container for managing DataSource instances with proper async initialization
|
|
4
|
+
*/
|
|
5
|
+
declare class Container {
|
|
6
|
+
private dataSources;
|
|
7
|
+
private initPromises;
|
|
8
|
+
/**
|
|
9
|
+
* Register a new DataSource with the given name and configuration
|
|
10
|
+
* Properly handles async initialization without arbitrary timeouts
|
|
11
|
+
*
|
|
12
|
+
* @param name - Unique identifier for the DataSource
|
|
13
|
+
* @param config - Configuration for the DataSource
|
|
14
|
+
* @returns Promise that resolves to the initialized DataSource
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```typescript
|
|
18
|
+
* const ds = await container.register('users', {
|
|
19
|
+
* url: 'https://api.example.com/users'
|
|
20
|
+
* });
|
|
21
|
+
* ```
|
|
22
|
+
*/
|
|
23
|
+
register<T extends Record<string, any> = any>(name: string, config: DataSourceConfig): Promise<DataSource<T>>;
|
|
24
|
+
/**
|
|
25
|
+
* Internal method to initialize a DataSource
|
|
26
|
+
* Handles both local and remote initialization
|
|
27
|
+
*/
|
|
28
|
+
private _initialize;
|
|
29
|
+
/**
|
|
30
|
+
* Get a DataSource by name
|
|
31
|
+
* If initialization is in progress, waits for it to complete
|
|
32
|
+
* No arbitrary timeouts - properly waits for async initialization
|
|
33
|
+
*
|
|
34
|
+
* @param name - Name of the DataSource to retrieve
|
|
35
|
+
* @returns Promise that resolves to the DataSource
|
|
36
|
+
*
|
|
37
|
+
* @example
|
|
38
|
+
* ```typescript
|
|
39
|
+
* const ds = await container.get<User>('users');
|
|
40
|
+
* ```
|
|
41
|
+
*/
|
|
42
|
+
get<T extends Record<string, any> = any>(name: string): Promise<DataSource<T>>;
|
|
43
|
+
/**
|
|
44
|
+
* Check if a DataSource is registered (regardless of initialization state)
|
|
45
|
+
*
|
|
46
|
+
* @param name - Name to check
|
|
47
|
+
* @returns true if DataSource is registered
|
|
48
|
+
*/
|
|
49
|
+
has(name: string): boolean;
|
|
50
|
+
/**
|
|
51
|
+
* Get the current status of a DataSource
|
|
52
|
+
*
|
|
53
|
+
* @param name - Name of the DataSource
|
|
54
|
+
* @returns Status: 'initializing', 'ready', 'error', or undefined if not found
|
|
55
|
+
*/
|
|
56
|
+
getStatus(name: string): 'initializing' | 'ready' | 'error' | undefined;
|
|
57
|
+
/**
|
|
58
|
+
* Unregister a DataSource and clean up resources
|
|
59
|
+
*
|
|
60
|
+
* @param name - Name of the DataSource to unregister
|
|
61
|
+
*/
|
|
62
|
+
unregister(name: string): Promise<void>;
|
|
63
|
+
/**
|
|
64
|
+
* Get all registered DataSource names
|
|
65
|
+
*
|
|
66
|
+
* @returns Array of DataSource names
|
|
67
|
+
*/
|
|
68
|
+
list(): string[];
|
|
69
|
+
/**
|
|
70
|
+
* Clear all DataSources from the container
|
|
71
|
+
*/
|
|
72
|
+
clear(): Promise<void>;
|
|
73
|
+
}
|
|
74
|
+
declare const container: Container;
|
|
75
|
+
/**
|
|
76
|
+
* Register a new DataSource with the global container
|
|
77
|
+
*
|
|
78
|
+
* @deprecated Use container.register() directly for better control
|
|
79
|
+
*/
|
|
80
|
+
declare function dataSource<T extends Record<string, any> = any>(name: string, config: DataSourceConfig): Promise<DataSource<T>>;
|
|
81
|
+
/**
|
|
82
|
+
* Get a DataSource from the global container
|
|
83
|
+
*
|
|
84
|
+
* @deprecated Use container.get() directly for better control
|
|
85
|
+
*/
|
|
86
|
+
declare function getDataSource<T extends Record<string, any> = any>(name: string): Promise<DataSource<T>>;
|
|
87
|
+
export { Container, container, dataSource, getDataSource };
|
|
88
|
+
//# sourceMappingURL=Container.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Container.d.ts","sourceRoot":"","sources":["../../src/core/Container.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,KAAK,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAcjE;;GAEG;AACH,cAAM,SAAS;IACb,OAAO,CAAC,WAAW,CAAsC;IACzD,OAAO,CAAC,YAAY,CAA0C;IAE9D;;;;;;;;;;;;;;OAcG;IACG,QAAQ,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,GAAG,EAChD,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,gBAAgB,GACvB,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;IA6CzB;;;OAGG;YACW,WAAW;IAazB;;;;;;;;;;;;OAYG;IACG,GAAG,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,GAAG,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;IAkCpF;;;;;OAKG;IACH,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO;IAI1B;;;;;OAKG;IACH,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,cAAc,GAAG,OAAO,GAAG,OAAO,GAAG,SAAS;IAIvE;;;;OAIG;IACG,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAqB7C;;;;OAIG;IACH,IAAI,IAAI,MAAM,EAAE;IAIhB;;OAEG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAI7B;AAGD,QAAA,MAAM,SAAS,WAAkB,CAAC;AAElC;;;;GAIG;AACH,iBAAe,UAAU,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,GAAG,EAC3D,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,gBAAgB,GACvB,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAExB;AAED;;;;GAIG;AACH,iBAAe,aAAa,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,GAAG,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAEtG;AAED,OAAO,EACL,SAAS,EACT,SAAS,EACT,UAAU,EACV,aAAa,EACd,CAAC"}
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
import { DataSource } from "./DataSource";
|
|
2
|
+
import { dispatch } from "./Dispatcher";
|
|
3
|
+
import { ConfigurationError } from "./errors";
|
|
4
|
+
/**
|
|
5
|
+
* Container for managing DataSource instances with proper async initialization
|
|
6
|
+
*/
|
|
7
|
+
class Container {
|
|
8
|
+
constructor() {
|
|
9
|
+
this.dataSources = new Map();
|
|
10
|
+
this.initPromises = new Map();
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Register a new DataSource with the given name and configuration
|
|
14
|
+
* Properly handles async initialization without arbitrary timeouts
|
|
15
|
+
*
|
|
16
|
+
* @param name - Unique identifier for the DataSource
|
|
17
|
+
* @param config - Configuration for the DataSource
|
|
18
|
+
* @returns Promise that resolves to the initialized DataSource
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* ```typescript
|
|
22
|
+
* const ds = await container.register('users', {
|
|
23
|
+
* url: 'https://api.example.com/users'
|
|
24
|
+
* });
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
async register(name, config) {
|
|
28
|
+
if (this.dataSources.has(name)) {
|
|
29
|
+
throw new ConfigurationError(`DataSource '${name}' already registered`, { name, existingConfig: this.dataSources.get(name)?.config });
|
|
30
|
+
}
|
|
31
|
+
// Mark as initializing
|
|
32
|
+
this.dataSources.set(name, {
|
|
33
|
+
instance: null,
|
|
34
|
+
config,
|
|
35
|
+
status: 'initializing'
|
|
36
|
+
});
|
|
37
|
+
// Create initialization promise
|
|
38
|
+
const initPromise = this._initialize(config);
|
|
39
|
+
this.initPromises.set(name, initPromise);
|
|
40
|
+
try {
|
|
41
|
+
const ds = await initPromise;
|
|
42
|
+
// Update to ready state
|
|
43
|
+
this.dataSources.set(name, {
|
|
44
|
+
instance: ds,
|
|
45
|
+
config,
|
|
46
|
+
status: 'ready'
|
|
47
|
+
});
|
|
48
|
+
dispatch('core:ds:created', name);
|
|
49
|
+
return ds;
|
|
50
|
+
}
|
|
51
|
+
catch (error) {
|
|
52
|
+
// Update to error state
|
|
53
|
+
this.dataSources.set(name, {
|
|
54
|
+
instance: null,
|
|
55
|
+
config,
|
|
56
|
+
status: 'error',
|
|
57
|
+
error: error
|
|
58
|
+
});
|
|
59
|
+
throw error;
|
|
60
|
+
}
|
|
61
|
+
finally {
|
|
62
|
+
this.initPromises.delete(name);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Internal method to initialize a DataSource
|
|
67
|
+
* Handles both local and remote initialization
|
|
68
|
+
*/
|
|
69
|
+
async _initialize(config) {
|
|
70
|
+
const ds = new DataSource(config);
|
|
71
|
+
// If remote, wait for initialization to complete
|
|
72
|
+
if (config.url) {
|
|
73
|
+
await ds._initRemote(config.url);
|
|
74
|
+
}
|
|
75
|
+
return ds;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Get a DataSource by name
|
|
79
|
+
* If initialization is in progress, waits for it to complete
|
|
80
|
+
* No arbitrary timeouts - properly waits for async initialization
|
|
81
|
+
*
|
|
82
|
+
* @param name - Name of the DataSource to retrieve
|
|
83
|
+
* @returns Promise that resolves to the DataSource
|
|
84
|
+
*
|
|
85
|
+
* @example
|
|
86
|
+
* ```typescript
|
|
87
|
+
* const ds = await container.get<User>('users');
|
|
88
|
+
* ```
|
|
89
|
+
*/
|
|
90
|
+
async get(name) {
|
|
91
|
+
// If initialization is in progress, wait for it
|
|
92
|
+
if (this.initPromises.has(name)) {
|
|
93
|
+
return this.initPromises.get(name);
|
|
94
|
+
}
|
|
95
|
+
const entry = this.dataSources.get(name);
|
|
96
|
+
if (!entry) {
|
|
97
|
+
throw new ConfigurationError(`DataSource '${name}' not found. Did you forget to register it?`, { name, available: Array.from(this.dataSources.keys()) });
|
|
98
|
+
}
|
|
99
|
+
if (entry.status === 'error') {
|
|
100
|
+
throw new ConfigurationError(`DataSource '${name}' failed to initialize: ${entry.error?.message}`, { name, originalError: entry.error });
|
|
101
|
+
}
|
|
102
|
+
if (entry.status === 'initializing') {
|
|
103
|
+
// This shouldn't happen if initPromises is managed correctly,
|
|
104
|
+
// but handle it gracefully
|
|
105
|
+
throw new ConfigurationError(`DataSource '${name}' is still initializing but no promise found`, { name, status: entry.status });
|
|
106
|
+
}
|
|
107
|
+
return entry.instance;
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Check if a DataSource is registered (regardless of initialization state)
|
|
111
|
+
*
|
|
112
|
+
* @param name - Name to check
|
|
113
|
+
* @returns true if DataSource is registered
|
|
114
|
+
*/
|
|
115
|
+
has(name) {
|
|
116
|
+
return this.dataSources.has(name);
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Get the current status of a DataSource
|
|
120
|
+
*
|
|
121
|
+
* @param name - Name of the DataSource
|
|
122
|
+
* @returns Status: 'initializing', 'ready', 'error', or undefined if not found
|
|
123
|
+
*/
|
|
124
|
+
getStatus(name) {
|
|
125
|
+
return this.dataSources.get(name)?.status;
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Unregister a DataSource and clean up resources
|
|
129
|
+
*
|
|
130
|
+
* @param name - Name of the DataSource to unregister
|
|
131
|
+
*/
|
|
132
|
+
async unregister(name) {
|
|
133
|
+
const entry = this.dataSources.get(name);
|
|
134
|
+
if (!entry) {
|
|
135
|
+
return; // Already unregistered
|
|
136
|
+
}
|
|
137
|
+
// If initializing, wait for it to complete before cleanup
|
|
138
|
+
if (this.initPromises.has(name)) {
|
|
139
|
+
try {
|
|
140
|
+
await this.initPromises.get(name);
|
|
141
|
+
}
|
|
142
|
+
catch {
|
|
143
|
+
// Ignore initialization errors during cleanup
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
// Cleanup and remove
|
|
147
|
+
this.dataSources.delete(name);
|
|
148
|
+
dispatch('core:ds:destroyed', name);
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Get all registered DataSource names
|
|
152
|
+
*
|
|
153
|
+
* @returns Array of DataSource names
|
|
154
|
+
*/
|
|
155
|
+
list() {
|
|
156
|
+
return Array.from(this.dataSources.keys());
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Clear all DataSources from the container
|
|
160
|
+
*/
|
|
161
|
+
async clear() {
|
|
162
|
+
const names = this.list();
|
|
163
|
+
await Promise.all(names.map(name => this.unregister(name)));
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
// Singleton instance
|
|
167
|
+
const container = new Container();
|
|
168
|
+
/**
|
|
169
|
+
* Register a new DataSource with the global container
|
|
170
|
+
*
|
|
171
|
+
* @deprecated Use container.register() directly for better control
|
|
172
|
+
*/
|
|
173
|
+
async function dataSource(name, config) {
|
|
174
|
+
return container.register(name, config);
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Get a DataSource from the global container
|
|
178
|
+
*
|
|
179
|
+
* @deprecated Use container.get() directly for better control
|
|
180
|
+
*/
|
|
181
|
+
async function getDataSource(name) {
|
|
182
|
+
return container.get(name);
|
|
183
|
+
}
|
|
184
|
+
export { Container, container, dataSource, getDataSource };
|
|
185
|
+
//# sourceMappingURL=Container.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Container.js","sourceRoot":"","sources":["../../src/core/Container.ts"],"names":[],"mappings":"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"}
|