@typewirets/core 0.1.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/LICENSE +21 -0
- package/README.md +206 -0
- package/dist/cjs/index.d.ts +780 -0
- package/dist/cjs/index.d.ts.map +1 -0
- package/dist/cjs/index.js +880 -0
- package/dist/cjs/index.js.map +1 -0
- package/dist/esm/index.d.ts +780 -0
- package/dist/esm/index.d.ts.map +1 -0
- package/dist/esm/index.js +868 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/types/index.d.ts +780 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +868 -0
- package/dist/types/index.js.map +1 -0
- package/package.json +68 -0
- package/src/index.ts +1544 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 TypewireTS
|
|
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,206 @@
|
|
|
1
|
+
# TypeWire Core
|
|
2
|
+
|
|
3
|
+
A lightweight, container-agnostic dependency injection library for TypeScript that provides strong typing with minimal overhead.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- 🔒 **Strongly typed** - Full TypeScript support with no type casting
|
|
8
|
+
- 🌱 **Lightweight** - Small bundle size with no external dependencies
|
|
9
|
+
- 🔌 **Container-agnostic** - Works with any container through adapters
|
|
10
|
+
- 🧩 **Immutable definitions** - Functional API for composing and reusing definitions
|
|
11
|
+
- ⚡ **Async support** - First-class support for asynchronous dependency resolution
|
|
12
|
+
- 🔍 **Circular dependency detection** - Automatically detects circular dependencies
|
|
13
|
+
- 🧠 **Smart scoping** - Support for singleton and transient scopes
|
|
14
|
+
|
|
15
|
+
## Why TypeWire?
|
|
16
|
+
|
|
17
|
+
TypeWire is designed to create clear boundaries and explicit dependencies in your codebase. It helps you:
|
|
18
|
+
|
|
19
|
+
- **Build clear boundaries** - Separate behavior from construction and configuration
|
|
20
|
+
- **Make dependencies explicit** - No magic, no ambient context, just clear dependencies
|
|
21
|
+
- **Compose behaviors easily** - Build complex systems from simple, well-defined components
|
|
22
|
+
- **Keep testing simple** - Replace implementations without changing consumer code
|
|
23
|
+
- **Control side effects** - Manage where and how side effects occur in your system
|
|
24
|
+
|
|
25
|
+
The goal is to help you build systems that are clear, testable, and maintainable.
|
|
26
|
+
|
|
27
|
+
## Installation
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
npm install @typewirets/core
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Core Concepts
|
|
34
|
+
|
|
35
|
+
### TypeSymbol
|
|
36
|
+
|
|
37
|
+
A typed wrapper around JavaScript's Symbol that preserves type information at compile time.
|
|
38
|
+
|
|
39
|
+
```typescript
|
|
40
|
+
import { typeSymbolOf } from '@typewirets/core';
|
|
41
|
+
|
|
42
|
+
const userSymbol = typeSymbolOf<User>('User');
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### TypeWire
|
|
46
|
+
|
|
47
|
+
Defines how to create and manage instances of a specific type. Works with both classes and functions:
|
|
48
|
+
|
|
49
|
+
```typescript
|
|
50
|
+
import { typeWireOf } from '@typewirets/core';
|
|
51
|
+
|
|
52
|
+
// Class-based service
|
|
53
|
+
const loggerWire = typeWireOf({
|
|
54
|
+
token: 'Logger',
|
|
55
|
+
creator: () => new Logger()
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
// Function-based service
|
|
59
|
+
const configWire = typeWireOf({
|
|
60
|
+
token: 'Config',
|
|
61
|
+
creator: async () => {
|
|
62
|
+
const config = await loadConfig();
|
|
63
|
+
return createConfig(config);
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
// Service with dependencies
|
|
68
|
+
const userServiceWire = typeWireOf({
|
|
69
|
+
token: 'UserService',
|
|
70
|
+
imports: {
|
|
71
|
+
logger: loggerWire,
|
|
72
|
+
config: configWire
|
|
73
|
+
},
|
|
74
|
+
createWith: ({ logger, config }) => new UserService(logger, config)
|
|
75
|
+
});
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### Managing Context
|
|
79
|
+
|
|
80
|
+
TypeWire helps you separate long-lived services from contextual state:
|
|
81
|
+
|
|
82
|
+
```typescript
|
|
83
|
+
// Long-lived service (use TypeWire)
|
|
84
|
+
const dbClientWire = typeWireOf({
|
|
85
|
+
token: 'DbClient',
|
|
86
|
+
creator: () => new DbClient()
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
// Contextual state (construct where needed)
|
|
90
|
+
class RequestContext {
|
|
91
|
+
constructor(
|
|
92
|
+
private userId: string,
|
|
93
|
+
private dbClient: DbClient
|
|
94
|
+
) {}
|
|
95
|
+
|
|
96
|
+
async getUser() {
|
|
97
|
+
return this.dbClient.getUser(this.userId);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const userServiceWire = typeWireOf({
|
|
102
|
+
token: 'UserService',
|
|
103
|
+
imports: {
|
|
104
|
+
dbClient: dbClientWire
|
|
105
|
+
},
|
|
106
|
+
createWith: ({ dbClient }) => ({
|
|
107
|
+
createContext: (userId: string) => new RequestContext(userId, dbClient)
|
|
108
|
+
})
|
|
109
|
+
});
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
### Composing Services
|
|
113
|
+
|
|
114
|
+
TypeWire makes it easy to compose and override services:
|
|
115
|
+
|
|
116
|
+
```typescript
|
|
117
|
+
// Base configuration
|
|
118
|
+
const baseConfigWire = typeWireOf({
|
|
119
|
+
token: 'Config',
|
|
120
|
+
creator: () => ({ apiUrl: 'https://api.example.com' })
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
// Development override
|
|
124
|
+
const devConfigWire = baseConfigWire.withCreator(() => ({
|
|
125
|
+
apiUrl: 'http://localhost:3000'
|
|
126
|
+
}));
|
|
127
|
+
|
|
128
|
+
// Testing override
|
|
129
|
+
const testConfigWire = baseConfigWire.withCreator(() => ({
|
|
130
|
+
apiUrl: 'http://test-api'
|
|
131
|
+
}));
|
|
132
|
+
|
|
133
|
+
// Feature flags
|
|
134
|
+
const featureFlagsWire = typeWireOf({
|
|
135
|
+
token: 'FeatureFlags',
|
|
136
|
+
imports: { config: baseConfigWire },
|
|
137
|
+
createWith: async ({ config }) => {
|
|
138
|
+
const flags = await loadFlags(config.apiUrl);
|
|
139
|
+
return createFeatureFlags(flags);
|
|
140
|
+
}
|
|
141
|
+
});
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
### Testing
|
|
145
|
+
|
|
146
|
+
TypeWire makes testing straightforward:
|
|
147
|
+
|
|
148
|
+
```typescript
|
|
149
|
+
describe('UserService', () => {
|
|
150
|
+
// Group related wires
|
|
151
|
+
const baseWires = typeWireGroupOf([
|
|
152
|
+
loggerWire,
|
|
153
|
+
userServiceWire
|
|
154
|
+
]);
|
|
155
|
+
|
|
156
|
+
it('logs user retrieval', async () => {
|
|
157
|
+
// Override just what you need
|
|
158
|
+
const testWires = baseWires.withExtraWires([
|
|
159
|
+
loggerWire.withCreator(() => {
|
|
160
|
+
const logger = new Logger();
|
|
161
|
+
vi.spyOn(logger, 'log');
|
|
162
|
+
return logger;
|
|
163
|
+
})
|
|
164
|
+
]);
|
|
165
|
+
|
|
166
|
+
const container = new TypeWireContainer();
|
|
167
|
+
await testWires.apply(container);
|
|
168
|
+
|
|
169
|
+
const userService = await userServiceWire.getInstance(container);
|
|
170
|
+
const logger = await loggerWire.getInstance(container);
|
|
171
|
+
|
|
172
|
+
await userService.getUser('123');
|
|
173
|
+
expect(logger.log).toHaveBeenCalledWith('Getting user: 123');
|
|
174
|
+
});
|
|
175
|
+
});
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
## Best Practices
|
|
179
|
+
|
|
180
|
+
1. **Clear Boundaries**:
|
|
181
|
+
- Use TypeWire for long-lived services
|
|
182
|
+
- Construct context where it's needed
|
|
183
|
+
- Keep persistent data in proper storage
|
|
184
|
+
- Make dependencies explicit
|
|
185
|
+
|
|
186
|
+
2. **Smart State Management**:
|
|
187
|
+
- Separate services from state
|
|
188
|
+
- Use proper storage for persistence
|
|
189
|
+
- Keep context close to usage
|
|
190
|
+
- Control state lifecycles
|
|
191
|
+
|
|
192
|
+
3. **Explicit Dependencies**:
|
|
193
|
+
- No ambient context
|
|
194
|
+
- Clear import declarations
|
|
195
|
+
- Visible dependency paths
|
|
196
|
+
- Easy to trace and test
|
|
197
|
+
|
|
198
|
+
4. **Composition Over Configuration**:
|
|
199
|
+
- Build complex systems from simple parts
|
|
200
|
+
- Override behavior where needed
|
|
201
|
+
- Keep testing simple
|
|
202
|
+
- Control side effects
|
|
203
|
+
|
|
204
|
+
## License
|
|
205
|
+
|
|
206
|
+
MIT
|