@lightningjs/renderer 0.7.4 → 0.7.5
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/dist/src/core/CoreNode.d.ts +4 -0
- package/dist/src/core/CoreNode.js +22 -0
- package/dist/src/core/CoreNode.js.map +1 -1
- package/dist/src/core/text-rendering/TrFontManager.js +71 -27
- package/dist/src/core/text-rendering/TrFontManager.js.map +1 -1
- package/dist/src/core/text-rendering/renderers/SdfTextRenderer/SdfTextRenderer.d.ts +1 -0
- package/dist/src/core/text-rendering/renderers/SdfTextRenderer/SdfTextRenderer.js +5 -1
- package/dist/src/core/text-rendering/renderers/SdfTextRenderer/SdfTextRenderer.js.map +1 -1
- package/dist/src/main-api/INode.d.ts +27 -0
- package/dist/src/main-api/Inspector.js +20 -15
- package/dist/src/main-api/Inspector.js.map +1 -1
- package/dist/src/main-api/RendererMain.js +3 -0
- package/dist/src/main-api/RendererMain.js.map +1 -1
- package/dist/src/render-drivers/main/MainOnlyNode.d.ts +4 -1
- package/dist/src/render-drivers/main/MainOnlyNode.js +10 -0
- package/dist/src/render-drivers/main/MainOnlyNode.js.map +1 -1
- package/dist/src/render-drivers/threadx/ThreadXMainNode.d.ts +4 -1
- package/dist/src/render-drivers/threadx/ThreadXMainNode.js +8 -0
- package/dist/src/render-drivers/threadx/ThreadXMainNode.js.map +1 -1
- package/dist/src/render-drivers/utils.d.ts +2 -0
- package/dist/src/render-drivers/utils.js +28 -0
- package/dist/src/render-drivers/utils.js.map +1 -1
- package/dist/tsconfig.dist.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/src/core/CoreNode.ts +29 -0
- package/src/core/text-rendering/TrFontManager.ts +104 -34
- package/src/core/text-rendering/renderers/SdfTextRenderer/SdfTextRenderer.ts +7 -4
- package/src/main-api/INode.ts +29 -0
- package/src/main-api/Inspector.ts +23 -16
- package/src/main-api/RendererMain.ts +3 -0
- package/src/render-drivers/main/MainOnlyNode.ts +13 -0
- package/src/render-drivers/threadx/ThreadXMainNode.ts +15 -1
- package/src/render-drivers/utils.ts +40 -0
|
@@ -17,10 +17,105 @@
|
|
|
17
17
|
* limitations under the License.
|
|
18
18
|
*/
|
|
19
19
|
|
|
20
|
-
import { SdfTrFontFace } from './font-face-types/SdfTrFontFace/SdfTrFontFace.js';
|
|
21
20
|
import type { TrFontFace } from './font-face-types/TrFontFace.js';
|
|
22
|
-
import { WebTrFontFace } from './font-face-types/WebTrFontFace.js';
|
|
23
21
|
import type { TextRendererMap, TrFontProps } from './renderers/TextRenderer.js';
|
|
22
|
+
import memize from 'memize';
|
|
23
|
+
|
|
24
|
+
const weightConversions: { [key: string]: number } = {
|
|
25
|
+
normal: 400,
|
|
26
|
+
bold: 700,
|
|
27
|
+
bolder: 900,
|
|
28
|
+
lighter: 100,
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
const fontWeightToNumber = (weight: string | number): number => {
|
|
32
|
+
if (typeof weight === 'number') {
|
|
33
|
+
return weight;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return weightConversions[weight] || 400;
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
function rawResolveFontToUse(
|
|
40
|
+
familyMapsByPriority: FontFamilyMap[],
|
|
41
|
+
family: string,
|
|
42
|
+
weightIn: string | number,
|
|
43
|
+
style: string,
|
|
44
|
+
stretch: string,
|
|
45
|
+
): TrFontFace | undefined {
|
|
46
|
+
const weight = fontWeightToNumber(weightIn);
|
|
47
|
+
let result = undefined;
|
|
48
|
+
|
|
49
|
+
for (const fontFamiles of familyMapsByPriority) {
|
|
50
|
+
if (result) {
|
|
51
|
+
break;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const fontFaces = fontFamiles[family];
|
|
55
|
+
if (!fontFaces) {
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if (fontFaces.size === 1) {
|
|
60
|
+
// No Exact match found, find nearest weight match
|
|
61
|
+
console.warn(
|
|
62
|
+
`TrFontManager: Only one font face found for family: '${family}' - will be used for all weights and styles`,
|
|
63
|
+
);
|
|
64
|
+
|
|
65
|
+
result = fontFaces.values().next().value as TrFontFace;
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const weightMap = new Map<number, TrFontFace>();
|
|
70
|
+
for (const fontFace of fontFaces) {
|
|
71
|
+
const fontFamilyWeight = fontWeightToNumber(fontFace.descriptors.weight);
|
|
72
|
+
if (
|
|
73
|
+
fontFamilyWeight === weight &&
|
|
74
|
+
fontFace.descriptors.style === style &&
|
|
75
|
+
fontFace.descriptors.stretch === stretch
|
|
76
|
+
) {
|
|
77
|
+
result = fontFace;
|
|
78
|
+
break;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
weightMap.set(fontFamilyWeight, fontFace);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// No Exact match found, find nearest weight match
|
|
85
|
+
const msg = `TrFontManager: No exact match: '${family} Weight: ${weight} Style: ${style} Stretch: ${stretch}'`;
|
|
86
|
+
console.error(msg);
|
|
87
|
+
|
|
88
|
+
if (!result && weight === 400 && weightMap.has(500)) {
|
|
89
|
+
result = weightMap.get(500);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
if (!result && weight === 500 && weightMap.has(400)) {
|
|
93
|
+
result = weightMap.get(400);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
if (!result && weight < 400) {
|
|
97
|
+
const lighter =
|
|
98
|
+
weightMap.get(300) || weightMap.get(200) || weightMap.get(100);
|
|
99
|
+
if (lighter) {
|
|
100
|
+
result = lighter;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
if (!result && weight > 500) {
|
|
105
|
+
const bolder =
|
|
106
|
+
weightMap.get(600) ||
|
|
107
|
+
weightMap.get(700) ||
|
|
108
|
+
weightMap.get(800) ||
|
|
109
|
+
weightMap.get(900);
|
|
110
|
+
if (bolder) {
|
|
111
|
+
result = bolder;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
return result;
|
|
117
|
+
}
|
|
118
|
+
const resolveFontToUse = memize(rawResolveFontToUse);
|
|
24
119
|
|
|
25
120
|
/**
|
|
26
121
|
* Structure mapping font family names to a set of font faces.
|
|
@@ -59,38 +154,13 @@ export class TrFontManager {
|
|
|
59
154
|
familyMapsByPriority: FontFamilyMap[],
|
|
60
155
|
props: TrFontProps,
|
|
61
156
|
): TrFontFace | undefined {
|
|
62
|
-
const
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
if (!fontFaces) {
|
|
70
|
-
return undefined;
|
|
71
|
-
}
|
|
72
|
-
const fontFacesCopy = new Set(fontFaces);
|
|
73
|
-
|
|
74
|
-
// Remove any font faces that don't match the style exactly
|
|
75
|
-
// TODO: In the future we may enhance this to find the best match
|
|
76
|
-
// based on font weight, style, etc.
|
|
77
|
-
// See https://www.w3.org/TR/2018/REC-css-fonts-3-20180920/#font-matching-algorithm
|
|
78
|
-
for (const fontFace of fontFacesCopy) {
|
|
79
|
-
if (
|
|
80
|
-
fontFace.descriptors.stretch !== props.fontStretch ||
|
|
81
|
-
fontFace.descriptors.style !== props.fontStyle ||
|
|
82
|
-
fontFace.descriptors.weight !== props.fontWeight
|
|
83
|
-
) {
|
|
84
|
-
fontFacesCopy.delete(fontFace);
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
// Return the first font face that matches the style exactly
|
|
89
|
-
return fontFacesCopy.values().next().value;
|
|
90
|
-
},
|
|
91
|
-
undefined,
|
|
157
|
+
const { fontFamily, fontWeight, fontStyle, fontStretch } = props;
|
|
158
|
+
return resolveFontToUse(
|
|
159
|
+
familyMapsByPriority,
|
|
160
|
+
fontFamily,
|
|
161
|
+
fontWeight,
|
|
162
|
+
fontStyle,
|
|
163
|
+
fontStretch,
|
|
92
164
|
);
|
|
93
|
-
|
|
94
|
-
return exactMatch || closeFaces[0];
|
|
95
165
|
}
|
|
96
166
|
}
|
|
@@ -127,6 +127,10 @@ export class SdfTextRenderer extends TextRenderer<SdfTextRendererState> {
|
|
|
127
127
|
*/
|
|
128
128
|
private ssdfFontFamilies: FontFamilyMap = {};
|
|
129
129
|
private msdfFontFamilies: FontFamilyMap = {};
|
|
130
|
+
private fontFamilyArray: FontFamilyMap[] = [
|
|
131
|
+
this.ssdfFontFamilies,
|
|
132
|
+
this.msdfFontFamilies,
|
|
133
|
+
];
|
|
130
134
|
private sdfShader: SdfShader;
|
|
131
135
|
private rendererBounds: Bound;
|
|
132
136
|
|
|
@@ -734,10 +738,9 @@ export class SdfTextRenderer extends TextRenderer<SdfTextRendererState> {
|
|
|
734
738
|
//#endregion Overrides
|
|
735
739
|
|
|
736
740
|
public resolveFontFace(props: TrFontProps): SdfTrFontFace | undefined {
|
|
737
|
-
return TrFontManager.resolveFontFace(
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
) as SdfTrFontFace | undefined;
|
|
741
|
+
return TrFontManager.resolveFontFace(this.fontFamilyArray, props) as
|
|
742
|
+
| SdfTrFontFace
|
|
743
|
+
| undefined;
|
|
741
744
|
}
|
|
742
745
|
|
|
743
746
|
/**
|
package/src/main-api/INode.ts
CHANGED
|
@@ -392,8 +392,36 @@ export interface INodeWritableProps {
|
|
|
392
392
|
* - `2 * Math.PI`: 360 rotation clockwise
|
|
393
393
|
*/
|
|
394
394
|
rotation: number;
|
|
395
|
+
/**
|
|
396
|
+
* Node data element for custom data storage (optional)
|
|
397
|
+
*
|
|
398
|
+
* @remarks
|
|
399
|
+
* This property is used to store custom data on the Node as a key/value data store.
|
|
400
|
+
* Data values are limited to string, numbers, booleans. Strings will be truncated
|
|
401
|
+
* to a 2048 character limit for performance reasons.
|
|
402
|
+
*
|
|
403
|
+
* This is not a data storage mechanism for large amounts of data please use a
|
|
404
|
+
* dedicated data storage mechanism for that.
|
|
405
|
+
*
|
|
406
|
+
* The custom data will be reflected in the inspector as part of `data-*` attributes
|
|
407
|
+
*
|
|
408
|
+
* @default `undefined`
|
|
409
|
+
*/
|
|
410
|
+
data?: CustomDataMap;
|
|
395
411
|
}
|
|
396
412
|
|
|
413
|
+
/**
|
|
414
|
+
* A custom data map which can be stored on the INode
|
|
415
|
+
*
|
|
416
|
+
* @remarks
|
|
417
|
+
* This is a map of key-value pairs that can be stored on an INode. It is used
|
|
418
|
+
* to store custom data that can be used by the application.
|
|
419
|
+
* The data stored can only be of type string, number or boolean.
|
|
420
|
+
*/
|
|
421
|
+
export type CustomDataMap = {
|
|
422
|
+
[key: string]: string | number | boolean;
|
|
423
|
+
};
|
|
424
|
+
|
|
397
425
|
export type INodeAnimatableProps = {
|
|
398
426
|
[Key in keyof INodeWritableProps as NonNullable<
|
|
399
427
|
INodeWritableProps[Key]
|
|
@@ -403,6 +431,7 @@ export type INodeAnimatableProps = {
|
|
|
403
431
|
};
|
|
404
432
|
|
|
405
433
|
export interface INodeEvents {
|
|
434
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
406
435
|
[s: string]: (target: INode, data: any) => void;
|
|
407
436
|
}
|
|
408
437
|
|
|
@@ -111,13 +111,6 @@ const stylePropertyMap: {
|
|
|
111
111
|
|
|
112
112
|
return { prop: 'transform', value: `scaleY(${v})` };
|
|
113
113
|
},
|
|
114
|
-
src: (v) => {
|
|
115
|
-
if (!v) {
|
|
116
|
-
return null;
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
return { prop: 'background-image', value: `url(${v})` };
|
|
120
|
-
},
|
|
121
114
|
color: (v) => {
|
|
122
115
|
if (v === 0) {
|
|
123
116
|
return null;
|
|
@@ -221,7 +214,7 @@ export class Inspector {
|
|
|
221
214
|
this.root.style.transformOrigin = '0 0 0';
|
|
222
215
|
this.root.style.transform = `scale(${this.scaleX}, ${this.scaleY})`;
|
|
223
216
|
this.root.style.overflow = 'hidden';
|
|
224
|
-
this.root.style.zIndex = '
|
|
217
|
+
this.root.style.zIndex = '65534';
|
|
225
218
|
}
|
|
226
219
|
|
|
227
220
|
createDiv(
|
|
@@ -254,6 +247,9 @@ export class Inspector {
|
|
|
254
247
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-explicit-any
|
|
255
248
|
(div as any).node = node;
|
|
256
249
|
|
|
250
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-explicit-any
|
|
251
|
+
(node as any).div = div;
|
|
252
|
+
|
|
257
253
|
return this.createProxy(node, div);
|
|
258
254
|
}
|
|
259
255
|
|
|
@@ -335,6 +331,18 @@ export class Inspector {
|
|
|
335
331
|
// special case for text
|
|
336
332
|
if (property === 'text') {
|
|
337
333
|
div.innerHTML = String(value);
|
|
334
|
+
|
|
335
|
+
// hide text because we can't render SDF fonts
|
|
336
|
+
// it would look weird and obstruct the WebGL rendering
|
|
337
|
+
div.style.visibility = 'hidden';
|
|
338
|
+
return;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
// special case for images
|
|
342
|
+
// we're not setting any CSS properties to avoid images getting loaded twice
|
|
343
|
+
// as the renderer will handle the loading of the image. Setting it to `data-src`
|
|
344
|
+
if (property === 'src' && value) {
|
|
345
|
+
div.setAttribute(`data-src`, String(value));
|
|
338
346
|
return;
|
|
339
347
|
}
|
|
340
348
|
|
|
@@ -376,14 +384,13 @@ export class Inspector {
|
|
|
376
384
|
}
|
|
377
385
|
|
|
378
386
|
// custom data properties
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
// }
|
|
387
|
+
if (property === 'data') {
|
|
388
|
+
for (const key in value) {
|
|
389
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
|
390
|
+
div.setAttribute(`data-${key}`, String(value[key]));
|
|
391
|
+
}
|
|
392
|
+
return;
|
|
393
|
+
}
|
|
387
394
|
}
|
|
388
395
|
|
|
389
396
|
// simple animation handler
|
|
@@ -39,6 +39,7 @@ import { FinalizationRegistryTextureUsageTracker } from './texture-usage-tracker
|
|
|
39
39
|
import type { TextureUsageTracker } from './texture-usage-trackers/TextureUsageTracker.js';
|
|
40
40
|
import { EventEmitter } from '../common/EventEmitter.js';
|
|
41
41
|
import { Inspector } from './Inspector.js';
|
|
42
|
+
import { santizeCustomDataMap } from '../render-drivers/utils.js';
|
|
42
43
|
|
|
43
44
|
/**
|
|
44
45
|
* An immutable reference to a specific Texture type
|
|
@@ -501,6 +502,7 @@ export class RendererMain extends EventEmitter {
|
|
|
501
502
|
props.colorBl ?? props.colorBottom ?? props.colorLeft ?? color;
|
|
502
503
|
const colorBr =
|
|
503
504
|
props.colorBr ?? props.colorBottom ?? props.colorRight ?? color;
|
|
505
|
+
const data = santizeCustomDataMap(props.data ?? {});
|
|
504
506
|
|
|
505
507
|
return {
|
|
506
508
|
x: props.x ?? 0,
|
|
@@ -536,6 +538,7 @@ export class RendererMain extends EventEmitter {
|
|
|
536
538
|
pivotX: props.pivotX ?? props.pivot ?? 0.5,
|
|
537
539
|
pivotY: props.pivotY ?? props.pivot ?? 0.5,
|
|
538
540
|
rotation: props.rotation ?? 0,
|
|
541
|
+
data: data,
|
|
539
542
|
};
|
|
540
543
|
}
|
|
541
544
|
|
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
*/
|
|
19
19
|
|
|
20
20
|
import type {
|
|
21
|
+
CustomDataMap,
|
|
21
22
|
INode,
|
|
22
23
|
INodeAnimatableProps,
|
|
23
24
|
INodeWritableProps,
|
|
@@ -39,6 +40,7 @@ import type {
|
|
|
39
40
|
NodeLoadedEventHandler,
|
|
40
41
|
NodeFailedEventHandler,
|
|
41
42
|
} from '../../common/CommonTypes.js';
|
|
43
|
+
import { santizeCustomDataMap } from '../utils.js';
|
|
42
44
|
|
|
43
45
|
let nextId = 1;
|
|
44
46
|
|
|
@@ -56,6 +58,7 @@ export class MainOnlyNode extends EventEmitter implements INode {
|
|
|
56
58
|
protected _parent: MainOnlyNode | null = null;
|
|
57
59
|
protected _texture: TextureRef | null = null;
|
|
58
60
|
protected _shader: ShaderRef | null = null;
|
|
61
|
+
protected _data: CustomDataMap | undefined = {};
|
|
59
62
|
|
|
60
63
|
constructor(
|
|
61
64
|
props: INodeWritableProps,
|
|
@@ -110,6 +113,7 @@ export class MainOnlyNode extends EventEmitter implements INode {
|
|
|
110
113
|
this.shader = props.shader;
|
|
111
114
|
this.texture = props.texture;
|
|
112
115
|
this.src = props.src;
|
|
116
|
+
this._data = props.data;
|
|
113
117
|
}
|
|
114
118
|
|
|
115
119
|
get x(): number {
|
|
@@ -425,8 +429,17 @@ export class MainOnlyNode extends EventEmitter implements INode {
|
|
|
425
429
|
}
|
|
426
430
|
}
|
|
427
431
|
|
|
432
|
+
get data(): CustomDataMap | undefined {
|
|
433
|
+
return this._data;
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
set data(d: CustomDataMap) {
|
|
437
|
+
this._data = santizeCustomDataMap(d);
|
|
438
|
+
}
|
|
439
|
+
|
|
428
440
|
destroy(): void {
|
|
429
441
|
this.emit('beforeDestroy', {});
|
|
442
|
+
this.coreNode.destroy();
|
|
430
443
|
this.parent = null;
|
|
431
444
|
this.texture = null;
|
|
432
445
|
this.emit('afterDestroy', {});
|
|
@@ -18,7 +18,11 @@
|
|
|
18
18
|
*/
|
|
19
19
|
|
|
20
20
|
import type { IAnimationController } from '../../common/IAnimationController.js';
|
|
21
|
-
import type {
|
|
21
|
+
import type {
|
|
22
|
+
CustomDataMap,
|
|
23
|
+
INode,
|
|
24
|
+
INodeAnimatableProps,
|
|
25
|
+
} from '../../main-api/INode.js';
|
|
22
26
|
import type {
|
|
23
27
|
RendererMain,
|
|
24
28
|
ShaderRef,
|
|
@@ -29,6 +33,7 @@ import type { NodeStruct } from './NodeStruct.js';
|
|
|
29
33
|
import { SharedNode } from './SharedNode.js';
|
|
30
34
|
import { ThreadXMainAnimationController } from './ThreadXMainAnimationController.js';
|
|
31
35
|
import type { AnimationSettings } from '../../core/animations/CoreAnimation.js';
|
|
36
|
+
import { santizeCustomDataMap } from '../utils.js';
|
|
32
37
|
|
|
33
38
|
export class ThreadXMainNode extends SharedNode implements INode {
|
|
34
39
|
private nextAnimationId = 1;
|
|
@@ -36,6 +41,7 @@ export class ThreadXMainNode extends SharedNode implements INode {
|
|
|
36
41
|
protected _children: ThreadXMainNode[] = [];
|
|
37
42
|
protected _texture: TextureRef | null = null;
|
|
38
43
|
protected _shader: ShaderRef | null = null;
|
|
44
|
+
protected _data: CustomDataMap | undefined = {};
|
|
39
45
|
private _src = '';
|
|
40
46
|
|
|
41
47
|
/**
|
|
@@ -171,6 +177,14 @@ export class ThreadXMainNode extends SharedNode implements INode {
|
|
|
171
177
|
return this.curProps;
|
|
172
178
|
}
|
|
173
179
|
|
|
180
|
+
get data(): CustomDataMap | undefined {
|
|
181
|
+
return this._data;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
set data(d: CustomDataMap) {
|
|
185
|
+
this._data = santizeCustomDataMap(d);
|
|
186
|
+
}
|
|
187
|
+
|
|
174
188
|
override destroy() {
|
|
175
189
|
super.destroy();
|
|
176
190
|
this.texture = null;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { CoreExtension } from '../../exports/core-api.js';
|
|
2
2
|
import type { Stage } from '../core/Stage.js';
|
|
3
|
+
import type { CustomDataMap } from '../main-api/INode.js';
|
|
3
4
|
|
|
4
5
|
/**
|
|
5
6
|
* Type guard that checks if a Class extends CoreExtension.
|
|
@@ -55,3 +56,42 @@ export async function loadCoreExtension(
|
|
|
55
56
|
);
|
|
56
57
|
}
|
|
57
58
|
}
|
|
59
|
+
|
|
60
|
+
export function santizeCustomDataMap(d: CustomDataMap): CustomDataMap {
|
|
61
|
+
const validTypes = { boolean: true, string: true, number: true };
|
|
62
|
+
|
|
63
|
+
const keys = Object.keys(d);
|
|
64
|
+
for (let i = 0; i < keys.length; i++) {
|
|
65
|
+
const key = keys[i];
|
|
66
|
+
if (!key) {
|
|
67
|
+
continue;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const value = d[key];
|
|
71
|
+
const valueType = typeof value;
|
|
72
|
+
|
|
73
|
+
// Typescript doesn't understand the above const valueType ¯\_(ツ)_/¯
|
|
74
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
75
|
+
// @ts-ignore-next-line
|
|
76
|
+
if (valueType === 'string' && value.length > 2048) {
|
|
77
|
+
console.warn(
|
|
78
|
+
`Custom Data value for ${key} is too long, it will be truncated to 2048 characters`,
|
|
79
|
+
);
|
|
80
|
+
|
|
81
|
+
// same here, see above comment, this can only be a string at this point
|
|
82
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
83
|
+
// @ts-ignore-next-line
|
|
84
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-call
|
|
85
|
+
d[key] = value.substring(0, 2048);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (!validTypes[valueType as keyof typeof validTypes]) {
|
|
89
|
+
console.warn(
|
|
90
|
+
`Custom Data value for ${key} is not a boolean, string, or number, it will be ignored`,
|
|
91
|
+
);
|
|
92
|
+
delete d[key];
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
return d;
|
|
97
|
+
}
|