@rws-framework/client 2.22.1 → 2.23.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 +160 -872
- package/builder/webpack/loaders/rws_fast_ts_loader.js +14 -21
- package/cfg/build_steps/webpack/_loaders.js +136 -7
- package/package.json +1 -1
- package/src/components/_component.ts +28 -72
- package/src/components/_css_injection.ts +187 -0
package/README.md
CHANGED
|
@@ -1,68 +1,61 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @rws-framework/client
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
This package provides the core client-side framework for Realtime Web Suit (RWS), enabling modular, asynchronous web components, state management, and integration with backend services. It is located in `.dev/client`.
|
|
4
4
|
|
|
5
5
|
## Table of Contents
|
|
6
6
|
|
|
7
7
|
1. [Overview](#overview)
|
|
8
8
|
2. [Getting Started](#getting-started)
|
|
9
|
-
3. [Key
|
|
9
|
+
3. [Key Concepts](#key-concepts)
|
|
10
10
|
4. [Component Initialization](#component-initialization)
|
|
11
|
-
5. [
|
|
12
|
-
6. [Frontend
|
|
11
|
+
5. [Dependency Injection](#dependency-injection)
|
|
12
|
+
6. [Frontend Routes](#frontend-routes)
|
|
13
13
|
7. [Backend Imports](#backend-imports)
|
|
14
14
|
8. [Utilizing APIService](#utilizing-apiservice)
|
|
15
15
|
9. [Notifier](#notifier)
|
|
16
16
|
10. [Service Worker](#service-worker)
|
|
17
17
|
11. [Example: WebChat Component](#example-webchat-component)
|
|
18
18
|
12. [Other configs](#other-configs)
|
|
19
|
-
13. [
|
|
20
|
-
14. [
|
|
19
|
+
13. [Plugin System](#plugin-system)
|
|
20
|
+
14. [Nest Interconnectors](#nest-interconnectors)
|
|
21
|
+
15. [Styles Injection](#styles-injection)
|
|
22
|
+
16. [Links](#links)
|
|
21
23
|
|
|
22
24
|
## Overview
|
|
23
25
|
|
|
24
|
-
|
|
26
|
+
`@rws-framework/client` is the heart of the RWS frontend framework. It manages configuration, plugin management, service registration, application lifecycle, and provides the base for all RWSView components. It is designed for modular, fullstack-oriented web applications using the RWS/FAST paradigm.
|
|
25
27
|
|
|
26
|
-
|
|
28
|
+
### Main Features
|
|
29
|
+
- **RWSClient**: Main entry point for the frontend application. Handles configuration, plugin management, service registration, and application lifecycle.
|
|
30
|
+
- **Dependency Injection**: Uses a DI container for services and components.
|
|
31
|
+
- **Component Registration**: Supports modular component definition and registration.
|
|
32
|
+
- **Plugin System**: Add plugins for routing, websockets, and more.
|
|
33
|
+
- **Notifier**: Customizable notification system for UI messages.
|
|
34
|
+
- **Service Worker Integration**: Pushes config and user data to service workers.
|
|
35
|
+
- **User and Config Management**: Handles user state and application configuration.
|
|
36
|
+
- **API Service**: Integrates with backend API routes and authentication.
|
|
27
37
|
|
|
28
|
-
|
|
38
|
+
## Getting Started
|
|
29
39
|
|
|
30
|
-
|
|
40
|
+
Install dependencies and initialize the project:
|
|
31
41
|
|
|
32
42
|
```bash
|
|
33
43
|
yarn
|
|
34
|
-
```
|
|
35
|
-
|
|
36
|
-
Initiate cfg files and webpack build:
|
|
37
|
-
```bash
|
|
38
44
|
rws-client init
|
|
45
|
+
yarn build # or yarn watch for dev
|
|
46
|
+
yarn server # to just start server
|
|
39
47
|
```
|
|
40
48
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
```bash
|
|
44
|
-
yarn build
|
|
45
|
-
```
|
|
46
|
-
or to watch for dev
|
|
47
|
-
|
|
48
|
-
```bash
|
|
49
|
-
yarn watch
|
|
50
|
-
```
|
|
51
|
-
or to just start server
|
|
52
|
-
```bash
|
|
53
|
-
yarn server
|
|
54
|
-
```
|
|
55
|
-
|
|
56
|
-
then start engine in the site javascript (can be inline):
|
|
49
|
+
Start the engine in your site JavaScript:
|
|
57
50
|
|
|
58
51
|
```javascript
|
|
59
|
-
window.RWS.client.start(CFG); //
|
|
52
|
+
window.RWS.client.start(CFG); // async function
|
|
60
53
|
```
|
|
61
54
|
|
|
62
|
-
|
|
55
|
+
Or for initial setup on an event:
|
|
63
56
|
|
|
64
57
|
```javascript
|
|
65
|
-
window.RWS.client.setup(CFG).then(() => {
|
|
58
|
+
window.RWS.client.setup(CFG).then(() => {
|
|
66
59
|
$.on('loaded', function(data){
|
|
67
60
|
const optionalNewCfg = { backendRoutes: data.backendRoutes };
|
|
68
61
|
window.RWSClient.start(optionalNewCfg).then();
|
|
@@ -70,11 +63,10 @@ window.RWS.client.setup(CFG).then(() => { // it is async function
|
|
|
70
63
|
});
|
|
71
64
|
```
|
|
72
65
|
|
|
73
|
-
###
|
|
66
|
+
### Default config for RWS
|
|
74
67
|
|
|
75
68
|
```javascript
|
|
76
69
|
const _DEFAULT_CONFIG_VARS = {
|
|
77
|
-
//Build configs
|
|
78
70
|
dev: false,
|
|
79
71
|
hot: false,
|
|
80
72
|
report: false,
|
|
@@ -82,943 +74,239 @@ const _DEFAULT_CONFIG_VARS = {
|
|
|
82
74
|
publicIndex: 'index.html',
|
|
83
75
|
outputFileName: 'client.rws.js',
|
|
84
76
|
outputDir: process.cwd() + '/build',
|
|
85
|
-
//Frontend RWS client configs
|
|
86
77
|
backendUrl: null,
|
|
87
78
|
wsUrl: null,
|
|
88
79
|
partedDirUrlPrefix: '/lib/rws',
|
|
89
80
|
partedPrefix: 'rws',
|
|
90
81
|
pubUrlFilePrefix: '/',
|
|
91
|
-
//Universal configs
|
|
92
|
-
transports: ['websocket'],
|
|
93
82
|
parted: false,
|
|
94
83
|
}
|
|
95
|
-
|
|
96
|
-
```
|
|
97
|
-
|
|
98
|
-
*The options description:*
|
|
99
|
-
|
|
100
|
-
| **Option** | **Description** | **Default** |
|
|
101
|
-
|--------------|-----------------|---------------|
|
|
102
|
-
| backendUrl | Url for backend integration (API calls) | null |
|
|
103
|
-
| wsUrl | Url for backend integration (Websocket calls) | null |
|
|
104
|
-
| backendRoutes | Backend routes object imported from backend node for integration with API calls | null |
|
|
105
|
-
| apiPrefix | Prefix for API calls | / |
|
|
106
|
-
| routes | Routes for frontend routing | {} |
|
|
107
|
-
| transports | Websockets transports method | ['websockets'] |
|
|
108
|
-
| user | User object for backend auth / frontend data source | null |
|
|
109
|
-
| ignoreRWSComponents | Do not declare base RWS components (uploader, progress) | false |
|
|
110
|
-
| pubUrlFilePrefix | the url for accessing files from browser URL | / |
|
|
111
|
-
| pubUrl | the url for accessing public dir from browser URL | / |
|
|
112
|
-
| outputDir | build dir | ./build |
|
|
113
|
-
| outputFileName | output file name | rws.client.js |
|
|
114
|
-
| publicDir | public dir for HTML serving | ./public |
|
|
115
|
-
| tsConfigPath | tsconfig.json path | ./tsconfig.njson |
|
|
116
|
-
| entry | default TS entry for transpilation | ./src/index.ts |
|
|
117
|
-
| parted | "Parted" mode if enabled. "Monolith" if disabled. Parted mode outputs every component as separate js file and asynchronously adds them to browser. Monolith is single file js build. | false |
|
|
118
|
-
| partedPrefix | parted file prefix ([prefix].[cmp name].js) | rws |
|
|
119
|
-
| partedDirUrlPrefix | URL for generated js parted files directory | / |
|
|
120
|
-
| copyAssets | An option for defining structure that will be copied after build | {} |
|
|
121
|
-
|
|
122
|
-
*copyAssets example*
|
|
123
|
-
|
|
124
|
-
```json
|
|
125
|
-
"copyAssets": {
|
|
126
|
-
"./public/js/": [ // target directory
|
|
127
|
-
"./build/", // copy this directory to target
|
|
128
|
-
"./src/styles/compiled/main.css" //copy this file to target
|
|
129
|
-
]
|
|
130
|
-
}
|
|
131
|
-
```
|
|
132
|
-
|
|
133
|
-
### The FRONT config TS interface:
|
|
134
|
-
|
|
135
|
-
```typescript
|
|
136
|
-
interface IRWSConfig {
|
|
137
|
-
defaultLayout?: typeof RWSViewComponent
|
|
138
|
-
backendUrl?: string
|
|
139
|
-
wsUrl?: string
|
|
140
|
-
backendRoutes?: any[]
|
|
141
|
-
apiPrefix?: string
|
|
142
|
-
routes?: IFrontRoutes
|
|
143
|
-
transports?: string[]
|
|
144
|
-
user?: any
|
|
145
|
-
ignoreRWSComponents?: boolean
|
|
146
|
-
pubUrl?: string
|
|
147
|
-
pubUrlFilePrefix?: string
|
|
148
|
-
partedDirUrlPrefix?: string
|
|
149
|
-
dontPushToSW?: boolean
|
|
150
|
-
parted?: boolean
|
|
151
|
-
partedFileDir?: string
|
|
152
|
-
partedPrefix?: string
|
|
153
|
-
routing_enabled?: boolean
|
|
154
|
-
_noLoad?: boolean
|
|
155
|
-
}
|
|
156
|
-
```
|
|
157
|
-
### The FRONT webpack config:
|
|
158
|
-
|
|
159
|
-
```javascript
|
|
160
|
-
const path = require('path');
|
|
161
|
-
|
|
162
|
-
const RWSWebpackWrapper = require('@rws-framework/client/rws.webpack.config');
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
const executionDir = process.cwd();
|
|
166
|
-
|
|
167
|
-
module.exports = RWSWebpackWrapper({
|
|
168
|
-
tsConfigPath: executionDir + '/tsconfig.json',
|
|
169
|
-
entry: `${executionDir}/src/index.ts`,
|
|
170
|
-
publicDir: path.resolve(executionDir, 'public'),
|
|
171
|
-
outputDir: path.resolve(executionDir, 'build'),
|
|
172
|
-
outputFileName: 'jtrainer.client.js'
|
|
173
|
-
});
|
|
174
84
|
```
|
|
175
85
|
|
|
86
|
+
*See the table in the original README for all config options.*
|
|
176
87
|
|
|
177
|
-
|
|
178
|
-
## Key Components
|
|
88
|
+
## Key Concepts
|
|
179
89
|
|
|
180
90
|
### RWSClient
|
|
181
|
-
|
|
182
|
-
`RWS.client` is the heart of the framework, managing configuration and initialization. It sets up routes, backend connections, and other essential framework services.
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
### RoutingService
|
|
186
|
-
|
|
187
|
-
`RoutingService` handles the navigation and routing within your application. It ensures that URL changes reflect the correct component rendering.
|
|
188
|
-
|
|
189
|
-
**Depreciation Notice**
|
|
190
|
-
|
|
191
|
-
***RoutingService will be moved to @rws-framework/browser-router near future***
|
|
192
|
-
|
|
193
|
-
### WSService
|
|
194
|
-
|
|
195
|
-
`WSService` handles Websockets messenging to the backend.
|
|
196
|
-
|
|
197
|
-
**Depreciation Notice**
|
|
198
|
-
***WSService will be moved to @rws-framework/nest-interconnectors in near future***
|
|
199
|
-
|
|
200
|
-
### APIService
|
|
91
|
+
`RWSClient` is the main class, instantiated and managed via DI. It manages configuration, plugins, services, and the application lifecycle.
|
|
201
92
|
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
Implementing the Framework
|
|
205
|
-
|
|
206
|
-
**Main File:**
|
|
207
|
-
|
|
208
|
-
The main file (`index.ts`) is where you initialize the RWSClient. Here, you configure your routes, backend routes, and component initializations.
|
|
209
|
-
|
|
210
|
-
Following is example of full usage of the framework
|
|
93
|
+
#### Example Usage
|
|
211
94
|
|
|
212
95
|
```typescript
|
|
213
|
-
|
|
214
|
-
const theClient = RWSContainer().get(RWSClient);
|
|
215
|
-
|
|
216
|
-
theClient.addRoutes(frontendRoutes);
|
|
217
|
-
theClient.setBackendRoutes(backendRoutes());
|
|
218
|
-
|
|
219
|
-
theClient.enableRouting();
|
|
220
|
-
|
|
221
|
-
theClient.onInit(async () => {
|
|
222
|
-
|
|
223
|
-
// For single file output:
|
|
224
|
-
initComponents(theClient.appConfig.get('parted')); // start components for monolith mode
|
|
225
|
-
theClient.defineComponents(); // start RWS conponents
|
|
226
|
-
|
|
227
|
-
//custom outside components registering
|
|
228
|
-
provideFASTDesignSystem()
|
|
229
|
-
.register(fastButton())
|
|
230
|
-
.register(fastTab())
|
|
231
|
-
.register(fastSlider())
|
|
232
|
-
.register(fastSelect())
|
|
233
|
-
.register(fastDivider())
|
|
234
|
-
.register(fastMenu())
|
|
235
|
-
.register(fastMenuItem())
|
|
236
|
-
;
|
|
237
|
-
|
|
238
|
-
// Service worker code
|
|
239
|
-
// const swFilePath: string = `${theClient.appConfig.get('pubUrl')}/service_worker.js`;
|
|
240
|
-
|
|
241
|
-
// await theClient.swService.registerServiceWorker();
|
|
242
|
-
|
|
243
|
-
//if(theClient.getUser()){
|
|
244
|
-
// theClient.pushUserToServiceWorker({...theClient.getUser(), instructor: false});
|
|
245
|
-
//}
|
|
246
|
-
|
|
247
|
-
});
|
|
248
|
-
|
|
249
|
-
theClient.setNotifier((message: string, logType: NotifyLogType, uiType: NotifyUiType = 'notification', onConfirm: (params: any) => void, notifierOptions: any = {}) => {
|
|
250
|
-
switch(uiType){
|
|
251
|
-
case 'notification':
|
|
252
|
-
let notifType = 'success';
|
|
253
|
-
|
|
254
|
-
if(logType === 'error'){
|
|
255
|
-
notifType = 'error';
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
if(logType === 'warning'){
|
|
259
|
-
notifType = 'warning';
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
return alertify.notify(message, notifType, 5, onConfirm);
|
|
263
|
-
|
|
264
|
-
case 'alert':
|
|
265
|
-
const alertObj = alertify.alert('Junction AI Notification', message, onConfirm);
|
|
266
|
-
|
|
267
|
-
Object.keys(notifierOptions).forEach(key => {
|
|
268
|
-
const optionValue = notifierOptions[key];
|
|
269
|
-
|
|
270
|
-
if(key === 'width'){
|
|
271
|
-
|
|
272
|
-
alertObj.elements.dialog.style = `max-width: ${optionValue};`;
|
|
273
|
-
|
|
274
|
-
return;
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
alertObj.set(key, optionValue);
|
|
278
|
-
});
|
|
279
|
-
|
|
280
|
-
alertObj.show();
|
|
281
|
-
|
|
282
|
-
return alertObj;
|
|
283
|
-
case 'silent':
|
|
284
|
-
if(logType == 'warning'){
|
|
285
|
-
console.warn(message);
|
|
286
|
-
}else if(logType == 'error'){
|
|
287
|
-
console.error(message);
|
|
288
|
-
}else{
|
|
289
|
-
console.log(message);
|
|
290
|
-
}
|
|
291
|
-
return;
|
|
292
|
-
}
|
|
293
|
-
});
|
|
294
|
-
|
|
295
|
-
theClient.assignClientToBrowser();
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
initializeApp().catch(console.error);
|
|
299
|
-
```
|
|
96
|
+
import RWSClient, { RWSClientInstance } from '@rws-framework/client';
|
|
300
97
|
|
|
301
|
-
|
|
98
|
+
const theClient: RWSClientInstance = RWSContainer().get(RWSClient);
|
|
302
99
|
|
|
303
|
-
|
|
100
|
+
theClient.addPlugin(RWSBrowserRouter);
|
|
101
|
+
theClient.addPlugin(RWSWebsocketsPlugin, { enabled: true });
|
|
304
102
|
|
|
305
|
-
|
|
103
|
+
theClient.assignClientToBrowser();
|
|
306
104
|
|
|
307
|
-
|
|
105
|
+
theClient.onInit(async () => {
|
|
106
|
+
// Register components, routes, etc.
|
|
107
|
+
});
|
|
308
108
|
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
template.html
|
|
313
|
-
styles/
|
|
314
|
-
layout.scss
|
|
315
|
-
```
|
|
109
|
+
theClient.setNotifier((message, logType) => {
|
|
110
|
+
// Custom notification logic
|
|
111
|
+
});
|
|
316
112
|
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
<chat-convo-models :chosenModel="${x => x.chosenModel}"></chat-convo-models>
|
|
325
|
-
</div>`)}
|
|
326
|
-
<div>
|
|
327
|
-
<h2>${ x => x.chatContext ? x.chatContext.label : 'loading...' }</h2>
|
|
328
|
-
<h3><strong>${ x => x.messageList.length }</strong> messages in total</h3>
|
|
329
|
-
</div>
|
|
330
|
-
<fast-divider></fast-divider>
|
|
331
|
-
</header>
|
|
332
|
-
<section>
|
|
333
|
-
<div class="scroll-area">
|
|
334
|
-
<div class="scroll-content">
|
|
335
|
-
${T.repeat(x => x.messageList, (item, index) => T.html`
|
|
336
|
-
<chat-convo-message :contentReturn="${item => item}" :item="${item => item}"/>
|
|
337
|
-
`)}
|
|
338
|
-
|
|
339
|
-
${T.when(x => !x.messageList.length, (item, index) => T.html`
|
|
340
|
-
<p class="no-chat">No messages</p>
|
|
341
|
-
`)}
|
|
342
|
-
</div>
|
|
343
|
-
</div>
|
|
344
|
-
</section>
|
|
345
|
-
|
|
346
|
-
</div>
|
|
113
|
+
theClient.start({
|
|
114
|
+
backendRoutes,
|
|
115
|
+
backendUrl: process.env.BACKEND_URL,
|
|
116
|
+
wsUrl: process.env.WS_URL,
|
|
117
|
+
hot: true,
|
|
118
|
+
parted: false
|
|
119
|
+
});
|
|
347
120
|
```
|
|
348
121
|
|
|
349
|
-
###
|
|
350
|
-
|
|
351
|
-
Only if parted mode is false.
|
|
122
|
+
### Component Registration
|
|
352
123
|
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
import { RWSIcon } from '../components/rws-icon/component';
|
|
357
|
-
import { LineSplitter } from '../components/line-splitter/component';
|
|
358
|
-
import { WebChat } from '../components/webchat/component';
|
|
359
|
-
|
|
360
|
-
export default (partedMode: boolean = false) => {
|
|
361
|
-
if(!partedMode){
|
|
362
|
-
WebChat;
|
|
363
|
-
LineSplitter;
|
|
364
|
-
DefaultLayout;
|
|
365
|
-
ChatNav;
|
|
366
|
-
RWSIcon;
|
|
367
|
-
}
|
|
368
|
-
};
|
|
369
|
-
```
|
|
124
|
+
- Components must extend `RWSViewComponent` and use the `@RWSView` decorator.
|
|
125
|
+
- Structure: `component-dir/component.ts`, `template.html`, `styles/layout.scss`
|
|
126
|
+
- See RWSDocs for more details.
|
|
370
127
|
|
|
371
|
-
|
|
128
|
+
### Plugin System
|
|
372
129
|
|
|
373
|
-
|
|
130
|
+
- Add plugins via `addPlugin` (e.g., routing, websockets).
|
|
131
|
+
- Plugins are initialized on client startup.
|
|
374
132
|
|
|
375
|
-
|
|
376
|
-
import { RWSViewComponent, RWSView, observable, attr } from '@rws-framework/client';
|
|
377
|
-
|
|
378
|
-
const options?: RWSDecoratorOptions;
|
|
379
|
-
|
|
380
|
-
@RWSView('tag-name', options)
|
|
381
|
-
class WebChat extends RWSViewComponent {
|
|
382
|
-
@attr tagAttr: string; //HTML tag attr
|
|
383
|
-
@ngAttr fromNgAttr: string; //HTML attr from angular template
|
|
384
|
-
@externalAttr fromExAttr: string; //HTML attr with change observation
|
|
385
|
-
@sanitizedAttr htmlAttr: string; //HTML attr that's sanitized with every val change
|
|
386
|
-
@observable someVar: any; //Var for templates/value change observation
|
|
387
|
-
@externalObservable someExVar: string; //Var for templates/value change observation with external watch
|
|
388
|
-
}
|
|
389
|
-
```
|
|
133
|
+
### Notifier
|
|
390
134
|
|
|
391
|
-
|
|
135
|
+
Set a custom notification handler with `setNotifier`:
|
|
392
136
|
|
|
393
137
|
```typescript
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
fastElementOptions?: any //the stuff you would insert into static definition in FASTElement class.
|
|
398
|
-
}
|
|
399
|
-
|
|
138
|
+
theClient.setNotifier((message: string, logType: NotifyLogType, uiType: NotifyUiType = 'notification', onConfirm: (params: any) => void) => {
|
|
139
|
+
// Implementation based on uiType
|
|
140
|
+
});
|
|
400
141
|
```
|
|
401
142
|
|
|
402
|
-
|
|
143
|
+
### Service Worker
|
|
403
144
|
|
|
404
|
-
|
|
145
|
+
If you pass `{serviceWorker: 'service_worker_class_path.ts'}` to the RWS Webpack wrapper, the code will build a ServiceWorker to pubDir.
|
|
405
146
|
|
|
406
|
-
|
|
407
|
-
import { RWSViewComponent, RWSView } from 'rws-js-client';
|
|
147
|
+
## Dependency Injection
|
|
408
148
|
|
|
409
|
-
|
|
410
|
-
class YourComponent extends RWSViewComponent {
|
|
411
|
-
someMethod(url: string): void
|
|
412
|
-
{
|
|
413
|
-
this.apiService.get(url);
|
|
414
|
-
}
|
|
415
|
-
}
|
|
416
|
-
|
|
417
|
-
```
|
|
149
|
+
All services and components are registered and resolved via a DI container. Default and custom services can be injected and used throughout your app.
|
|
418
150
|
|
|
419
|
-
|
|
151
|
+
## Frontend Routes
|
|
420
152
|
|
|
421
|
-
|
|
422
|
-
window.RWS.client.get('ApiService').dateMethodFromRWS();
|
|
423
|
-
```
|
|
424
|
-
|
|
425
|
-
Default services: https://github.com/papablack/rws-client/blob/7d16d9c6d83c81c9fe470eb0f507756bc6c71b35/src/components/_component.ts#L58
|
|
426
|
-
|
|
427
|
-
## Custom service usage:
|
|
153
|
+
Define frontend routes using `renderRouteComponent` and pass them to the router plugin. Example route definitions for use with the router component (see `.dev/router`):
|
|
428
154
|
|
|
429
155
|
```typescript
|
|
430
|
-
import {
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
} from '
|
|
434
|
-
|
|
435
|
-
import DateService, {DateServiceInstance} from '../../my-custom-services/DateService';
|
|
156
|
+
import { renderRouteComponent } from '@rws-framework/browser-router';
|
|
157
|
+
import { HomePage } from './pages/home/component';
|
|
158
|
+
import { CompanyList } from './pages/company/list/component';
|
|
159
|
+
import { GeneralSettings } from './pages/settings/general/component';
|
|
436
160
|
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
private @RWSInject(DateService) protected dateService: DateServiceInstance
|
|
446
|
-
) {
|
|
447
|
-
super();
|
|
448
|
-
}
|
|
449
|
-
|
|
450
|
-
someMethod(url: string): void
|
|
161
|
+
export const frontRoutes = [
|
|
162
|
+
{
|
|
163
|
+
path: '/',
|
|
164
|
+
name: 'Home',
|
|
165
|
+
component: HomePage,
|
|
166
|
+
icon: 'home',
|
|
167
|
+
inMenu: true
|
|
168
|
+
},
|
|
451
169
|
{
|
|
452
|
-
|
|
170
|
+
path: '/company/list',
|
|
171
|
+
name: 'Companies',
|
|
172
|
+
component: CompanyList,
|
|
173
|
+
icon: 'company',
|
|
174
|
+
inMenu: true
|
|
175
|
+
},
|
|
176
|
+
{
|
|
177
|
+
path: '/settings/general',
|
|
178
|
+
name: 'Settings',
|
|
179
|
+
component: GeneralSettings,
|
|
180
|
+
icon: 'settings',
|
|
181
|
+
inMenu: true
|
|
453
182
|
}
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
```
|
|
457
|
-
|
|
458
|
-
Custom service needs to export .getSingleton() as default export and have service class exported as classic export for TS typing:
|
|
459
|
-
|
|
460
|
-
```typescript
|
|
461
|
-
import { RWSService } from '@rws-framework/client';
|
|
183
|
+
];
|
|
462
184
|
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
185
|
+
// Convert to route map for the router
|
|
186
|
+
const routeMap = {};
|
|
187
|
+
for (const route of frontRoutes) {
|
|
188
|
+
routeMap[route.path] = renderRouteComponent(route.name, route.component);
|
|
467
189
|
}
|
|
468
190
|
|
|
469
|
-
export default
|
|
470
|
-
export { DateService as DateServiceInstance }; // the custom service class type
|
|
191
|
+
export default routeMap;
|
|
471
192
|
```
|
|
472
193
|
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
```javascript
|
|
478
|
-
window.RWS.client.get('DateService').dateMethodFromRWS();
|
|
479
|
-
```
|
|
480
|
-
|
|
481
|
-
## Frontend routes
|
|
482
|
-
|
|
483
|
-
if you are passing routes this is example routing file for frontend:
|
|
484
|
-
|
|
485
|
-
```typescript
|
|
486
|
-
export default {
|
|
487
|
-
'/': renderRouteComponent('Home page', WebChat),
|
|
488
|
-
'/the/path': renderRouteComponent('Component title', ComponentClassName),
|
|
489
|
-
}
|
|
490
|
-
```
|
|
491
|
-
|
|
492
|
-
Router tag:
|
|
493
|
-
|
|
494
|
-
```html
|
|
495
|
-
<section>
|
|
496
|
-
<rws-router></rws-router>
|
|
497
|
-
</section>
|
|
498
|
-
```
|
|
194
|
+
- Each route object can include `path`, `name`, `component`, `icon`, and `inMenu` fields.
|
|
195
|
+
- Use `renderRouteComponent` to wrap the component for the router.
|
|
196
|
+
- Pass the resulting `routeMap` to the router plugin or RWS client configuration.
|
|
499
197
|
|
|
500
198
|
## Backend Imports
|
|
501
199
|
|
|
502
|
-
`
|
|
503
|
-
|
|
504
|
-
```typescript
|
|
505
|
-
import IBook from '../../backend/src/models/interfaces/IBook';
|
|
506
|
-
|
|
507
|
-
import {
|
|
508
|
-
IBookInfo,
|
|
509
|
-
} from '../../backend/src/interfaces/IBookInfo';
|
|
510
|
-
|
|
511
|
-
import backendRoutes from '../../backend/src/routing/routes';
|
|
512
|
-
|
|
513
|
-
export {
|
|
514
|
-
IBook,
|
|
515
|
-
IBookInfo,
|
|
516
|
-
backendRoutes
|
|
517
|
-
}
|
|
518
|
-
|
|
519
|
-
```
|
|
200
|
+
`backendImport.ts` consolidates backend interfaces, routes, and models for synchronized frontend/backend development.
|
|
520
201
|
|
|
521
|
-
|
|
202
|
+
### HTTPRoutes Interface
|
|
522
203
|
|
|
204
|
+
The RWS client uses a typed interface for backend HTTP routes, typically called `IBackendRoute` or `HTTPRoutes`. This interface defines the structure for backend API endpoints, allowing for type-safe integration between frontend and backend. You can import and use these routes as follows:
|
|
523
205
|
|
|
524
206
|
```typescript
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
//index.ts
|
|
528
|
-
const theClient = new RWSClient();
|
|
529
|
-
theClient.setBackendRoutes(backendRoutes());
|
|
207
|
+
import { backendRoutes } from './backendImport';
|
|
530
208
|
|
|
209
|
+
theClient.setBackendRoutes(backendRoutes);
|
|
531
210
|
```
|
|
532
211
|
|
|
212
|
+
- Each route entry in `backendRoutes` should conform to the `IBackendRoute` (or `HTTPRoutes`) interface, describing the HTTP method, path, and any metadata required for API calls.
|
|
213
|
+
- This enables strong typing and autocompletion for API requests throughout your frontend codebase.
|
|
533
214
|
|
|
534
215
|
## Utilizing APIService
|
|
535
216
|
|
|
536
|
-
`APIService` is used for making HTTP requests to the backend. It
|
|
537
|
-
|
|
538
|
-
after control method we have dynamic types those are: <**ResponseType**, **PayloadType**>
|
|
217
|
+
`APIService` is used for making HTTP requests to the backend. It supports dynamic types for response and payload, and can be accessed via DI or through the RWS client instance.
|
|
539
218
|
|
|
540
|
-
|
|
219
|
+
### Basic Usage
|
|
541
220
|
|
|
542
221
|
```typescript
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
model: this.chosenModel,
|
|
546
|
-
});
|
|
547
|
-
```
|
|
548
|
-
|
|
549
|
-
Example Usage by url
|
|
222
|
+
// Injected in a component or service
|
|
223
|
+
@RWSInject(ApiService, true) protected apiService: ApiServiceInstance;
|
|
550
224
|
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
message: msg,
|
|
554
|
-
model: this.chosenModel,
|
|
555
|
-
});
|
|
225
|
+
// Or via the client
|
|
226
|
+
const apiService = window.RWS.client.get('ApiService');
|
|
556
227
|
```
|
|
557
228
|
|
|
558
|
-
|
|
229
|
+
### Making Requests
|
|
559
230
|
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
The Notifier feature in the RWS Client is a versatile tool for handling notifications within the application. It allows for different types of user interface interactions like alerts, notifications, and silent logging, with varying levels of visibility and user interaction.
|
|
563
|
-
Usage
|
|
564
|
-
|
|
565
|
-
### Setting the Notifier
|
|
231
|
+
You can use RESTful methods directly:
|
|
566
232
|
|
|
567
233
|
```typescript
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
});
|
|
234
|
+
// GET request
|
|
235
|
+
apiService.get('/api/some-endpoint');
|
|
571
236
|
|
|
237
|
+
// POST request with payload
|
|
238
|
+
type MyResponse = { ... };
|
|
239
|
+
type MyPayload = { ... };
|
|
240
|
+
const result = await apiService.post<MyResponse, MyPayload>('/api/some-endpoint', { foo: 'bar' });
|
|
572
241
|
```
|
|
573
242
|
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
Alert, Notify, and Silent
|
|
577
|
-
|
|
578
|
-
- alert: Displays an alert dialog with the message.
|
|
579
|
-
- notify: Shows a notification with the message.
|
|
580
|
-
- silent: Silently logs the message to the console.
|
|
243
|
+
### Using Named Backend Routes
|
|
581
244
|
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
Note
|
|
585
|
-
|
|
586
|
-
Ensure that a notifier is set in the RWS Client to use the `NotifyService` effectively. If no notifier is set, it will default to a warning in the console.
|
|
587
|
-
|
|
588
|
-
## Service Worker
|
|
589
|
-
|
|
590
|
-
If you pass ```{serviceWorker: 'service_worker_class_path.ts'}``` to RWS Webpack wrapper function param, the code will build ServiceWorker to pubDir.
|
|
591
|
-
|
|
592
|
-
example ServiceWorker class:
|
|
245
|
+
If you use named backend routes (from `backendRoutes`):
|
|
593
246
|
|
|
594
247
|
```typescript
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
import RWSServiceWorker from '@rws-framework/client/src/service_worker/src/_service_worker';
|
|
598
|
-
import { RWSWSService as WSService } from '@rws-framework/client/src/services/WSService'
|
|
599
|
-
|
|
600
|
-
declare const self: ServiceWorkerGlobalScope;
|
|
601
|
-
|
|
602
|
-
class MyServiceWorker extends RWSServiceWorker {
|
|
603
|
-
public tracker: { currentTracker: TimeTracker | null };
|
|
604
|
-
public trackersToSync: TimeTracker[];
|
|
605
|
-
|
|
606
|
-
protected regExTypes: { [key: string]: RegExp } = {
|
|
607
|
-
SOME_VIEW: new RegExp('.*:\\/\\/.*\\/#\\/([a-z0-9].*)\\/route\\/action$')
|
|
608
|
-
};
|
|
609
|
-
ignoredUrls = [
|
|
610
|
-
new RegExp('(.*(?=.[^.]*$).*)/#/login'),
|
|
611
|
-
new RegExp('(.*(?=.[^.]*$).*)/#/logout'),
|
|
612
|
-
];
|
|
613
|
-
|
|
614
|
-
constructor(){
|
|
615
|
-
super(self, RWSContainer());
|
|
616
|
-
}
|
|
617
|
-
|
|
618
|
-
checkForbidden(url: string): boolean {
|
|
619
|
-
if (!url) {
|
|
620
|
-
return true;
|
|
621
|
-
}
|
|
622
|
-
|
|
623
|
-
console.log('[SW] Check forbidden', url);
|
|
624
|
-
|
|
625
|
-
return this.ignoredUrls.some((item) => url.match(item));
|
|
626
|
-
}
|
|
627
|
-
|
|
628
|
-
isExtraType(id: string){
|
|
629
|
-
let result: string | null = null;
|
|
630
|
-
const _self = this;
|
|
631
|
-
|
|
632
|
-
Object.keys(this.regExTypes).forEach(function(key){
|
|
633
|
-
if(result === null && _self.regExTypes[key].exec(id) !== null){
|
|
634
|
-
result = key;
|
|
635
|
-
}
|
|
636
|
-
});
|
|
637
|
-
|
|
638
|
-
return result;
|
|
639
|
-
}
|
|
640
|
-
|
|
641
|
-
startServiceWorker(regExTypes: { [key: string]: RegExp }, forbiddenUrls: RegExp[]): JunctionServiceWorker
|
|
642
|
-
{
|
|
643
|
-
this.tracker = { currentTracker: null };
|
|
644
|
-
this.ignoredUrls = forbiddenUrls;
|
|
645
|
-
this.trackersToSync = [];
|
|
646
|
-
this.regExTypes = regExTypes;
|
|
647
|
-
|
|
648
|
-
return this;
|
|
649
|
-
}
|
|
650
|
-
|
|
651
|
-
async onInit(): Promise<void>
|
|
652
|
-
{
|
|
653
|
-
const _self: JunctionServiceWorker = this;
|
|
654
|
-
let THE_USER: IJunctionUser | null = null;
|
|
655
|
-
const toSync: TimeTracker[] = [];
|
|
656
|
-
|
|
657
|
-
let WS_URL: string | null;
|
|
658
|
-
|
|
659
|
-
console.log('Initiating ServiceWorker');
|
|
660
|
-
|
|
661
|
-
this.workerScope.addEventListener('message', (event: MSGEvent) => {
|
|
662
|
-
// console.log(event);
|
|
663
|
-
if(!event.data){
|
|
664
|
-
console.warn('[SW] Got empty message');
|
|
665
|
-
return;
|
|
666
|
-
}
|
|
667
|
-
|
|
668
|
-
if (event.data.command){
|
|
669
|
-
console.log('[SW] OP Message:', event.data.command);
|
|
670
|
-
|
|
671
|
-
switch (event.data.command) {
|
|
672
|
-
case 'SET_WS_URL':
|
|
673
|
-
WS_URL = event.data.params.url;
|
|
674
|
-
break;
|
|
675
|
-
case 'SET_USER':
|
|
676
|
-
if(!this.getUser()){
|
|
677
|
-
THE_USER = event.data.params;
|
|
678
|
-
this.setUser(THE_USER);
|
|
679
|
-
}
|
|
680
|
-
_self.checkWs(WS_URL, this.getUser());
|
|
681
|
-
break;
|
|
682
|
-
case 'START_TRACKING':
|
|
683
|
-
_self.checkWs(WS_URL, this.getUser());
|
|
684
|
-
if(!this.wsService.socket() && this.getUser()){
|
|
685
|
-
break;
|
|
686
|
-
}
|
|
687
|
-
_self.trackActivity(event.data.asset_type, event.data.params.page_location, event.data.params, toSync);
|
|
688
|
-
break;
|
|
689
|
-
case 'TRACKER_SAVED':
|
|
690
|
-
const { clientId, tracker } = event.data.params;
|
|
691
|
-
|
|
692
|
-
_self.sendMessageToClient(clientId, { message: 'TRACKER_SAVED_RESPONSE', data: tracker });
|
|
693
|
-
break;
|
|
694
|
-
}
|
|
695
|
-
}
|
|
696
|
-
});
|
|
697
|
-
}
|
|
698
|
-
|
|
699
|
-
async onActivate(): Promise<void>
|
|
700
|
-
{
|
|
701
|
-
console.log('Activated ServiceWorker');
|
|
702
|
-
|
|
703
|
-
this.startServiceWorker(this.regExTypes, this.ignoredUrls);
|
|
704
|
-
}
|
|
705
|
-
|
|
706
|
-
private checkWs(WS_URL: string, THE_USER: IJunctionUser): boolean
|
|
707
|
-
{
|
|
708
|
-
if(!this.wsService.socket() && WS_URL){
|
|
709
|
-
this.wsService.init(WS_URL, THE_USER);
|
|
710
|
-
|
|
711
|
-
return true;
|
|
712
|
-
}
|
|
713
|
-
|
|
714
|
-
return false;
|
|
715
|
-
};
|
|
716
|
-
}
|
|
248
|
+
// By route name (controller:action)
|
|
249
|
+
const data = await apiService.back.get<MyResponse>('user:getProfile', { routeParams: { id: '123' } });
|
|
717
250
|
|
|
718
|
-
|
|
251
|
+
// POST with payload
|
|
252
|
+
type Payload = { name: string };
|
|
253
|
+
const result = await apiService.back.post<MyResponse, Payload>('user:updateProfile', { name: 'John' });
|
|
719
254
|
```
|
|
720
255
|
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
## Example: WebChat Component
|
|
724
|
-
|
|
725
|
-
The WebChat component demonstrates a practical use of `APIService` in a real-world scenario. It shows how to send and receive data from the backend.
|
|
726
|
-
|
|
727
|
-
### WebChat Component Implementation
|
|
256
|
+
### File Upload Example
|
|
728
257
|
|
|
729
258
|
```typescript
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
import WebChatEvents from './events';
|
|
736
|
-
import { IContext } from './children/left-bar/component';
|
|
737
|
-
import { IMessage } from '../chat-message/component';
|
|
738
|
-
import { ITalkApiResponse, BedrockBaseModel, IHyperParameter,
|
|
739
|
-
|
|
740
|
-
@RWSView('web-chat')
|
|
741
|
-
class WebChat extends RWSViewComponent {
|
|
742
|
-
|
|
743
|
-
static fileList: string[] = [
|
|
744
|
-
'svg/icon_talk_1.svg'
|
|
745
|
-
];
|
|
746
|
-
|
|
747
|
-
@observable messages: IMessage[] = [];
|
|
748
|
-
@observable hyperParameters: { key: string, value: any } | any = {};
|
|
749
|
-
@observable bookId: string = null;
|
|
750
|
-
@observable chapterNr: string = null;
|
|
751
|
-
|
|
752
|
-
@observable chosenModel: BedrockBaseModel = null;
|
|
753
|
-
@observable chatContext: IContext = { label: 'Book chat' };
|
|
754
|
-
|
|
755
|
-
@observable bookModel: IBook = null;
|
|
756
|
-
|
|
757
|
-
@observable minified: boolean = true;
|
|
758
|
-
|
|
759
|
-
@ngAttr custombookid: string = null;
|
|
760
|
-
@ngAttr customchapternr: string = null;
|
|
761
|
-
|
|
762
|
-
@observable customTemperature: number = 0.7;
|
|
763
|
-
@observable customTopK: number = 250;
|
|
764
|
-
@observable customMaxTokensToSample: number = 1024;
|
|
765
|
-
@observable customTopP: number = 0.7;
|
|
766
|
-
|
|
767
|
-
@ngAttr hTemperature?: string = '0.7';
|
|
768
|
-
@ngAttr hTopK?: string = '250';
|
|
769
|
-
@ngAttr hMaxTokensToSample?: string = '1024';
|
|
770
|
-
@ngAttr hTopP?: string = '0.7';
|
|
771
|
-
|
|
772
|
-
@observable convoId: string;
|
|
773
|
-
@observable wsId: string;
|
|
774
|
-
|
|
775
|
-
@ngAttr dev: PseudoBool = 'false';
|
|
776
|
-
@ngAttr opened: PseudoBool = 'false';
|
|
777
|
-
|
|
778
|
-
@ngAttr userImage: string | null = null;
|
|
779
|
-
@ngAttr initials: string | null = 'U';
|
|
780
|
-
|
|
781
|
-
handlers: (this: WebChat) => IWebChatHandlers = assignHandlers;
|
|
782
|
-
streamCall: (msg: IMessage) => Promise<void> = callStreamApi;
|
|
783
|
-
|
|
784
|
-
getDefaultHyperParams = getDefaultParams;
|
|
785
|
-
setHyperParam = setHyperParam;
|
|
786
|
-
|
|
787
|
-
public msgOptions: IConvoMsgOptions = {
|
|
788
|
-
headerEnabled: false,
|
|
789
|
-
dateEnabled: false
|
|
790
|
-
};
|
|
791
|
-
|
|
792
|
-
connectedCallback() {
|
|
793
|
-
super.connectedCallback();
|
|
794
|
-
|
|
795
|
-
if (this.routeParams?.dev || this.dev === 'true') {
|
|
796
|
-
this.dev = 'true';
|
|
797
|
-
} else {
|
|
798
|
-
this.dev = 'false';
|
|
799
|
-
}
|
|
800
|
-
|
|
801
|
-
this.checkForBookId();
|
|
802
|
-
this.checkForBookChapter();
|
|
803
|
-
|
|
804
|
-
this.chosenModel = ClaudeModel;
|
|
805
|
-
|
|
806
|
-
const provider = this.chosenModel?.providerName?.toLowerCase() || null;
|
|
807
|
-
const defParams = this.getDefaultHyperParams(provider);
|
|
808
|
-
|
|
809
|
-
const defaultParams: { [key: string]: any } = {};
|
|
810
|
-
|
|
811
|
-
Object.keys(defParams).forEach(paramKey => {
|
|
812
|
-
if (defParams[paramKey]) {
|
|
813
|
-
defaultParams[paramKey] = this.setHyperParam(paramKey, defParams[paramKey]);
|
|
814
|
-
}
|
|
815
|
-
});
|
|
816
|
-
|
|
817
|
-
this.hyperParameters = { ...defaultParams, ...this.hyperParameters };
|
|
818
|
-
|
|
819
|
-
this.wsId = uuid();
|
|
820
|
-
|
|
821
|
-
this.on<{ item: IMessage }>(WebChatEvents.message.send, (event: CustomEvent<{ item: IMessage }>) => {
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
this.streamCall(event.detail.item);
|
|
825
|
-
});
|
|
826
|
-
|
|
827
|
-
if (this.routeParams?.opened || this.opened === 'true') {
|
|
828
|
-
this.minified = false;
|
|
829
|
-
}
|
|
830
|
-
|
|
831
|
-
if (this.hTemperature) {
|
|
832
|
-
this.hHandlers.hTemperature(null, this.hTemperature);
|
|
833
|
-
}
|
|
834
|
-
|
|
835
|
-
if (this.hMaxTokensToSample) {
|
|
836
|
-
this.hHandlers.hMaxTokensToSample(null, this.hMaxTokensToSample);
|
|
837
|
-
}
|
|
838
|
-
|
|
839
|
-
if (this.hTopK) {
|
|
840
|
-
this.hHandlers.hTopK(null, this.hTopK);
|
|
841
|
-
}
|
|
842
|
-
|
|
843
|
-
if (this.hTopP) {
|
|
844
|
-
this.hHandlers.hTopP(null, this.hTopP);
|
|
845
|
-
}
|
|
846
|
-
}
|
|
847
|
-
checkForBookId() {
|
|
848
|
-
this.bookId = this.routeParams.bookId || this.custombookid || null;
|
|
849
|
-
|
|
850
|
-
if (this.bookId) {
|
|
851
|
-
this.apiService.back.get<IBook>('train:get:book', { routeParams: { bookId: this.bookId } }).then((data: IBook) => {
|
|
852
|
-
this.bookModel = data;
|
|
853
|
-
});
|
|
854
|
-
}
|
|
855
|
-
}
|
|
856
|
-
|
|
857
|
-
checkForBookChapter() {
|
|
858
|
-
this.chapterNr = this.routeParams.chapterNr || this.customchapternr || null;
|
|
859
|
-
}
|
|
860
|
-
|
|
861
|
-
custombookidChanged(oldVal: string, newVal: string) {
|
|
862
|
-
if (newVal) {
|
|
863
|
-
this.custombookid = newVal;
|
|
864
|
-
this.checkForBookId();
|
|
865
|
-
} else {
|
|
866
|
-
this.custombookid = null;
|
|
867
|
-
}
|
|
868
|
-
}
|
|
869
|
-
|
|
870
|
-
customchapternrChanged(oldVal: string, newVal: string) {
|
|
871
|
-
if (newVal) {
|
|
872
|
-
this.customchapternr = newVal;
|
|
873
|
-
this.checkForBookChapter();
|
|
874
|
-
} else {
|
|
875
|
-
this.customchapternr = null;
|
|
876
|
-
}
|
|
877
|
-
}
|
|
878
|
-
|
|
879
|
-
devChanged(oldVal: string, newVal: string) {
|
|
880
|
-
if (oldVal !== newVal) {
|
|
881
|
-
this.dev = newVal === 'true' ? 'true' : 'false';
|
|
882
|
-
}
|
|
883
|
-
}
|
|
884
|
-
|
|
885
|
-
hHandlers: IHyperHandler = getParamChangeHandlers.bind(this)();
|
|
259
|
+
await apiService.uploadFile('/api/upload', file, progress => {
|
|
260
|
+
console.log('Progress:', progress);
|
|
261
|
+
});
|
|
262
|
+
```
|
|
886
263
|
|
|
887
|
-
|
|
888
|
-
hMaxTokensToSampleChanged: ChangeHandlerType<string> = this.hHandlers.hMaxTokensToSample;
|
|
889
|
-
hTopKChanged: ChangeHandlerType<string> = this.hHandlers.hTopK;
|
|
890
|
-
hTopPChanged: ChangeHandlerType<string> = this.hHandlers.hTopP;
|
|
264
|
+
### Route Type Safety
|
|
891
265
|
|
|
892
|
-
|
|
893
|
-
if (newVal && oldVal !== newVal) {
|
|
894
|
-
this.userImage = newVal;
|
|
895
|
-
}
|
|
896
|
-
}
|
|
266
|
+
If you use `IBackendRoute`/`HTTPRoutes` for your backend route definitions, you get type safety and autocompletion for all API calls.
|
|
897
267
|
|
|
898
|
-
|
|
899
|
-
if (newVal && oldVal !== newVal) {
|
|
900
|
-
this.initials = newVal;
|
|
901
|
-
}
|
|
902
|
-
}
|
|
268
|
+
## Example: WebChat Component
|
|
903
269
|
|
|
904
|
-
|
|
905
|
-
if (newVal && oldVal !== newVal) {
|
|
906
|
-
console.log(this.convoId);
|
|
907
|
-
this.convoId = newVal;
|
|
908
|
-
}
|
|
909
|
-
}
|
|
910
|
-
}
|
|
270
|
+
See the WebChat component for a practical example of APIService and RWSView usage.
|
|
911
271
|
|
|
912
|
-
|
|
272
|
+
## Other configs
|
|
913
273
|
|
|
274
|
+
See the original README for example `tsconfig.json` and webpack config.
|
|
914
275
|
|
|
915
|
-
|
|
276
|
+
## Plugin System
|
|
916
277
|
|
|
917
|
-
|
|
278
|
+
The plugin system allows you to extend the client with additional features. For example, you can add routing with `@rws-framework/browser-router` or websockets with `@rws-framework/nest-interconnectors`.
|
|
918
279
|
|
|
919
|
-
|
|
280
|
+
## Nest Interconnectors
|
|
920
281
|
|
|
921
|
-
The
|
|
282
|
+
The `@rws-framework/nest-interconnectors` package provides seamless integration with NestJS-based backend websockets and real-time features. You can add the plugin as follows:
|
|
922
283
|
|
|
923
284
|
```typescript
|
|
924
|
-
|
|
925
|
-
public async modelTalkAction(params: IRequestParams): Promise<ITalkApiResponse>
|
|
926
|
-
{
|
|
927
|
-
// (...)
|
|
928
|
-
}
|
|
929
|
-
```
|
|
930
|
-
|
|
931
|
-
and src/config/config
|
|
285
|
+
import { RWSWebsocketsPlugin, WSOptions } from '@rws-framework/nest-interconnectors';
|
|
932
286
|
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
routes: [
|
|
938
|
-
{
|
|
939
|
-
name: 'action:route:name',
|
|
940
|
-
path: '/path/to/action'
|
|
941
|
-
},
|
|
942
|
-
{
|
|
943
|
-
name: 'action:route:name',
|
|
944
|
-
path: '/path/to/action'
|
|
945
|
-
}
|
|
946
|
-
]
|
|
947
|
-
},
|
|
948
|
-
{
|
|
949
|
-
name: 'home:index',
|
|
950
|
-
path: '/*', //if no routes detected pass request to frontend
|
|
951
|
-
noParams: true, //do not read params from the request leave it to the front
|
|
952
|
-
},
|
|
953
|
-
]
|
|
287
|
+
theClient.addPlugin<WSOptions>(RWSWebsocketsPlugin, {
|
|
288
|
+
enabled: true,
|
|
289
|
+
auto_notify: true
|
|
290
|
+
});
|
|
954
291
|
```
|
|
955
292
|
|
|
956
|
-
|
|
293
|
+
This enables real-time communication and event-driven features between your RWS frontend and a NestJS backend.
|
|
957
294
|
|
|
958
|
-
|
|
295
|
+
## Styles Injection
|
|
959
296
|
|
|
960
|
-
|
|
961
|
-
WSService.sendMessage<PayloadType>('send_msg', {
|
|
962
|
-
modelId: this.chosenModel.modelId,
|
|
963
|
-
prompt: msg.content
|
|
964
|
-
});
|
|
965
|
-
|
|
966
|
-
```
|
|
967
|
-
|
|
968
|
-
are defined in backend/src/config/config
|
|
297
|
+
RWS supports advanced styles injection for components. You can inject global or component-specific stylesheets using the static method:
|
|
969
298
|
|
|
970
299
|
```typescript
|
|
971
|
-
|
|
972
|
-
'send_msg' : ChatSocket,
|
|
973
|
-
'process_book' : TrainSocket,
|
|
974
|
-
}
|
|
975
|
-
```
|
|
976
|
-
|
|
977
|
-
## Other configs
|
|
978
|
-
|
|
979
|
-
### example tsconfig.json
|
|
980
|
-
|
|
981
|
-
```json
|
|
982
|
-
{
|
|
983
|
-
"compilerOptions": {
|
|
984
|
-
"baseUrl": "../",
|
|
985
|
-
"experimentalDecorators": true,
|
|
986
|
-
"emitDecoratorMetadata": true,
|
|
987
|
-
"target": "ES2018",
|
|
988
|
-
"module": "es2022",
|
|
989
|
-
"moduleResolution": "node",
|
|
990
|
-
"strict": true,
|
|
991
|
-
"esModuleInterop": true,
|
|
992
|
-
"sourceMap": true,
|
|
993
|
-
"outDir": "dist",
|
|
994
|
-
"strictNullChecks": false,
|
|
995
|
-
"allowSyntheticDefaultImports": true,
|
|
996
|
-
"lib": ["DOM", "ESNext", "WebWorker"],
|
|
997
|
-
"paths": {
|
|
998
|
-
}
|
|
999
|
-
},
|
|
1000
|
-
"include": [
|
|
1001
|
-
"src",
|
|
1002
|
-
"../node_modules/@rws-framework/client/declarations.d.ts", //TEMPORARILY NEEDED TO WORK
|
|
1003
|
-
],
|
|
1004
|
-
"exclude": [
|
|
1005
|
-
"../node_modules/@rws-framework/client/src/tests"
|
|
1006
|
-
]
|
|
1007
|
-
}
|
|
1008
|
-
```
|
|
1009
|
-
|
|
1010
|
-
**Remember to have lib field set in tsconfig.json**
|
|
1011
|
-
|
|
1012
|
-
```json
|
|
1013
|
-
{
|
|
1014
|
-
"lib": ["DOM", "ESNext"]
|
|
1015
|
-
}
|
|
300
|
+
RWSViewComponent.injectStyles(["/css/global.css", "/css/theme.css"]);
|
|
1016
301
|
```
|
|
1017
302
|
|
|
1018
|
-
|
|
303
|
+
- Styles can be injected in `adopted`, `legacy`, or `both` modes (default is `adopted`).
|
|
304
|
+
- Styles are cached in IndexedDB for performance and can be hot-reloaded.
|
|
305
|
+
- Each component can also inject its own styles via the `injectStyles` method or by specifying styles in the component definition.
|
|
1019
306
|
|
|
1020
|
-
|
|
307
|
+
This allows for efficient, encapsulated, and dynamic styling of your RWS components, supporting both modern and legacy browsers.
|
|
1021
308
|
|
|
1022
309
|
## Links
|
|
1023
|
-
-
|
|
1024
|
-
- https://www.
|
|
310
|
+
- [RWSDocs instructions](../../.github/instructions/RWSDocs.instructions.md)
|
|
311
|
+
- [FAST documentation](https://www.fast.design/docs/fast-element/getting-started)
|
|
312
|
+
- [WebComponents.org](https://www.webcomponents.org)
|