@adaas/a-utils 0.1.6 → 0.1.8

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.
Files changed (31) hide show
  1. package/README.md +393 -6
  2. package/dist/src/lib/A-Channel/A-Channel.component.d.ts +19 -0
  3. package/dist/src/lib/A-Channel/A-Channel.component.js +76 -0
  4. package/dist/src/lib/A-Channel/A-Channel.component.js.map +1 -1
  5. package/dist/src/lib/A-Channel/A-Channel.error.d.ts +1 -0
  6. package/dist/src/lib/A-Channel/A-Channel.error.js +1 -0
  7. package/dist/src/lib/A-Channel/A-Channel.error.js.map +1 -1
  8. package/dist/src/lib/A-Config/A-Config.container.js +1 -1
  9. package/dist/src/lib/A-Config/A-Config.container.js.map +1 -1
  10. package/dist/src/lib/A-Manifest/A-Manifest.context.d.ts +52 -0
  11. package/dist/src/lib/A-Manifest/A-Manifest.context.js +154 -0
  12. package/dist/src/lib/A-Manifest/A-Manifest.context.js.map +1 -0
  13. package/dist/src/lib/A-Manifest/A-Manifest.error.d.ts +4 -0
  14. package/dist/src/lib/A-Manifest/A-Manifest.error.js +9 -0
  15. package/dist/src/lib/A-Manifest/A-Manifest.error.js.map +1 -0
  16. package/dist/src/lib/A-Manifest/A-Manifest.types.d.ts +43 -0
  17. package/dist/src/lib/A-Manifest/A-Manifest.types.js +3 -0
  18. package/dist/src/lib/A-Manifest/A-Manifest.types.js.map +1 -0
  19. package/dist/src/lib/A-Manifest/classes/A-ManifestChecker.class.d.ts +13 -0
  20. package/dist/src/lib/A-Manifest/classes/A-ManifestChecker.class.js +24 -0
  21. package/dist/src/lib/A-Manifest/classes/A-ManifestChecker.class.js.map +1 -0
  22. package/package.json +2 -2
  23. package/src/lib/A-Channel/A-Channel.component.ts +70 -2
  24. package/src/lib/A-Channel/A-Channel.error.ts +2 -0
  25. package/src/lib/A-Config/A-Config.container.ts +1 -1
  26. package/src/lib/A-Manifest/A-Manifest.context.ts +198 -0
  27. package/src/lib/A-Manifest/A-Manifest.error.ts +7 -0
  28. package/src/lib/A-Manifest/A-Manifest.types.ts +62 -0
  29. package/src/lib/A-Manifest/README.md +201 -0
  30. package/src/lib/A-Manifest/classes/A-ManifestChecker.class.ts +24 -0
  31. package/tests/A-Manifest.test.ts +290 -0
@@ -0,0 +1,201 @@
1
+ # A-Manifest Usage Guide
2
+
3
+ The `A_Manifest` class provides a flexible configuration system for controlling component access and method permissions using regex patterns. It allows you to include or exclude components for particular methods based on sophisticated rule-based configurations.
4
+
5
+ ## Core Concepts
6
+
7
+ ### 1. **Component-Level Rules**
8
+ Apply rules to all methods of a component:
9
+
10
+ ```typescript
11
+ const manifest = new A_Manifest([
12
+ {
13
+ component: UserController,
14
+ exclude: [GuestUser] // Guests cannot access any UserController methods
15
+ }
16
+ ]);
17
+ ```
18
+
19
+ ### 2. **Method-Level Rules**
20
+ Apply specific rules to individual methods:
21
+
22
+ ```typescript
23
+ const manifest = new A_Manifest([
24
+ {
25
+ component: UserController,
26
+ methods: [
27
+ {
28
+ method: 'delete',
29
+ apply: [AdminUser, SuperAdmin] // Only admins can delete
30
+ },
31
+ {
32
+ method: 'post',
33
+ exclude: [GuestUser] // Guests cannot create
34
+ }
35
+ ]
36
+ }
37
+ ]);
38
+ ```
39
+
40
+ ### 3. **Regex Support**
41
+ Use regex patterns for flexible matching:
42
+
43
+ ```typescript
44
+ const manifest = new A_Manifest([
45
+ {
46
+ component: UserController,
47
+ methods: [
48
+ {
49
+ method: /^(post|put|delete)$/, // Match mutating operations
50
+ exclude: [GuestUser]
51
+ }
52
+ ]
53
+ }
54
+ ]);
55
+ ```
56
+
57
+ ## API Reference
58
+
59
+ ### Constructor
60
+ ```typescript
61
+ new A_Manifest(config: A_UTILS_TYPES__Manifest_Init)
62
+ ```
63
+
64
+ Creates a new manifest with the provided configuration.
65
+
66
+ ### isAllowed()
67
+ ```typescript
68
+ isAllowed<T extends A_Component>(
69
+ ctor: T | A_TYPES__Component_Constructor<T>,
70
+ method: string
71
+ ): A_ManifestChecker
72
+ ```
73
+
74
+ Returns a fluent checker to verify if access is allowed.
75
+
76
+ **Usage:**
77
+ ```typescript
78
+ const allowed = manifest.isAllowed(UserController, 'post').for(GuestUser);
79
+ ```
80
+
81
+ ### isExcluded()
82
+ ```typescript
83
+ isExcluded<T extends A_Component>(
84
+ ctor: T | A_TYPES__Component_Constructor<T>,
85
+ method: string
86
+ ): A_ManifestChecker
87
+ ```
88
+
89
+ Returns a fluent checker to verify if access is explicitly excluded.
90
+
91
+ **Usage:**
92
+ ```typescript
93
+ const excluded = manifest.isExcluded(UserController, 'post').for(GuestUser);
94
+ ```
95
+
96
+ ## Configuration Types
97
+
98
+ ### A_UTILS_TYPES__Manifest_ComponentLevelConfig
99
+ ```typescript
100
+ {
101
+ component: A_TYPES__Component_Constructor<T>,
102
+ methods?: Array<A_UTILS_TYPES__Manifest_MethodLevelConfig<T>>,
103
+ apply?: Array<A_UTILS_TYPES__Manifest_AllowedComponents> | RegExp,
104
+ exclude?: Array<A_UTILS_TYPES__Manifest_AllowedComponents> | RegExp
105
+ }
106
+ ```
107
+
108
+ ### A_UTILS_TYPES__Manifest_MethodLevelConfig
109
+ ```typescript
110
+ {
111
+ method: string | RegExp,
112
+ apply?: Array<A_UTILS_TYPES__Manifest_AllowedComponents> | RegExp,
113
+ exclude?: Array<A_UTILS_TYPES__Manifest_AllowedComponents> | RegExp
114
+ }
115
+ ```
116
+
117
+ ## Rule Precedence
118
+
119
+ 1. **Method-level rules** override component-level rules
120
+ 2. **Exclude rules** take precedence over apply rules
121
+ 3. **No rules** = allow by default
122
+
123
+ ## Advanced Examples
124
+
125
+ ### Example 1: Hierarchical Permissions
126
+ ```typescript
127
+ const manifest = new A_Manifest([
128
+ {
129
+ component: UserController,
130
+ exclude: [GuestUser], // Base rule: no access for guests
131
+ methods: [
132
+ {
133
+ method: 'get',
134
+ apply: [GuestUser, RegisteredUser, AdminUser] // Override: guests can read
135
+ },
136
+ {
137
+ method: 'delete',
138
+ apply: [AdminUser] // Only admins can delete
139
+ }
140
+ ]
141
+ }
142
+ ]);
143
+ ```
144
+
145
+ ### Example 2: Using Regex for Complex Patterns
146
+ ```typescript
147
+ const manifest = new A_Manifest([
148
+ {
149
+ component: AdminController,
150
+ methods: [
151
+ {
152
+ method: /^admin/, // Methods starting with "admin"
153
+ apply: [SuperAdmin] // Only super admins
154
+ },
155
+ {
156
+ method: /^(get|list)/, // Read operations
157
+ apply: [AdminUser, SuperAdmin] // All admins
158
+ }
159
+ ]
160
+ }
161
+ ]);
162
+ ```
163
+
164
+ ### Example 3: Multiple Components
165
+ ```typescript
166
+ const manifest = new A_Manifest([
167
+ {
168
+ component: UserController,
169
+ exclude: [GuestUser]
170
+ },
171
+ {
172
+ component: AdminController,
173
+ apply: [AdminUser, SuperAdmin]
174
+ },
175
+ {
176
+ component: PublicController,
177
+ // No rules = accessible to all
178
+ }
179
+ ]);
180
+ ```
181
+
182
+ ## Best Practices
183
+
184
+ 1. **Start with restrictive rules** and add exceptions rather than the opposite
185
+ 2. **Use component-level rules** for broad access control
186
+ 3. **Use method-level rules** for fine-grained permissions
187
+ 4. **Leverage regex patterns** for dynamic method matching
188
+ 5. **Test your configurations** thoroughly with the provided fluent API
189
+
190
+ ## Error Handling
191
+
192
+ The manifest will throw `A_ManifestError` for:
193
+ - Invalid configuration structure
194
+ - Non-component constructors in configuration
195
+ - Malformed regex patterns
196
+
197
+ ## Performance Considerations
198
+
199
+ - Rules are compiled to regex patterns during initialization
200
+ - Runtime checks are optimized for fast pattern matching
201
+ - Consider the number of rules when designing complex hierarchies
@@ -0,0 +1,24 @@
1
+ import { A_TYPES__Component_Constructor } from "@adaas/a-concept";
2
+ import { A_Manifest } from "../A-Manifest.context";
3
+
4
+ /**
5
+ * Fluent API for checking manifest permissions
6
+ */
7
+ export class A_ManifestChecker {
8
+ constructor(
9
+ private manifest: A_Manifest,
10
+ private component: A_TYPES__Component_Constructor,
11
+ private method: string,
12
+ private checkExclusion: boolean = false
13
+ ) {}
14
+
15
+ for(target: A_TYPES__Component_Constructor): boolean {
16
+ const result = this.manifest.internal_checkAccess({
17
+ component: this.component,
18
+ method: this.method,
19
+ target: target
20
+ });
21
+
22
+ return this.checkExclusion ? !result : result;
23
+ }
24
+ }
@@ -0,0 +1,290 @@
1
+ import { A_Component } from "@adaas/a-concept";
2
+ import { A_Manifest } from "../src/lib/A-Manifest/A-Manifest.context";
3
+
4
+ describe('A_Manifest', () => {
5
+ it('Should deny access to excluded components at component level', () => {
6
+ class UserController extends A_Component {
7
+ get() { return 'user.get'; }
8
+ post() { return 'user.post'; }
9
+ }
10
+ class GuestUser extends A_Component {}
11
+ class RegisteredUser extends A_Component {}
12
+
13
+ const manifest = new A_Manifest([
14
+ {
15
+ component: UserController,
16
+ exclude: [GuestUser]
17
+ }
18
+ ]);
19
+
20
+ expect(manifest.isAllowed(UserController, 'get').for(GuestUser)).toBe(false);
21
+ expect(manifest.isAllowed(UserController, 'post').for(GuestUser)).toBe(false);
22
+ expect(manifest.isAllowed(UserController, 'get').for(RegisteredUser)).toBe(true);
23
+ });
24
+
25
+ it('Should allow access only to included components at component level', () => {
26
+ class AdminController extends A_Component {
27
+ get() { return 'admin.get'; }
28
+ adminPanel() { return 'admin.adminPanel'; }
29
+ }
30
+ class AdminUser extends A_Component {}
31
+ class SuperAdmin extends A_Component {}
32
+ class RegisteredUser extends A_Component {}
33
+
34
+ const manifest = new A_Manifest([
35
+ {
36
+ component: AdminController,
37
+ apply: [AdminUser, SuperAdmin]
38
+ }
39
+ ]);
40
+
41
+ expect(manifest.isAllowed(AdminController, 'get').for(RegisteredUser)).toBe(false);
42
+ expect(manifest.isAllowed(AdminController, 'get').for(AdminUser)).toBe(true);
43
+ expect(manifest.isAllowed(AdminController, 'adminPanel').for(SuperAdmin)).toBe(true);
44
+ });
45
+
46
+ it('Should respect method-level exclusion rules', () => {
47
+ class UserController extends A_Component {
48
+ get() { return 'user.get'; }
49
+ post() { return 'user.post'; }
50
+ delete() { return 'user.delete'; }
51
+ }
52
+ class GuestUser extends A_Component {}
53
+ class RegisteredUser extends A_Component {}
54
+ class AdminUser extends A_Component {}
55
+ class SuperAdmin extends A_Component {}
56
+
57
+ const manifest = new A_Manifest([
58
+ {
59
+ component: UserController,
60
+ methods: [
61
+ {
62
+ method: 'delete',
63
+ apply: [AdminUser, SuperAdmin]
64
+ },
65
+ {
66
+ method: 'post',
67
+ exclude: [GuestUser]
68
+ }
69
+ ]
70
+ }
71
+ ]);
72
+
73
+ expect(manifest.isAllowed(UserController, 'get').for(GuestUser)).toBe(true);
74
+ expect(manifest.isAllowed(UserController, 'post').for(GuestUser)).toBe(false);
75
+ expect(manifest.isAllowed(UserController, 'post').for(RegisteredUser)).toBe(true);
76
+ expect(manifest.isAllowed(UserController, 'delete').for(RegisteredUser)).toBe(false);
77
+ expect(manifest.isAllowed(UserController, 'delete').for(AdminUser)).toBe(true);
78
+ });
79
+
80
+ it('Should handle regex patterns for method matching', () => {
81
+ class UserController extends A_Component {
82
+ get() { return 'user.get'; }
83
+ post() { return 'user.post'; }
84
+ put() { return 'user.put'; }
85
+ delete() { return 'user.delete'; }
86
+ }
87
+ class GuestUser extends A_Component {}
88
+ class RegisteredUser extends A_Component {}
89
+
90
+ const manifest = new A_Manifest([
91
+ {
92
+ component: UserController,
93
+ methods: [
94
+ {
95
+ method: /^(post|put|delete)$/,
96
+ exclude: [GuestUser]
97
+ }
98
+ ]
99
+ }
100
+ ]);
101
+
102
+ expect(manifest.isAllowed(UserController, 'get').for(GuestUser)).toBe(true);
103
+ expect(manifest.isAllowed(UserController, 'post').for(GuestUser)).toBe(false);
104
+ expect(manifest.isAllowed(UserController, 'put').for(GuestUser)).toBe(false);
105
+ expect(manifest.isAllowed(UserController, 'delete').for(GuestUser)).toBe(false);
106
+ expect(manifest.isAllowed(UserController, 'post').for(RegisteredUser)).toBe(true);
107
+ });
108
+
109
+ it('Should allow method-level overrides of component-level rules', () => {
110
+ class UserController extends A_Component {
111
+ get() { return 'user.get'; }
112
+ post() { return 'user.post'; }
113
+ }
114
+ class AdminController extends A_Component {
115
+ get() { return 'admin.get'; }
116
+ }
117
+ class GuestUser extends A_Component {}
118
+ class RegisteredUser extends A_Component {}
119
+ class AdminUser extends A_Component {}
120
+ class SuperAdmin extends A_Component {}
121
+
122
+ const manifest = new A_Manifest([
123
+ {
124
+ component: UserController,
125
+ exclude: [GuestUser],
126
+ methods: [
127
+ {
128
+ method: 'get',
129
+ apply: [GuestUser, RegisteredUser, AdminUser, SuperAdmin]
130
+ }
131
+ ]
132
+ },
133
+ {
134
+ component: AdminController,
135
+ apply: [AdminUser, SuperAdmin]
136
+ }
137
+ ]);
138
+
139
+ // Method-level rule Should override component-level exclusion
140
+ expect(manifest.isAllowed(UserController, 'get').for(GuestUser)).toBe(true);
141
+
142
+ // Component-level exclusion Should apply to methods without specific rules
143
+ expect(manifest.isAllowed(UserController, 'post').for(GuestUser)).toBe(false);
144
+
145
+ // Other component rules Should work independently
146
+ expect(manifest.isAllowed(AdminController, 'get').for(RegisteredUser)).toBe(false);
147
+ expect(manifest.isAllowed(AdminController, 'get').for(AdminUser)).toBe(true);
148
+ });
149
+
150
+ it('Should work with isExcluded method', () => {
151
+ class UserController extends A_Component {
152
+ post() { return 'user.post'; }
153
+ }
154
+ class GuestUser extends A_Component {}
155
+ class RegisteredUser extends A_Component {}
156
+
157
+ const manifest = new A_Manifest([
158
+ {
159
+ component: UserController,
160
+ exclude: [GuestUser]
161
+ }
162
+ ]);
163
+
164
+ expect(manifest.isExcluded(UserController, 'post').for(GuestUser)).toBe(true);
165
+ expect(manifest.isExcluded(UserController, 'post').for(RegisteredUser)).toBe(false);
166
+ });
167
+
168
+ it('Should handle empty configuration and allow all by default', () => {
169
+ class UserController extends A_Component {
170
+ get() { return 'user.get'; }
171
+ }
172
+ class GuestUser extends A_Component {}
173
+
174
+ const manifest = new A_Manifest([]);
175
+
176
+ expect(manifest.isAllowed(UserController, 'get').for(GuestUser)).toBe(true);
177
+ });
178
+
179
+ it('Should handle component with no specific rules and allow all by default', () => {
180
+ class UserController extends A_Component {
181
+ get() { return 'user.get'; }
182
+ }
183
+ class GuestUser extends A_Component {}
184
+
185
+ const manifest = new A_Manifest([
186
+ {
187
+ component: UserController
188
+ // No apply or exclude rules
189
+ }
190
+ ]);
191
+
192
+ expect(manifest.isAllowed(UserController, 'get').for(GuestUser)).toBe(true);
193
+ });
194
+
195
+ it('Should apply component-level rules to all methods when no method rules exist', () => {
196
+ class UserController extends A_Component {
197
+ get() { return 'user.get'; }
198
+ post() { return 'user.post'; }
199
+ }
200
+ class GuestUser extends A_Component {}
201
+
202
+ const manifest = new A_Manifest([
203
+ {
204
+ component: UserController,
205
+ exclude: [GuestUser]
206
+ }
207
+ ]);
208
+
209
+ expect(manifest.isAllowed(UserController, 'get').for(GuestUser)).toBe(false);
210
+ expect(manifest.isAllowed(UserController, 'post').for(GuestUser)).toBe(false);
211
+ expect(manifest.isAllowed(UserController, 'anyMethod').for(GuestUser)).toBe(false);
212
+ });
213
+
214
+ it('Should throw error for invalid configuration type', () => {
215
+ expect(() => {
216
+ // @ts-ignore - Testing runtime error
217
+ new A_Manifest("invalid");
218
+ }).toThrow();
219
+ });
220
+
221
+ it('Should throw error for non-component constructor in configuration', () => {
222
+ class GuestUser extends A_Component {}
223
+
224
+ expect(() => {
225
+ new A_Manifest([
226
+ {
227
+ // @ts-ignore - Testing runtime error
228
+ component: "not-a-constructor",
229
+ exclude: [GuestUser]
230
+ }
231
+ ]);
232
+ }).toThrow();
233
+ });
234
+
235
+ it('Should handle multiple method rules for the same component', () => {
236
+ class UserController extends A_Component {
237
+ get() { return 'user.get'; }
238
+ post() { return 'user.post'; }
239
+ put() { return 'user.put'; }
240
+ delete() { return 'user.delete'; }
241
+ }
242
+ class GuestUser extends A_Component {}
243
+ class RegisteredUser extends A_Component {}
244
+ class AdminUser extends A_Component {}
245
+
246
+ const manifest = new A_Manifest([
247
+ {
248
+ component: UserController,
249
+ methods: [
250
+ {
251
+ method: 'get',
252
+ apply: [GuestUser, RegisteredUser, AdminUser]
253
+ },
254
+ {
255
+ method: 'post',
256
+ apply: [RegisteredUser, AdminUser]
257
+ },
258
+ {
259
+ method: 'delete',
260
+ apply: [AdminUser]
261
+ }
262
+ ]
263
+ }
264
+ ]);
265
+
266
+ expect(manifest.isAllowed(UserController, 'get').for(GuestUser)).toBe(true);
267
+ expect(manifest.isAllowed(UserController, 'post').for(GuestUser)).toBe(false);
268
+ expect(manifest.isAllowed(UserController, 'post').for(RegisteredUser)).toBe(true);
269
+ expect(manifest.isAllowed(UserController, 'delete').for(RegisteredUser)).toBe(false);
270
+ expect(manifest.isAllowed(UserController, 'delete').for(AdminUser)).toBe(true);
271
+ expect(manifest.isAllowed(UserController, 'put').for(GuestUser)).toBe(true); // No rule = allow
272
+ });
273
+
274
+ it('Should prioritize exclude rules over apply rules', () => {
275
+ class UserController extends A_Component {
276
+ post() { return 'user.post'; }
277
+ }
278
+ class RegisteredUser extends A_Component {}
279
+
280
+ const manifest = new A_Manifest([
281
+ {
282
+ component: UserController,
283
+ apply: [RegisteredUser],
284
+ exclude: [RegisteredUser] // Exclude Should take precedence
285
+ }
286
+ ]);
287
+
288
+ expect(manifest.isAllowed(UserController, 'post').for(RegisteredUser)).toBe(false);
289
+ });
290
+ });