@reality.eth/reality-eth-lib 3.1.15 → 3.2.0

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.
@@ -7,6 +7,8 @@ const vsprintf = require("sprintf-js").vsprintf
7
7
  const QUESTION_MAX_OUTCOMES = 128;
8
8
  const marked = require('marked');
9
9
  const DOMPurify = require('isomorphic-dompurify');
10
+ const { convert} = require('html-to-text');
11
+ marked.setOptions({headerIds: false});
10
12
 
11
13
  exports.delimiter = function() {
12
14
  return '\u241f'; // Thought about '\u0000' but it seems to break something;
@@ -190,17 +192,16 @@ exports.parseQuestionJSON = function(data, errors_to_title) {
190
192
  };
191
193
  }
192
194
 
193
- if (question_json['outcomes'] && question_json['outcomes'].length > QUESTION_MAX_OUTCOMES)
194
- if(question_json['errors'])
195
- question_json['errors']['too_many_outcomes'] = true
196
- else
197
- question_json['errors'] = {'too_many_outcomes': true};
198
- if ('type' in question_json && question_json['type'] == 'datetime' && 'precision' in question_json)
199
- if (!(['Y', 'm', 'd', 'H', 'i', 's'].includes(question_json['precision'])))
200
- if(question_json['errors'])
201
- question_json['errors']['invalid_precision'] = true
202
- else
203
- question_json['errors'] = {'invalid_precision': true};
195
+ if (question_json['outcomes'] && question_json['outcomes'].length > QUESTION_MAX_OUTCOMES) {
196
+ if (!question_json['errors']) question_json['errors'] = {};
197
+ question_json['errors']['too_many_outcomes'] = true;
198
+ }
199
+ if ('type' in question_json && question_json['type'] == 'datetime' && 'precision' in question_json) {
200
+ if (!(['Y', 'm', 'd', 'H', 'i', 's'].includes(question_json['precision']))) {
201
+ if (!question_json['errors']) question_json['errors'] = {};
202
+ question_json['errors']['invalid_precision'] = true;
203
+ }
204
+ }
204
205
  // If errors_to_title is specified, we add any error message to the title to make sure we don't lose it
205
206
  if (errors_to_title) {
206
207
  if ('errors' in question_json) {
@@ -216,34 +217,31 @@ exports.parseQuestionJSON = function(data, errors_to_title) {
216
217
  }
217
218
  }
218
219
 
219
- try{
220
- switch(question_json['format']){
221
- case 'text/markdown':{
222
- const safeMarkdown = DOMPurify.sanitize(question_json['title'], { USE_PROFILES: {html: false}});
223
- if (safeMarkdown !== question_json['title'])
224
- if(question_json['errors'])
225
- question_json['errors']['unsafe_markdown'] = true;
226
- else
227
- question_json['errors'] = {'unsafe_markdown': true};
228
- else
229
- question_json['title-markdown-html'] = marked.parse(safeMarkdown).replace(/<img.*src=\"(.*?)\".*alt=\"(.*?)\".*\/?>/, '<a href="$1">$2</a>');
230
- break;
231
- }
232
- case 'text/plain': {
233
- break;
234
- }
235
- case undefined:{
236
- question_json['format'] = 'text/plain';
237
- break;
238
- }
239
- default:{
240
- question_json['errors'] = {'invalid_format': true};
241
- break;
220
+ if (!question_json['format']) {
221
+ question_json['format'] = 'text/plain';
222
+ }
223
+
224
+ if (question_json['format'] == 'text/plain') {
225
+ question_json['title_text'] = question_json['title'];
226
+ } else if (question_json['format'] == 'text/markdown') {
227
+ try{
228
+ const safeMarkdown = DOMPurify.sanitize(question_json['title'], { USE_PROFILES: {html: false}});
229
+ if (safeMarkdown !== question_json['title']) {
230
+ if (!question_json['errors']) question_json['errors'] = {};
231
+ question_json['errors']['unsafe_markdown'] = true;
232
+ } else {
233
+ question_json['title_html'] = marked.parse(safeMarkdown).replace(/<img.*src=\"(.*?)\".*alt=\"(.*?)\".*\/?>/, '<a href="$1">$2</a>');
234
+ question_json['title_text'] = convert(question_json['title_html'], {
235
+ selectors: [{selector: 'h1', options: { uppercase: false }}, {selector: 'h2', options: { uppercase: false }}]
236
+ });
242
237
  }
243
- }
244
- } catch(e){
245
- if(question_json && question_json['errors'])
238
+ } catch(e){
239
+ if (!question_json['errors']) question_json['errors'] = {};
246
240
  question_json['errors']['markdown_parse_failed'] = true
241
+ }
242
+ } else {
243
+ if (!question_json['errors']) question_json['errors'] = {};
244
+ question_json['errors']['invalid_format'] = true;
247
245
  }
248
246
 
249
247
  // If errors_to_title is specified, we add any error message to the title to make sure we don't lose it
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@reality.eth/reality-eth-lib",
3
- "version": "3.1.15",
3
+ "version": "3.2.0",
4
4
  "description": "Tools for handling questions in the reality.eth fact verification platform",
5
5
  "scripts": {
6
6
  "test": "env TZ='Asia/Kabul' mocha ./test"
@@ -16,10 +16,11 @@
16
16
  "author": "Edmund Edgar (https://reality.eth.link)",
17
17
  "license": "GPL-3.0",
18
18
  "dependencies": {
19
- "@reality.eth/contracts": "^3.0.16",
19
+ "@reality.eth/contracts": "^3.0.17",
20
20
  "bignumber.js": "^7.2.1",
21
21
  "bn.js": "^5.2.1",
22
22
  "ethereumjs-abi": "^0.6.5",
23
+ "html-to-text": "^8.2.1",
23
24
  "isomorphic-dompurify": "^0.23.0",
24
25
  "marked": "^4.1.1",
25
26
  "sprintf-js": "^1.1.1"
@@ -38,5 +39,5 @@
38
39
  "url": "https://github.com/RealityETH/monorepo/issues"
39
40
  },
40
41
  "homepage": "https://reality.eth.link",
41
- "gitHead": "5b0b4dd179e68567d2a0086fc10a6d0f9967af47"
42
+ "gitHead": "1962e3224fbde8811b888a731e714c8bd23b42f7"
42
43
  }
@@ -240,6 +240,66 @@ describe('Broken questions', function() {
240
240
  });
241
241
  });
242
242
 
243
+ describe('Markdown questions', function() {
244
+ it('Sets title, title_html and title_text appropriatly', function() {
245
+ const qMarkdown = "{\"title\": \"# my title oh yes\", \"type\": \"bool\", \"category\": \"art\", \"lang\": \"en_US\", \"format\": \"text/markdown\"}";
246
+ const q = rc_question.parseQuestionJSON(qMarkdown, true);
247
+ expect(q.errors).to.equal(undefined);
248
+ expect(q.format).to.equal('text/markdown');
249
+ expect(q.title_text).to.equal('my title oh yes');
250
+ expect(q.title).to.equal('# my title oh yes');
251
+ expect(q.title_html).to.equal('<h1>my title oh yes</h1>'+"\n");
252
+ });
253
+ it('Set title_text appropriatly for italic and bold headings', function() {
254
+ const qMarkdown = "{\"title\": \"# _Italic Heading 1_\\n## __Bold Heading 2__\", \"type\": \"bool\", \"category\": \"art\", \"lang\": \"en_US\", \"format\": \"text/markdown\"}";
255
+ const q = rc_question.parseQuestionJSON(qMarkdown, true);
256
+ expect(q.errors).to.equal(undefined);
257
+ expect(q.format).to.equal('text/markdown');
258
+ expect(q.title_text).to.equal('Italic Heading 1\n\n\nBold Heading 2');
259
+ expect(q.title).to.equal('# _Italic Heading 1_\n## __Bold Heading 2__');
260
+ expect(q.title_html).to.equal(`<h1><em>Italic Heading 1</em></h1>\n<h2><strong>Bold Heading 2</strong></h2>`+"\n");
261
+ });
262
+ it('Set title_text appropriatly for italic and bold quotes', function() {
263
+ const qMarkdown = "{\"title\": \">_Italic_ __Bold__\", \"type\": \"bool\", \"category\": \"art\", \"lang\": \"en_US\", \"format\": \"text/markdown\"}";
264
+ const q = rc_question.parseQuestionJSON(qMarkdown, true);
265
+ expect(q.errors).to.equal(undefined);
266
+ expect(q.format).to.equal('text/markdown');
267
+ expect(q.title_text).to.equal('> Italic Bold');
268
+ expect(q.title).to.equal('>_Italic_ __Bold__');
269
+ expect(q.title_html).to.equal('<blockquote>\n<p><em>Italic</em> <strong>Bold</strong></p>\n</blockquote>'+"\n");
270
+ });
271
+ it('Set title_text appropriatly for lists', function() {
272
+ const qMarkdown = "{\"title\": \"* __Item One__\\n* __Item Two__\\n* __Item Three__\\n1. Item One\\n2. Item Two\\n3. Item Three\", \"type\": \"bool\", \"category\": \"art\", \"lang\": \"en_US\", \"format\": \"text/markdown\"}";
273
+ const q = rc_question.parseQuestionJSON(qMarkdown, true);
274
+ expect(q.errors).to.equal(undefined);
275
+ expect(q.format).to.equal('text/markdown');
276
+ expect(q.title_text).to.equal(' * Item One\n * Item Two\n * Item Three\n\n 1. Item One\n 2. Item Two\n 3. Item Three');
277
+ expect(q.title).to.equal('* __Item One__\n* __Item Two__\n* __Item Three__\n1. Item One\n2. Item Two\n3. Item Three');
278
+ expect(q.title_html).to.equal(
279
+ `<ul>
280
+ <li><strong>Item One</strong></li>
281
+ <li><strong>Item Two</strong></li>
282
+ <li><strong>Item Three</strong></li>
283
+ </ul>
284
+ <ol>
285
+ <li>Item One</li>
286
+ <li>Item Two</li>
287
+ <li>Item Three</li>
288
+ </ol>` + "\n");
289
+ });
290
+ it('Set title_text appropriatly for code blocks', function() {
291
+ const qMarkdown = "{\"title\": \"`Inline code` with backticks\\n\\n```# code block\\nprint '3 backticks or'\\nprint 'indent 4 spaces'```\", \"type\": \"bool\", \"category\": \"art\", \"lang\": \"en_US\", \"format\": \"text/markdown\"}";
292
+ const q = rc_question.parseQuestionJSON(qMarkdown, true);
293
+ expect(q.errors).to.equal(undefined);
294
+ expect(q.format).to.equal('text/markdown');
295
+ expect(q.title_text).to.equal(`Inline code with backticks\n\n# code block print '3 backticks or' print 'indent 4 spaces'`);
296
+ expect(q.title).to.equal("`Inline code` with backticks\n\n```# code block\nprint '3 backticks or'\nprint 'indent 4 spaces'```");
297
+ expect(q.title_html).to.equal(
298
+ `<p><code>Inline code</code> with backticks</p>
299
+ <p><code># code block print &#39;3 backticks or&#39; print &#39;indent 4 spaces&#39;</code></p>`+ "\n");
300
+ });
301
+ });
302
+
243
303
  describe('Unsafe markdown questions', function() {
244
304
  it('Sets an error if a question includes unsafe html in markdown', function() {
245
305
  const qUnsafeMarkdown = "{\"title\": \"# Title <p>abc<iframe\/\/src=jAva&Tab;script:alert(3)>def<\/p>\", \"type\": \"bool\", \"category\": \"art\", \"lang\": \"en_US\", \"format\": \"text/markdown\"}";
@@ -247,8 +307,8 @@ describe('Unsafe markdown questions', function() {
247
307
  expect(q.errors.unsafe_markdown).to.equal(true);
248
308
  });
249
309
  it('Sets no error if a question includes valid markdown without html', function() {
250
- const qUnsafeMarkdown = "{\"title\": \"# Title\", \"type\": \"bool\", \"category\": \"art\", \"lang\": \"en_US\", \"format\": \"text/markdown\"}";
251
- const q = rc_question.parseQuestionJSON(qUnsafeMarkdown, true);
310
+ const qSafeMarkdown = "{\"title\": \"# Title\", \"type\": \"bool\", \"category\": \"art\", \"lang\": \"en_US\", \"format\": \"text/markdown\"}";
311
+ const q = rc_question.parseQuestionJSON(qSafeMarkdown, true);
252
312
  expect(q.errors).to.equal(undefined);
253
313
  expect(q.format).to.equal('text/markdown');
254
314
  });