structured-fw 1.7.5 → 1.7.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -0
- package/build/system/Util.d.ts +13 -2
- package/build/system/Util.js +118 -23
- package/build/system/types/component.types.d.ts +37 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -206,6 +206,8 @@ Session allows you to store temporary data for the users of your web application
|
|
|
206
206
|
|
|
207
207
|
Session data is tied to a visitor via sessionId, which is always available on `RequestContext`, which means you can interact with session data from routes and server side part of your components.
|
|
208
208
|
|
|
209
|
+
*Session data is stored in memory, which means it is lost when you restart the app. If you want to have persistent sessions that survive restart, you can use [structured-plugin-session-redis](https://www.npmjs.com/package/structured-plugin-session-redis).*
|
|
210
|
+
|
|
209
211
|
**Configuration**\
|
|
210
212
|
`StructuredConfig`.`session`:
|
|
211
213
|
```
|
|
@@ -590,6 +592,9 @@ Your lucky number is {{luckyNumber}}<br>
|
|
|
590
592
|
```
|
|
591
593
|
|
|
592
594
|
That's it. `AnotherComponent` will receive the `luckyNumber` as a number, you can pass any type of data, string, number, boolean, object, array... it will be received by the child as the same type of data. However, *keep in mind the data gets serialized and de-serialized in the process, so if you pass an object to a child, it won't be a reference to the original object, rather a copy of it*.
|
|
595
|
+
|
|
596
|
+
Since version 1.7.6, additional data types can be passed to child components without them breaking due to serialization: Date, BigInt, RegExp, Map, Uint8Array. *Let me know if you need support for more types that JSON.stringify breaks.*
|
|
597
|
+
|
|
593
598
|
\
|
|
594
599
|
Let's see how we can use the passed data within `AnotherComponent`, create `/app/views/AnotherComponent/AnotherComponent.html`:
|
|
595
600
|
```
|
package/build/system/Util.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { SerializableDate, SerializableRegExp, Serializable, ValueSerializable, SerializableBigInt, SerializableMap, SerializableUint8Array } from './types/component.types.js';
|
|
1
2
|
import { LooseObject } from './types/general.types.js';
|
|
2
3
|
import { PostedDataDecoded } from "./types/request.types.js";
|
|
3
4
|
export declare function queryStringDecode(queryString: string, initialValue?: PostedDataDecoded, trimValues?: boolean): PostedDataDecoded;
|
|
@@ -10,13 +11,23 @@ export declare function isAsync(fn: Function): boolean;
|
|
|
10
11
|
export declare function randomString(len: number, method?: 'alphanumeric' | 'numbers' | 'letters' | 'lettersUppercase' | 'lettersLowercase'): string;
|
|
11
12
|
export declare function unique<T>(arr: Array<T>): Array<T>;
|
|
12
13
|
export declare function stripTags(contentWithHTML: string, keepTags?: Array<string>): string;
|
|
14
|
+
export declare function toSerializableValue(value: any): ValueSerializable;
|
|
15
|
+
export declare function fromSerializableValue(data: SerializableDate): Date;
|
|
16
|
+
export declare function fromSerializableValue(data: SerializableRegExp): RegExp;
|
|
17
|
+
export declare function fromSerializableValue(data: SerializableMap): Map<any, any>;
|
|
18
|
+
export declare function fromSerializableValue(data: SerializableBigInt): BigInt;
|
|
19
|
+
export declare function fromSerializableValue(data: SerializableUint8Array): Uint8Array;
|
|
20
|
+
export declare function fromSerializableValue(data: Serializable): any;
|
|
21
|
+
export declare function serializableObject(data: LooseObject): LooseObject;
|
|
22
|
+
export declare function deserializeObject(data: LooseObject): LooseObject;
|
|
23
|
+
export declare function escapeAttributeValue(value: string): string;
|
|
24
|
+
export declare function unescapeAttributeValue(str: string): string;
|
|
13
25
|
export declare function attributeValueToString(key: string, value: any): string;
|
|
14
26
|
export declare function attributeValueFromString(attributeValue: string): string | {
|
|
15
27
|
key: string;
|
|
16
28
|
value: any;
|
|
17
29
|
};
|
|
18
|
-
export declare function
|
|
19
|
-
export declare function isObject(item: any): boolean;
|
|
30
|
+
export declare function isObject(value: any): boolean;
|
|
20
31
|
export declare function equalDeep(a: LooseObject, b: LooseObject): boolean;
|
|
21
32
|
export declare function mergeDeep(target: any, ...sources: Array<any>): LooseObject;
|
|
22
33
|
export declare function stripBOM(str: string): string;
|
package/build/system/Util.js
CHANGED
|
@@ -226,30 +226,128 @@ export function stripTags(contentWithHTML, keepTags = []) {
|
|
|
226
226
|
return sub.replaceAll('<', '<').replaceAll('>', '>');
|
|
227
227
|
});
|
|
228
228
|
}
|
|
229
|
-
function
|
|
230
|
-
|
|
231
|
-
|
|
229
|
+
export function toSerializableValue(value) {
|
|
230
|
+
if (value instanceof Date) {
|
|
231
|
+
return {
|
|
232
|
+
__sv: 1,
|
|
233
|
+
type: 'date',
|
|
234
|
+
value: value.toISOString(),
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
if (typeof value === 'bigint') {
|
|
238
|
+
return {
|
|
239
|
+
__sv: 1,
|
|
240
|
+
type: 'bigint',
|
|
241
|
+
value: value.toString(),
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
if (value instanceof RegExp) {
|
|
245
|
+
return {
|
|
246
|
+
__sv: 1,
|
|
247
|
+
type: 'regexp',
|
|
248
|
+
value: {
|
|
249
|
+
source: value.source,
|
|
250
|
+
flags: value.flags,
|
|
251
|
+
}
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
if (value instanceof Map) {
|
|
255
|
+
return {
|
|
256
|
+
__sv: 1,
|
|
257
|
+
type: 'map',
|
|
258
|
+
value: [...value.entries()],
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
if (value instanceof Uint8Array) {
|
|
262
|
+
return {
|
|
263
|
+
__sv: 1,
|
|
264
|
+
type: 'uint8array',
|
|
265
|
+
value: Array.from(value),
|
|
266
|
+
};
|
|
267
|
+
}
|
|
268
|
+
return {
|
|
269
|
+
__sv: 1,
|
|
270
|
+
value,
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
export function fromSerializableValue(data) {
|
|
274
|
+
if (!('type' in data)) {
|
|
275
|
+
return data.value;
|
|
276
|
+
}
|
|
277
|
+
if (data.type === 'date') {
|
|
278
|
+
return new Date(data.value);
|
|
279
|
+
}
|
|
280
|
+
if (data.type === 'regexp') {
|
|
281
|
+
return new RegExp(data.value.source, data.value.flags);
|
|
282
|
+
}
|
|
283
|
+
if (data.type === 'map') {
|
|
284
|
+
return new Map(data.value);
|
|
285
|
+
}
|
|
286
|
+
if (data.type === 'bigint') {
|
|
287
|
+
return BigInt(data.value);
|
|
288
|
+
}
|
|
289
|
+
if (data.type === 'uint8array') {
|
|
290
|
+
return new Uint8Array(data.value);
|
|
291
|
+
}
|
|
292
|
+
return undefined;
|
|
232
293
|
}
|
|
233
|
-
function
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
294
|
+
export function serializableObject(data) {
|
|
295
|
+
const copy = {};
|
|
296
|
+
objectEach(data, (key, val) => {
|
|
297
|
+
if (isObject(val)) {
|
|
298
|
+
copy[key] = serializableObject(val);
|
|
299
|
+
}
|
|
300
|
+
else {
|
|
301
|
+
copy[key] = toSerializableValue(val);
|
|
302
|
+
}
|
|
303
|
+
});
|
|
304
|
+
return copy;
|
|
305
|
+
}
|
|
306
|
+
export function deserializeObject(data) {
|
|
307
|
+
const copy = {};
|
|
308
|
+
objectEach(data, (key, val) => {
|
|
309
|
+
if (typeof val === 'object' && val !== null && val !== undefined) {
|
|
310
|
+
if ('__sv' in val) {
|
|
311
|
+
copy[key] = fromSerializableValue(val);
|
|
312
|
+
}
|
|
313
|
+
else {
|
|
314
|
+
copy[key] = deserializeObject(val);
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
else {
|
|
318
|
+
copy[key] = fromSerializableValue(val);
|
|
319
|
+
}
|
|
320
|
+
});
|
|
321
|
+
return copy;
|
|
322
|
+
}
|
|
323
|
+
export function escapeAttributeValue(value) {
|
|
324
|
+
return value
|
|
325
|
+
.replace(/~/g, '~~')
|
|
326
|
+
.replace(/"/g, '~q');
|
|
327
|
+
}
|
|
328
|
+
export function unescapeAttributeValue(str) {
|
|
329
|
+
const tempToken = `--${randomString(10)}--`;
|
|
330
|
+
return str
|
|
331
|
+
.split('~~').join(tempToken)
|
|
332
|
+
.split('~q').join('"')
|
|
333
|
+
.split(tempToken).join('~');
|
|
237
334
|
}
|
|
238
335
|
export function attributeValueToString(key, value) {
|
|
239
|
-
|
|
336
|
+
const data = serializableObject({ key, data: value });
|
|
337
|
+
return escapeAttributeValue(JSON.stringify(data));
|
|
240
338
|
}
|
|
241
339
|
export function attributeValueFromString(attributeValue) {
|
|
242
|
-
if (attributeValue.indexOf('
|
|
340
|
+
if (attributeValue.indexOf('{') === 0) {
|
|
243
341
|
try {
|
|
244
|
-
const decoded =
|
|
245
|
-
|
|
246
|
-
return attributeValue;
|
|
247
|
-
}
|
|
248
|
-
const valObj = JSON.parse(decoded);
|
|
342
|
+
const decoded = unescapeAttributeValue(attributeValue);
|
|
343
|
+
const valObj = deserializeObject(JSON.parse(decoded));
|
|
249
344
|
if (!('key' in valObj)) {
|
|
250
345
|
return decoded;
|
|
251
346
|
}
|
|
252
|
-
return
|
|
347
|
+
return {
|
|
348
|
+
key: valObj.key,
|
|
349
|
+
value: valObj.data,
|
|
350
|
+
};
|
|
253
351
|
}
|
|
254
352
|
catch (e) {
|
|
255
353
|
return attributeValue;
|
|
@@ -257,14 +355,11 @@ export function attributeValueFromString(attributeValue) {
|
|
|
257
355
|
}
|
|
258
356
|
return attributeValue;
|
|
259
357
|
}
|
|
260
|
-
export function
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
return (item && typeof item === 'object' && !Array.isArray(item)) && !Buffer.isBuffer(item);
|
|
266
|
-
}
|
|
267
|
-
return (item && typeof item === 'object' && !Array.isArray(item));
|
|
358
|
+
export function isObject(value) {
|
|
359
|
+
if (value === null || typeof value !== 'object')
|
|
360
|
+
return false;
|
|
361
|
+
const proto = Object.getPrototypeOf(value);
|
|
362
|
+
return proto === Object.prototype || proto === null;
|
|
268
363
|
}
|
|
269
364
|
export function equalDeep(a, b) {
|
|
270
365
|
if (a === b) {
|
|
@@ -39,6 +39,43 @@ export type ComponentEvents = {
|
|
|
39
39
|
componentCreated: Component;
|
|
40
40
|
ready: undefined;
|
|
41
41
|
};
|
|
42
|
+
export type Serializable = {
|
|
43
|
+
__sv: 1;
|
|
44
|
+
value: any;
|
|
45
|
+
};
|
|
46
|
+
export type SerializableDate = {
|
|
47
|
+
__sv: 1;
|
|
48
|
+
type: 'date';
|
|
49
|
+
value: string;
|
|
50
|
+
};
|
|
51
|
+
export type SerializableRegExp = {
|
|
52
|
+
__sv: 1;
|
|
53
|
+
type: 'regexp';
|
|
54
|
+
value: {
|
|
55
|
+
source: string;
|
|
56
|
+
flags: string;
|
|
57
|
+
};
|
|
58
|
+
};
|
|
59
|
+
export type SerializableMap = {
|
|
60
|
+
__sv: 1;
|
|
61
|
+
type: 'map';
|
|
62
|
+
value: Array<[any, any]>;
|
|
63
|
+
};
|
|
64
|
+
export type SerializableBigInt = {
|
|
65
|
+
__sv: 1;
|
|
66
|
+
type: 'bigint';
|
|
67
|
+
value: string;
|
|
68
|
+
};
|
|
69
|
+
export type SerializableUint8Array = {
|
|
70
|
+
__sv: 1;
|
|
71
|
+
type: 'uint8array';
|
|
72
|
+
value: Array<number>;
|
|
73
|
+
};
|
|
74
|
+
export type ValueSerializable = Serializable | SerializableDate | SerializableRegExp | SerializableMap | SerializableBigInt | SerializableUint8Array;
|
|
75
|
+
export type AttributeEncodedObject = {
|
|
76
|
+
key: string;
|
|
77
|
+
data: ValueSerializable;
|
|
78
|
+
};
|
|
42
79
|
export type ClientComponentTransition = {
|
|
43
80
|
fade: false | number;
|
|
44
81
|
slide: false | number;
|
package/package.json
CHANGED
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
"license": "MIT",
|
|
20
20
|
"type": "module",
|
|
21
21
|
"main": "build/index",
|
|
22
|
-
"version": "1.7.
|
|
22
|
+
"version": "1.7.8",
|
|
23
23
|
"scripts": {
|
|
24
24
|
"develop": "tsc --watch",
|
|
25
25
|
"startDev": "cd build && nodemon --watch '../app/**/*' --watch '../build/**/*' -e js,html,hbs,css index.js",
|