@med1802/repository-manager 2.2.2 → 3.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/README.md +430 -381
- package/dist/index.d.ts +1 -11
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -24
- package/dist/manager.d.ts +14 -0
- package/dist/manager.d.ts.map +1 -0
- package/dist/manager.js +17 -0
- package/dist/workspace/index.d.ts +13 -0
- package/dist/workspace/index.d.ts.map +1 -0
- package/dist/workspace/index.js +30 -0
- package/dist/workspace/infrastructure/index.d.ts +4 -0
- package/dist/workspace/infrastructure/index.d.ts.map +1 -0
- package/dist/workspace/infrastructure/index.js +3 -0
- package/dist/{logger.d.ts → workspace/infrastructure/logger.d.ts} +1 -1
- package/dist/workspace/infrastructure/logger.d.ts.map +1 -0
- package/dist/workspace/infrastructure/scope/index.d.ts +3 -0
- package/dist/workspace/infrastructure/scope/index.d.ts.map +1 -0
- package/dist/workspace/infrastructure/scope/index.js +2 -0
- package/dist/workspace/infrastructure/scope/scope.d.ts +7 -0
- package/dist/workspace/infrastructure/scope/scope.d.ts.map +1 -0
- package/dist/workspace/infrastructure/scope/scope.js +32 -0
- package/dist/workspace/infrastructure/scope/types.d.ts +8 -0
- package/dist/workspace/infrastructure/scope/types.d.ts.map +1 -0
- package/dist/workspace/infrastructure/store.d.ts.map +1 -0
- package/dist/workspace/modules/index.d.ts +2 -0
- package/dist/workspace/modules/index.d.ts.map +1 -0
- package/dist/workspace/modules/index.js +1 -0
- package/dist/workspace/modules/repository/createRepositoryModule.d.ts +10 -0
- package/dist/workspace/modules/repository/createRepositoryModule.d.ts.map +1 -0
- package/dist/{workspace.js → workspace/modules/repository/createRepositoryModule.js} +22 -23
- package/dist/workspace/modules/repository/index.d.ts +3 -0
- package/dist/workspace/modules/repository/index.d.ts.map +1 -0
- package/dist/workspace/modules/repository/index.js +2 -0
- package/dist/workspace/modules/repository/middleware.d.ts.map +1 -0
- package/dist/workspace/modules/repository/repositoryAccessor.d.ts +9 -0
- package/dist/workspace/modules/repository/repositoryAccessor.d.ts.map +1 -0
- package/dist/workspace/modules/repository/repositoryAccessor.js +42 -0
- package/dist/workspace/modules/repository/types.d.ts +22 -0
- package/dist/workspace/modules/repository/types.d.ts.map +1 -0
- package/dist/workspace/modules/repository/types.js +1 -0
- package/dist/workspace/providers/index.d.ts +5 -0
- package/dist/workspace/providers/index.d.ts.map +1 -0
- package/dist/workspace/providers/index.js +3 -0
- package/dist/workspace/types.d.ts +5 -0
- package/dist/workspace/types.d.ts.map +1 -0
- package/dist/workspace/types.js +1 -0
- package/package.json +1 -1
- package/src/index.ts +1 -33
- package/src/manager.ts +23 -0
- package/src/workspace/index.ts +38 -0
- package/src/workspace/infrastructure/index.ts +3 -0
- package/src/{logger.ts → workspace/infrastructure/logger.ts} +1 -1
- package/src/workspace/infrastructure/scope/index.ts +2 -0
- package/src/workspace/infrastructure/scope/scope.ts +35 -0
- package/src/workspace/infrastructure/scope/types.ts +8 -0
- package/src/workspace/modules/index.ts +1 -0
- package/src/{workspace.ts → workspace/modules/repository/createRepositoryModule.ts} +22 -35
- package/src/workspace/modules/repository/index.ts +2 -0
- package/src/{middleware.ts → workspace/modules/repository/middleware.ts} +1 -5
- package/src/{repositoryAccessor.ts → workspace/modules/repository/repositoryAccessor.ts} +14 -12
- package/src/workspace/modules/repository/types.ts +29 -0
- package/src/workspace/providers/index.ts +5 -0
- package/src/workspace/types.ts +4 -0
- package/tsconfig.tsbuildinfo +1 -1
- package/dist/logger.d.ts.map +0 -1
- package/dist/middleware.d.ts.map +0 -1
- package/dist/repositoryAccessor.d.ts +0 -9
- package/dist/repositoryAccessor.d.ts.map +0 -1
- package/dist/repositoryAccessor.js +0 -40
- package/dist/store.d.ts.map +0 -1
- package/dist/types.d.ts +0 -28
- package/dist/types.d.ts.map +0 -1
- package/dist/workspace.d.ts +0 -10
- package/dist/workspace.d.ts.map +0 -1
- package/src/types.ts +0 -37
- /package/dist/{logger.js → workspace/infrastructure/logger.js} +0 -0
- /package/dist/{types.js → workspace/infrastructure/scope/types.js} +0 -0
- /package/dist/{store.d.ts → workspace/infrastructure/store.d.ts} +0 -0
- /package/dist/{store.js → workspace/infrastructure/store.js} +0 -0
- /package/dist/{middleware.d.ts → workspace/modules/repository/middleware.d.ts} +0 -0
- /package/dist/{middleware.js → workspace/modules/repository/middleware.js} +0 -0
- /package/src/{store.ts → workspace/infrastructure/store.ts} +0 -0
package/README.md
CHANGED
|
@@ -1,87 +1,166 @@
|
|
|
1
|
-
# 🔄 Repository Manager
|
|
1
|
+
# 🔄 Repository Manager v3
|
|
2
2
|
|
|
3
|
-
A lightweight, type-safe repository manager with dependency injection, lifecycle management, and multi-workspace support for TypeScript/JavaScript applications.
|
|
3
|
+
A lightweight, type-safe repository manager with dependency injection, scope management, lifecycle management, and multi-workspace support for TypeScript/JavaScript applications.
|
|
4
4
|
|
|
5
|
-
## Features
|
|
5
|
+
## ✨ Features
|
|
6
6
|
|
|
7
7
|
- ✅ **Dependency Injection** - Inject infrastructure dependencies into repositories
|
|
8
|
+
- ✅ **Scope Management** - Runtime scoped state management
|
|
8
9
|
- ✅ **Lifecycle Management** - Automatic connection/disconnection with reference counting
|
|
9
10
|
- ✅ **Multi-Workspace Support** - Manage multiple isolated workspaces with different dependencies
|
|
10
11
|
- ✅ **Type Safety** - Full TypeScript support with generics
|
|
11
12
|
- ✅ **Lazy Initialization** - Repository instances are created only when needed
|
|
12
|
-
- ✅ **
|
|
13
|
+
- ✅ **Workspace Pattern** - `createApp` pattern for clean API
|
|
13
14
|
- ✅ **Logging** - Built-in logging with colored console output
|
|
14
15
|
- ✅ **Memory Efficient** - Automatic cleanup when no connections remain
|
|
15
16
|
- ✅ **Middleware Support** - Intercept and modify repository method calls
|
|
16
17
|
|
|
17
|
-
## Installation
|
|
18
|
+
## 📦 Installation
|
|
18
19
|
|
|
19
20
|
```bash
|
|
20
21
|
npm install @med1802/repository-manager
|
|
21
22
|
```
|
|
22
23
|
|
|
23
|
-
## Quick Start
|
|
24
|
+
## 🚀 Quick Start
|
|
24
25
|
|
|
25
26
|
```typescript
|
|
26
27
|
import { repositoryManager } from "@med1802/repository-manager";
|
|
27
28
|
|
|
28
|
-
// Define your
|
|
29
|
-
interface
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
post(url: string, data: any): Promise<any>;
|
|
33
|
-
};
|
|
29
|
+
// Define your repository interface
|
|
30
|
+
interface IUserRepository {
|
|
31
|
+
getUsers(): Promise<User[]>;
|
|
32
|
+
createUser(user: User): Promise<User>;
|
|
34
33
|
}
|
|
35
34
|
|
|
36
35
|
// Create manager instance
|
|
37
36
|
const manager = repositoryManager();
|
|
38
37
|
|
|
39
38
|
// Define infrastructure dependencies
|
|
40
|
-
const infrastructure
|
|
39
|
+
const infrastructure = {
|
|
41
40
|
httpClient: {
|
|
42
|
-
get: async (url) => fetch(url).then((r) => r.json()),
|
|
43
|
-
post: async (url, data) =>
|
|
41
|
+
get: async (url: string) => fetch(url).then((r) => r.json()),
|
|
42
|
+
post: async (url: string, data: any) =>
|
|
44
43
|
fetch(url, { method: "POST", body: JSON.stringify(data) }),
|
|
45
44
|
},
|
|
46
45
|
};
|
|
47
46
|
|
|
48
|
-
// Create
|
|
49
|
-
const { defineRepository } = manager.workspace(
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
47
|
+
// Create workspace (entry point for all operations)
|
|
48
|
+
const { defineRepository, queryRepository, createScope } = manager.workspace(
|
|
49
|
+
infrastructure,
|
|
50
|
+
{
|
|
51
|
+
id: "app",
|
|
52
|
+
logging: true,
|
|
53
|
+
}
|
|
54
|
+
);
|
|
53
55
|
|
|
54
|
-
// Define
|
|
55
|
-
defineRepository(
|
|
56
|
-
|
|
57
|
-
|
|
56
|
+
// Define repository
|
|
57
|
+
defineRepository<IUserRepository>({
|
|
58
|
+
id: "user-repo",
|
|
59
|
+
install({ instance }) {
|
|
60
|
+
const { infrastructure } = instance;
|
|
61
|
+
return {
|
|
62
|
+
async getUsers() {
|
|
63
|
+
return infrastructure.httpClient.get("/api/users");
|
|
64
|
+
},
|
|
65
|
+
async createUser(user) {
|
|
66
|
+
return infrastructure.httpClient.post("/api/users", user);
|
|
67
|
+
},
|
|
68
|
+
};
|
|
58
69
|
},
|
|
59
|
-
|
|
60
|
-
|
|
70
|
+
onConnect: () => {
|
|
71
|
+
console.log("User repository initialized");
|
|
61
72
|
},
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
defineRepository("postRepo", (infrastructure) => ({
|
|
65
|
-
async getPosts() {
|
|
66
|
-
return infrastructure.httpClient.get("/api/posts");
|
|
73
|
+
onDisconnect: () => {
|
|
74
|
+
console.log("User repository cleaned up");
|
|
67
75
|
},
|
|
68
|
-
})
|
|
76
|
+
});
|
|
69
77
|
|
|
70
|
-
//
|
|
71
|
-
const { repository: userRepo, disconnect } =
|
|
78
|
+
// Query and use repository
|
|
79
|
+
const { repository: userRepo, disconnect } =
|
|
80
|
+
queryRepository<IUserRepository>("user-repo");
|
|
72
81
|
const users = await userRepo.getUsers();
|
|
73
82
|
|
|
74
83
|
// Cleanup when done
|
|
75
84
|
disconnect();
|
|
76
85
|
```
|
|
77
86
|
|
|
78
|
-
##
|
|
87
|
+
## 📖 Core Concepts
|
|
88
|
+
|
|
89
|
+
### 1. Manager → Workspace → Repository
|
|
90
|
+
|
|
91
|
+
Repository Manager follows a hierarchical pattern `createApp`:
|
|
92
|
+
|
|
93
|
+
```typescript
|
|
94
|
+
const manager = repositoryManager(); // Global manager
|
|
95
|
+
const workspace = manager.workspace(infrastructure, config); // Workspace instance
|
|
96
|
+
workspace.defineRepository({...}); // Define repositories
|
|
97
|
+
workspace.queryRepository("repo-id"); // Query repositories
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
### 2. Workspace Pattern
|
|
101
|
+
|
|
102
|
+
Workspace is the **entry point** for all operations. It encapsulates:
|
|
103
|
+
|
|
104
|
+
- Infrastructure dependencies
|
|
105
|
+
- Repository definitions
|
|
106
|
+
- Scope management
|
|
107
|
+
- Logging configuration
|
|
108
|
+
|
|
109
|
+
```typescript
|
|
110
|
+
const { defineRepository, queryRepository, createScope } = manager.workspace(
|
|
111
|
+
infrastructure,
|
|
112
|
+
{
|
|
113
|
+
id: "app-workspace",
|
|
114
|
+
logging: true,
|
|
115
|
+
}
|
|
116
|
+
);
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
### 3. Scope Management
|
|
120
|
+
|
|
121
|
+
Scopes provide **runtime scoped state management** :
|
|
122
|
+
|
|
123
|
+
```typescript
|
|
124
|
+
// Create scope
|
|
125
|
+
const userScope = createScope({
|
|
126
|
+
userId: null,
|
|
127
|
+
permissions: [],
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
// Use in repository
|
|
131
|
+
defineRepository({
|
|
132
|
+
id: "auth-repo",
|
|
133
|
+
install({ instance }) {
|
|
134
|
+
const { useScope } = instance;
|
|
135
|
+
return {
|
|
136
|
+
checkPermission(action: string) {
|
|
137
|
+
const user = useScope(userScope);
|
|
138
|
+
return user.permissions.includes(action);
|
|
139
|
+
},
|
|
140
|
+
};
|
|
141
|
+
},
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
// Provide scope value
|
|
145
|
+
userScope.provider({
|
|
146
|
+
value: {
|
|
147
|
+
userId: "123",
|
|
148
|
+
permissions: ["read", "write"],
|
|
149
|
+
},
|
|
150
|
+
children() {
|
|
151
|
+
const { repository } = queryRepository("auth-repo");
|
|
152
|
+
repository.checkPermission("write"); // Has access to scoped value
|
|
153
|
+
},
|
|
154
|
+
});
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
## 📚 API Reference
|
|
79
158
|
|
|
80
159
|
### `repositoryManager()`
|
|
81
160
|
|
|
82
161
|
Creates a repository manager instance.
|
|
83
162
|
|
|
84
|
-
**Returns:** Manager object with `workspace`
|
|
163
|
+
**Returns:** Manager object with `workspace` method
|
|
85
164
|
|
|
86
165
|
**Example:**
|
|
87
166
|
|
|
@@ -91,398 +170,402 @@ const manager = repositoryManager();
|
|
|
91
170
|
|
|
92
171
|
### `manager.workspace<I>(infrastructure, config)`
|
|
93
172
|
|
|
94
|
-
Creates a workspace with infrastructure dependencies
|
|
173
|
+
Creates a workspace with infrastructure dependencies.
|
|
95
174
|
|
|
96
175
|
**Parameters:**
|
|
97
176
|
|
|
98
177
|
- `infrastructure: I` - Infrastructure dependencies to inject into repositories
|
|
99
|
-
- `config: IConfiguration`
|
|
178
|
+
- `config: IConfiguration`
|
|
100
179
|
- `id: string` - Unique identifier for the workspace
|
|
101
180
|
- `logging?: boolean` - Enable/disable logging (default: `false`)
|
|
102
181
|
|
|
103
|
-
**Returns:** Object with
|
|
182
|
+
**Returns:** Object with workspace methods:
|
|
183
|
+
|
|
184
|
+
- `defineRepository` - Define repositories
|
|
185
|
+
- `queryRepository` - Query repositories
|
|
186
|
+
- `createScope` - Create scoped state
|
|
104
187
|
|
|
105
188
|
**Example:**
|
|
106
189
|
|
|
107
190
|
```typescript
|
|
108
|
-
const { defineRepository } = manager.workspace(
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
191
|
+
const { defineRepository, queryRepository, createScope } = manager.workspace(
|
|
192
|
+
infrastructure,
|
|
193
|
+
{
|
|
194
|
+
id: "app",
|
|
195
|
+
logging: true,
|
|
196
|
+
}
|
|
197
|
+
);
|
|
112
198
|
```
|
|
113
199
|
|
|
114
|
-
### `defineRepository(
|
|
200
|
+
### `defineRepository<R>(config)`
|
|
115
201
|
|
|
116
202
|
Defines a repository within the workspace.
|
|
117
203
|
|
|
118
204
|
**Parameters:**
|
|
119
205
|
|
|
120
|
-
- `
|
|
121
|
-
- `
|
|
122
|
-
- `
|
|
123
|
-
|
|
124
|
-
- `
|
|
125
|
-
|
|
126
|
-
- `
|
|
206
|
+
- `config: IRepositoryPlugin<I, R>`
|
|
207
|
+
- `id: string` - Unique identifier for the repository
|
|
208
|
+
- `install: ({ instance }) => R` - Factory function that returns repository instance
|
|
209
|
+
- `instance.infrastructure` - Injected infrastructure
|
|
210
|
+
- `instance.useScope` - Function to access scoped state
|
|
211
|
+
- `onConnect?: () => void` - Called when repository is first connected
|
|
212
|
+
- `onDisconnect?: () => void` - Called when repository is last disconnected
|
|
213
|
+
- `middlewares?: Middleware[]` - Array of middleware functions
|
|
127
214
|
|
|
128
215
|
**Example:**
|
|
129
216
|
|
|
130
217
|
```typescript
|
|
131
|
-
defineRepository(
|
|
132
|
-
|
|
133
|
-
})
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
(infrastructure) => ({
|
|
139
|
-
getUsers: () => infrastructure.httpClient.get("/users"),
|
|
140
|
-
}),
|
|
141
|
-
{
|
|
142
|
-
lifecycle: {
|
|
143
|
-
onConnect: () => {
|
|
144
|
-
console.log("User repository initialized");
|
|
145
|
-
},
|
|
146
|
-
onDisconnect: () => {
|
|
147
|
-
console.log("User repository cleaned up");
|
|
218
|
+
defineRepository<IUserRepository>({
|
|
219
|
+
id: "user-repo",
|
|
220
|
+
install({ instance }) {
|
|
221
|
+
const { infrastructure, useScope } = instance;
|
|
222
|
+
return {
|
|
223
|
+
getUsers() {
|
|
224
|
+
return infrastructure.httpClient.get("/users");
|
|
148
225
|
},
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
)
|
|
226
|
+
};
|
|
227
|
+
},
|
|
228
|
+
onConnect: () => console.log("Connected"),
|
|
229
|
+
onDisconnect: () => console.log("Disconnected"),
|
|
230
|
+
});
|
|
152
231
|
```
|
|
153
232
|
|
|
154
|
-
### `
|
|
233
|
+
### `queryRepository<R>(id)`
|
|
155
234
|
|
|
156
|
-
Queries a repository from
|
|
235
|
+
Queries a repository from the workspace.
|
|
157
236
|
|
|
158
237
|
**Parameters:**
|
|
159
238
|
|
|
160
|
-
- `
|
|
239
|
+
- `id: string` - Repository identifier
|
|
161
240
|
|
|
162
241
|
**Returns:** Object with:
|
|
163
242
|
|
|
164
243
|
- `repository: R` - The repository instance
|
|
165
244
|
- `disconnect: () => void` - Function to disconnect and cleanup
|
|
166
245
|
|
|
167
|
-
**Throws:** `Error` if
|
|
246
|
+
**Throws:** `Error` if repository is not found
|
|
168
247
|
|
|
169
248
|
**Example:**
|
|
170
249
|
|
|
171
250
|
```typescript
|
|
172
|
-
const { repository, disconnect } =
|
|
251
|
+
const { repository, disconnect } =
|
|
252
|
+
queryRepository<IUserRepository>("user-repo");
|
|
173
253
|
await repository.getUsers();
|
|
174
254
|
disconnect();
|
|
175
255
|
```
|
|
176
256
|
|
|
177
|
-
|
|
257
|
+
### `createScope<V>(defaultValue)`
|
|
178
258
|
|
|
179
|
-
|
|
259
|
+
Creates a scope for runtime state management.
|
|
180
260
|
|
|
181
|
-
|
|
261
|
+
**Parameters:**
|
|
182
262
|
|
|
183
|
-
|
|
184
|
-
const manager = repositoryManager();
|
|
263
|
+
- `defaultValue: V` - Default value for the scope
|
|
185
264
|
|
|
186
|
-
|
|
187
|
-
const { defineRepository: defineApiRepo } = manager.workspace(
|
|
188
|
-
{
|
|
189
|
-
httpClient: apiHttpClient,
|
|
190
|
-
cache: redisCache,
|
|
191
|
-
},
|
|
192
|
-
{
|
|
193
|
-
id: "api",
|
|
194
|
-
logging: true,
|
|
195
|
-
}
|
|
196
|
-
);
|
|
265
|
+
**Returns:** Scope object with:
|
|
197
266
|
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
}));
|
|
267
|
+
- `provider: (options) => void` - Provide scoped value
|
|
268
|
+
- `currentValue: V` - Current scoped value (getter)
|
|
201
269
|
|
|
202
|
-
|
|
203
|
-
const { defineRepository: defineDbRepo } = manager.workspace(
|
|
204
|
-
{
|
|
205
|
-
db: postgresClient,
|
|
206
|
-
logger: winstonLogger,
|
|
207
|
-
},
|
|
208
|
-
{
|
|
209
|
-
id: "database",
|
|
210
|
-
logging: false,
|
|
211
|
-
}
|
|
212
|
-
);
|
|
270
|
+
**Example:**
|
|
213
271
|
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
272
|
+
```typescript
|
|
273
|
+
const userScope = createScope({
|
|
274
|
+
userId: null,
|
|
275
|
+
role: "guest",
|
|
276
|
+
});
|
|
217
277
|
|
|
218
|
-
//
|
|
219
|
-
|
|
220
|
-
|
|
278
|
+
// Provide value
|
|
279
|
+
userScope.provider({
|
|
280
|
+
value: { userId: "123", role: "admin" },
|
|
281
|
+
children() {
|
|
282
|
+
// Code that runs with scoped value
|
|
283
|
+
},
|
|
284
|
+
});
|
|
221
285
|
```
|
|
222
286
|
|
|
223
|
-
###
|
|
287
|
+
### `useScope<V>(scope)`
|
|
224
288
|
|
|
225
|
-
|
|
289
|
+
Access scoped state within repository methods.
|
|
226
290
|
|
|
227
|
-
|
|
228
|
-
interface IUserRepo {
|
|
229
|
-
getUsers(): Promise<User[]>;
|
|
230
|
-
createUser(user: User): Promise<User>;
|
|
231
|
-
updateUser(id: string, user: Partial<User>): Promise<User>;
|
|
232
|
-
deleteUser(id: string): Promise<void>;
|
|
233
|
-
}
|
|
291
|
+
**Parameters:**
|
|
234
292
|
|
|
235
|
-
|
|
236
|
-
httpClient: IHttpClient;
|
|
237
|
-
cache: ICache;
|
|
238
|
-
}
|
|
293
|
+
- `scope: IScope<V>` - Scope to access
|
|
239
294
|
|
|
240
|
-
|
|
295
|
+
**Returns:** Current scoped value
|
|
241
296
|
|
|
242
|
-
|
|
243
|
-
{
|
|
244
|
-
httpClient: myHttpClient,
|
|
245
|
-
cache: myCache,
|
|
246
|
-
},
|
|
247
|
-
{
|
|
248
|
-
id: "app",
|
|
249
|
-
}
|
|
250
|
-
);
|
|
251
|
-
|
|
252
|
-
defineRepository<IUserRepo>(
|
|
253
|
-
"userRepo",
|
|
254
|
-
(infrastructure): IUserRepo => ({
|
|
255
|
-
async getUsers() {
|
|
256
|
-
/* ... */
|
|
257
|
-
},
|
|
258
|
-
async createUser(user) {
|
|
259
|
-
/* ... */
|
|
260
|
-
},
|
|
261
|
-
async updateUser(id, user) {
|
|
262
|
-
/* ... */
|
|
263
|
-
},
|
|
264
|
-
async deleteUser(id) {
|
|
265
|
-
/* ... */
|
|
266
|
-
},
|
|
267
|
-
})
|
|
268
|
-
);
|
|
297
|
+
**Example:**
|
|
269
298
|
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
299
|
+
```typescript
|
|
300
|
+
defineRepository({
|
|
301
|
+
id: "auth-repo",
|
|
302
|
+
install({ instance }) {
|
|
303
|
+
const { useScope } = instance;
|
|
304
|
+
return {
|
|
305
|
+
getCurrentUser() {
|
|
306
|
+
return useScope(userScope);
|
|
307
|
+
},
|
|
308
|
+
};
|
|
309
|
+
},
|
|
310
|
+
});
|
|
273
311
|
```
|
|
274
312
|
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
Repositories use reference counting for automatic lifecycle management:
|
|
313
|
+
## 🎯 Advanced Usage
|
|
278
314
|
|
|
279
|
-
|
|
280
|
-
// First query creates the repository instance
|
|
281
|
-
const conn1 = manager.query("app/userRepo"); // Connections: 1
|
|
282
|
-
const conn2 = manager.query("app/userRepo"); // Connections: 2 (reuses instance)
|
|
283
|
-
const conn3 = manager.query("app/userRepo"); // Connections: 3 (reuses instance)
|
|
315
|
+
### Multiple Scopes
|
|
284
316
|
|
|
285
|
-
|
|
286
|
-
conn2.disconnect(); // Connections: 1 (still active)
|
|
287
|
-
conn3.disconnect(); // Connections: 0 (instance destroyed)
|
|
317
|
+
You can create and use multiple scopes:
|
|
288
318
|
|
|
289
|
-
|
|
290
|
-
const
|
|
319
|
+
```typescript
|
|
320
|
+
const userScope = createScope({ userId: null });
|
|
321
|
+
const companyScope = createScope({ companyId: null });
|
|
322
|
+
const featureFlagsScope = createScope({ features: [] });
|
|
323
|
+
|
|
324
|
+
defineRepository({
|
|
325
|
+
id: "billing-repo",
|
|
326
|
+
install({ instance }) {
|
|
327
|
+
const { useScope } = instance;
|
|
328
|
+
return {
|
|
329
|
+
getBillingInfo() {
|
|
330
|
+
const user = useScope(userScope);
|
|
331
|
+
const company = useScope(companyScope);
|
|
332
|
+
const flags = useScope(featureFlagsScope);
|
|
333
|
+
|
|
334
|
+
// Use all scopes
|
|
335
|
+
return { user, company, flags };
|
|
336
|
+
},
|
|
337
|
+
};
|
|
338
|
+
},
|
|
339
|
+
});
|
|
291
340
|
```
|
|
292
341
|
|
|
293
|
-
###
|
|
342
|
+
### Nested Scopes
|
|
294
343
|
|
|
295
|
-
|
|
344
|
+
Scopes can be nested and override outer values:
|
|
345
|
+
|
|
346
|
+
```typescript
|
|
347
|
+
userScope.provider({
|
|
348
|
+
value: { userId: "outer" },
|
|
349
|
+
children() {
|
|
350
|
+
// Inner scope overrides outer
|
|
351
|
+
userScope.provider({
|
|
352
|
+
value: { userId: "inner" },
|
|
353
|
+
children() {
|
|
354
|
+
const { repository } = queryRepository("user-repo");
|
|
355
|
+
// useScope(userScope) returns "inner"
|
|
356
|
+
},
|
|
357
|
+
});
|
|
358
|
+
},
|
|
359
|
+
});
|
|
360
|
+
```
|
|
296
361
|
|
|
297
|
-
|
|
298
|
-
- **`onDisconnect`** - Called only when the repository is last disconnected (when connections go from 1 to 0)
|
|
362
|
+
### Multiple Workspaces
|
|
299
363
|
|
|
300
|
-
|
|
364
|
+
Create multiple isolated workspaces with different dependencies:
|
|
301
365
|
|
|
302
366
|
```typescript
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
(infrastructure) => ({
|
|
306
|
-
getUsers: () => infrastructure.httpClient.get("/api/users"),
|
|
307
|
-
}),
|
|
367
|
+
// API workspace
|
|
368
|
+
const apiWorkspace = manager.workspace(
|
|
308
369
|
{
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
},
|
|
314
|
-
onDisconnect: () => {
|
|
315
|
-
console.log("User repository cleaned up");
|
|
316
|
-
// Perform cleanup tasks (e.g., clear cache, close connections)
|
|
317
|
-
},
|
|
318
|
-
},
|
|
319
|
-
}
|
|
370
|
+
httpClient: apiClient,
|
|
371
|
+
cache: redisCache,
|
|
372
|
+
},
|
|
373
|
+
{ id: "api" }
|
|
320
374
|
);
|
|
321
375
|
|
|
322
|
-
//
|
|
323
|
-
const
|
|
324
|
-
|
|
376
|
+
// Database workspace
|
|
377
|
+
const dbWorkspace = manager.workspace(
|
|
378
|
+
{
|
|
379
|
+
db: postgresClient,
|
|
380
|
+
logger: winstonLogger,
|
|
381
|
+
},
|
|
382
|
+
{ id: "database" }
|
|
383
|
+
);
|
|
325
384
|
|
|
326
|
-
|
|
327
|
-
|
|
385
|
+
// Each workspace has isolated repositories
|
|
386
|
+
apiWorkspace.defineRepository({...});
|
|
387
|
+
dbWorkspace.defineRepository({...});
|
|
328
388
|
```
|
|
329
389
|
|
|
330
|
-
|
|
390
|
+
### TypeScript Best Practices
|
|
331
391
|
|
|
332
|
-
|
|
333
|
-
- **Cleanup**: Clear cache, close connections, release resources
|
|
334
|
-
- **Analytics**: Track repository usage and lifecycle events
|
|
335
|
-
- **Debugging**: Monitor when repositories are created and destroyed
|
|
392
|
+
Define clear interfaces for type safety:
|
|
336
393
|
|
|
337
|
-
|
|
394
|
+
```typescript
|
|
395
|
+
// Infrastructure interface
|
|
396
|
+
interface IInfrastructure {
|
|
397
|
+
httpClient: IHttpClient;
|
|
398
|
+
cache: ICache;
|
|
399
|
+
logger: ILogger;
|
|
400
|
+
}
|
|
338
401
|
|
|
339
|
-
|
|
402
|
+
// Repository interface
|
|
403
|
+
interface IUserRepository {
|
|
404
|
+
getUsers(): Promise<User[]>;
|
|
405
|
+
createUser(user: User): Promise<User>;
|
|
406
|
+
}
|
|
340
407
|
|
|
341
|
-
|
|
408
|
+
// Workspace with typed infrastructure
|
|
409
|
+
const { defineRepository, queryRepository } =
|
|
410
|
+
manager.workspace<IInfrastructure>(infrastructure, { id: "app" });
|
|
411
|
+
|
|
412
|
+
// Repository with typed interface
|
|
413
|
+
defineRepository<IUserRepository>({
|
|
414
|
+
id: "user-repo",
|
|
415
|
+
install({ instance }) {
|
|
416
|
+
const { infrastructure } = instance;
|
|
417
|
+
return {
|
|
418
|
+
async getUsers() {
|
|
419
|
+
// TypeScript knows infrastructure type
|
|
420
|
+
return infrastructure.httpClient.get("/users");
|
|
421
|
+
},
|
|
422
|
+
async createUser(user) {
|
|
423
|
+
// TypeScript validates User type
|
|
424
|
+
return infrastructure.httpClient.post("/users", user);
|
|
425
|
+
},
|
|
426
|
+
};
|
|
427
|
+
},
|
|
428
|
+
});
|
|
342
429
|
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
args: any[],
|
|
347
|
-
next: (...nextArgs: any[]) => any
|
|
348
|
-
) => any;
|
|
430
|
+
// Query with typed repository
|
|
431
|
+
const { repository } = queryRepository<IUserRepository>("user-repo");
|
|
432
|
+
// TypeScript knows all repository methods
|
|
349
433
|
```
|
|
350
434
|
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
- `method: string` - The name of the method being called
|
|
354
|
-
- `args: any[]` - The arguments passed to the method
|
|
355
|
-
- `next: (...nextArgs: any[]) => any` - Function to call the next middleware or the original method
|
|
435
|
+
### Lifecycle Management
|
|
356
436
|
|
|
357
|
-
|
|
437
|
+
Repositories use reference counting for automatic lifecycle:
|
|
358
438
|
|
|
359
439
|
```typescript
|
|
360
|
-
//
|
|
361
|
-
const
|
|
362
|
-
console.log(`[${new Date().toISOString()}] Calling ${method}`, args);
|
|
363
|
-
const result = next();
|
|
364
|
-
console.log(`[${new Date().toISOString()}] ${method} returned:`, result);
|
|
365
|
-
return result;
|
|
366
|
-
};
|
|
440
|
+
// First query creates instance (onConnect called)
|
|
441
|
+
const conn1 = queryRepository("user-repo"); // Connections: 1
|
|
367
442
|
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
const duration = performance.now() - start;
|
|
443
|
+
// Subsequent queries reuse instance (onConnect NOT called)
|
|
444
|
+
const conn2 = queryRepository("user-repo"); // Connections: 2
|
|
445
|
+
const conn3 = queryRepository("user-repo"); // Connections: 3
|
|
372
446
|
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
return result;
|
|
378
|
-
};
|
|
447
|
+
// Disconnect reduces count
|
|
448
|
+
conn1.disconnect(); // Connections: 2
|
|
449
|
+
conn2.disconnect(); // Connections: 1
|
|
379
450
|
|
|
380
|
-
//
|
|
381
|
-
|
|
382
|
-
"userRepo",
|
|
383
|
-
(infrastructure) => ({
|
|
384
|
-
getUsers: () => infrastructure.httpClient.get("/api/users"),
|
|
385
|
-
createUser: (user) => infrastructure.httpClient.post("/api/users", user),
|
|
386
|
-
}),
|
|
387
|
-
{
|
|
388
|
-
middlewares: [loggingMiddleware, performanceMiddleware],
|
|
389
|
-
}
|
|
390
|
-
);
|
|
391
|
-
|
|
392
|
-
// When you call repository methods, middleware intercepts them
|
|
393
|
-
const { repository } = manager.query("app/userRepo");
|
|
394
|
-
await repository.getUsers(); // Middleware logs and measures performance
|
|
451
|
+
// Last disconnect destroys instance (onDisconnect called)
|
|
452
|
+
conn3.disconnect(); // Connections: 0, instance destroyed
|
|
395
453
|
```
|
|
396
454
|
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
```typescript
|
|
400
|
-
// When you call: repository.getUsers("filter")
|
|
401
|
-
// 1. loggingMiddleware - logs "Calling getUsers"
|
|
402
|
-
// 2. performanceMiddleware - starts timer
|
|
403
|
-
// 3. repository.getUsers("filter") - original method executes
|
|
404
|
-
// 4. performanceMiddleware - ends timer, logs if slow
|
|
405
|
-
// 5. loggingMiddleware - logs result
|
|
406
|
-
// 6. Returns result to caller
|
|
407
|
-
```
|
|
408
|
-
|
|
409
|
-
**Common Use Cases:**
|
|
410
|
-
|
|
411
|
-
- **Logging**: Log all method calls and their results
|
|
412
|
-
- **Performance Monitoring**: Measure execution time and warn about slow calls
|
|
413
|
-
- **Caching**: Cache method results to avoid redundant calls
|
|
414
|
-
- **Validation**: Validate arguments before method execution
|
|
415
|
-
- **Error Handling**: Centralized error handling and retry logic
|
|
416
|
-
- **Data Transformation**: Transform arguments or results
|
|
417
|
-
- **Authentication**: Check permissions before method execution
|
|
418
|
-
- **Rate Limiting**: Limit the number of calls per method
|
|
455
|
+
### Middleware
|
|
419
456
|
|
|
420
|
-
|
|
457
|
+
Intercept and modify repository method calls:
|
|
421
458
|
|
|
422
459
|
```typescript
|
|
423
|
-
const
|
|
460
|
+
const loggingMiddleware: Middleware = (method, args, next) => {
|
|
461
|
+
console.log(`Calling ${method}`, args);
|
|
462
|
+
const result = next();
|
|
463
|
+
console.log(`${method} returned:`, result);
|
|
464
|
+
return result;
|
|
465
|
+
};
|
|
424
466
|
|
|
425
467
|
const cacheMiddleware: Middleware = (method, args, next) => {
|
|
426
468
|
const cacheKey = `${method}-${JSON.stringify(args)}`;
|
|
469
|
+
if (cache.has(cacheKey)) return cache.get(cacheKey);
|
|
427
470
|
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
}
|
|
431
|
-
|
|
432
|
-
const result = next(); // Execute method
|
|
433
|
-
cache.set(cacheKey, result); // Cache result
|
|
471
|
+
const result = next();
|
|
472
|
+
cache.set(cacheKey, result);
|
|
434
473
|
return result;
|
|
435
474
|
};
|
|
436
475
|
|
|
437
|
-
defineRepository(
|
|
438
|
-
"
|
|
439
|
-
(
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
);
|
|
476
|
+
defineRepository({
|
|
477
|
+
id: "user-repo",
|
|
478
|
+
install({ instance }) {
|
|
479
|
+
return {
|
|
480
|
+
getUsers: () => instance.infrastructure.httpClient.get("/users"),
|
|
481
|
+
};
|
|
482
|
+
},
|
|
483
|
+
middlewares: [loggingMiddleware, cacheMiddleware],
|
|
484
|
+
});
|
|
446
485
|
```
|
|
447
486
|
|
|
448
|
-
**
|
|
487
|
+
**Common Middleware Use Cases:**
|
|
449
488
|
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
};
|
|
489
|
+
- **Logging** - Log all method calls and results
|
|
490
|
+
- **Caching** - Cache method results
|
|
491
|
+
- **Validation** - Validate arguments before execution
|
|
492
|
+
- **Performance Monitoring** - Measure execution time
|
|
493
|
+
- **Error Handling** - Centralized error handling and retry logic
|
|
494
|
+
- **Authentication** - Check permissions before execution
|
|
457
495
|
|
|
458
|
-
|
|
459
|
-
"userRepo",
|
|
460
|
-
(infrastructure) => ({
|
|
461
|
-
createUser: (user) => infrastructure.httpClient.post("/api/users", user),
|
|
462
|
-
}),
|
|
463
|
-
{
|
|
464
|
-
middlewares: [validationMiddleware],
|
|
465
|
-
}
|
|
466
|
-
);
|
|
467
|
-
```
|
|
468
|
-
|
|
469
|
-
### Using with React
|
|
496
|
+
### Real-World Example: User Management
|
|
470
497
|
|
|
471
498
|
```typescript
|
|
472
|
-
|
|
499
|
+
// Define scopes
|
|
500
|
+
const authScope = createScope({ token: null, user: null });
|
|
501
|
+
const featureFlagsScope = createScope({ features: [] });
|
|
473
502
|
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
503
|
+
// Infrastructure
|
|
504
|
+
const infrastructure = {
|
|
505
|
+
httpClient: {
|
|
506
|
+
get: async (url) =>
|
|
507
|
+
fetch(url, {
|
|
508
|
+
headers: { Authorization: `Bearer ${useScope(authScope).token}` },
|
|
509
|
+
}).then((r) => r.json()),
|
|
510
|
+
post: async (url, data) =>
|
|
511
|
+
fetch(url, {
|
|
512
|
+
method: "POST",
|
|
513
|
+
headers: { Authorization: `Bearer ${useScope(authScope).token}` },
|
|
514
|
+
body: JSON.stringify(data),
|
|
515
|
+
}).then((r) => r.json()),
|
|
516
|
+
},
|
|
517
|
+
cache: new Map(),
|
|
518
|
+
};
|
|
477
519
|
|
|
478
|
-
|
|
520
|
+
// Create workspace
|
|
521
|
+
const { defineRepository, queryRepository } = manager.workspace(
|
|
522
|
+
infrastructure,
|
|
523
|
+
{ id: "app", logging: true }
|
|
524
|
+
);
|
|
479
525
|
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
526
|
+
// Define repositories
|
|
527
|
+
defineRepository<IUserRepository>({
|
|
528
|
+
id: "user-repo",
|
|
529
|
+
install({ instance }) {
|
|
530
|
+
const { infrastructure, useScope } = instance;
|
|
531
|
+
return {
|
|
532
|
+
async getUsers() {
|
|
533
|
+
const auth = useScope(authScope);
|
|
534
|
+
if (!auth.token) throw new Error("Not authenticated");
|
|
535
|
+
return infrastructure.httpClient.get("/api/users");
|
|
536
|
+
},
|
|
537
|
+
async createUser(user) {
|
|
538
|
+
const flags = useScope(featureFlagsScope);
|
|
539
|
+
if (!flags.features.includes("create-user")) {
|
|
540
|
+
throw new Error("Feature not enabled");
|
|
541
|
+
}
|
|
542
|
+
return infrastructure.httpClient.post("/api/users", user);
|
|
543
|
+
},
|
|
544
|
+
};
|
|
545
|
+
},
|
|
546
|
+
onConnect: () => console.log("User repo connected"),
|
|
547
|
+
onDisconnect: () => console.log("User repo disconnected"),
|
|
548
|
+
});
|
|
483
549
|
|
|
484
|
-
|
|
485
|
-
|
|
550
|
+
// Use with authentication
|
|
551
|
+
authScope.provider({
|
|
552
|
+
value: { token: "abc123", user: { id: "1", name: "John" } },
|
|
553
|
+
children() {
|
|
554
|
+
featureFlagsScope.provider({
|
|
555
|
+
value: { features: ["create-user", "delete-user"] },
|
|
556
|
+
async children() {
|
|
557
|
+
const { repository, disconnect } =
|
|
558
|
+
queryRepository<IUserRepository>("user-repo");
|
|
559
|
+
|
|
560
|
+
// Has access to auth and feature flags
|
|
561
|
+
const users = await repository.getUsers();
|
|
562
|
+
await repository.createUser({ name: "Jane" });
|
|
563
|
+
|
|
564
|
+
disconnect();
|
|
565
|
+
},
|
|
566
|
+
});
|
|
567
|
+
},
|
|
568
|
+
});
|
|
486
569
|
```
|
|
487
570
|
|
|
488
571
|
### Logging
|
|
@@ -490,73 +573,28 @@ function UserList() {
|
|
|
490
573
|
Enable logging to see connection lifecycle:
|
|
491
574
|
|
|
492
575
|
```typescript
|
|
493
|
-
const manager = repositoryManager();
|
|
494
|
-
|
|
495
576
|
const { defineRepository } = manager.workspace(infrastructure, {
|
|
496
577
|
id: "app",
|
|
497
578
|
logging: true, // Enables colored console output
|
|
498
579
|
});
|
|
499
580
|
|
|
500
581
|
// Console output will show:
|
|
501
|
-
// repository.
|
|
502
|
-
//
|
|
503
|
-
// │ (index) │
|
|
504
|
-
//
|
|
505
|
-
// │ 0 │
|
|
506
|
-
//
|
|
582
|
+
// repository.define (user-repo)
|
|
583
|
+
// ┌─────────┬───────────┐
|
|
584
|
+
// │ (index) │repository │
|
|
585
|
+
// ├─────────┼───────────┤
|
|
586
|
+
// │ 0 │ user-repo │
|
|
587
|
+
// └─────────┴───────────┘
|
|
588
|
+
//
|
|
589
|
+
// repository.connect (user-repo)
|
|
590
|
+
// ┌─────────┬───────────┬─────────────┐
|
|
591
|
+
// │ (index) │repository │ connections │
|
|
592
|
+
// ├─────────┼───────────┼─────────────┤
|
|
593
|
+
// │ 0 │ user-repo │ 1 │
|
|
594
|
+
// └─────────┴───────────┴─────────────┘
|
|
507
595
|
```
|
|
508
596
|
|
|
509
|
-
##
|
|
510
|
-
|
|
511
|
-
This package follows the **Dependency Inversion Principle (DIP)**:
|
|
512
|
-
|
|
513
|
-
- **High-level modules** (repositories) depend on abstractions (dependencies interface)
|
|
514
|
-
- **Low-level modules** (infrastructure implementations) are injected
|
|
515
|
-
- Easy to test with mock dependencies
|
|
516
|
-
- Easy to swap implementations
|
|
517
|
-
|
|
518
|
-
```typescript
|
|
519
|
-
// Define abstractions
|
|
520
|
-
interface IDatabase {
|
|
521
|
-
query(sql: string): Promise<any>;
|
|
522
|
-
}
|
|
523
|
-
|
|
524
|
-
interface ICache {
|
|
525
|
-
get(key: string): Promise<any>;
|
|
526
|
-
set(key: string, value: any): Promise<void>;
|
|
527
|
-
}
|
|
528
|
-
|
|
529
|
-
interface IDependencies {
|
|
530
|
-
database: IDatabase;
|
|
531
|
-
cache: ICache;
|
|
532
|
-
}
|
|
533
|
-
|
|
534
|
-
// Inject concrete implementations
|
|
535
|
-
const manager = repositoryManager();
|
|
536
|
-
|
|
537
|
-
const { defineRepository } = manager.workspace<IDependencies>(
|
|
538
|
-
{
|
|
539
|
-
database: new PostgreSQLClient(),
|
|
540
|
-
cache: new RedisCache(),
|
|
541
|
-
},
|
|
542
|
-
{
|
|
543
|
-
id: "prod",
|
|
544
|
-
}
|
|
545
|
-
);
|
|
546
|
-
|
|
547
|
-
// Easy to test with mocks
|
|
548
|
-
const { defineRepository: defineTestRepo } = manager.workspace<IDependencies>(
|
|
549
|
-
{
|
|
550
|
-
database: new MockDatabase(),
|
|
551
|
-
cache: new MockCache(),
|
|
552
|
-
},
|
|
553
|
-
{
|
|
554
|
-
id: "test",
|
|
555
|
-
}
|
|
556
|
-
);
|
|
557
|
-
```
|
|
558
|
-
|
|
559
|
-
## Design Patterns
|
|
597
|
+
## 🏗️ Design Patterns
|
|
560
598
|
|
|
561
599
|
This library implements several design patterns:
|
|
562
600
|
|
|
@@ -564,20 +602,31 @@ This library implements several design patterns:
|
|
|
564
602
|
- **Factory Pattern** - Repositories are created using factory functions
|
|
565
603
|
- **Singleton Pattern** - Each repository is a singleton per workspace (with reference counting)
|
|
566
604
|
- **Repository Pattern** - Abstracts data access logic
|
|
567
|
-
- **Workspace Pattern** -
|
|
605
|
+
- **Workspace Pattern** - Vue-like pattern for managing dependencies and lifecycle
|
|
606
|
+
- **Provider Pattern** - Scope management similar to React Context
|
|
568
607
|
|
|
569
|
-
## Error Handling
|
|
608
|
+
## ⚠️ Error Handling
|
|
570
609
|
|
|
571
610
|
```typescript
|
|
572
611
|
try {
|
|
573
|
-
const { repository } =
|
|
612
|
+
const { repository } = queryRepository("non-existent-repo");
|
|
574
613
|
} catch (error) {
|
|
575
|
-
console.error(error.message); //
|
|
614
|
+
console.error(error.message); // Repository "non-existent-repo" not found
|
|
576
615
|
}
|
|
577
616
|
|
|
617
|
+
// With scope
|
|
578
618
|
try {
|
|
579
|
-
const
|
|
619
|
+
const user = useScope(userScope);
|
|
620
|
+
if (!user) throw new Error("No user in scope");
|
|
580
621
|
} catch (error) {
|
|
581
|
-
console.error(error
|
|
622
|
+
console.error("Scope error:", error);
|
|
582
623
|
}
|
|
583
624
|
```
|
|
625
|
+
|
|
626
|
+
## 📝 License
|
|
627
|
+
|
|
628
|
+
MIT
|
|
629
|
+
|
|
630
|
+
## 🤝 Contributing
|
|
631
|
+
|
|
632
|
+
Contributions are welcome! Please feel free to submit a Pull Request.
|