@rws-framework/client 2.22.0 → 2.22.1

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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@rws-framework/client",
3
3
  "private": false,
4
- "version": "2.22.0",
4
+ "version": "2.22.1",
5
5
  "main": "src/index.ts",
6
6
  "scripts": {
7
7
  "docs": "typedoc --tsconfig ./tsconfig.json"
@@ -13,6 +13,7 @@ import TheRWSService from '../services/_service';
13
13
  import { handleExternalChange } from './_attrs/_external_handler';
14
14
  import { IFastDefinition, isDefined, defineComponent, getDefinition } from './_definitions';
15
15
  import { on, $emitDown, observe, sendEventToOutside } from './_event_handling';
16
+ import { domEvents } from '../events';
16
17
 
17
18
  type ComposeMethodType<
18
19
  T extends FoundationElementDefinition,
@@ -21,6 +22,8 @@ type ComposeMethodType<
21
22
 
22
23
  type CSSInjectMode = 'adopted' | 'legacy' | 'both';
23
24
 
25
+ const _DEFAULT_INJECT_CSS_CACHE_LIMIT_DAYS = 1;
26
+
24
27
  export interface IWithCompose<T extends RWSViewComponent> {
25
28
  [key: string]: any
26
29
  new(...args: any[]): T;
@@ -250,55 +253,81 @@ abstract class RWSViewComponent extends FoundationElement implements IRWSViewCom
250
253
  return RWSViewComponent.instances;
251
254
  }
252
255
 
253
- protected async injectStyles(styleLinks: string[], mode: CSSInjectMode = 'adopted') {
256
+ protected async injectStyles(styleLinks: string[], mode: CSSInjectMode = 'adopted', maxDaysExp?: number) {
254
257
  const dbName = 'css-cache';
255
258
  const storeName = 'styles';
256
259
  const db = await this.indexedDBService.openDB(dbName, storeName);
260
+ const maxAgeMs = 1000 * 60 * 60 * 24; // 24h
261
+ const maxDaysAge = maxDaysExp ? maxDaysExp : _DEFAULT_INJECT_CSS_CACHE_LIMIT_DAYS;
262
+ const maxAgeDays = maxAgeMs * maxDaysAge;
257
263
 
258
264
  let adoptedSheets: CSSStyleSheet[] = [];
259
265
 
266
+ let doneAdded = false;
267
+
260
268
  for (const styleLink of styleLinks) {
261
- if (mode === 'legacy' || mode === 'both') {
262
- const link = document.createElement('link');
263
- link.rel = 'stylesheet';
264
- link.href = styleLink;
265
- this.getShadowRoot().appendChild(link);
266
- }
269
+ const loadPromise = new Promise<void>(async (resolve, reject) => {
270
+ if (mode === 'legacy' || mode === 'both') {
271
+ const link = document.createElement('link');
272
+ link.rel = 'stylesheet';
273
+ link.href = styleLink;
274
+ this.getShadowRoot().appendChild(link);
275
+
276
+ link.onload = () => {
277
+ doneAdded = true;
278
+
279
+ if(mode === 'legacy'){
280
+ resolve();
281
+ }
282
+ };
283
+ }
267
284
 
268
- if (mode === 'adopted' || mode === 'both') {
269
- const entry = await this.indexedDBService.getFromDB(db, storeName, styleLink);
270
- const maxAgeMs = 1000 * 60 * 60 * 24; // 24h
285
+ if (mode === 'adopted' || mode === 'both') {
286
+ const entry = await this.indexedDBService.getFromDB(db, storeName, styleLink);
271
287
 
272
- let cssText: string | null = null;
288
+ let cssText: string | null = null;
273
289
 
274
- if (entry && typeof entry === 'object' && 'css' in entry && 'timestamp' in entry) {
275
- const expired = Date.now() - entry.timestamp > maxAgeMs;
276
- if (!expired) {
277
- cssText = entry.css;
290
+ if (entry && typeof entry === 'object' && 'css' in entry && 'timestamp' in entry) {
291
+ const expired = Date.now() - entry.timestamp > maxAgeDays;
292
+ if (!expired) {
293
+ cssText = entry.css;
294
+ }
278
295
  }
279
- }
280
296
 
281
- if (!cssText) {
282
- cssText = await fetch(styleLink).then(res => res.text());
283
- await this.indexedDBService.saveToDB(db, storeName, styleLink, {
284
- css: cssText,
285
- timestamp: Date.now()
286
- });
287
- console.log(`System saved stylesheet: ${styleLink} to IndexedDB`)
288
- }
297
+ if (!cssText) {
298
+ cssText = await fetch(styleLink).then(res => res.text());
299
+ await this.indexedDBService.saveToDB(db, storeName, styleLink, {
300
+ css: cssText,
301
+ timestamp: Date.now()
302
+ });
303
+ console.log(`System saved stylesheet: ${styleLink} to IndexedDB`)
304
+ }
289
305
 
290
- const sheet = new CSSStyleSheet();
291
- await sheet.replace(cssText);
306
+ const sheet = new CSSStyleSheet();
307
+ await sheet.replace(cssText);
292
308
 
293
- adoptedSheets.push(sheet);
294
- }
309
+ adoptedSheets.push(sheet);
310
+
311
+ if(mode === 'adopted' || mode === 'both'){
312
+ resolve();
313
+ }
314
+ }
315
+ });
316
+
317
+ await loadPromise;
295
318
  }
296
319
 
297
- if(adoptedSheets.length){
298
- this.getShadowRoot().adoptedStyleSheets = [
320
+ if (adoptedSheets.length) {
321
+ this.getShadowRoot().adoptedStyleSheets = [
299
322
  ...adoptedSheets,
300
323
  ...this.getShadowRoot().adoptedStyleSheets,
301
324
  ];
325
+
326
+ doneAdded = true;
327
+ }
328
+
329
+ if (doneAdded) {
330
+ this.$emit(domEvents.loadedLinkedStyles);
302
331
  }
303
332
  }
304
333
  }
package/src/events.ts ADDED
@@ -0,0 +1,3 @@
1
+ export const domEvents = {
2
+ loadedLinkedStyles: 'loaded:linked'
3
+ }
package/src/index.ts CHANGED
@@ -22,14 +22,14 @@ import { RWSIgnore, RWSInject, RWSView } from './components/_decorator';
22
22
  import type { DefaultRWSPluginOptionsType } from './plugins/_plugin';
23
23
  import type { IRWSPlugin, IStaticRWSPlugin, IPluginSpawnOption } from './types/IRWSPlugin';
24
24
  import type IRWSUser from './types/IRWSUser';
25
- import type { IAssetShowOptions } from './components/_component';
25
+ import type { IAssetShowOptions, IRWSViewComponent } from './components/_component';
26
26
  import type { RWSDecoratorOptions } from './components/_decorator';
27
- import type { IKDBTypeInfo, IKDBTypesResponse } from './types/IBackendCore';
28
27
  import type { DOMOutputType, TagsProcessorType } from './services/DOMService';
29
28
  import type { IBackendRoute, IHTTProute, IPrefixedHTTProutes } from './services/ApiService';
30
29
  import type IRWSConfig from './types/IRWSConfig';
31
30
  import type RWSNotify from './types/RWSNotify';
32
31
  import type { NotifyUiType, NotifyLogType } from './types/RWSNotify';
32
+ import * as RWSEvents from './events';
33
33
 
34
34
  export default RWSClient;
35
35
 
@@ -70,11 +70,12 @@ export {
70
70
  RWSService,
71
71
  RWSViewComponent,
72
72
 
73
- RWSContainer
73
+ RWSContainer,
74
+
75
+ RWSEvents
74
76
  };
75
77
 
76
- export type {
77
- IKDBTypeInfo, IKDBTypesResponse,
78
+ export type {
78
79
  NotifyUiType,
79
80
  NotifyLogType,
80
81
  IBackendRoute as IRWSBackendRoute,
@@ -84,5 +85,6 @@ export type {
84
85
  IAssetShowOptions as IRWSAssetShowOptions,
85
86
  IRWSConfig,
86
87
  IRWSUser,
87
- TagsProcessorType
88
+ TagsProcessorType,
89
+ IRWSViewComponent
88
90
  }
@@ -1,4 +1,4 @@
1
- import { IKDBTypesResponse } from '../types/IBackendCore';
1
+ import { ITypesResponse } from '../../../components/src/types/IBackendCore';
2
2
  import TheService from './_service';
3
3
 
4
4
  //@4DI
@@ -20,11 +20,14 @@ interface IAPIOptions {
20
20
  routeParams?: {
21
21
  [key: string]: string
22
22
  },
23
+ queryParams?: {
24
+ [key: string]: string
25
+ }
23
26
  }
24
27
 
25
28
  interface IHTTProute<P = {[key: string]: any}> {
26
29
  name: string;
27
- path: string;
30
+ path: string | string[];
28
31
  method: string;
29
32
  noParams?: boolean;
30
33
  options?: any;
@@ -89,16 +92,16 @@ class ApiService extends TheService {
89
92
  public delete = calls.delete;
90
93
 
91
94
  public back = {
92
- get: async <T>(routeName: string, options?: IAPIOptions, token?: string): Promise<T> => calls.get.bind(this)(backend.getBackendUrl.bind(this)(routeName, options?.routeParams), options) as Promise<T>,
93
- post: async <T, P extends object = object>(routeName: string, payload?: P, options?: IAPIOptions): Promise<T> => calls.post.bind(this)(backend.getBackendUrl.bind(this)(routeName, options?.routeParams), payload, options) as Promise<T>,
94
- put: async <T, P extends object = object>(routeName: string, payload: P, options?: IAPIOptions): Promise<T> => calls.put.bind(this)(backend.getBackendUrl.bind(this)(routeName, options?.routeParams), payload, options) as Promise<T>,
95
- delete: async <T>(routeName: string, options?: IAPIOptions): Promise<T> => calls.delete.bind(this)(backend.getBackendUrl.bind(this)(routeName, options?.routeParams), options) as Promise<T>,
95
+ get: async <T>(routeName: string, options?: IAPIOptions, token?: string): Promise<T> => calls.get.bind(this)(backend.getBackendUrl.bind(this)(routeName, options?.routeParams, options?.queryParams), options) as Promise<T>,
96
+ post: async <T, P extends object = object>(routeName: string, payload?: P, options?: IAPIOptions): Promise<T> => calls.post.bind(this)(backend.getBackendUrl.bind(this)(routeName, options?.routeParams, options?.queryParams), payload, options) as Promise<T>,
97
+ put: async <T, P extends object = object>(routeName: string, payload: P, options?: IAPIOptions): Promise<T> => calls.put.bind(this)(backend.getBackendUrl.bind(this)(routeName, options?.routeParams, options?.queryParams), payload, options) as Promise<T>,
98
+ delete: async <T>(routeName: string, options?: IAPIOptions): Promise<T> => calls.delete.bind(this)(backend.getBackendUrl.bind(this)(routeName, options?.routeParams, options?.queryParams), options) as Promise<T>,
96
99
  uploadFile: async (routeName: string, file: File, onProgress: (progress: number) => void, options: IAPIOptions = {}, payload: any = {}): Promise<UploadResponse> => this.uploadFile(backend.getBackendUrl.bind(this)(routeName, options?.routeParams), file, onProgress, payload),
97
100
  };
98
101
 
99
- async getResource(resourceName: string): Promise<IKDBTypesResponse>
102
+ async getResource(resourceName: string): Promise<ITypesResponse>
100
103
  {
101
- return calls.get.bind(this)(`${this.config.get('backendUrl')}${this.config.get('apiPrefix') || ''}/api/rws/resource/${resourceName}`) as Promise<IKDBTypesResponse>
104
+ return calls.get.bind(this)(`${this.config.get('backendUrl')}${this.config.get('apiPrefix') || ''}/api/rws/resource/${resourceName}`) as Promise<ITypesResponse>
102
105
  }
103
106
  }
104
107
 
@@ -83,7 +83,7 @@ class DOMService extends RWSService {
83
83
  sanitizeOptions: DOMPurify.Config = { })
84
84
  {
85
85
  const output: string = line.trim();
86
- const sanitized = DOMPurify.sanitize(output, { USE_PROFILES: { html: true }, ...sanitizeOptions});
86
+ const sanitized = DOMPurify.sanitize(output, { USE_PROFILES: { html: true }, ...sanitizeOptions}) as string;
87
87
  return sanitized;
88
88
  }
89
89
  }
@@ -2,7 +2,7 @@ import { ApiServiceInstance, IBackendRoute, IHTTProute } from "../ApiService";
2
2
  import { ConfigServiceInstance } from "../ConfigService";
3
3
 
4
4
  export const backend = {
5
- getBackendUrl(this: ApiServiceInstance, routeName: string, params: {[key: string]: string} = {}): string
5
+ getBackendUrl(this: ApiServiceInstance, routeName: string, params: {[key: string]: string} = {}, queryParams: {[key: string]: string} = {}): string
6
6
  {
7
7
  const config = this.config;
8
8
  const routesPackage = config.get('backendRoutes');
@@ -43,13 +43,12 @@ export const backend = {
43
43
  ];
44
44
  }
45
45
 
46
- routes = [...routes, ...item.routes.map((subRouteItem: IHTTProute): IHTTProute => {
46
+ routes = [...routes, ...item.routes.map((subRouteItem: IHTTProute): IHTTProute => {
47
47
  const subRoute: IHTTProute = {
48
- path: item.prefix + subRouteItem.path,
48
+ path: Array.isArray(subRouteItem.path) ? subRouteItem.path.map(subPath => item.prefix + subPath) : item.prefix + subRouteItem.path,
49
49
  name: backend.checkPrefixedRouteName(subRouteItem.name, item.controllerName),
50
50
  method: subRouteItem.method || 'GET'
51
- };
52
-
51
+ };
53
52
  return subRoute;
54
53
  })];
55
54
 
@@ -65,15 +64,48 @@ export const backend = {
65
64
  throw new Error(`Backend route '${routeName}' does not exist.`);
66
65
  }
67
66
 
68
- let apiPath = route.path;
67
+ let apiPath: string | string[] = route.path;
68
+
69
+ if(Array.isArray(apiPath)){
70
+ const paramsLength = Object.keys(params).length;
71
+ if(paramsLength > 0){
72
+ for(const searchedPath of apiPath){
73
+ let foundParams = 0;
74
+ for(const p of Object.keys(params)){
75
+ if(searchedPath.indexOf(`:${p}`) !== -1){
76
+ foundParams++;
77
+ }
78
+ }
79
+
80
+ if(foundParams === paramsLength){
81
+ apiPath = searchedPath;
82
+ break;
83
+ }
84
+ }
85
+ }else{
86
+ for(const searchedPath of apiPath){
87
+ if(!searchedPath.includes(':')){
88
+ apiPath = searchedPath;
89
+ break;
90
+ }
91
+ }
92
+ }
93
+ }
69
94
 
70
95
  Object.keys(params).forEach((paramKey: string) => {
71
96
  const paramValue = params[paramKey];
72
97
 
73
- apiPath = apiPath.replace(`:${paramKey}`, paramValue);
74
- });
98
+ apiPath = (apiPath as string).replace(`:${paramKey}`, paramValue);
99
+ });
100
+
101
+ let finalUrl = `${config.get('backendUrl')}${config.get('apiPrefix') || ''}${apiPath}`;
102
+
103
+ if(Object.keys(queryParams).length > 0){
104
+ const queryString = new URLSearchParams(queryParams).toString();
105
+ finalUrl += `?${queryString}`;
106
+ }
75
107
 
76
- return `${config.get('backendUrl')}${config.get('apiPrefix') || ''}${apiPath}`;
108
+ return finalUrl;
77
109
  },
78
110
  checkPrefixedRouteName(routeName: string, prefixName: string){
79
111
  let finalRoute = routeName;
@@ -1,9 +1,9 @@
1
- import { ViewTemplate } from '@microsoft/fast-element';
1
+ import { FASTElement, ViewTemplate } from '@microsoft/fast-element';
2
2
  import { DOMOutputType } from '../services/DOMService';
3
3
 
4
4
  type IAssetShowOptions = Record<string, any>;
5
5
 
6
- interface IRWSViewComponent extends Node {
6
+ interface IRWSViewComponent extends FASTElement {
7
7
  __isLoading: boolean;
8
8
  routeParams: Record<string, string>;
9
9
  trashIterator: number;
@@ -1,12 +0,0 @@
1
- export interface IKDBTypeInfo {
2
- fieldName: string;
3
- type: string;
4
- boundModel?: string
5
- }
6
-
7
- export interface IKDBTypesResponse {
8
- success: boolean;
9
- data: {
10
- types: IKDBTypeInfo[];
11
- };
12
- }
@@ -1,5 +0,0 @@
1
- export interface IRWSResourceQuery {
2
- fieldName: string,
3
- type: String,
4
- boundModel: string
5
- }
@@ -1,5 +0,0 @@
1
- export interface IReFormerField {
2
- name: string,
3
- defaultValue?: any,
4
- setForm: (field: string, value: any) => Promise<void>
5
- }