structured-fw 1.7.5 → 1.7.7

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 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
  ```
@@ -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,22 @@ 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;
13
23
  export declare function attributeValueToString(key: string, value: any): string;
14
24
  export declare function attributeValueFromString(attributeValue: string): string | {
15
25
  key: string;
16
26
  value: any;
17
27
  };
18
28
  export declare function attributeValueEscape(str: string): string;
19
- export declare function isObject(item: any): boolean;
29
+ export declare function isObject(value: any): boolean;
20
30
  export declare function equalDeep(a: LooseObject, b: LooseObject): boolean;
21
31
  export declare function mergeDeep(target: any, ...sources: Array<any>): LooseObject;
22
32
  export declare function stripBOM(str: string): string;
@@ -235,8 +235,103 @@ function bytesToBase64(bytes) {
235
235
  return prev + String.fromCharCode(curr);
236
236
  }, ''));
237
237
  }
238
+ export function toSerializableValue(value) {
239
+ if (value instanceof Date) {
240
+ return {
241
+ __structured_value: true,
242
+ type: 'date',
243
+ value: value.toISOString(),
244
+ };
245
+ }
246
+ if (typeof value === 'bigint') {
247
+ return {
248
+ __structured_value: true,
249
+ type: 'bigint',
250
+ value: value.toString(),
251
+ };
252
+ }
253
+ if (value instanceof RegExp) {
254
+ return {
255
+ __structured_value: true,
256
+ type: 'regexp',
257
+ value: {
258
+ source: value.source,
259
+ flags: value.flags,
260
+ }
261
+ };
262
+ }
263
+ if (value instanceof Map) {
264
+ return {
265
+ __structured_value: true,
266
+ type: 'map',
267
+ value: [...value.entries()],
268
+ };
269
+ }
270
+ if (value instanceof Uint8Array) {
271
+ return {
272
+ __structured_value: true,
273
+ type: 'uint8array',
274
+ value: Array.from(value),
275
+ };
276
+ }
277
+ return {
278
+ __structured_value: true,
279
+ value,
280
+ };
281
+ }
282
+ export function fromSerializableValue(data) {
283
+ if (!('type' in data)) {
284
+ return data.value;
285
+ }
286
+ if (data.type === 'date') {
287
+ return new Date(data.value);
288
+ }
289
+ if (data.type === 'regexp') {
290
+ return new RegExp(data.value.source, data.value.flags);
291
+ }
292
+ if (data.type === 'map') {
293
+ return new Map(data.value);
294
+ }
295
+ if (data.type === 'bigint') {
296
+ return BigInt(data.value);
297
+ }
298
+ if (data.type === 'uint8array') {
299
+ return new Uint8Array(data.value);
300
+ }
301
+ return undefined;
302
+ }
303
+ export function serializableObject(data) {
304
+ const copy = {};
305
+ objectEach(data, (key, val) => {
306
+ if (isObject(val)) {
307
+ copy[key] = serializableObject(val);
308
+ }
309
+ else {
310
+ copy[key] = toSerializableValue(val);
311
+ }
312
+ });
313
+ return copy;
314
+ }
315
+ export function deserializeObject(data) {
316
+ const copy = {};
317
+ objectEach(data, (key, val) => {
318
+ if (typeof val === 'object' && val !== null && val !== undefined) {
319
+ if ('__structured_value' in val) {
320
+ copy[key] = fromSerializableValue(val);
321
+ }
322
+ else {
323
+ copy[key] = deserializeObject(val);
324
+ }
325
+ }
326
+ else {
327
+ copy[key] = fromSerializableValue(val);
328
+ }
329
+ });
330
+ return copy;
331
+ }
238
332
  export function attributeValueToString(key, value) {
239
- return 'base64:' + bytesToBase64(new TextEncoder().encode(JSON.stringify({ key, value })));
333
+ const data = serializableObject({ key, data: value });
334
+ return 'base64:' + bytesToBase64(new TextEncoder().encode(JSON.stringify(data)));
240
335
  }
241
336
  export function attributeValueFromString(attributeValue) {
242
337
  if (attributeValue.indexOf('base64:') === 0) {
@@ -245,11 +340,14 @@ export function attributeValueFromString(attributeValue) {
245
340
  if (decoded.indexOf('{') !== 0) {
246
341
  return attributeValue;
247
342
  }
248
- const valObj = JSON.parse(decoded);
343
+ const valObj = deserializeObject(JSON.parse(decoded));
249
344
  if (!('key' in valObj)) {
250
345
  return decoded;
251
346
  }
252
- return valObj;
347
+ return {
348
+ key: valObj.key,
349
+ value: valObj.data,
350
+ };
253
351
  }
254
352
  catch (e) {
255
353
  return attributeValue;
@@ -260,11 +358,11 @@ export function attributeValueFromString(attributeValue) {
260
358
  export function attributeValueEscape(str) {
261
359
  return str.replaceAll('"', '&quot;');
262
360
  }
263
- export function isObject(item) {
264
- if (typeof window === 'undefined') {
265
- return (item && typeof item === 'object' && !Array.isArray(item)) && !Buffer.isBuffer(item);
266
- }
267
- return (item && typeof item === 'object' && !Array.isArray(item));
361
+ export function isObject(value) {
362
+ if (value === null || typeof value !== 'object')
363
+ return false;
364
+ const proto = Object.getPrototypeOf(value);
365
+ return proto === Object.prototype || proto === null;
268
366
  }
269
367
  export function equalDeep(a, b) {
270
368
  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
+ __structured_value: true;
44
+ value: any;
45
+ };
46
+ export type SerializableDate = {
47
+ __structured_value: true;
48
+ type: 'date';
49
+ value: string;
50
+ };
51
+ export type SerializableRegExp = {
52
+ __structured_value: true;
53
+ type: 'regexp';
54
+ value: {
55
+ source: string;
56
+ flags: string;
57
+ };
58
+ };
59
+ export type SerializableMap = {
60
+ __structured_value: true;
61
+ type: 'map';
62
+ value: Array<[any, any]>;
63
+ };
64
+ export type SerializableBigInt = {
65
+ __structured_value: true;
66
+ type: 'bigint';
67
+ value: string;
68
+ };
69
+ export type SerializableUint8Array = {
70
+ __structured_value: true;
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.5",
22
+ "version": "1.7.7",
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",