@owlmeans/module 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 +521 -0
- package/build/.gitkeep +0 -0
- package/build/consts.d.ts +7 -0
- package/build/consts.d.ts.map +1 -0
- package/build/consts.js +8 -0
- package/build/consts.js.map +1 -0
- package/build/filter.d.ts +8 -0
- package/build/filter.d.ts.map +1 -0
- package/build/filter.js +20 -0
- package/build/filter.js.map +1 -0
- package/build/helper.d.ts +7 -0
- package/build/helper.d.ts.map +1 -0
- package/build/helper.js +26 -0
- package/build/helper.js.map +1 -0
- package/build/index.d.ts +6 -0
- package/build/index.d.ts.map +1 -0
- package/build/index.js +5 -0
- package/build/index.js.map +1 -0
- package/build/module.d.ts +5 -0
- package/build/module.d.ts.map +1 -0
- package/build/module.js +79 -0
- package/build/module.js.map +1 -0
- package/build/types.d.ts +80 -0
- package/build/types.d.ts.map +1 -0
- package/build/types.js +2 -0
- package/build/types.js.map +1 -0
- package/build/utils/index.d.ts +3 -0
- package/build/utils/index.d.ts.map +1 -0
- package/build/utils/index.js +3 -0
- package/build/utils/index.js.map +1 -0
- package/build/utils/module.d.ts +3 -0
- package/build/utils/module.d.ts.map +1 -0
- package/build/utils/module.js +2 -0
- package/build/utils/module.js.map +1 -0
- package/build/utils/types.d.ts +6 -0
- package/build/utils/types.d.ts.map +1 -0
- package/build/utils/types.js +2 -0
- package/build/utils/types.js.map +1 -0
- package/package.json +44 -0
- package/src/consts.ts +7 -0
- package/src/filter.ts +26 -0
- package/src/helper.ts +37 -0
- package/src/index.ts +6 -0
- package/src/module.ts +105 -0
- package/src/types.ts +93 -0
- package/src/utils/index.ts +3 -0
- package/src/utils/module.ts +3 -0
- package/src/utils/types.ts +6 -0
- package/tsconfig.json +15 -0
- package/tsconfig.tsbuildinfo +1 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 OwlMeans Common — Fullstack typescript framework
|
|
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,521 @@
|
|
|
1
|
+
# OwlMeans Module — Shared library
|
|
2
|
+
|
|
3
|
+
The `@owlmeans/module` package provides a comprehensive module system for OwlMeans Common Libraries, designed for fullstack microservices and microclients development.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
In the context of OwlMeans Common Libraries, a **module** is not a programmatic module but a **URL unit** in the system. Modules allow you to:
|
|
8
|
+
|
|
9
|
+
- Declare URLs and their nesting relationships
|
|
10
|
+
- Transform URLs into routes with attached handlers (backend) or specify components to render (frontend)
|
|
11
|
+
- Generate final URLs for navigation or API calls on both backend and frontend
|
|
12
|
+
- Provide a centralized place where all possible routes are registered
|
|
13
|
+
- Enable micro-applications or micro-services to flawlessly address different parts of the system
|
|
14
|
+
|
|
15
|
+
For **client-side applications**, the [`@owlmeans/client-module`](../client-module) package extends this base module system with client-specific capabilities such as API calls, URL generation, and request validation.
|
|
16
|
+
|
|
17
|
+
## Core Concepts
|
|
18
|
+
|
|
19
|
+
### Module
|
|
20
|
+
A module represents a URL unit that can be transformed into routes or components depending on the environment (frontend/backend). It consists of:
|
|
21
|
+
- A route with URL path and alias
|
|
22
|
+
- Optional guards for authentication/authorization
|
|
23
|
+
- Optional gates for parameter validation
|
|
24
|
+
- Optional filters for request/response validation
|
|
25
|
+
- Optional handlers for processing requests
|
|
26
|
+
|
|
27
|
+
### Parent-Child Relationships
|
|
28
|
+
Modules can be organized in hierarchical structures where child modules inherit properties from their parents, such as guards and gates.
|
|
29
|
+
|
|
30
|
+
### Fullstack Consistency
|
|
31
|
+
A key architectural benefit of the module system is that all validators are defined at the module level using **AJV (Another JSON Schema Validator)** format. Since the system is designed for fullstack development (existing on both frontend and backend), these validation schemas are consistently accessible across all application services and clients. This ensures:
|
|
32
|
+
|
|
33
|
+
- **Unified validation**: Same validation rules apply on both frontend and backend
|
|
34
|
+
- **Consistent data contracts**: API contracts are shared between client and server
|
|
35
|
+
- **Reduced duplication**: No need to define validation schemas separately for frontend and backend
|
|
36
|
+
- **Type safety**: AJV schemas provide runtime validation that complements TypeScript's compile-time checking
|
|
37
|
+
|
|
38
|
+
## API Reference
|
|
39
|
+
|
|
40
|
+
### Types
|
|
41
|
+
|
|
42
|
+
#### CommonModule
|
|
43
|
+
The main module interface that extends `BasicModule` from `@owlmeans/context`.
|
|
44
|
+
|
|
45
|
+
```typescript
|
|
46
|
+
interface CommonModule extends BasicModule {
|
|
47
|
+
route: CommonRouteModel
|
|
48
|
+
sticky: boolean // If true, router attaches this module unconditionally
|
|
49
|
+
filter?: Filter // Request/response validation schemas
|
|
50
|
+
guards?: string[] // Authentication guards
|
|
51
|
+
gate?: string // Authorization gate
|
|
52
|
+
gateParams?: string | string[] // Gate parameters
|
|
53
|
+
handle?: ModuleHandler // Request handler function
|
|
54
|
+
|
|
55
|
+
// Methods
|
|
56
|
+
getAlias(): string
|
|
57
|
+
getPath(): string
|
|
58
|
+
getParentAlias(): string | null
|
|
59
|
+
hasParent(): boolean
|
|
60
|
+
resolve<M extends CommonModule>(): Promise<M>
|
|
61
|
+
getParent<M extends CommonModule>(): M
|
|
62
|
+
setService(service: string): void
|
|
63
|
+
getGuards(): string[]
|
|
64
|
+
getGates(): [string, string[]][]
|
|
65
|
+
}
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
#### ModuleHandler
|
|
69
|
+
Function signature for handling module requests.
|
|
70
|
+
|
|
71
|
+
```typescript
|
|
72
|
+
interface ModuleHandler {
|
|
73
|
+
<T, R extends AbstractRequest<any> = AbstractRequest<any>,
|
|
74
|
+
P extends AbstractResponse<any> = AbstractResponse<any>>
|
|
75
|
+
(req: R, res: P): T | Promise<T>
|
|
76
|
+
}
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
#### Filter
|
|
80
|
+
Schema definitions for request/response validation using **AJV (Another JSON Schema Validator)** format. All validators are defined at the module level, ensuring consistent validation schemas across both frontend and backend environments in fullstack applications.
|
|
81
|
+
|
|
82
|
+
```typescript
|
|
83
|
+
interface Filter {
|
|
84
|
+
query?: AnySchemaObject // Query parameters validation (AJV schema)
|
|
85
|
+
params?: AnySchemaObject // Path parameters validation (AJV schema)
|
|
86
|
+
body?: AnySchemaObject // Request body validation (AJV schema)
|
|
87
|
+
response?: AnySchemaObject // Response validation (AJV schema)
|
|
88
|
+
headers?: AnySchemaObject // Headers validation (AJV schema)
|
|
89
|
+
}
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
Since the module system is designed for fullstack development, these AJV validation schemas are accessible and consistent across all application services and clients, providing unified data validation throughout the entire application stack.
|
|
93
|
+
|
|
94
|
+
#### AbstractRequest
|
|
95
|
+
Generic request interface for both frontend and backend.
|
|
96
|
+
|
|
97
|
+
```typescript
|
|
98
|
+
interface AbstractRequest<T extends {} = {}> {
|
|
99
|
+
alias: string
|
|
100
|
+
auth?: Auth
|
|
101
|
+
params: Record<string, string | number | undefined | null> | Partial<T>
|
|
102
|
+
body?: Record<string, any> | Partial<T>
|
|
103
|
+
headers: Record<string, string[] | string | undefined>
|
|
104
|
+
query: Record<string, string | number | undefined | null> | Partial<T>
|
|
105
|
+
path: string
|
|
106
|
+
original?: any
|
|
107
|
+
canceled?: boolean
|
|
108
|
+
cancel?: () => void
|
|
109
|
+
host?: string
|
|
110
|
+
base?: string | boolean
|
|
111
|
+
}
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
#### AbstractResponse
|
|
115
|
+
Generic response interface for handling module responses.
|
|
116
|
+
|
|
117
|
+
```typescript
|
|
118
|
+
interface AbstractResponse<T> {
|
|
119
|
+
responseProvider?: any
|
|
120
|
+
value?: T
|
|
121
|
+
outcome?: ModuleOutcome
|
|
122
|
+
error?: Error
|
|
123
|
+
resolve(value: T, outcome?: ModuleOutcome): void
|
|
124
|
+
reject(error: Error): void
|
|
125
|
+
}
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
### Core Functions
|
|
129
|
+
|
|
130
|
+
#### module(route, opts?)
|
|
131
|
+
Creates a new module instance.
|
|
132
|
+
|
|
133
|
+
```typescript
|
|
134
|
+
function module(route: CommonRouteModel, opts?: CommonModuleOptions): CommonModule
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
**Parameters:**
|
|
138
|
+
- `route`: CommonRouteModel - The route configuration
|
|
139
|
+
- `opts`: CommonModuleOptions - Optional module configuration
|
|
140
|
+
|
|
141
|
+
**Returns:** CommonModule instance
|
|
142
|
+
|
|
143
|
+
**Example:**
|
|
144
|
+
```typescript
|
|
145
|
+
import { module } from '@owlmeans/module'
|
|
146
|
+
import { route } from '@owlmeans/route'
|
|
147
|
+
|
|
148
|
+
const userModule = module(route('users', '/users'), {
|
|
149
|
+
sticky: true,
|
|
150
|
+
guards: ['authenticated']
|
|
151
|
+
})
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
#### parent(module, aliasOrParent, _parent?)
|
|
155
|
+
Sets parent-child relationships between modules.
|
|
156
|
+
|
|
157
|
+
```typescript
|
|
158
|
+
function parent<T extends CommonModule | CommonModule[]>(
|
|
159
|
+
module: T,
|
|
160
|
+
aliasOrParent: string,
|
|
161
|
+
_parent?: string
|
|
162
|
+
): T
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
**Parameters:**
|
|
166
|
+
- `module`: CommonModule or CommonModule[] - Module(s) to set parent for
|
|
167
|
+
- `aliasOrParent`: string - Parent alias or module alias (when working with arrays)
|
|
168
|
+
- `_parent`: string - Parent name (required when working with arrays)
|
|
169
|
+
|
|
170
|
+
**Returns:** The module(s) with parent relationship set
|
|
171
|
+
|
|
172
|
+
**Example:**
|
|
173
|
+
```typescript
|
|
174
|
+
const userModule = module(route('users', '/users'))
|
|
175
|
+
const userProfileModule = module(route('user-profile', '/profile'))
|
|
176
|
+
|
|
177
|
+
parent(userProfileModule, 'users') // Sets users as parent of user-profile
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
### Helper Functions
|
|
181
|
+
|
|
182
|
+
#### filter(filter, opts?)
|
|
183
|
+
Creates module options with filter configuration.
|
|
184
|
+
|
|
185
|
+
```typescript
|
|
186
|
+
function filter(filter: Filter, opts?: CommonModuleOptions): CommonModuleOptions
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
**Example:**
|
|
190
|
+
```typescript
|
|
191
|
+
const userModule = module(route('users', '/users'), filter({
|
|
192
|
+
query: { type: 'object', properties: { limit: { type: 'number' } } }
|
|
193
|
+
}))
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
#### guard(guard, opts?)
|
|
197
|
+
Adds authentication guard to module options.
|
|
198
|
+
|
|
199
|
+
```typescript
|
|
200
|
+
function guard(guard: string, opts?: CommonModuleOptions): CommonModuleOptions
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
**Example:**
|
|
204
|
+
```typescript
|
|
205
|
+
const adminModule = module(route('admin', '/admin'), guard('admin'))
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
#### gate(gate, params, opts?)
|
|
209
|
+
Adds authorization gate with parameters to module options.
|
|
210
|
+
|
|
211
|
+
```typescript
|
|
212
|
+
function gate(gate: string, params: string | string[], opts?: CommonModuleOptions): CommonModuleOptions
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
**Example:**
|
|
216
|
+
```typescript
|
|
217
|
+
const userModule = module(route('user', '/user/:id'), gate('user-access', ['id']))
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
#### provideResponse(originalResponse?)
|
|
221
|
+
Creates an abstract response handler.
|
|
222
|
+
|
|
223
|
+
```typescript
|
|
224
|
+
function provideResponse<T>(originalResponse?: unknown): AbstractResponse<T>
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
**Example:**
|
|
228
|
+
```typescript
|
|
229
|
+
const response = provideResponse<UserData>()
|
|
230
|
+
response.resolve(userData, ModuleOutcome.Ok)
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
#### clone(modules, from, to, service)
|
|
234
|
+
Clones an existing module with new alias and service.
|
|
235
|
+
|
|
236
|
+
```typescript
|
|
237
|
+
function clone<M extends CommonModule>(
|
|
238
|
+
modules: M[],
|
|
239
|
+
from: string,
|
|
240
|
+
to: string,
|
|
241
|
+
service: string
|
|
242
|
+
): void
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
**Parameters:**
|
|
246
|
+
- `modules`: M[] - Array of modules to add cloned module to
|
|
247
|
+
- `from`: string - Source module alias
|
|
248
|
+
- `to`: string - New module alias
|
|
249
|
+
- `service`: string - Service name for the cloned module
|
|
250
|
+
|
|
251
|
+
### Filter Building Functions
|
|
252
|
+
|
|
253
|
+
These functions create validation filters using **AJV (Another JSON Schema Validator)** format. All schemas are defined at the module level and remain consistent across both frontend and backend environments, ensuring unified validation throughout your fullstack application.
|
|
254
|
+
|
|
255
|
+
#### body(schema, filter?)
|
|
256
|
+
Creates or extends a filter with body validation schema using AJV format.
|
|
257
|
+
|
|
258
|
+
```typescript
|
|
259
|
+
function body<T>(schema: JSONSchemaType<T>, filter?: Filter): Filter
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
#### query(schema, filter?)
|
|
263
|
+
Creates or extends a filter with query parameters validation schema using AJV format.
|
|
264
|
+
|
|
265
|
+
```typescript
|
|
266
|
+
function query<T>(schema: JSONSchemaType<T>, filter?: Filter): Filter
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
#### params(schema, filter?)
|
|
270
|
+
Creates or extends a filter with path parameters validation schema using AJV format.
|
|
271
|
+
|
|
272
|
+
```typescript
|
|
273
|
+
function params<T>(schema: JSONSchemaType<T>, filter?: Filter): Filter
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
#### response(schema, code?, filter?)
|
|
277
|
+
Creates or extends a filter with response validation schema using AJV format.
|
|
278
|
+
|
|
279
|
+
```typescript
|
|
280
|
+
function response<T>(schema: JSONSchemaType<T>, code?: number, filter?: Filter): Filter
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
#### headers(schema, filter?)
|
|
284
|
+
Creates or extends a filter with headers validation schema using AJV format.
|
|
285
|
+
|
|
286
|
+
```typescript
|
|
287
|
+
function headers<T>(schema: JSONSchemaType<T>, filter?: Filter): Filter
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
**Example with AJV Schema Format:**
|
|
291
|
+
```typescript
|
|
292
|
+
import { body, query, params, response } from '@owlmeans/module'
|
|
293
|
+
|
|
294
|
+
// AJV schema for request body validation
|
|
295
|
+
const userFilter = body({
|
|
296
|
+
type: 'object',
|
|
297
|
+
properties: {
|
|
298
|
+
name: { type: 'string' },
|
|
299
|
+
email: { type: 'string' }
|
|
300
|
+
},
|
|
301
|
+
required: ['name', 'email']
|
|
302
|
+
}, query({
|
|
303
|
+
type: 'object',
|
|
304
|
+
properties: {
|
|
305
|
+
include: { type: 'string', enum: ['profile', 'preferences'] }
|
|
306
|
+
}
|
|
307
|
+
}))
|
|
308
|
+
|
|
309
|
+
// These AJV schemas are accessible on both frontend and backend
|
|
310
|
+
// providing consistent validation across your fullstack application
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
### Utility Functions
|
|
314
|
+
|
|
315
|
+
#### isModule(object)
|
|
316
|
+
Type guard to check if an object is a CommonModule.
|
|
317
|
+
|
|
318
|
+
```typescript
|
|
319
|
+
function isModule(module: Object): module is CommonModule
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
**Example:**
|
|
323
|
+
```typescript
|
|
324
|
+
if (isModule(someObject)) {
|
|
325
|
+
// someObject is definitely a CommonModule
|
|
326
|
+
console.log(someObject.getAlias())
|
|
327
|
+
}
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
### Constants
|
|
331
|
+
|
|
332
|
+
#### ModuleOutcome
|
|
333
|
+
Enumeration of possible module response outcomes.
|
|
334
|
+
|
|
335
|
+
```typescript
|
|
336
|
+
enum ModuleOutcome {
|
|
337
|
+
Ok = 'ok',
|
|
338
|
+
Accepted = 'accepted',
|
|
339
|
+
Created = 'created',
|
|
340
|
+
Finished = 'finished'
|
|
341
|
+
}
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
### Service Interfaces
|
|
345
|
+
|
|
346
|
+
#### GuardService
|
|
347
|
+
Service interface for implementing authentication guards.
|
|
348
|
+
|
|
349
|
+
```typescript
|
|
350
|
+
interface GuardService extends InitializedService {
|
|
351
|
+
token?: string
|
|
352
|
+
authenticated(req?: Partial<AbstractRequest>): Promise<string | null>
|
|
353
|
+
match: ModuleMatch
|
|
354
|
+
handle: ModuleHandler
|
|
355
|
+
}
|
|
356
|
+
```
|
|
357
|
+
|
|
358
|
+
#### GateService
|
|
359
|
+
Service interface for implementing authorization gates.
|
|
360
|
+
|
|
361
|
+
```typescript
|
|
362
|
+
interface GateService extends LazyService {
|
|
363
|
+
assert: ModuleAssert // Throws Error if assertion fails
|
|
364
|
+
}
|
|
365
|
+
```
|
|
366
|
+
|
|
367
|
+
## Usage Examples
|
|
368
|
+
|
|
369
|
+
### Basic Module Creation
|
|
370
|
+
|
|
371
|
+
```typescript
|
|
372
|
+
import { module, guard, filter, body } from '@owlmeans/module'
|
|
373
|
+
import { route } from '@owlmeans/route'
|
|
374
|
+
|
|
375
|
+
// Create a simple module
|
|
376
|
+
const homeModule = module(route('home', '/'))
|
|
377
|
+
|
|
378
|
+
// Create a protected module with authentication
|
|
379
|
+
const dashboardModule = module(
|
|
380
|
+
route('dashboard', '/dashboard'),
|
|
381
|
+
guard('authenticated')
|
|
382
|
+
)
|
|
383
|
+
|
|
384
|
+
// Create a module with request validation
|
|
385
|
+
const createUserModule = module(
|
|
386
|
+
route('create-user', '/users', { method: 'POST' }),
|
|
387
|
+
filter(body({
|
|
388
|
+
type: 'object',
|
|
389
|
+
properties: {
|
|
390
|
+
name: { type: 'string' },
|
|
391
|
+
email: { type: 'string' }
|
|
392
|
+
},
|
|
393
|
+
required: ['name', 'email']
|
|
394
|
+
}))
|
|
395
|
+
)
|
|
396
|
+
```
|
|
397
|
+
|
|
398
|
+
### Module Hierarchies
|
|
399
|
+
|
|
400
|
+
```typescript
|
|
401
|
+
// Create parent module
|
|
402
|
+
const apiModule = module(route('api', '/api'))
|
|
403
|
+
|
|
404
|
+
// Create child modules
|
|
405
|
+
const usersModule = module(route('users', '/users'))
|
|
406
|
+
const postsModule = module(route('posts', '/posts'))
|
|
407
|
+
|
|
408
|
+
// Set parent relationships
|
|
409
|
+
parent(usersModule, 'api')
|
|
410
|
+
parent(postsModule, 'api')
|
|
411
|
+
|
|
412
|
+
// Child modules inherit parent guards and gates
|
|
413
|
+
const userProfileModule = module(
|
|
414
|
+
route('user-profile', '/profile'),
|
|
415
|
+
guard('user-access')
|
|
416
|
+
)
|
|
417
|
+
parent(userProfileModule, 'users')
|
|
418
|
+
```
|
|
419
|
+
|
|
420
|
+
### Module with Handler
|
|
421
|
+
|
|
422
|
+
```typescript
|
|
423
|
+
const userModule = module(route('get-user', '/users/:id'), {
|
|
424
|
+
handle: async (req, res) => {
|
|
425
|
+
const userId = req.params.id
|
|
426
|
+
const user = await getUserById(userId)
|
|
427
|
+
res.resolve(user, ModuleOutcome.Ok)
|
|
428
|
+
}
|
|
429
|
+
})
|
|
430
|
+
```
|
|
431
|
+
|
|
432
|
+
### Complex Filter Example
|
|
433
|
+
|
|
434
|
+
```typescript
|
|
435
|
+
import { filter, body, query, params, response } from '@owlmeans/module'
|
|
436
|
+
|
|
437
|
+
const complexModule = module(
|
|
438
|
+
route('complex-api', '/api/users/:id'),
|
|
439
|
+
filter(
|
|
440
|
+
params({
|
|
441
|
+
type: 'object',
|
|
442
|
+
properties: {
|
|
443
|
+
id: { type: 'string', pattern: '^[0-9]+$' }
|
|
444
|
+
},
|
|
445
|
+
required: ['id']
|
|
446
|
+
},
|
|
447
|
+
query({
|
|
448
|
+
type: 'object',
|
|
449
|
+
properties: {
|
|
450
|
+
include: { type: 'string', enum: ['profile', 'posts'] },
|
|
451
|
+
limit: { type: 'number', minimum: 1, maximum: 100 }
|
|
452
|
+
}
|
|
453
|
+
},
|
|
454
|
+
response({
|
|
455
|
+
type: 'object',
|
|
456
|
+
properties: {
|
|
457
|
+
user: { type: 'object' },
|
|
458
|
+
profile: { type: 'object' }
|
|
459
|
+
},
|
|
460
|
+
required: ['user']
|
|
461
|
+
})))
|
|
462
|
+
)
|
|
463
|
+
)
|
|
464
|
+
```
|
|
465
|
+
|
|
466
|
+
## Integration Patterns
|
|
467
|
+
|
|
468
|
+
### Backend Integration
|
|
469
|
+
On the backend, modules are typically transformed into Express-like routes:
|
|
470
|
+
|
|
471
|
+
```typescript
|
|
472
|
+
// Transform module to route with handler
|
|
473
|
+
app.get(userModule.getPath(), async (req, res) => {
|
|
474
|
+
const moduleReq = adaptRequest(req)
|
|
475
|
+
const moduleRes = provideResponse()
|
|
476
|
+
|
|
477
|
+
await userModule.handle(moduleReq, moduleRes)
|
|
478
|
+
|
|
479
|
+
if (moduleRes.error) {
|
|
480
|
+
res.status(500).json({ error: moduleRes.error.message })
|
|
481
|
+
} else {
|
|
482
|
+
res.json(moduleRes.value)
|
|
483
|
+
}
|
|
484
|
+
})
|
|
485
|
+
```
|
|
486
|
+
|
|
487
|
+
### Frontend Integration
|
|
488
|
+
On the frontend, modules help generate URLs and determine which components to render:
|
|
489
|
+
|
|
490
|
+
```typescript
|
|
491
|
+
// Generate URL for navigation
|
|
492
|
+
const userUrl = userModule.getPath() // '/users/:id'
|
|
493
|
+
const finalUrl = userUrl.replace(':id', userId)
|
|
494
|
+
|
|
495
|
+
// Navigate to the route
|
|
496
|
+
router.push(finalUrl)
|
|
497
|
+
```
|
|
498
|
+
|
|
499
|
+
## Best Practices
|
|
500
|
+
|
|
501
|
+
1. **Organize modules hierarchically** to take advantage of guard and gate inheritance
|
|
502
|
+
2. **Use meaningful aliases** for modules to make them easy to reference
|
|
503
|
+
3. **Apply filters consistently** to ensure proper validation
|
|
504
|
+
4. **Leverage guards and gates** for security at the module level
|
|
505
|
+
5. **Keep modules focused** on a single responsibility
|
|
506
|
+
6. **Use the clone function** to create variations of existing modules for different services
|
|
507
|
+
|
|
508
|
+
## Dependencies
|
|
509
|
+
|
|
510
|
+
This package depends on:
|
|
511
|
+
- `@owlmeans/route` - For route management
|
|
512
|
+
- `@owlmeans/context` - For contextual module support
|
|
513
|
+
- `@owlmeans/auth` - For authentication integration
|
|
514
|
+
- `ajv` - For JSON schema validation using AJV format, providing consistent validation across fullstack applications
|
|
515
|
+
|
|
516
|
+
## Related Packages
|
|
517
|
+
|
|
518
|
+
- [`@owlmeans/client-module`](../client-module) - Client-side extension with API calls and URL generation
|
|
519
|
+
- [`@owlmeans/server-module`](../server-module) - Server-side module implementation
|
|
520
|
+
- [`@owlmeans/route`](../route) - Core routing functionality
|
|
521
|
+
- [`@owlmeans/context`](../context) - Context management system
|
package/build/.gitkeep
ADDED
|
File without changes
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"consts.d.ts","sourceRoot":"","sources":["../src/consts.ts"],"names":[],"mappings":"AACA,oBAAY,aAAa;IACvB,EAAE,OAAO;IACT,QAAQ,aAAa;IACrB,OAAO,YAAY;IACnB,QAAQ,aAAa;CACtB"}
|
package/build/consts.js
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export var ModuleOutcome;
|
|
2
|
+
(function (ModuleOutcome) {
|
|
3
|
+
ModuleOutcome["Ok"] = "ok";
|
|
4
|
+
ModuleOutcome["Accepted"] = "accepted";
|
|
5
|
+
ModuleOutcome["Created"] = "created";
|
|
6
|
+
ModuleOutcome["Finished"] = "finished";
|
|
7
|
+
})(ModuleOutcome || (ModuleOutcome = {}));
|
|
8
|
+
//# sourceMappingURL=consts.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"consts.js","sourceRoot":"","sources":["../src/consts.ts"],"names":[],"mappings":"AACA,MAAM,CAAN,IAAY,aAKX;AALD,WAAY,aAAa;IACvB,0BAAS,CAAA;IACT,sCAAqB,CAAA;IACrB,oCAAmB,CAAA;IACnB,sCAAqB,CAAA;AACvB,CAAC,EALW,aAAa,KAAb,aAAa,QAKxB"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { JSONSchemaType } from 'ajv';
|
|
2
|
+
import type { Filter } from './types.js';
|
|
3
|
+
export declare const body: <T>(schema: JSONSchemaType<T>, filter?: Filter) => Filter;
|
|
4
|
+
export declare const query: <T>(schema: JSONSchemaType<T>, filter?: Filter) => Filter;
|
|
5
|
+
export declare const params: <T>(schema: JSONSchemaType<T>, filter?: Filter) => Filter;
|
|
6
|
+
export declare const response: <T>(schema: JSONSchemaType<T>, code?: number, filter?: Filter) => Filter;
|
|
7
|
+
export declare const headers: <T>(schema: JSONSchemaType<T>, filter?: Filter) => Filter;
|
|
8
|
+
//# sourceMappingURL=filter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"filter.d.ts","sourceRoot":"","sources":["../src/filter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,KAAK,CAAA;AACzC,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,YAAY,CAAA;AAExC,eAAO,MAAM,IAAI,GAAI,CAAC,UAAU,cAAc,CAAC,CAAC,CAAC,WAAW,MAAM,KAAG,MAEpE,CAAA;AAED,eAAO,MAAM,KAAK,GAAI,CAAC,UAAU,cAAc,CAAC,CAAC,CAAC,WAAW,MAAM,KAAG,MAErE,CAAA;AAED,eAAO,MAAM,MAAM,GAAI,CAAC,UAAU,cAAc,CAAC,CAAC,CAAC,WAAW,MAAM,KAAG,MAEtE,CAAA;AAED,eAAO,MAAM,QAAQ,GAAI,CAAC,UAAU,cAAc,CAAC,CAAC,CAAC,SAAS,MAAM,WAAW,MAAM,KAAG,MAMvF,CAAA;AAED,eAAO,MAAM,OAAO,GAAI,CAAC,UAAU,cAAc,CAAC,CAAC,CAAC,WAAW,MAAM,KAAG,MAEvE,CAAA"}
|
package/build/filter.js
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export const body = (schema, filter) => {
|
|
2
|
+
return { ...filter, body: schema };
|
|
3
|
+
};
|
|
4
|
+
export const query = (schema, filter) => {
|
|
5
|
+
return { ...filter, query: schema };
|
|
6
|
+
};
|
|
7
|
+
export const params = (schema, filter) => {
|
|
8
|
+
return { ...filter, params: schema };
|
|
9
|
+
};
|
|
10
|
+
export const response = (schema, code, filter) => {
|
|
11
|
+
let _schema = schema;
|
|
12
|
+
if (filter?.response != null && code != null) {
|
|
13
|
+
_schema = { ...filter.response, [code]: schema };
|
|
14
|
+
}
|
|
15
|
+
return { ...filter, response: _schema };
|
|
16
|
+
};
|
|
17
|
+
export const headers = (schema, filter) => {
|
|
18
|
+
return { ...filter, headers: schema };
|
|
19
|
+
};
|
|
20
|
+
//# sourceMappingURL=filter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"filter.js","sourceRoot":"","sources":["../src/filter.ts"],"names":[],"mappings":"AAGA,MAAM,CAAC,MAAM,IAAI,GAAG,CAAI,MAAyB,EAAE,MAAe,EAAU,EAAE;IAC5E,OAAO,EAAE,GAAG,MAAM,EAAE,IAAI,EAAE,MAAa,EAAE,CAAA;AAC3C,CAAC,CAAA;AAED,MAAM,CAAC,MAAM,KAAK,GAAG,CAAI,MAAyB,EAAE,MAAe,EAAU,EAAE;IAC7E,OAAO,EAAE,GAAG,MAAM,EAAE,KAAK,EAAE,MAAa,EAAE,CAAA;AAC5C,CAAC,CAAA;AAED,MAAM,CAAC,MAAM,MAAM,GAAG,CAAI,MAAyB,EAAE,MAAe,EAAU,EAAE;IAC9E,OAAO,EAAE,GAAG,MAAM,EAAE,MAAM,EAAE,MAAa,EAAE,CAAA;AAC7C,CAAC,CAAA;AAED,MAAM,CAAC,MAAM,QAAQ,GAAG,CAAI,MAAyB,EAAE,IAAa,EAAE,MAAe,EAAU,EAAE;IAC/F,IAAI,OAAO,GAAQ,MAAM,CAAA;IACzB,IAAI,MAAM,EAAE,QAAQ,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,EAAE,CAAC;QAC7C,OAAO,GAAG,EAAE,GAAG,MAAM,CAAC,QAAQ,EAAE,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,CAAA;IAClD,CAAC;IACD,OAAO,EAAE,GAAG,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAA;AACzC,CAAC,CAAA;AAED,MAAM,CAAC,MAAM,OAAO,GAAG,CAAI,MAAyB,EAAE,MAAe,EAAU,EAAE;IAC/E,OAAO,EAAE,GAAG,MAAM,EAAE,OAAO,EAAE,MAAa,EAAE,CAAA;AAC9C,CAAC,CAAA"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { Filter, CommonModuleOptions, AbstractResponse, CommonModule } from './types.js';
|
|
2
|
+
export declare const filter: (filter: Filter, opts?: CommonModuleOptions) => CommonModuleOptions;
|
|
3
|
+
export declare const guard: (guard: string, opts?: CommonModuleOptions) => CommonModuleOptions;
|
|
4
|
+
export declare const gate: (gate: string, params: string | string[], opts?: CommonModuleOptions) => CommonModuleOptions;
|
|
5
|
+
export declare const provideResponse: <T>(originalResponse?: unknown) => AbstractResponse<T>;
|
|
6
|
+
export declare const clone: <M extends CommonModule>(modules: M[], from: string, to: string, service: string) => void;
|
|
7
|
+
//# sourceMappingURL=helper.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"helper.d.ts","sourceRoot":"","sources":["../src/helper.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AAGxF,eAAO,MAAM,MAAM,WAAY,MAAM,SAAS,mBAAmB,KAAG,mBAA4C,CAAA;AAEhH,eAAO,MAAM,KAAK,UAAW,MAAM,SAAS,mBAAmB,KAAG,mBACK,CAAA;AAEvE,eAAO,MAAM,IAAI,SAAU,MAAM,UAAU,MAAM,GAAG,MAAM,EAAE,SAAS,mBAAmB,KAAG,mBAClD,CAAA;AAEzC,eAAO,MAAM,eAAe,GAAI,CAAC,qBAAqB,OAAO,KAAG,gBAAgB,CAAC,CAAC,CAejF,CAAA;AAED,eAAO,MAAM,KAAK,GAAI,CAAC,SAAS,YAAY,WAAW,CAAC,EAAE,QAAQ,MAAM,MAAM,MAAM,WAAW,MAAM,SAOpG,CAAA"}
|
package/build/helper.js
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { route } from '@owlmeans/route';
|
|
2
|
+
import { module } from './module.js';
|
|
3
|
+
export const filter = (filter, opts) => ({ filter, ...opts });
|
|
4
|
+
export const guard = (guard, opts) => ({ ...opts, guards: [...new Set([guard, ...(opts?.guards ?? [])])] });
|
|
5
|
+
export const gate = (gate, params, opts) => ({ ...opts, gate, gateParams: params });
|
|
6
|
+
export const provideResponse = (originalResponse) => {
|
|
7
|
+
const hanlder = {
|
|
8
|
+
responseProvider: originalResponse,
|
|
9
|
+
resolve: (value, outcome) => {
|
|
10
|
+
hanlder.value = value;
|
|
11
|
+
hanlder.outcome = outcome;
|
|
12
|
+
},
|
|
13
|
+
reject: (error) => {
|
|
14
|
+
hanlder.error = error;
|
|
15
|
+
}
|
|
16
|
+
};
|
|
17
|
+
return hanlder;
|
|
18
|
+
};
|
|
19
|
+
export const clone = (modules, from, to, service) => {
|
|
20
|
+
const source = modules.find(m => m.alias === from);
|
|
21
|
+
if (source?.route.route != null) {
|
|
22
|
+
const _route = { ...source.route.route, service, resolved: false, alias: to };
|
|
23
|
+
modules.push(module(route(to, _route.path, _route)));
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
//# sourceMappingURL=helper.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"helper.js","sourceRoot":"","sources":["../src/helper.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,iBAAiB,CAAA;AAEvC,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AAEpC,MAAM,CAAC,MAAM,MAAM,GAAG,CAAC,MAAc,EAAE,IAA0B,EAAuB,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,CAAC,CAAA;AAEhH,MAAM,CAAC,MAAM,KAAK,GAAG,CAAC,KAAa,EAAE,IAA0B,EAAuB,EAAE,CACtF,CAAC,EAAE,GAAG,IAAI,EAAE,MAAM,EAAE,CAAC,GAAG,IAAI,GAAG,CAAC,CAAC,KAAK,EAAE,GAAG,CAAC,IAAI,EAAE,MAAM,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAA;AAEvE,MAAM,CAAC,MAAM,IAAI,GAAG,CAAC,IAAY,EAAE,MAAyB,EAAE,IAA0B,EAAuB,EAAE,CAC/G,CAAC,EAAE,GAAG,IAAI,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,EAAE,CAAC,CAAA;AAEzC,MAAM,CAAC,MAAM,eAAe,GAAG,CAAI,gBAA0B,EAAuB,EAAE;IACpF,MAAM,OAAO,GAAwB;QACnC,gBAAgB,EAAE,gBAAgB;QAElC,OAAO,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,EAAE;YAC1B,OAAO,CAAC,KAAK,GAAG,KAAK,CAAA;YACrB,OAAO,CAAC,OAAO,GAAG,OAAO,CAAA;QAC3B,CAAC;QAED,MAAM,EAAE,CAAC,KAAK,EAAE,EAAE;YAChB,OAAO,CAAC,KAAK,GAAG,KAAK,CAAA;QACvB,CAAC;KACF,CAAA;IAED,OAAO,OAAO,CAAA;AAChB,CAAC,CAAA;AAED,MAAM,CAAC,MAAM,KAAK,GAAG,CAAyB,OAAY,EAAE,IAAY,EAAE,EAAU,EAAE,OAAe,EAAE,EAAE;IACvG,MAAM,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,KAAK,IAAI,CAAC,CAAA;IAElD,IAAI,MAAM,EAAE,KAAK,CAAC,KAAK,IAAI,IAAI,EAAE,CAAC;QAChC,MAAM,MAAM,GAAG,EAAE,GAAG,MAAM,CAAC,KAAK,CAAC,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,EAAE,CAAA;QAC7E,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,EAAE,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,CAAM,CAAC,CAAA;IAC3D,CAAC;AACH,CAAC,CAAA"}
|
package/build/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,mBAAmB,YAAY,CAAA;AAC/B,cAAc,aAAa,CAAA;AAC3B,cAAc,aAAa,CAAA;AAC3B,cAAc,aAAa,CAAA;AAC3B,cAAc,aAAa,CAAA"}
|
package/build/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,cAAc,aAAa,CAAA;AAC3B,cAAc,aAAa,CAAA;AAC3B,cAAc,aAAa,CAAA;AAC3B,cAAc,aAAa,CAAA"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { CommonModule } from './types.js';
|
|
2
|
+
import type { CreateModuleSignature } from './utils/types.js';
|
|
3
|
+
export declare const module: CreateModuleSignature<CommonModule>;
|
|
4
|
+
export declare const parent: <T extends CommonModule | CommonModule[]>(module: T, aliasOrParent: string, _parent?: string) => T;
|
|
5
|
+
//# sourceMappingURL=module.d.ts.map
|