@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 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