@owlmeans/api-config-client 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 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,313 @@
1
+ # @owlmeans/api-config-client
2
+
3
+ The **@owlmeans/api-config-client** package provides client-side functionality for fetching and applying API configuration from OwlMeans Common Libraries servers, designed for fullstack microservices and microclients development with focus on security and proper authentication and authorization.
4
+
5
+ ## Purpose
6
+
7
+ This package serves as the client-side component of the OwlMeans configuration system that:
8
+
9
+ - **Fetches configuration from servers** using the API config module system
10
+ - **Applies configuration dynamically** to client contexts during initialization
11
+ - **Integrates with client modules** for seamless API communication
12
+ - **Supports multi-service architecture** with configurable primary hosts
13
+ - **Provides middleware integration** for automatic configuration loading
14
+
15
+ ## Core Concepts
16
+
17
+ ### API Configuration Module
18
+ The package elevates the common API config module to a client module, enabling it to make HTTP requests to fetch configuration from servers.
19
+
20
+ ### Configuration Middleware
21
+ Provides middleware that automatically fetches and merges server configuration during client context initialization, ensuring the client has the latest configuration before becoming operational.
22
+
23
+ ### Primary Host Configuration
24
+ Supports configuration of primary hosts and ports for API communication, allowing clients to know where to fetch their configuration from.
25
+
26
+ ## API Reference
27
+
28
+ ### Modules
29
+
30
+ #### `modules`
31
+
32
+ Exported client modules array containing the elevated API config module.
33
+
34
+ ```typescript
35
+ import { modules } from '@owlmeans/api-config-client'
36
+
37
+ // Register all client API config modules
38
+ context.registerModules(modules)
39
+ ```
40
+
41
+ ### Middleware
42
+
43
+ #### `apiConfigMiddleware`
44
+
45
+ Middleware that fetches configuration from the API config endpoint during context loading.
46
+
47
+ ```typescript
48
+ import { apiConfigMiddleware } from '@owlmeans/api-config-client'
49
+
50
+ context.registerMiddleware(apiConfigMiddleware)
51
+ ```
52
+
53
+ **Properties:**
54
+ - `type`: `MiddlewareType.Context`
55
+ - `stage`: `MiddlewareStage.Loading`
56
+
57
+ **Behavior:**
58
+ - Executes during the Loading stage of context initialization
59
+ - Checks if `primaryHost` is configured in the client config
60
+ - Fetches configuration from the API config module endpoint
61
+ - Merges received configuration with the existing client configuration
62
+ - Handles errors gracefully by logging them without breaking initialization
63
+
64
+ ### Dependencies
65
+
66
+ The package depends on several OwlMeans Common packages:
67
+ - `@owlmeans/api-config` - Common API config module definitions
68
+ - `@owlmeans/client-context` - Client-side context management
69
+ - `@owlmeans/client-module` - Client-side module system
70
+ - `@owlmeans/context` - Core context functionality
71
+
72
+ ## Usage Examples
73
+
74
+ ### Basic Integration
75
+
76
+ ```typescript
77
+ import { makeClientContext, makeClientConfig } from '@owlmeans/client-context'
78
+ import { modules, apiConfigMiddleware } from '@owlmeans/api-config-client'
79
+ import { AppType } from '@owlmeans/context'
80
+
81
+ // Create client configuration with primary host
82
+ const config = makeClientConfig(AppType.Frontend, 'web-client', {
83
+ primaryHost: 'api.myapp.com',
84
+ primaryPort: 443
85
+ })
86
+
87
+ // Create and configure context
88
+ const context = makeClientContext(config)
89
+
90
+ // Register API config middleware
91
+ context.registerMiddleware(apiConfigMiddleware)
92
+
93
+ // Register API config modules
94
+ context.registerModules(modules)
95
+
96
+ // Configure and initialize - configuration will be fetched automatically
97
+ context.configure()
98
+ await context.init()
99
+
100
+ // Context now has merged configuration from server
101
+ console.log('Final config:', await context.config)
102
+ ```
103
+
104
+ ### Manual Configuration Fetching
105
+
106
+ ```typescript
107
+ import { modules } from '@owlmeans/api-config-client'
108
+ import { API_CONFIG } from '@owlmeans/api-config'
109
+
110
+ // Get the API config module
111
+ const configModule = context.module(API_CONFIG)
112
+
113
+ // Manually fetch configuration
114
+ try {
115
+ const [serverConfig] = await configModule.call()
116
+ console.log('Server configuration:', serverConfig)
117
+ } catch (error) {
118
+ console.error('Failed to fetch configuration:', error)
119
+ }
120
+ ```
121
+
122
+ ### Advanced Configuration
123
+
124
+ ```typescript
125
+ import { makeClientContext, makeClientConfig } from '@owlmeans/client-context'
126
+ import { modules, apiConfigMiddleware } from '@owlmeans/api-config-client'
127
+ import { AppType } from '@owlmeans/context'
128
+
129
+ const config = makeClientConfig(AppType.Frontend, 'mobile-client', {
130
+ primaryHost: 'api.example.com',
131
+ primaryPort: 8080,
132
+ // Additional client configuration
133
+ debug: { all: true },
134
+ services: {
135
+ 'user-service': {
136
+ host: 'users.example.com',
137
+ port: 8081
138
+ }
139
+ }
140
+ })
141
+
142
+ const context = makeClientContext(config)
143
+
144
+ // Register middleware first
145
+ context.registerMiddleware(apiConfigMiddleware)
146
+
147
+ // Register modules
148
+ context.registerModules(modules)
149
+
150
+ // Add custom middleware to run after config is loaded
151
+ context.registerMiddleware({
152
+ type: MiddlewareType.Context,
153
+ stage: MiddlewareStage.Ready,
154
+ apply: async (ctx) => {
155
+ console.log('Configuration loaded and merged')
156
+ const finalConfig = await ctx.config
157
+ console.log('Available services:', Object.keys(finalConfig.services || {}))
158
+ }
159
+ })
160
+
161
+ await context.configure().init()
162
+ ```
163
+
164
+ ### Error Handling
165
+
166
+ ```typescript
167
+ import { modules, apiConfigMiddleware } from '@owlmeans/api-config-client'
168
+
169
+ // The middleware handles errors gracefully
170
+ context.registerMiddleware(apiConfigMiddleware)
171
+
172
+ // You can also add custom error handling
173
+ context.registerMiddleware({
174
+ type: MiddlewareType.Context,
175
+ stage: MiddlewareStage.Loading,
176
+ apply: async (ctx) => {
177
+ const clientCtx = ctx as ClientContext
178
+ if (clientCtx.cfg.primaryHost) {
179
+ try {
180
+ const configModule = clientCtx.module(API_CONFIG)
181
+ const [config] = await configModule.call()
182
+ console.log('Successfully fetched config')
183
+ } catch (error) {
184
+ console.warn('Config fetch failed, using defaults:', error.message)
185
+ // Implement fallback configuration logic
186
+ }
187
+ }
188
+ }
189
+ })
190
+ ```
191
+
192
+ ### Multiple Service Configuration
193
+
194
+ ```typescript
195
+ // Client can fetch configuration that includes multiple service endpoints
196
+ const config = makeClientConfig(AppType.Frontend, 'dashboard-client', {
197
+ primaryHost: 'config.myapp.com'
198
+ })
199
+
200
+ const context = makeClientContext(config)
201
+ context.registerMiddleware(apiConfigMiddleware)
202
+ context.registerModules(modules)
203
+
204
+ await context.configure().init()
205
+
206
+ // After initialization, context will have configuration for all services
207
+ const finalConfig = await context.config
208
+ console.log('Auth service:', finalConfig.services?.auth)
209
+ console.log('User service:', finalConfig.services?.users)
210
+ console.log('Payment service:', finalConfig.services?.payments)
211
+ ```
212
+
213
+ ## Integration Patterns
214
+
215
+ ### With React Applications
216
+
217
+ ```typescript
218
+ import React from 'react'
219
+ import { ClientContext } from '@owlmeans/client-context'
220
+ import { modules, apiConfigMiddleware } from '@owlmeans/api-config-client'
221
+
222
+ const initializeApp = async () => {
223
+ const config = makeClientConfig(AppType.Frontend, 'react-app', {
224
+ primaryHost: process.env.REACT_APP_API_HOST
225
+ })
226
+
227
+ const context = makeClientContext(config)
228
+ context.registerMiddleware(apiConfigMiddleware)
229
+ context.registerModules(modules)
230
+
231
+ await context.configure().init()
232
+ return context
233
+ }
234
+
235
+ const App: React.FC = () => {
236
+ const [context, setContext] = useState<ClientContext | null>(null)
237
+
238
+ useEffect(() => {
239
+ initializeApp().then(setContext)
240
+ }, [])
241
+
242
+ if (!context) return <div>Loading configuration...</div>
243
+
244
+ return (
245
+ <ClientContext.Provider value={context}>
246
+ {/* Your app components */}
247
+ </ClientContext.Provider>
248
+ )
249
+ }
250
+ ```
251
+
252
+ ### With Service Workers
253
+
254
+ ```typescript
255
+ // In service worker or background process
256
+ import { modules, apiConfigMiddleware } from '@owlmeans/api-config-client'
257
+
258
+ const setupBackgroundSync = async () => {
259
+ const config = makeClientConfig(AppType.Frontend, 'sw-client', {
260
+ primaryHost: self.registration.scope + 'api'
261
+ })
262
+
263
+ const context = makeClientContext(config)
264
+ context.registerMiddleware(apiConfigMiddleware)
265
+ context.registerModules(modules)
266
+
267
+ await context.configure().init()
268
+
269
+ // Now context has all service configurations for background operations
270
+ return context
271
+ }
272
+ ```
273
+
274
+ ## Configuration Flow
275
+
276
+ 1. **Context Creation** - Create client context with `primaryHost` configuration
277
+ 2. **Middleware Registration** - Register `apiConfigMiddleware` to handle automatic config fetching
278
+ 3. **Module Registration** - Register API config modules for communication
279
+ 4. **Context Initialization** - During `init()`, middleware fetches server configuration
280
+ 5. **Configuration Merge** - Server configuration is merged with local client configuration
281
+ 6. **Ready State** - Context becomes ready with complete configuration
282
+
283
+ ## Error Handling
284
+
285
+ The package handles various error scenarios:
286
+
287
+ - **Network failures** - Logged but don't prevent initialization
288
+ - **Invalid responses** - Gracefully handled with fallback to existing configuration
289
+ - **Missing primary host** - Middleware skips execution if no primary host configured
290
+ - **Module call failures** - Errors are caught and logged without breaking the initialization flow
291
+
292
+ ## Best Practices
293
+
294
+ 1. **Set primary host early** - Configure `primaryHost` in your client configuration
295
+ 2. **Register middleware first** - Ensure middleware is registered before modules
296
+ 3. **Handle initialization** - Always await context initialization before use
297
+ 4. **Monitor errors** - Log and monitor configuration fetch failures
298
+ 5. **Implement fallbacks** - Have default configurations ready for offline scenarios
299
+ 6. **Secure communication** - Use HTTPS for production API config endpoints
300
+
301
+ ## Security Considerations
302
+
303
+ - **Validate endpoints** - Ensure primary host is a trusted server
304
+ - **Handle secrets carefully** - Configuration may contain sensitive service information
305
+ - **Monitor traffic** - Log configuration requests for security auditing
306
+ - **Use authentication** - Combine with auth modules for secured configuration access
307
+
308
+ ## Related Packages
309
+
310
+ - [`@owlmeans/api-config`](../api-config) - Common API config module definitions
311
+ - [`@owlmeans/api-config-server`](../api-config-server) - Server-side API config implementation
312
+ - [`@owlmeans/client-context`](../client-context) - Client-side context management
313
+ - [`@owlmeans/client-module`](../client-module) - Client-side module system
package/build/.gitkeep ADDED
File without changes
@@ -0,0 +1,3 @@
1
+ export * from './middleware.js';
2
+ export * from './modules.js';
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,cAAc,iBAAiB,CAAA;AAC/B,cAAc,cAAc,CAAA"}
package/build/index.js ADDED
@@ -0,0 +1,3 @@
1
+ export * from './middleware.js';
2
+ export * from './modules.js';
3
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,cAAc,iBAAiB,CAAA;AAC/B,cAAc,cAAc,CAAA"}
@@ -0,0 +1,3 @@
1
+ import type { Middleware } from '@owlmeans/context';
2
+ export declare const apiConfigMiddleware: Middleware;
3
+ //# sourceMappingURL=middleware.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"middleware.d.ts","sourceRoot":"","sources":["../src/middleware.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAA;AASnD,eAAO,MAAM,mBAAmB,EAAE,UAsBjC,CAAA"}
@@ -0,0 +1,26 @@
1
+ import { MiddlewareType, MiddlewareStage, assertContext } from '@owlmeans/context';
2
+ import { API_CONFIG } from '@owlmeans/api-config';
3
+ import { mergeConfig } from '@owlmeans/config';
4
+ export const apiConfigMiddleware = {
5
+ type: MiddlewareType.Context,
6
+ stage: MiddlewareStage.Loading,
7
+ apply: async (ctx) => {
8
+ const context = assertContext(ctx, 'api-config-middleware');
9
+ const module = context.module(API_CONFIG);
10
+ if (context.cfg.primaryHost != null) {
11
+ module.route.route.host = context.cfg.primaryHost;
12
+ if (context.cfg.primaryPort != null) {
13
+ module.route.route.port = context.cfg.primaryPort;
14
+ }
15
+ try {
16
+ const [config] = await module.call();
17
+ const target = context.cfg;
18
+ mergeConfig(target, config);
19
+ }
20
+ catch (e) {
21
+ console.error(e);
22
+ }
23
+ }
24
+ }
25
+ };
26
+ //# sourceMappingURL=middleware.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"middleware.js","sourceRoot":"","sources":["../src/middleware.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAA;AAGlF,OAAO,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAA;AACjD,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAA;AAI9C,MAAM,CAAC,MAAM,mBAAmB,GAAe;IAC7C,IAAI,EAAE,cAAc,CAAC,OAAO;IAC5B,KAAK,EAAE,eAAe,CAAC,OAAO;IAC9B,KAAK,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE;QACnB,MAAM,OAAO,GAAG,aAAa,CAC3B,GAAU,EAAE,uBAAuB,CACpC,CAAA;QACD,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAA0B,UAAU,CAAC,CAAA;QAClE,IAAI,OAAO,CAAC,GAAG,CAAC,WAAW,IAAI,IAAI,EAAE,CAAC;YACpC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,WAAW,CAAA;YACjD,IAAI,OAAO,CAAC,GAAG,CAAC,WAAW,IAAI,IAAI,EAAE,CAAC;gBACpC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,WAAW,CAAA;YACnD,CAAC;YACD,IAAI,CAAC;gBACH,MAAM,CAAC,MAAM,CAAC,GAAG,MAAM,MAAM,CAAC,IAAI,EAAE,CAAA;gBACpC,MAAM,MAAM,GAAiB,OAAO,CAAC,GAA8B,CAAA;gBACnE,WAAW,CAAC,MAAM,EAAE,MAAsB,CAAC,CAAA;YAC7C,CAAC;YAAC,OAAO,CAAC,EAAE,CAAC;gBACX,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAA;YAClB,CAAC;QACH,CAAC;IACH,CAAC;CACF,CAAA"}
@@ -0,0 +1,2 @@
1
+ export declare const modules: import("@owlmeans/module").CommonModule[];
2
+ //# sourceMappingURL=modules.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"modules.d.ts","sourceRoot":"","sources":["../src/modules.ts"],"names":[],"mappings":"AAMA,eAAO,MAAM,OAAO,2CAAS,CAAA"}
@@ -0,0 +1,5 @@
1
+ import { modules as config, API_CONFIG } from '@owlmeans/api-config';
2
+ import { elevate } from '@owlmeans/client-module';
3
+ elevate(config, API_CONFIG);
4
+ export const modules = config;
5
+ //# sourceMappingURL=modules.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"modules.js","sourceRoot":"","sources":["../src/modules.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,IAAI,MAAM,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAA;AACpE,OAAO,EAAE,OAAO,EAAE,MAAM,yBAAyB,CAAA;AAEjD,OAAO,CAAC,MAAM,EAAE,UAAU,CAAC,CAAA;AAE3B,MAAM,CAAC,MAAM,OAAO,GAAG,MAAM,CAAA"}
package/package.json ADDED
@@ -0,0 +1,36 @@
1
+ {
2
+ "name": "@owlmeans/api-config-client",
3
+ "version": "0.1.0",
4
+ "type": "module",
5
+ "scripts": {
6
+ "build": "tsc -b",
7
+ "dev": "sleep 12 && nodemon -e ts,tsx,json --watch src --exec \"tsc -p ./tsconfig.json\"",
8
+ "watch": "tsc -b -w --preserveWatchOutput --pretty"
9
+ },
10
+ "main": "build/index.js",
11
+ "module": "build/index.js",
12
+ "types": "build/index.d.ts",
13
+ "exports": {
14
+ ".": {
15
+ "import": "./build/index.js",
16
+ "require": "./build/index.js",
17
+ "default": "./build/index.js",
18
+ "module": "./build/index.js",
19
+ "types": "./build/index.d.ts"
20
+ }
21
+ },
22
+ "dependencies": {
23
+ "@owlmeans/api-config": "^0.1.0",
24
+ "@owlmeans/client-context": "^0.1.0",
25
+ "@owlmeans/client-module": "^0.1.0",
26
+ "@owlmeans/context": "^0.1.0"
27
+ },
28
+ "devDependencies": {
29
+ "nodemon": "^3.1.7",
30
+ "typescript": "^5.6.3"
31
+ },
32
+ "private": false,
33
+ "publishConfig": {
34
+ "access": "public"
35
+ }
36
+ }
package/src/index.ts ADDED
@@ -0,0 +1,3 @@
1
+
2
+ export * from './middleware.js'
3
+ export * from './modules.js'
@@ -0,0 +1,32 @@
1
+ import type { Middleware } from '@owlmeans/context'
2
+ import { MiddlewareType, MiddlewareStage, assertContext } from '@owlmeans/context'
3
+ import type { ClientModule } from '@owlmeans/client-module'
4
+ import type { ApiConfig } from '@owlmeans/api-config'
5
+ import { API_CONFIG } from '@owlmeans/api-config'
6
+ import { mergeConfig } from '@owlmeans/config'
7
+ import type { CommonConfig } from '@owlmeans/config'
8
+ import type { ClientContext, ClientConfig } from '@owlmeans/client-context'
9
+
10
+ export const apiConfigMiddleware: Middleware = {
11
+ type: MiddlewareType.Context,
12
+ stage: MiddlewareStage.Loading,
13
+ apply: async (ctx) => {
14
+ const context = assertContext<ClientConfig, ClientContext<ClientConfig>>(
15
+ ctx as any, 'api-config-middleware'
16
+ )
17
+ const module = context.module<ClientModule<ApiConfig>>(API_CONFIG)
18
+ if (context.cfg.primaryHost != null) {
19
+ module.route.route.host = context.cfg.primaryHost
20
+ if (context.cfg.primaryPort != null) {
21
+ module.route.route.port = context.cfg.primaryPort
22
+ }
23
+ try {
24
+ const [config] = await module.call()
25
+ const target: CommonConfig = context.cfg as unknown as CommonConfig
26
+ mergeConfig(target, config as CommonConfig)
27
+ } catch (e) {
28
+ console.error(e)
29
+ }
30
+ }
31
+ }
32
+ }
package/src/modules.ts ADDED
@@ -0,0 +1,7 @@
1
+
2
+ import { modules as config, API_CONFIG } from '@owlmeans/api-config'
3
+ import { elevate } from '@owlmeans/client-module'
4
+
5
+ elevate(config, API_CONFIG)
6
+
7
+ export const modules = config
package/tsconfig.json ADDED
@@ -0,0 +1,14 @@
1
+ {
2
+ "extends": [
3
+ "../tsconfig.default.json"
4
+ ],
5
+ "compilerOptions": {
6
+ "rootDir": "./src/", /* Specify the root folder within your source files. */
7
+ "outDir": "./build/", /* Specify an output folder for all emitted files. */
8
+ },
9
+ "exclude": [
10
+ "./dist/**/*",
11
+ "./build/**/*",
12
+ "./*.ts"
13
+ ]
14
+ }
@@ -0,0 +1 @@
1
+ {"root":["./src/index.ts","./src/middleware.ts","./src/modules.ts"],"version":"5.6.3"}