@ecency/render-helper 2.2.4 → 2.2.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ecency/render-helper",
3
- "version": "2.2.4",
3
+ "version": "2.2.8",
4
4
  "description": "Markdown+Html Render helper",
5
5
  "main": "lib/index.js",
6
6
  "types": "lib/index.d.ts",
@@ -63,6 +63,7 @@
63
63
  "multihashes": "0.4.13",
64
64
  "path": "^0.12.7",
65
65
  "querystring": "^0.2.0",
66
+ "react-native-crypto-js": "^1.0.0",
66
67
  "remarkable": "^2.0.1",
67
68
  "url": "^0.11.0",
68
69
  "xmldom": "^0.5.0",
@@ -15,7 +15,8 @@ export const ALLOWED_ATTRIBUTES: XSSWhiteList = {
15
15
  'data-video-href',
16
16
  'data-proposal',
17
17
  'class',
18
- 'title'
18
+ 'title',
19
+ 'data-id'
19
20
  ],
20
21
  'img': ['src', 'alt', 'class'],
21
22
  'span': ['class'],
@@ -12,7 +12,7 @@ export const INTERNAL_POST_TAG_REGEX = /\/(.*)\/(@[\w.\d-]+)\/(.*)/i
12
12
  export const INTERNAL_POST_REGEX = /^\/(@[\w.\d-]+)\/(.*)$/i
13
13
  export const CUSTOM_COMMUNITY_REGEX = /^https?:\/\/(.*)\/c\/(hive-\d+)(.*)/i
14
14
  export const YOUTUBE_REGEX = /(?:https?:\/\/)?(?:www\.)?(?:youtube\.com\/watch\?v=|youtu\.be\/)([^& \n<]+)(?:[^ \n<]+)?/g
15
- export const YOUTUBE_EMBED_REGEX = /^(https?:)?\/\/www.youtube.com\/embed\/.*/i
15
+ export const YOUTUBE_EMBED_REGEX = /^(https?:)?\/\/www.youtube.com\/(embed|shorts)\/.*/i
16
16
  export const VIMEO_REGEX = /(https?:\/\/)?(www\.)?(?:vimeo)\.com.*(?:videos|video|channels|)\/([\d]+)/i
17
17
  export const VIMEO_EMBED_REGEX = /https:\/\/player\.vimeo\.com\/video\/([0-9]+)/
18
18
  export const BITCHUTE_REGEX = /^(?:https?:\/\/)?(?:www\.)?bitchute.com\/(?:video|embed)\/([a-z0-9]+)/i
@@ -22,5 +22,7 @@ export const WHITE_LIST = [
22
22
  'naturalmedicine.io',
23
23
  'bilpcoin.com',
24
24
  'stemgeeks.net',
25
- 'hiveblockexplorer.com'
25
+ 'hiveblockexplorer.com',
26
+ 'proofofbrain.blog',
27
+ 'liketu.com',
26
28
  ]
@@ -428,14 +428,14 @@ describe('Markdown2Html', () => {
428
428
  expect(markdown2Html(input)).toBe(expected)
429
429
  })
430
430
 
431
- it('28- Should handle peakd post links', () => {
431
+ it('28 - Should handle peakd post links', () => {
432
432
  const input = {
433
433
  author: 'foo3343',
434
434
  permlink: 'bar3243',
435
435
  last_update: '2019-05-10T09:15:21',
436
436
  body: 'https://peakd.com/@demo/tests'
437
437
  }
438
- const expected = '<p><a class="markdown-post-link" data-tag="post" data-author="demo" data-permlink="tests">/@demo/tests</a></p>'
438
+ const expected = '<p><a class=\"markdown-post-link\" data-tag=\"post\" data-author=\"demo\" data-permlink=\"tests\">@demo/tests</a></p>'
439
439
 
440
440
  expect(markdown2Html(input)).toBe(expected)
441
441
  })
@@ -476,14 +476,14 @@ describe('Markdown2Html', () => {
476
476
  expect(markdown2Html(input)).toBe(expected)
477
477
  })
478
478
 
479
- it('32- Should handle whitelisted post links', () => {
479
+ it('32 - Should handle whitelisted post links', () => {
480
480
  const input = {
481
481
  author: 'foo33435',
482
482
  permlink: 'bar32435',
483
483
  last_update: '2019-05-10T09:15:21',
484
484
  body: 'https://peakd.com/tag/@demo/tests and https://steemit.com/test/@demo/post'
485
485
  }
486
- const expected = '<p><a class="markdown-post-link" data-tag="tag" data-author="demo" data-permlink="tests">/@demo/tests</a> and <a class="markdown-post-link" data-tag="test" data-author="demo" data-permlink="post">/@demo/post</a></p>'
486
+ const expected = '<p><a class=\"markdown-post-link\" data-tag=\"tag\" data-author=\"demo\" data-permlink=\"tests\">@demo/tests</a> and <a class=\"markdown-post-link\" data-tag=\"test\" data-author=\"demo\" data-permlink=\"post\">@demo/post</a></p>'
487
487
 
488
488
  expect(markdown2Html(input)).toBe(expected)
489
489
  })
@@ -644,14 +644,14 @@ describe('Markdown2Html', () => {
644
644
  expect(markdown2Html(input)).toBe(expected)
645
645
  })
646
646
 
647
- it('45- Should handle collection linking', () => {
647
+ it('45 - Should handle collection linking', () => {
648
648
  const input = {
649
649
  author: 'foo345',
650
650
  permlink: 'bar345',
651
651
  last_update: '2019-05-10T09:15:21',
652
652
  body: 'this is link https://peakd.com/ccc/jarvie/one-week-roadtrip-to-all-5-utah-national-parks-and-more'
653
653
  }
654
- const expected = '<p>this is link <a class=\"markdown-post-link\" data-tag=\"ccc\" data-author=\"jarvie\" data-permlink=\"one-week-roadtrip-to-all-5-utah-national-parks-and-more\">/@jarvie/one-week-roadtrip-to-all-5-utah-national-parks-and-more</a></p>'
654
+ const expected = '<p>this is link <a class=\"markdown-post-link\" data-tag=\"ccc\" data-author=\"jarvie\" data-permlink=\"one-week-roadtrip-to-all-5-utah-national-parks-and-more\">@jarvie/one-week-roadtrip-to-all-5-utah-national-parks-and-more</a></p>'
655
655
 
656
656
  expect(markdown2Html(input)).toBe(expected)
657
657
  })
@@ -663,7 +663,7 @@ describe('Markdown2Html', () => {
663
663
  last_update: '2019-05-10T09:15:21',
664
664
  body: '[click here](https://peakd.com/@praetoria-cartel/wallet) direct link https://peakd.com/@praetoria-cartel/posts'
665
665
  }
666
- const expected = '<p><a href="https://ecency.com/@praetoria-cartel/wallet" class="markdown-profile-link">click here</a> direct link <a href="https://ecency.com/@praetoria-cartel/posts" class="markdown-profile-link">/@praetoria-cartel/posts</a></p>'
666
+ const expected = '<p><a href=\"https://ecency.com/@praetoria-cartel/wallet\" class=\"markdown-profile-link\">click here</a> direct link <a href=\"https://ecency.com/@praetoria-cartel/posts\" class=\"markdown-profile-link\">@praetoria-cartel/posts</a></p>'
667
667
 
668
668
  expect(markdown2Html(input)).toBe(expected)
669
669
  })
@@ -675,12 +675,12 @@ describe('Markdown2Html', () => {
675
675
  last_update: '2019-05-10T09:15:21',
676
676
  body: 'for history refer to this fine post /@offgridlife/proofofbrain-golden-rule-do-to-others-what-you-want-them-to-do-to-you and while you are in the community'
677
677
  }
678
- const expected = '<p><span>for history refer to this fine post <a class=\"markdown-post-link\" data-author=\"offgridlife\" data-tag=\"post\" data-permlink=\"proofofbrain-golden-rule-do-to-others-what-you-want-them-to-do-to-you\">/@offgridlife/proofofbrain-golden-rule-do-to-others-what-you-want-them-to-do-to-you</a> and while you are in the community</span></p>'
678
+ const expected = '<p><span>for history refer to this fine post <a class=\"markdown-post-link\" data-author=\"offgridlife\" data-tag=\"post\" data-permlink=\"proofofbrain-golden-rule-do-to-others-what-you-want-them-to-do-to-you\">@offgridlife/proofofbrain-golden-rule-do-to-others-what-you-want-them-to-do-to-you</a> and while you are in the community</span></p>'
679
679
 
680
680
  expect(markdown2Html(input)).toBe(expected)
681
681
  })
682
682
 
683
- it('47 - Should handle Bitchute links', () => {
683
+ it('48 - Should handle Bitchute links', () => {
684
684
  const input = {
685
685
  author: 'foo347',
686
686
  permlink: 'bar347',
@@ -817,6 +817,66 @@ describe('Markdown2Html', () => {
817
817
 
818
818
  expect(markdown2Html(input)).toBe(expected)
819
819
  })
820
+
821
+ it('60 - Should username with permlink', () => {
822
+ const input = {
823
+ author: 'foo360',
824
+ permlink: 'bar360',
825
+ last_update: '2021-10-23T09:15:21',
826
+ body: 'this is link @demo/test for internal'
827
+ }
828
+ const expected = '<p><span>this is link <a class=\"markdown-post-link\" data-author=\"demo\" data-tag=\"post\" data-permlink=\"test\">@demo/test</a> for internal</span></p>'
829
+
830
+ expect(markdown2Html(input)).toBe(expected)
831
+ })
832
+
833
+ it('61 - Should username with permlink with slash', () => {
834
+ const input = {
835
+ author: 'foo361',
836
+ permlink: 'bar361',
837
+ last_update: '2021-10-23T09:15:21',
838
+ body: 'this is link /@demo/test for internal'
839
+ }
840
+ const expected = '<p><span>this is link <a class=\"markdown-post-link\" data-author=\"demo\" data-tag=\"post\" data-permlink=\"test\">@demo/test</a> for internal</span></p>'
841
+
842
+ expect(markdown2Html(input)).toBe(expected)
843
+ })
844
+
845
+ it('62 - Should username with permlink new line', () => {
846
+ const input = {
847
+ author: 'foo362',
848
+ permlink: 'bar362',
849
+ last_update: '2021-10-23T09:15:21',
850
+ body: '@demo/test for internal'
851
+ }
852
+ const expected = '<p><span> <a class=\"markdown-post-link\" data-author=\"demo\" data-tag=\"post\" data-permlink=\"test\">@demo/test</a> for internal</span></p>'
853
+
854
+ expect(markdown2Html(input)).toBe(expected)
855
+ })
856
+
857
+ it('63 - Should username with permlink with slash new line', () => {
858
+ const input = {
859
+ author: 'foo363',
860
+ permlink: 'bar363',
861
+ last_update: '2021-10-23T09:15:21',
862
+ body: '/@demo/test for internal'
863
+ }
864
+ const expected = '<p><span> <a class=\"markdown-post-link\" data-author=\"demo\" data-tag=\"post\" data-permlink=\"test\">@demo/test</a> for internal</span></p>'
865
+
866
+ expect(markdown2Html(input)).toBe(expected)
867
+ })
868
+
869
+ it('64 - Should username with section link', () => {
870
+ const input = {
871
+ author: 'foo364',
872
+ permlink: 'bar364',
873
+ last_update: '2021-10-23T09:15:21',
874
+ body: '@demo/wallet for internal'
875
+ }
876
+ const expected = '<p><span> <a class=\"markdown-profile-link\" href=\"/@demo/wallet\">@demo/wallet</a> for internal</span></p>'
877
+
878
+ expect(markdown2Html(input, false)).toBe(expected)
879
+ })
820
880
  })
821
881
 
822
882
  describe("Rumble support", () => {
@@ -99,7 +99,7 @@ export function a(el: HTMLElement, forApp: boolean, webp: boolean): void {
99
99
  const author = postMatch[3].replace('@', '')
100
100
  const permlink = postMatch[4]
101
101
  if (el.textContent === href) {
102
- el.textContent = `/@${author}/${permlink}`
102
+ el.textContent = `@${author}/${permlink}`
103
103
  }
104
104
  if (forApp) {
105
105
  el.removeAttribute('href')
@@ -119,16 +119,18 @@ export function a(el: HTMLElement, forApp: boolean, webp: boolean): void {
119
119
  if (mentionMatch && WHITE_LIST.includes(mentionMatch[1].replace(/www./,'')) && mentionMatch.length === 3) {
120
120
  el.setAttribute('class', 'markdown-author-link')
121
121
  const author = mentionMatch[2].replace('@', '').toLowerCase()
122
- if (el.textContent === href) {
123
- el.textContent = `@${author}`
124
- }
125
- if (forApp) {
126
- el.removeAttribute('href')
127
-
128
- el.setAttribute('data-author', author)
129
- } else {
130
- const h = `/@${author}`
131
- el.setAttribute('href', h)
122
+ if (author.indexOf('/')===-1) {
123
+ if (el.textContent === href) {
124
+ el.textContent = `@${author}`
125
+ }
126
+ if (forApp) {
127
+ el.removeAttribute('href')
128
+
129
+ el.setAttribute('data-author', author)
130
+ } else {
131
+ const h = `/@${author}`
132
+ el.setAttribute('href', h)
133
+ }
132
134
  }
133
135
  return
134
136
  }
@@ -144,7 +146,7 @@ export function a(el: HTMLElement, forApp: boolean, webp: boolean): void {
144
146
  const section = tpostMatch[3]
145
147
 
146
148
  if (el.textContent === href) {
147
- el.textContent = `/@${author}/${section}`
149
+ el.textContent = `@${author}/${section}`
148
150
  }
149
151
  if (forApp) {
150
152
  const ha = `https://ecency.com/@${author}/${section}`
@@ -165,7 +167,7 @@ export function a(el: HTMLElement, forApp: boolean, webp: boolean): void {
165
167
  const author = tpostMatch[2].replace('@', '')
166
168
  const permlink = tpostMatch[3]
167
169
  if (el.textContent === href) {
168
- el.textContent = `/@${author}/${permlink}`
170
+ el.textContent = `@${author}/${permlink}`
169
171
  }
170
172
  if (forApp) {
171
173
  el.removeAttribute('href')
@@ -186,16 +188,18 @@ export function a(el: HTMLElement, forApp: boolean, webp: boolean): void {
186
188
  if (imentionMatch) {
187
189
  el.setAttribute('class', 'markdown-author-link')
188
190
  const author = imentionMatch[0].replace('/@', '').toLowerCase()
189
- if (el.textContent === href) {
190
- el.textContent = `@${author}`
191
- }
192
- if (forApp) {
193
- el.removeAttribute('href')
191
+ if (author.indexOf('/')===-1) {
192
+ if (el.textContent === href) {
193
+ el.textContent = `@${author}`
194
+ }
195
+ if (forApp) {
196
+ el.removeAttribute('href')
194
197
 
195
- el.setAttribute('data-author', author)
196
- } else {
197
- const h = `/@${author}`
198
- el.setAttribute('href', h)
198
+ el.setAttribute('data-author', author)
199
+ } else {
200
+ const h = `/@${author}`
201
+ el.setAttribute('href', h)
202
+ }
199
203
  }
200
204
  return
201
205
  }
@@ -211,7 +215,7 @@ export function a(el: HTMLElement, forApp: boolean, webp: boolean): void {
211
215
  const section = cpostMatch[2]
212
216
 
213
217
  if (el.textContent === href) {
214
- el.textContent = `/@${author}/${section}`
218
+ el.textContent = `@${author}/${section}`
215
219
  }
216
220
  if (forApp) {
217
221
  const ha = `https://ecency.com/@${author}/${section}`
@@ -229,7 +233,7 @@ export function a(el: HTMLElement, forApp: boolean, webp: boolean): void {
229
233
  const author = cpostMatch[1].replace('@', '')
230
234
  const permlink = cpostMatch[2]
231
235
  if (el.textContent === href) {
232
- el.textContent = `/@${author}/${permlink}`
236
+ el.textContent = `@${author}/${permlink}`
233
237
  }
234
238
  if (forApp) {
235
239
  el.removeAttribute('href')
@@ -325,7 +329,7 @@ export function a(el: HTMLElement, forApp: boolean, webp: boolean): void {
325
329
  const author = cccMatch[2].replace('@', '')
326
330
  const permlink = cccMatch[3]
327
331
  if (el.textContent === href) {
328
- el.textContent = `/@${author}/${permlink}`
332
+ el.textContent = `@${author}/${permlink}`
329
333
  }
330
334
  if (forApp) {
331
335
  el.removeAttribute('href')
@@ -662,7 +666,12 @@ export function a(el: HTMLElement, forApp: boolean, webp: boolean): void {
662
666
  el.setAttribute('data-href', href)
663
667
  el.removeAttribute('href')
664
668
  } else {
665
- el.setAttribute('target', '_blank')
666
- el.setAttribute('rel', 'noopener')
669
+ const externalRegex =/(\b(https?|ftp|file):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/ig;
670
+ if(externalRegex.test(href)) {
671
+ el.setAttribute('target', '_blank');
672
+ el.setAttribute('rel', 'noopener');
673
+ } else {
674
+ el.setAttribute('class', 'markdown-internal-link')
675
+ }
667
676
  }
668
677
  }
@@ -18,6 +18,8 @@ export function cleanReply(s: string): string {
18
18
  .filter(item => item.toLowerCase().includes('read this post on travelfeed.io for the best experience') === false)
19
19
  .filter(item => item.toLowerCase().includes('posted via <a href="https://www.dporn.co/"') === false)
20
20
  .filter(item => item.toLowerCase().includes('▶️ [watch on 3speak](https://3speak') === false)
21
+ .filter(item => item.toLowerCase().includes('<sup><sub>Posted via [inji.com]') === false)
22
+ .filter(item => item.toLowerCase().includes('view this post on [Liketu]') === false)
21
23
  .join('\n') : '')
22
24
  .replace('Posted via <a href="https://d.buzz" data-link="promote-link">D.Buzz</a>', '')
23
25
  .replace('<div class="pull-right"><a href="/@hive.engage">![](https://i.imgur.com/XsrNmcl.png)</a></div>', '')
@@ -16,22 +16,31 @@ export function linkify(content: string, forApp: boolean, webp: boolean): string
16
16
 
17
17
  // User mentions
18
18
  content = content.replace(
19
- /(^|[^a-zA-Z0-9_!#$%&*@@/]|(^|[^a-zA-Z0-9_+~.-/]))[@@]([a-z][-.a-z\d]+[a-z\d])/gi,
19
+ /(^|[^a-zA-Z0-9_!#$%&*@@/]|(^|[^a-zA-Z0-9_+~.-/]))[@@]([a-z][-.a-z\d^/]+[a-z\d])/gi,
20
20
  (match, preceeding1, preceeding2, user) => {
21
21
  const userLower = user.toLowerCase()
22
22
  const preceedings = (preceeding1 || '') + (preceeding2 || '')
23
-
24
- const attrs = forApp ? `data-author="${userLower}"` : `href="/@${userLower}"`
25
- return `${preceedings}<a class="markdown-author-link" ${attrs}>@${user}</a>`
23
+ if (userLower.indexOf('/')===-1) {
24
+ const attrs = forApp ? `data-author="${userLower}"` : `href="/@${userLower}"`
25
+ return `${preceedings}<a class="markdown-author-link" ${attrs}>@${user}</a>`
26
+ } else {
27
+ return match
28
+ }
26
29
  }
27
30
  )
28
31
 
29
32
  // internal links
30
33
  content = content.replace(
31
- /(\s\/@[\w.\d-]+)\/(\S+)/gi, (match, u, p) => {
32
- const uu = u.trim().toLowerCase().replace('/@','');
33
- const attrs = forApp ? `data-author="${uu}" data-tag="post" data-permlink="${p.trim()}"` : `href="/post/@${uu}/${p.trim()}"`
34
- return ` <a class="markdown-post-link" ${attrs}>/@${uu}/${p.trim()}</a>`
34
+ /((^|\s)(\/|)@[\w.\d-]+)\/(\S+)/gi, (match, u, p1, p2, p3) => {
35
+ const uu = u.trim().toLowerCase().replace('/@','').replace('@','');
36
+ const perm = p3;
37
+ if (['wallet', 'feed', 'followers', 'following', 'points', 'communities', 'posts', 'blog', 'comments', 'replies', 'settings', 'engine'].includes(p3)) {
38
+ const attrs = forApp ? `https://ecency.com/@${uu}/${perm}` : `href="/@${uu}/${perm}"`
39
+ return ` <a class="markdown-profile-link" ${attrs}>@${uu}/${perm}</a>`
40
+ } else {
41
+ const attrs = forApp ? `data-author="${uu}" data-tag="post" data-permlink="${perm}"` : `href="/post/@${uu}/${perm}"`
42
+ return ` <a class="markdown-post-link" ${attrs}>@${uu}/${perm}</a>`
43
+ }
35
44
  }
36
45
  )
37
46
 
@@ -140,4 +140,11 @@ So, how can you qualify to get one?`
140
140
  const expected = 'Lorem Ipsum Dolor'
141
141
  expect(getPostBodySummary(input)).toBe(expected)
142
142
  })
143
+
144
+
145
+ it('12- Test entity parsing', () => {
146
+ const input = 'http://lorem.com Lorem &lt; Ipsum &amp; Dolor &euro; '
147
+ const expected = 'Lorem < Ipsum & Dolor €'
148
+ expect(getPostBodySummary(input)).toBe(expected)
149
+ })
143
150
  })
@@ -31,7 +31,7 @@ const joint = (arr: string[], limit = 200) => {
31
31
  return result.trim();
32
32
  };
33
33
 
34
- function postBodySummary(entryBody: string, length?: number): string {
34
+ function postBodySummary(entryBody: string, length?: number, platform:'ios'|'android'|'web' = 'web'): string {
35
35
  if (!entryBody) {
36
36
  return ''
37
37
  }
@@ -57,6 +57,19 @@ function postBodySummary(entryBody: string, length?: number): string {
57
57
  'sup'
58
58
  ]);
59
59
 
60
+ //encrypt entities
61
+ const entities = entryBody.match(/&(.*?);/g);
62
+ const encEntities:string[] = [];
63
+ if(entities && platform !== 'web'){
64
+ entities.forEach((entity)=>{
65
+ var CryptoJS = require("react-native-crypto-js");
66
+ const encData = CryptoJS.AES.encrypt(entity, 'key').toString();
67
+ let encyptedEntity = CryptoJS.enc.Base64.stringify(CryptoJS.enc.Utf8.parse(encData));
68
+ encEntities.push(encyptedEntity);
69
+ entryBody = entryBody.replace(entity, encyptedEntity);
70
+ })
71
+ }
72
+
60
73
  // Convert markdown to html
61
74
  let text = '';
62
75
  try {
@@ -65,6 +78,18 @@ function postBodySummary(entryBody: string, length?: number): string {
65
78
  console.log(err)
66
79
  }
67
80
 
81
+
82
+ //decrypt and put back entiteis
83
+ if(platform !== 'web'){
84
+ encEntities.forEach((encEntity)=>{
85
+ var CryptoJS = require("react-native-crypto-js");
86
+ let decData = CryptoJS.enc.Base64.parse(encEntity).toString(CryptoJS.enc.Utf8);
87
+ let entity = CryptoJS.AES.decrypt(decData, 'key').toString(CryptoJS.enc.Utf8);
88
+ text = text.replace(encEntity, entity);
89
+ })
90
+ }
91
+
92
+
68
93
  text = text
69
94
  .replace(/(<([^>]+)>)/gi, '') // Remove html tags
70
95
  .replace(/\r?\n|\r/g, ' ') // Remove new lines
@@ -84,9 +109,9 @@ function postBodySummary(entryBody: string, length?: number): string {
84
109
  return text
85
110
  }
86
111
 
87
- export function getPostBodySummary(obj: Entry | string, length?: number): any {
112
+ export function getPostBodySummary(obj: Entry | string, length?: number, platform?:'ios'|'android'|'web'): any {
88
113
  if (typeof obj === 'string') {
89
- return postBodySummary(obj as string, length)
114
+ return postBodySummary(obj as string, length, platform)
90
115
  }
91
116
 
92
117
  const key = `${makeEntryCacheKey(obj)}-sum-${length}`
@@ -96,7 +121,7 @@ export function getPostBodySummary(obj: Entry | string, length?: number): any {
96
121
  return item
97
122
  }
98
123
 
99
- const res = postBodySummary(obj.body, length)
124
+ const res = postBodySummary(obj.body, length, platform)
100
125
  cacheSet(key, res)
101
126
 
102
127
  return res