@windstream/react-shared-components 0.2.82 → 0.2.84

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.
@@ -0,0 +1,89 @@
1
+ export type TokenEntry = {
2
+ token?: string;
3
+ value?: string;
4
+ promo?: string;
5
+ [key: string]: unknown;
6
+ };
7
+
8
+ export type TokenSource = TokenEntry[] | string | null | undefined;
9
+
10
+ export type RichTextDocument = {
11
+ nodeType: string;
12
+ content: unknown[];
13
+ };
14
+
15
+ export type ReplaceTokensResult<T> = T extends string ? string : T;
16
+
17
+ const TOKEN_PATTERN = /\$\{([^${}]+)\}/g;
18
+
19
+ const normalizeEntries = (tokens: TokenSource): TokenEntry[] => {
20
+ if (Array.isArray(tokens)) return tokens;
21
+ if (typeof tokens === "string" && tokens.trim()) {
22
+ try {
23
+ const parsed = JSON.parse(tokens);
24
+ return Array.isArray(parsed) ? (parsed as TokenEntry[]) : [];
25
+ } catch {
26
+ return [];
27
+ }
28
+ }
29
+ return [];
30
+ };
31
+
32
+ const buildTokenMap = (tokens: TokenSource): Record<string, string> =>
33
+ normalizeEntries(tokens).reduce<Record<string, string>>((acc, entry) => {
34
+ if (entry && typeof entry.token === "string" && entry.token) {
35
+ const promo = typeof entry.promo === "string" ? entry.promo : undefined;
36
+ const value = typeof entry.value === "string" ? entry.value : undefined;
37
+ const resolved = promo ?? value;
38
+ if (resolved !== undefined) acc[entry.token] = resolved;
39
+ }
40
+ return acc;
41
+ }, {});
42
+
43
+ const replaceInString = (input: string, map: Record<string, string>): string =>
44
+ input.replace(TOKEN_PATTERN, (match, token) =>
45
+ Object.prototype.hasOwnProperty.call(map, token) ? map[token] : match
46
+ );
47
+
48
+ const isRichTextDocument = (value: unknown): value is RichTextDocument =>
49
+ !!value &&
50
+ typeof value === "object" &&
51
+ "nodeType" in (value as Record<string, unknown>) &&
52
+ "content" in (value as Record<string, unknown>);
53
+
54
+ const replaceInNode = <T>(node: T, map: Record<string, string>): T => {
55
+ if (!node || typeof node !== "object") return node;
56
+ const current = node as Record<string, unknown>;
57
+
58
+ if (current.nodeType === "text" && typeof current.value === "string") {
59
+ return { ...current, value: replaceInString(current.value, map) } as T;
60
+ }
61
+
62
+ if (Array.isArray(current.content)) {
63
+ return {
64
+ ...current,
65
+ content: current.content.map(child => replaceInNode(child, map)),
66
+ } as T;
67
+ }
68
+
69
+ return node;
70
+ };
71
+
72
+ export function replaceTokens<
73
+ T extends string | RichTextDocument | null | undefined,
74
+ >(input: T, tokens: TokenSource): ReplaceTokensResult<T> {
75
+ if (input == null || input === "") return input as ReplaceTokensResult<T>;
76
+
77
+ const map = buildTokenMap(tokens);
78
+ if (Object.keys(map).length === 0) return input as ReplaceTokensResult<T>;
79
+
80
+ if (typeof input === "string") {
81
+ return replaceInString(input, map) as ReplaceTokensResult<T>;
82
+ }
83
+
84
+ if (isRichTextDocument(input)) {
85
+ return replaceInNode(input, map) as ReplaceTokensResult<T>;
86
+ }
87
+
88
+ return input as ReplaceTokensResult<T>;
89
+ }