stated-protocol-parser 1.0.7 → 5.0.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.
Files changed (129) hide show
  1. package/README.md +231 -22
  2. package/dist/constants.d.ts +193 -15
  3. package/dist/constants.d.ts.map +1 -1
  4. package/dist/constants.js +197 -20
  5. package/dist/constants.js.map +1 -1
  6. package/dist/esm/constants.d.ts +193 -15
  7. package/dist/esm/constants.d.ts.map +1 -1
  8. package/dist/esm/{hash.browser.d.ts → hash.d.ts} +11 -5
  9. package/dist/esm/hash.d.ts.map +1 -0
  10. package/dist/esm/index.d.ts +4 -42
  11. package/dist/esm/index.d.ts.map +1 -1
  12. package/dist/esm/index.js +2102 -641
  13. package/dist/esm/index.js.map +7 -1
  14. package/dist/esm/protocol.d.ts +18 -30
  15. package/dist/esm/protocol.d.ts.map +1 -1
  16. package/dist/esm/signature.d.ts +49 -0
  17. package/dist/esm/signature.d.ts.map +1 -0
  18. package/dist/esm/types.d.ts +26 -60
  19. package/dist/esm/types.d.ts.map +1 -1
  20. package/dist/esm/utils.d.ts +10 -0
  21. package/dist/esm/utils.d.ts.map +1 -1
  22. package/dist/{hash.browser.d.ts → hash.d.ts} +11 -5
  23. package/dist/hash.d.ts.map +1 -0
  24. package/dist/{hash.browser.js → hash.js} +44 -10
  25. package/dist/hash.js.map +1 -0
  26. package/dist/index.d.ts +4 -42
  27. package/dist/index.d.ts.map +1 -1
  28. package/dist/index.js +4 -674
  29. package/dist/index.js.map +1 -1
  30. package/dist/protocol.d.ts +18 -30
  31. package/dist/protocol.d.ts.map +1 -1
  32. package/dist/protocol.js +565 -572
  33. package/dist/protocol.js.map +1 -1
  34. package/dist/signature.d.ts +49 -0
  35. package/dist/signature.d.ts.map +1 -0
  36. package/dist/signature.js +169 -0
  37. package/dist/signature.js.map +1 -0
  38. package/dist/types.d.ts +26 -60
  39. package/dist/types.d.ts.map +1 -1
  40. package/dist/types.js +27 -0
  41. package/dist/types.js.map +1 -1
  42. package/dist/utils.d.ts +10 -0
  43. package/dist/utils.d.ts.map +1 -1
  44. package/dist/utils.js +79 -11
  45. package/dist/utils.js.map +1 -1
  46. package/package.json +32 -27
  47. package/src/constants.ts +228 -44
  48. package/src/fixtures.test.ts +236 -0
  49. package/src/hash.test.ts +217 -215
  50. package/src/hash.ts +99 -0
  51. package/src/index.ts +5 -0
  52. package/src/organisation-verification.test.ts +50 -0
  53. package/src/person-verification.test.ts +55 -0
  54. package/src/poll.test.ts +28 -0
  55. package/src/protocol.ts +855 -650
  56. package/src/rating.test.ts +25 -0
  57. package/src/signature.test.ts +200 -0
  58. package/src/signature.ts +159 -0
  59. package/src/statement.test.ts +101 -0
  60. package/src/types.ts +155 -156
  61. package/src/utils.test.ts +140 -0
  62. package/src/utils.ts +102 -16
  63. package/dist/esm/constants.js +0 -47
  64. package/dist/esm/constants.js.map +0 -1
  65. package/dist/esm/hash.browser.d.ts.map +0 -1
  66. package/dist/esm/hash.browser.js +0 -58
  67. package/dist/esm/hash.browser.js.map +0 -1
  68. package/dist/esm/hash.node.d.ts +0 -31
  69. package/dist/esm/hash.node.d.ts.map +0 -1
  70. package/dist/esm/hash.node.js +0 -43
  71. package/dist/esm/hash.node.js.map +0 -1
  72. package/dist/esm/hash.test.d.ts +0 -2
  73. package/dist/esm/hash.test.d.ts.map +0 -1
  74. package/dist/esm/hash.test.js +0 -181
  75. package/dist/esm/hash.test.js.map +0 -1
  76. package/dist/esm/index.browser.d.ts +0 -3
  77. package/dist/esm/index.browser.d.ts.map +0 -1
  78. package/dist/esm/index.browser.js +0 -5
  79. package/dist/esm/index.browser.js.map +0 -1
  80. package/dist/esm/index.node.d.ts +0 -3
  81. package/dist/esm/index.node.d.ts.map +0 -1
  82. package/dist/esm/index.node.js +0 -5
  83. package/dist/esm/index.node.js.map +0 -1
  84. package/dist/esm/index.test.d.ts +0 -2
  85. package/dist/esm/index.test.d.ts.map +0 -1
  86. package/dist/esm/index.test.js +0 -293
  87. package/dist/esm/index.test.js.map +0 -1
  88. package/dist/esm/protocol.js +0 -639
  89. package/dist/esm/protocol.js.map +0 -1
  90. package/dist/esm/types.js +0 -2
  91. package/dist/esm/types.js.map +0 -1
  92. package/dist/esm/utils.js +0 -23
  93. package/dist/esm/utils.js.map +0 -1
  94. package/dist/esm/v3.d.ts +0 -5
  95. package/dist/esm/v3.d.ts.map +0 -1
  96. package/dist/esm/v3.js +0 -60
  97. package/dist/esm/v3.js.map +0 -1
  98. package/dist/hash.browser.d.ts.map +0 -1
  99. package/dist/hash.browser.js.map +0 -1
  100. package/dist/hash.node.d.ts +0 -31
  101. package/dist/hash.node.d.ts.map +0 -1
  102. package/dist/hash.node.js +0 -53
  103. package/dist/hash.node.js.map +0 -1
  104. package/dist/hash.test.d.ts +0 -2
  105. package/dist/hash.test.d.ts.map +0 -1
  106. package/dist/hash.test.js +0 -183
  107. package/dist/hash.test.js.map +0 -1
  108. package/dist/index.browser.d.ts +0 -3
  109. package/dist/index.browser.d.ts.map +0 -1
  110. package/dist/index.browser.js +0 -21
  111. package/dist/index.browser.js.map +0 -1
  112. package/dist/index.node.d.ts +0 -3
  113. package/dist/index.node.d.ts.map +0 -1
  114. package/dist/index.node.js +0 -21
  115. package/dist/index.node.js.map +0 -1
  116. package/dist/index.test.d.ts +0 -2
  117. package/dist/index.test.d.ts.map +0 -1
  118. package/dist/index.test.js +0 -295
  119. package/dist/index.test.js.map +0 -1
  120. package/dist/v3.d.ts +0 -5
  121. package/dist/v3.d.ts.map +0 -1
  122. package/dist/v3.js +0 -64
  123. package/dist/v3.js.map +0 -1
  124. package/src/hash.browser.ts +0 -65
  125. package/src/hash.node.ts +0 -47
  126. package/src/index.browser.ts +0 -4
  127. package/src/index.node.ts +0 -4
  128. package/src/index.test.ts +0 -378
  129. package/src/v3.ts +0 -62
package/dist/protocol.js CHANGED
@@ -14,671 +14,664 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
14
  for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
15
  };
16
16
  Object.defineProperty(exports, "__esModule", { value: true });
17
- exports.parseBoycott = exports.buildBoycott = exports.parseObservation = exports.buildObservation = exports.parseBounty = exports.buildBounty = exports.parseRating = exports.buildRating = exports.parsePDFSigning = exports.buildPDFSigningContent = exports.parseResponseContent = exports.buildResponseContent = exports.parseDisputeContent = exports.buildDisputeContentContent = exports.parseDisputeAuthenticity = exports.buildDisputeAuthenticityContent = exports.parseVote = exports.buildVoteContent = exports.parsePersonVerification = exports.buildPersonVerificationContent = exports.parseOrganisationVerification = exports.buildOrganisationVerificationContent = exports.parsePoll = exports.buildPollContent = exports.parseQuotation = exports.buildQuotationContent = exports.parseStatement = exports.buildStatement = void 0;
18
- /* eslint-disable no-useless-concat */
17
+ exports.parseRating = exports.buildRating = exports.parsePDFSigning = exports.buildPDFSigningContent = exports.parseResponseContent = exports.buildResponseContent = exports.parseDisputeContent = exports.buildDisputeContentContent = exports.parseDisputeAuthenticity = exports.buildDisputeAuthenticityContent = exports.parseVote = exports.buildVoteContent = exports.parsePersonVerification = exports.buildPersonVerificationContent = exports.parseOrganisationVerification = exports.buildOrganisationVerificationContent = exports.parsePoll = exports.buildPollContent = exports.parseStatement = exports.buildStatement = void 0;
19
18
  const constants_1 = require("./constants");
20
- const v3_1 = require("./v3");
21
19
  const utils_1 = require("./utils");
22
- const fallBackVersion = 3;
23
- const version = 4;
20
+ const signature_1 = require("./signature");
21
+ const hash_1 = require("./hash");
22
+ const types_1 = require("./types");
23
+ const version = 5;
24
24
  __exportStar(require("./types"), exports);
25
25
  __exportStar(require("./constants"), exports);
26
26
  __exportStar(require("./utils"), exports);
27
- __exportStar(require("./v3"), exports);
28
- const buildStatement = ({ domain, author, time, tags, content, representative, supersededStatement }) => {
27
+ const buildStatement = ({ domain, author, time, tags, content, representative, supersededStatement, translations, attachments, }) => {
29
28
  if (content.match(/\nPublishing domain: /))
30
- throw (new Error("Statement must not contain 'Publishing domain: ', as this marks the beginning of a new statement."));
29
+ throw new Error("Statement must not contain 'Publishing domain: ', as this marks the beginning of a new statement.");
31
30
  if (content.match(/\n\n/))
32
- throw (new Error("Statement must not contain two line breaks in a row, as this is used for separating statements."));
31
+ throw new Error('Statement content must not contain two line breaks in a row, as this is used for separating statements.');
32
+ if (content.match(/\nTranslation [a-z]{2,3}:\n/))
33
+ throw new Error("Statement content must not contain 'Translation XX:\\n' pattern, as this is reserved for translations.");
34
+ if (content.match(/\nAttachments: /))
35
+ throw new Error("Statement content must not contain 'Attachments: ' on a new line, as this is reserved for file attachments.");
33
36
  if (typeof time !== 'object' || !time.toUTCString)
34
- throw (new Error("Time must be a Date object."));
37
+ throw new Error('Time must be a Date object.');
35
38
  if (!domain)
36
- throw (new Error("Publishing domain missing."));
37
- const statement = "Publishing domain: " + domain + "\n" +
38
- "Author: " + (author || "") + "\n" +
39
- (representative && representative?.length > 0 ? "Authorized signing representative: " + (representative || "") + "\n" : '') +
40
- "Time: " + time.toUTCString() + "\n" +
41
- (tags && tags.length > 0 ? "Tags: " + tags.join(', ') + "\n" : '') +
42
- (supersededStatement && supersededStatement?.length > 0 ? "Superseded statement: " + (supersededStatement || "") + "\n" : '') +
43
- "Format version: " + version + "\n" +
44
- "Statement content: " + content + (content.match(/\n$/) ? '' : "\n");
39
+ throw new Error('Publishing domain missing.');
40
+ if (attachments && attachments.length > 5)
41
+ throw new Error('Maximum 5 attachments allowed.');
42
+ if (attachments) {
43
+ attachments.forEach((attachment, index) => {
44
+ if (!attachment.match(/^[A-Za-z0-9_-]+\.[a-zA-Z0-9]+$/)) {
45
+ throw new Error(`Attachment ${index + 1} must be in format 'base64hash.extension' (URL-safe base64)`);
46
+ }
47
+ });
48
+ }
49
+ if (translations) {
50
+ for (const [lang, translation] of Object.entries(translations)) {
51
+ if (translation.match(/\nPublishing domain: /))
52
+ throw new Error(`Translation for ${lang} must not contain 'Publishing domain: '.`);
53
+ if (translation.match(/Translation [a-z]{2,3}:\n/))
54
+ throw new Error(`Translation for ${lang} must not contain 'Translation XX:\\n' pattern.`);
55
+ }
56
+ }
57
+ const translationLines = translations
58
+ ? Object.entries(translations)
59
+ .map(([lang, translation]) => {
60
+ const translationWithNewline = translation + (translation.match(/\n$/) ? '' : '\n');
61
+ const indentedTranslation = translationWithNewline
62
+ .split('\n')
63
+ .map((line) => (line ? ' ' + line : line))
64
+ .join('\n');
65
+ return `Translation ${lang}:\n${indentedTranslation}`;
66
+ })
67
+ .join('')
68
+ : '';
69
+ const attachmentLines = attachments && attachments.length > 0 ? 'Attachments: ' + attachments.join(', ') + '\n' : '';
70
+ const contentWithNewline = content + (content.match(/\n$/) ? '' : '\n');
71
+ // Only indent plain content (non-typed statements). Typed statements already have indentation from build functions.
72
+ const isTypedContent = contentWithNewline.trim().startsWith('Type:');
73
+ const finalContent = isTypedContent
74
+ ? contentWithNewline
75
+ : contentWithNewline
76
+ .split('\n')
77
+ .map((line) => (line ? ' ' + line : line))
78
+ .join('\n');
79
+ const statement = 'Stated protocol version: ' +
80
+ version +
81
+ '\n' +
82
+ 'Publishing domain: ' +
83
+ domain +
84
+ '\n' +
85
+ 'Author: ' +
86
+ (author || '') +
87
+ '\n' +
88
+ (representative && representative?.length > 0
89
+ ? 'Authorized signing representative: ' + (representative || '') + '\n'
90
+ : '') +
91
+ 'Time: ' +
92
+ time.toUTCString() +
93
+ '\n' +
94
+ (tags && tags.length > 0 ? 'Tags: ' + tags.join(', ') + '\n' : '') +
95
+ (supersededStatement && supersededStatement?.length > 0
96
+ ? 'Superseded statement: ' + (supersededStatement || '') + '\n'
97
+ : '') +
98
+ 'Statement content:\n' +
99
+ finalContent +
100
+ translationLines +
101
+ attachmentLines;
45
102
  if (statement.length > 3000)
46
- throw (new Error("Statement must not be longer than 3,000 characters."));
103
+ throw new Error('Statement must not be longer than 3,000 characters.');
47
104
  return statement;
48
105
  };
49
106
  exports.buildStatement = buildStatement;
50
- const parseStatement = ({ statement: s, allowNoVersion = false }) => {
51
- if (s.length > 3000)
52
- throw (new Error("Statement must not be longer than 3,000 characters."));
53
- if (s.match(/\n\n/))
54
- throw new Error("Statements cannot contain two line breaks in a row, as this is used for separating statements.");
55
- const statementRegex = new RegExp(''
56
- + /^Publishing domain: (?<domain>[^\n]+?)\n/.source
57
- + /Author: (?<author>[^\n]+?)\n/.source
58
- + /(?:Authorized signing representative: (?<representative>[^\n]*?)\n)?/.source
59
- + /Time: (?<time>[^\n]+?)\n/.source
60
- + /(?:Tags: (?<tags>[^\n]*?)\n)?/.source
61
- + /(?:Superseded statement: (?<supersededStatement>[^\n]*?)\n)?/.source
62
- + /(?:Format version: (?<formatVersion>[^\n]*?)\n)?/.source
63
- + /Statement content: (?:(?<typedContent>\n\tType: (?<type>[^\n]+?)\n[\s\S]+?\n$)|(?<content>[\s\S]+?\n$))/.source);
64
- const match = s.match(statementRegex);
65
- if (!match)
66
- throw new Error("Invalid statement format:" + s);
67
- const m = {
68
- domain: match[1], author: match[2], representative: match[3], timeStr: match[4], tagsStr: match[5],
69
- supersededStatement: match[6], formatVersion: match[7], content: match[8] || match[10],
70
- type: match[9] ? match[9].toLowerCase().replace(' ', '_') : undefined
107
+ const parseStatement = ({ statement: input, }) => {
108
+ if (input.length > 3000)
109
+ throw new Error('Statement must not be longer than 3,000 characters.');
110
+ const beforeTranslations = input.split(/\nTranslation [a-z]{2,3}:\n/)[0];
111
+ if (beforeTranslations.match(/\n\n/))
112
+ throw new Error('Statements cannot contain two line breaks in a row before translations, as this is used for separating statements.');
113
+ const signatureRegex = new RegExp('' +
114
+ /^(?<statement>[\s\S]+?)---\n/.source +
115
+ /Statement hash: (?<statementHash>[A-Za-z0-9_-]+)\n/.source +
116
+ /Public key: (?<publicKey>[A-Za-z0-9_-]+)\n/.source +
117
+ /Signature: (?<signature>[A-Za-z0-9_-]+)\n/.source +
118
+ /Algorithm: (?<algorithm>[^\n]+)\n/.source +
119
+ /$/.source);
120
+ const signatureMatch = input.match(signatureRegex);
121
+ let statementToVerify = input;
122
+ let publicKey;
123
+ let signature;
124
+ if (signatureMatch && signatureMatch.groups) {
125
+ statementToVerify = signatureMatch.groups.statement;
126
+ const statementHash = signatureMatch.groups.statementHash;
127
+ publicKey = signatureMatch.groups.publicKey;
128
+ signature = signatureMatch.groups.signature;
129
+ const algorithm = signatureMatch.groups.algorithm;
130
+ if (algorithm !== 'Ed25519') {
131
+ throw new Error('Unsupported signature algorithm: ' + algorithm);
132
+ }
133
+ const computedHash = (0, hash_1.sha256)(statementToVerify);
134
+ if (computedHash !== statementHash) {
135
+ throw new Error('Statement hash mismatch');
136
+ }
137
+ const isValid = (0, signature_1.verifySignature)(statementToVerify, signature, publicKey);
138
+ if (!isValid) {
139
+ throw new Error('Invalid cryptographic signature');
140
+ }
141
+ }
142
+ const statementRegex = new RegExp('' +
143
+ /^Stated protocol version: (?<formatVersion>[^\n]+?)\n/.source +
144
+ /Publishing domain: (?<domain>[^\n]+?)\n/.source +
145
+ /Author: (?<author>[^\n]+?)\n/.source +
146
+ /(?:Authorized signing representative: (?<representative>[^\n]*?)\n)?/.source +
147
+ /Time: (?<time>[^\n]+?)\n/.source +
148
+ /(?:Tags: (?<tags>[^\n]*?)\n)?/.source +
149
+ /(?:Superseded statement: (?<supersededStatement>[^\n]*?)\n)?/.source +
150
+ /Statement content:\n/.source +
151
+ /(?:(?<typedContent> Type: (?<type>[^\n]+?)\n[\s\S]+?)(?=\nTranslation [a-z]{2,3}:\n|Attachments: |$)|(?<content>(?: [\s\S]+?)?))/
152
+ .source +
153
+ /(?=\nTranslation [a-z]{2,3}:\n|Attachments: |$)/.source +
154
+ /(?<translations>(?:\nTranslation [a-z]{2,3}:\n[\s\S]+?)*)/.source +
155
+ /(?:Attachments: (?<attachments>[^\n]+?)\n)?/.source +
156
+ /$/.source);
157
+ const match = statementToVerify.match(statementRegex);
158
+ if (!match || !match.groups)
159
+ throw new Error('Invalid statement format: ' + input);
160
+ const { domain, author, representative, time: timeStr, tags: tagsStr, supersededStatement, attachments: attachmentsStr, formatVersion, content, typedContent, type, translations: translationsStr, } = match.groups;
161
+ const parsed = {
162
+ domain,
163
+ author,
164
+ representative,
165
+ timeStr,
166
+ tagsStr,
167
+ supersededStatement,
168
+ formatVersion,
169
+ content: content
170
+ ? content
171
+ .split('\n')
172
+ .map((line) => (line.startsWith(' ') ? line.substring(4) : line))
173
+ .join('\n')
174
+ .replace(/\n$/, '')
175
+ : typedContent,
176
+ type: type ? type.toLowerCase().replace(' ', '_') : undefined,
177
+ translationsStr,
71
178
  };
72
- if (!(m['timeStr'].match(constants_1.UTCFormat)))
73
- throw new Error("Invalid statement format: time must be in UTC");
74
- if (!m['domain'])
75
- throw new Error("Invalid statement format: domain is required");
76
- if (!m['author'])
77
- throw new Error("Invalid statement format: author is required");
78
- if (!m['content'])
79
- throw new Error("Invalid statement format: statement content is required");
80
- if (!allowNoVersion && !m['formatVersion'])
81
- throw new Error("Invalid statement format: format version is required");
82
- const tags = m['tagsStr']?.split(', ');
83
- const time = new Date(m['timeStr']);
179
+ if (!parsed.timeStr.match(constants_1.UTCFormat))
180
+ throw new Error('Invalid statement format: time must be in UTC');
181
+ if (!parsed.domain)
182
+ throw new Error('Invalid statement format: domain is required');
183
+ if (!parsed.author)
184
+ throw new Error('Invalid statement format: author is required');
185
+ if (!parsed.content)
186
+ throw new Error('Invalid statement format: statement content is required');
187
+ if (!parsed.formatVersion)
188
+ throw new Error('Invalid statement format: format version is required');
189
+ if (parsed.formatVersion !== '5')
190
+ throw new Error(`Invalid statement format: only version 5 is supported, got version ${parsed.formatVersion}`);
191
+ const tags = parsed.tagsStr?.split(', ');
192
+ const time = new Date(parsed.timeStr);
193
+ let attachments = undefined;
194
+ if (attachmentsStr && attachmentsStr.length > 0) {
195
+ attachments = attachmentsStr.split(', ').map((a) => a.trim());
196
+ if (attachments.length > 5) {
197
+ throw new Error('Maximum 5 attachments allowed');
198
+ }
199
+ }
200
+ let translations = undefined;
201
+ if (parsed.translationsStr && parsed.translationsStr.length > 0) {
202
+ translations = {};
203
+ const translationParts = parsed.translationsStr
204
+ .split(/\nTranslation ([a-z]{2,3}):\n/)
205
+ .filter((part) => part.length > 0);
206
+ for (let i = 0; i < translationParts.length; i += 2) {
207
+ if (i + 1 < translationParts.length) {
208
+ const lang = translationParts[i];
209
+ const rawTranslation = translationParts[i + 1];
210
+ // Strip indentation from translation content
211
+ const translation = rawTranslation
212
+ .split('\n')
213
+ .map((line) => (line.startsWith(' ') ? line.substring(4) : line))
214
+ .join('\n')
215
+ .replace(/\n$/, '');
216
+ translations[lang] = translation;
217
+ }
218
+ }
219
+ }
84
220
  return {
85
- domain: m['domain'],
86
- author: m['author'],
87
- representative: m['representative'],
221
+ domain: parsed.domain,
222
+ author: parsed.author,
223
+ representative: parsed.representative,
88
224
  time,
89
- tags: (tags && tags.length > 0) ? tags : undefined,
90
- supersededStatement: m['supersededStatement'],
91
- formatVersion: m['formatVersion'] || ('' + fallBackVersion),
92
- content: m['content'],
93
- type: m['type']?.toLowerCase().replace(' ', '_'),
225
+ tags: tags && tags.length > 0 ? tags : undefined,
226
+ supersededStatement: parsed.supersededStatement,
227
+ formatVersion: parsed.formatVersion,
228
+ content: parsed.content,
229
+ type: parsed.type?.toLowerCase().replace(' ', '_'),
230
+ translations: translations && Object.keys(translations).length > 0 ? translations : undefined,
231
+ attachments: attachments && attachments.length > 0 ? attachments : undefined,
94
232
  };
95
233
  };
96
234
  exports.parseStatement = parseStatement;
97
- const buildQuotationContent = ({ originalAuthor, authorVerification, originalTime, source, quotation, paraphrasedStatement, picture, confidence }) => {
98
- if (quotation && quotation.match(/\n/))
99
- throw (new Error("Quotation must not contain line breaks."));
100
- if (!paraphrasedStatement && !quotation)
101
- throw (new Error("Quotation must contain either a quotation or a paraphrased statement."));
102
- const content = "\n" +
103
- "\t" + "Type: Quotation" + "\n" +
104
- "\t" + "Original author: " + originalAuthor + "\n" +
105
- "\t" + "Author verification: " + authorVerification + "\n" +
106
- (originalTime && originalTime?.length > 0 ? "\t" + "Original publication time: " + originalTime + "\n" : "") +
107
- (source && source?.length > 0 ? "\t" + "Source: " + (source || "") + "\n" : '') +
108
- (picture && picture.length > 0 ? "\t" + "Picture proof: " + (picture || "") + "\n" : '') +
109
- (confidence && confidence?.length > 0 ? "\t" + "Confidence: " + (confidence || "") + "\n" : '') +
110
- (quotation && quotation?.length > 0 ? "\t" + "Quotation: " + (quotation || "") + "\n" : '') +
111
- (paraphrasedStatement && paraphrasedStatement?.length > 0 ? "\t" + "Paraphrased statement: " +
112
- (paraphrasedStatement || "").replace(/\n\t([^\t])/, '\n\t\t($1)') + "\n" : '') +
113
- "";
114
- return content;
115
- };
116
- exports.buildQuotationContent = buildQuotationContent;
117
- const parseQuotation = (s) => {
118
- const voteRegex = new RegExp(''
119
- + /^\n\tType: Quotation\n/.source
120
- + /\tOriginal author: (?<originalAuthor>[^\n]+?)\n/.source
121
- + /\tAuthor verification: (?<authorVerification>[^\n]+?)\n/.source
122
- + /(?:\tOriginal publication time: (?<originalTime>[^\n]+?)\n)?/.source
123
- + /(?:\tSource: (?<source>[^\n]+?)\n)?/.source
124
- + /(?:\tPicture proof: (?<picture>[^\n]+?)\n)?/.source
125
- + /(?:\tConfidence: (?<confidence>[^\n]+?)\n)?/.source
126
- + /(?:\tQuotation: (?<quotation>[^\n]+?)\n)?/.source
127
- + /(?:\tParaphrased statement: (?:(?<paraphrasedTypedStatement>\n\t\tType: (?<type>[^\n]+?)\n[\s\S]+?)|(?<paraphrasedStatement>[\s\S]+?)))/.source
128
- + /$/.source);
129
- let match = s.match(voteRegex);
130
- if (!match)
131
- throw new Error("Invalid quotation format: " + s);
132
- let m = {};
133
- m = {
134
- originalAuthor: match[1], authorVerification: match[2], originalTime: match[3], source: match[4],
135
- picture: match[5], confidence: match[6], quotation: match[7], paraphrasedStatement: match[8] || match[10],
136
- type: match[9] ? match[9].toLowerCase().replace(' ', '_') : undefined
137
- };
138
- return {
139
- originalAuthor: m['originalAuthor'],
140
- authorVerification: m['authorVerification'],
141
- originalTime: m['originalTime'],
142
- source: m['source'],
143
- picture: m['picture'],
144
- confidence: m['confidence'],
145
- quotation: m['quotation'],
146
- paraphrasedStatement: (m['paraphrasedStatement']?.replace(/\n\t\t/g, "\n\t")),
147
- type: m['type']?.toLowerCase().replace(' ', '_'),
148
- };
149
- };
150
- exports.parseQuotation = parseQuotation;
151
- const buildPollContent = ({ country, city, legalEntity, domainScope, judges, deadline, poll, scopeDescription, scopeQueryLink, options, allowArbitraryVote, requiredProperty: propertyScope, requiredPropertyObserver: propertyScopeObserver }) => {
235
+ const buildPollContent = ({ deadline, poll, scopeDescription, options, allowArbitraryVote, }) => {
152
236
  if (!poll)
153
- throw (new Error("Poll must contain a poll question."));
154
- const scopeContent = (scopeDescription ? "\t\t" + "Description: " + scopeDescription + "\n" : "") +
155
- (country ? "\t\t" + "Country scope: " + country + "\n" : "") +
156
- (city ? "\t\t" + "City scope: " + city + "\n" : "") +
157
- (legalEntity ? "\t\t" + "Legal form scope: " + legalEntity + "\n" : "") +
158
- (domainScope && domainScope?.length > 0 ? "\t\t" + "Domain scope: " + domainScope.join(', ') + "\n" : "") +
159
- (propertyScope ? "\t\t" + "All entities with the following property: " + propertyScope + "\n" : "") +
160
- (propertyScopeObserver ? "\t\t" + "As observed by: " + propertyScopeObserver + "\n" : "") +
161
- (scopeQueryLink ? "\t\t" + "Link to query defining who can vote: " + scopeQueryLink + "\n" : "");
162
- if (scopeContent.length > 0 && !scopeDescription)
163
- throw (new Error("Poll must contain a description of who can vote."));
164
- const content = "\n" +
165
- "\t" + "Type: Poll" + "\n" +
166
- (judges ? "\t" + "The poll outcome is finalized when the following nodes agree: " + judges + "\n" : "") +
167
- (deadline ? "\t" + "Voting deadline: " + deadline.toUTCString() + "\n" : "") +
168
- "\t" + "Poll: " + poll + "\n" +
169
- (options.length > 0 && options[0] ? "\t" + "Option 1: " + options[0] + "\n" : "") +
170
- (options.length > 1 && options[1] ? "\t" + "Option 2: " + options[1] + "\n" : "") +
171
- (options.length > 2 && options[2] ? "\t" + "Option 3: " + options[2] + "\n" : "") +
172
- (options.length > 3 && options[3] ? "\t" + "Option 4: " + options[3] + "\n" : "") +
173
- (options.length > 4 && options[4] ? "\t" + "Option 5: " + options[4] + "\n" : "") +
174
- ((allowArbitraryVote === true || allowArbitraryVote === false) ? ("\t" + "Allow free text votes: " + (allowArbitraryVote ? 'Yes' : 'No') + "\n") : "") +
175
- (scopeContent ? "\t" + "Who can vote: \n" + scopeContent : "") +
176
- "";
237
+ throw new Error('Poll must contain a poll question.');
238
+ if (poll.includes('\n'))
239
+ throw new Error('Poll question must be single line.');
240
+ if (scopeDescription && scopeDescription.includes('\n'))
241
+ throw new Error('Scope description must be single line.');
242
+ options.forEach((option, index) => {
243
+ if (option && option.includes('\n'))
244
+ throw new Error(`Option ${index + 1} must be single line.`);
245
+ });
246
+ const content = ' Type: Poll\n' +
247
+ (deadline ? ' Voting deadline: ' + deadline.toUTCString() + '\n' : '') +
248
+ ' Poll: ' +
249
+ poll +
250
+ '\n' +
251
+ (options.length > 0 && options[0] ? ' Option 1: ' + options[0] + '\n' : '') +
252
+ (options.length > 1 && options[1] ? ' Option 2: ' + options[1] + '\n' : '') +
253
+ (options.length > 2 && options[2] ? ' Option 3: ' + options[2] + '\n' : '') +
254
+ (options.length > 3 && options[3] ? ' Option 4: ' + options[3] + '\n' : '') +
255
+ (options.length > 4 && options[4] ? ' Option 5: ' + options[4] + '\n' : '') +
256
+ (allowArbitraryVote === true || allowArbitraryVote === false
257
+ ? ' Allow free text votes: ' + (allowArbitraryVote ? 'Yes' : 'No') + '\n'
258
+ : '') +
259
+ (scopeDescription ? ' Who can vote: ' + scopeDescription + '\n' : '');
177
260
  return content;
178
261
  };
179
262
  exports.buildPollContent = buildPollContent;
180
- const parsePoll = (s, version) => {
181
- if (version && version === '3')
182
- return (0, v3_1.parsePollV3)(s);
183
- if (version && version !== '4')
184
- throw new Error("Invalid version " + version);
185
- const pollRegex = new RegExp(''
186
- + /^\n\tType: Poll\n/.source
187
- + /(?:\tThe poll outcome is finalized when the following nodes agree: (?<judges>[^\n]+?)\n)?/.source
188
- + /(?:\tVoting deadline: (?<deadline>[^\n]+?)\n)?/.source
189
- + /\tPoll: (?<poll>[^\n]+?)\n/.source
190
- + /(?:\tOption 1: (?<option1>[^\n]+?)\n)?/.source
191
- + /(?:\tOption 2: (?<option2>[^\n]+?)\n)?/.source
192
- + /(?:\tOption 3: (?<option3>[^\n]+?)\n)?/.source
193
- + /(?:\tOption 4: (?<option4>[^\n]+?)\n)?/.source
194
- + /(?:\tOption 5: (?<option5>[^\n]+?)\n)?/.source
195
- + /(?:\tAllow free text votes: (?<allowArbitraryVote>Yes|No)\n)?/.source
196
- + /(?:\tWho can vote: (?<whoCanVote>\n[\s\S]+?\n))?/.source
197
- + /$/.source);
198
- let m = s.match(pollRegex);
199
- if (!m)
200
- throw new Error("Invalid poll format: " + s);
201
- m = {
202
- judges: m[1], deadline: m[2], poll: m[3],
203
- option1: m[4], option2: m[5], option3: m[6], option4: m[7], option5: m[8],
204
- allowArbitraryVote: m[9],
205
- whoCanVote: m[10]
206
- };
207
- const whoCanVoteParsed = {};
208
- if (m.whoCanVote) {
209
- const whoCanVoteRegex = new RegExp(''
210
- + /^\n\t\tDescription: (?<scopeDescription>[^\n]+?)\n/.source
211
- + /(?:\t\tCountry scope: (?<countryScope>[^\n]+?)\n)?/.source
212
- + /(?:\t\tCity scope: (?<cityScope>[^\n]+?)\n)?/.source
213
- + /(?:\t\tLegal form scope: (?<legalEntity>[^\n]+?)\n)?/.source
214
- + /(?:\t\tDomain scope: (?<domainScope>[^\n]+?)\n)?/.source
215
- + /(?:\t\tAll entities with the following property: (?<propertyScope>[^\n]+?)\n)?/.source
216
- + /(?:\t\tAs observed by: (?<propertyScopeObserver>[^\n]+?)\n)?/.source
217
- + /(?:\t\tLink to query defining who can vote: (?<scopeQueryLink>[^\n]+?)\n)?/.source
218
- + /$/.source);
219
- let m2 = m.whoCanVote.match(whoCanVoteRegex);
220
- if (!m2)
221
- throw new Error("Invalid who can vote section: " + m.whoCanVote);
222
- whoCanVoteParsed['scopeDescription'] = m2[1];
223
- whoCanVoteParsed['country'] = m2[2];
224
- whoCanVoteParsed['city'] = m2[3];
225
- whoCanVoteParsed['legalEntity'] = m2[4];
226
- whoCanVoteParsed['domainScopeStr'] = m2[5];
227
- whoCanVoteParsed['requiredProperty'] = m2[6];
228
- whoCanVoteParsed['requiredPropertyObserver'] = m2[7];
229
- whoCanVoteParsed['scopeQueryLink'] = m2[8];
230
- }
231
- const options = [m.option1, m.option2, m.option3, m.option4, m.option5].filter(o => o);
232
- const domainScope = whoCanVoteParsed.domainScopeStr?.split(', ');
233
- const allowArbitraryVote = (m['allowArbitraryVote'] === 'Yes' ? true :
234
- (m['allowArbitraryVote'] === 'No' ? false : undefined));
235
- const deadlineStr = m.deadline;
263
+ const parsePoll = (content, version) => {
264
+ if (version !== '5')
265
+ throw new Error('Invalid version ' + version);
266
+ const pollRegex = new RegExp('' +
267
+ /^ Type: Poll\n/.source +
268
+ /(?: Voting deadline: (?<deadline>[^\n]+?)\n)?/.source +
269
+ / Poll: (?<poll>[^\n]+?)\n/.source +
270
+ /(?: Option 1: (?<option1>[^\n]+?)\n)?/.source +
271
+ /(?: Option 2: (?<option2>[^\n]+?)\n)?/.source +
272
+ /(?: Option 3: (?<option3>[^\n]+?)\n)?/.source +
273
+ /(?: Option 4: (?<option4>[^\n]+?)\n)?/.source +
274
+ /(?: Option 5: (?<option5>[^\n]+?)\n)?/.source +
275
+ /(?: Allow free text votes: (?<allowArbitraryVote>Yes|No)\n)?/.source +
276
+ /(?: Who can vote: (?<scopeDescription>[^\n]+?)\n)?/.source +
277
+ /$/.source);
278
+ const match = content.match(pollRegex);
279
+ if (!match || !match.groups)
280
+ throw new Error('Invalid poll format: ' + content);
281
+ const { deadline, poll, option1, option2, option3, option4, option5, allowArbitraryVote: allowArbitraryVoteStr, scopeDescription, } = match.groups;
282
+ const options = [option1, option2, option3, option4, option5].filter((o) => o);
283
+ const allowArbitraryVote = allowArbitraryVoteStr === 'Yes' ? true : allowArbitraryVoteStr === 'No' ? false : undefined;
284
+ const deadlineStr = deadline;
236
285
  if (deadlineStr && !deadlineStr.match(constants_1.UTCFormat))
237
- throw new Error("Invalid poll, deadline must be in UTC: " + deadlineStr);
286
+ throw new Error('Invalid poll, deadline must be in UTC: ' + deadlineStr);
238
287
  return {
239
- judges: m['judges'],
240
288
  deadline: deadlineStr ? new Date(deadlineStr) : undefined,
241
- poll: m['poll'],
289
+ poll,
242
290
  options,
243
291
  allowArbitraryVote,
244
- country: whoCanVoteParsed['country'],
245
- scopeDescription: whoCanVoteParsed['scopeDescription'],
246
- requiredProperty: whoCanVoteParsed['requiredProperty'],
247
- requiredPropertyObserver: whoCanVoteParsed['requiredPropertyObserver'],
248
- scopeQueryLink: whoCanVoteParsed['scopeQueryLink'],
249
- city: whoCanVoteParsed['city'],
250
- legalEntity: whoCanVoteParsed['legalEntity'],
251
- domainScope: (domainScope && domainScope.length > 0) ? domainScope : undefined,
292
+ scopeDescription,
252
293
  };
253
294
  };
254
295
  exports.parsePoll = parsePoll;
255
- const buildOrganisationVerificationContent = ({ name, englishName, country, city, province, legalForm, department, domain, foreignDomain, serialNumber, confidence, reliabilityPolicy, employeeCount, pictureHash, latitude, longitude, population }) => {
296
+ const buildOrganisationVerificationContent = ({ name, englishName, country, city, province, legalForm, department, domain, foreignDomain, serialNumber, confidence, reliabilityPolicy, employeeCount, pictureHash, latitude, longitude, population, publicKey, }) => {
256
297
  if (!name || !country || !legalForm || (!domain && !foreignDomain))
257
- throw new Error("Missing required fields");
298
+ throw new Error('Missing required fields');
258
299
  if (!Object.values(constants_1.legalForms).includes(legalForm))
259
- throw new Error("Invalid legal form " + legalForm);
300
+ throw new Error('Invalid legal form ' + legalForm);
260
301
  if (employeeCount && !Object.values(constants_1.peopleCountBuckets).includes(employeeCount))
261
- throw new Error("Invalid employee count " + employeeCount);
302
+ throw new Error('Invalid employee count ' + employeeCount);
262
303
  if (population && !Object.values(constants_1.peopleCountBuckets).includes(population))
263
- throw new Error("Invalid population " + population);
304
+ throw new Error('Invalid population ' + population);
264
305
  if (confidence && !('' + confidence)?.match(/^[0-9.]+$/))
265
- throw new Error("Invalid confidence " + confidence);
266
- return "\n" +
267
- "\t" + "Type: Organisation verification" + "\n" +
268
- "\t" + "Description: We verified the following information about an organisation." + "\n" +
269
- "\t" + "Name: " + name + "\n" +
270
- (englishName ? "\t" + "English name: " + englishName + "\n" : "") +
271
- "\t" + "Country: " + country + "\n" +
272
- "\t" + "Legal form: " + legalForm + "\n" +
273
- (domain ? "\t" + "Owner of the domain: " + domain + "\n" : "") +
274
- (foreignDomain ? "\t" + "Foreign domain used for publishing statements: " + foreignDomain + "\n" : "") +
275
- (department ? "\t" + "Department using the domain: " + department + "\n" : "") +
276
- (province ? "\t" + "Province or state: " + province + "\n" : "") +
277
- (serialNumber ? "\t" + "Business register number: " + serialNumber + "\n" : "") +
278
- (city ? "\t" + "City: " + city + "\n" : "") +
279
- (latitude ? "\t" + "Latitude: " + latitude + "\n" : "") +
280
- (longitude ? "\t" + "Longitude: " + longitude + "\n" : "") +
281
- (population ? "\t" + "Population: " + population + "\n" : "") +
282
- (pictureHash ? "\t" + "Logo: " + pictureHash + "\n" : "") +
283
- (employeeCount ? "\t" + "Employee count: " + employeeCount + "\n" : "") +
284
- (reliabilityPolicy ? "\t" + "Reliability policy: " + reliabilityPolicy + "\n" : "") +
285
- (confidence ? "\t" + "Confidence: " + confidence + "\n" : "") +
286
- "";
306
+ throw new Error('Invalid confidence ' + confidence);
307
+ if (pictureHash && !pictureHash.match(/^[A-Za-z0-9_-]+\.[a-zA-Z0-9]+$/)) {
308
+ throw new Error("Logo must be in format 'base64hash.extension' (URL-safe base64)");
309
+ }
310
+ if (publicKey && !publicKey.match(/^[A-Za-z0-9_-]+$/)) {
311
+ throw new Error('Public key must be in URL-safe base64 format (A-Z, a-z, 0-9, _, -)');
312
+ }
313
+ return (' Type: Organisation verification\n' +
314
+ ' Description: We verified the following information about an organisation.\n' +
315
+ ' Name: ' +
316
+ name +
317
+ '\n' +
318
+ (englishName ? ' English name: ' + englishName + '\n' : '') +
319
+ ' Country: ' +
320
+ country +
321
+ '\n' +
322
+ ' Legal form: ' +
323
+ legalForm +
324
+ '\n' +
325
+ (domain ? ' Owner of the domain: ' + domain + '\n' : '') +
326
+ (foreignDomain
327
+ ? ' Foreign domain used for publishing statements: ' + foreignDomain + '\n'
328
+ : '') +
329
+ (department ? ' Department using the domain: ' + department + '\n' : '') +
330
+ (province ? ' Province or state: ' + province + '\n' : '') +
331
+ (serialNumber ? ' Business register number: ' + serialNumber + '\n' : '') +
332
+ (city ? ' City: ' + city + '\n' : '') +
333
+ (latitude ? ' Latitude: ' + latitude + '\n' : '') +
334
+ (longitude ? ' Longitude: ' + longitude + '\n' : '') +
335
+ (population ? ' Population: ' + population + '\n' : '') +
336
+ (pictureHash ? ' Logo: ' + pictureHash + '\n' : '') +
337
+ (employeeCount ? ' Employee count: ' + employeeCount + '\n' : '') +
338
+ (publicKey ? ' Public key: ' + publicKey + '\n' : '') +
339
+ (reliabilityPolicy ? ' Reliability policy: ' + reliabilityPolicy + '\n' : '') +
340
+ (confidence ? ' Confidence: ' + confidence + '\n' : ''));
287
341
  };
288
342
  exports.buildOrganisationVerificationContent = buildOrganisationVerificationContent;
289
- const parseOrganisationVerification = (s) => {
290
- const organisationVerificationRegex = new RegExp(''
291
- + /^\n\tType: Organisation verification\n/.source
292
- + /\tDescription: We verified the following information about an organisation.\n/.source
293
- + /\tName: (?<name>[^\n]+?)\n/.source
294
- + /(?:\tEnglish name: (?<englishName>[^\n]+?)\n)?/.source
295
- + /\tCountry: (?<country>[^\n]+?)\n/.source
296
- + /\tLegal (?:form|entity): (?<legalForm>[^\n]+?)\n/.source
297
- + /(?:\tOwner of the domain: (?<domain>[^\n]+?)\n)?/.source
298
- + /(?:\tForeign domain used for publishing statements: (?<foreignDomain>[^\n]+?)\n)?/.source
299
- + /(?:\tDepartment using the domain: (?<department>[^\n]+?)\n)?/.source
300
- + /(?:\tProvince or state: (?<province>[^\n]+?)\n)?/.source
301
- + /(?:\tBusiness register number: (?<serialNumber>[^\n]+?)\n)?/.source
302
- + /(?:\tCity: (?<city>[^\n]+?)\n)?/.source
303
- + /(?:\tLatitude: (?<latitude>[^\n]+?)\n)?/.source
304
- + /(?:\tLongitude: (?<longitude>[^\n]+?)\n)?/.source
305
- + /(?:\tPopulation: (?<population>[^\n]+?)\n)?/.source
306
- + /(?:\tLogo: (?<pictureHash>[^\n]+?)\n)?/.source
307
- + /(?:\tEmployee count: (?<employeeCount>[01,+-]+?)\n)?/.source
308
- + /(?:\tReliability policy: (?<reliabilityPolicy>[^\n]+?)\n)?/.source
309
- + /(?:\tConfidence: (?<confidence>[0-9.]+?)\n)?/.source
310
- + /$/.source);
311
- const m = s.match(organisationVerificationRegex);
312
- if (!m)
313
- throw new Error("Invalid organisation verification format: " + s);
343
+ const parseOrganisationVerification = (content) => {
344
+ const organisationVerificationRegex = new RegExp('' +
345
+ /^ Type: Organisation verification\n/.source +
346
+ / Description: We verified the following information about an organisation.\n/.source +
347
+ / Name: (?<name>[^\n]+?)\n/.source +
348
+ /(?: English name: (?<englishName>[^\n]+?)\n)?/.source +
349
+ / Country: (?<country>[^\n]+?)\n/.source +
350
+ / Legal (?:form|entity): (?<legalForm>[^\n]+?)\n/.source +
351
+ /(?: Owner of the domain: (?<domain>[^\n]+?)\n)?/.source +
352
+ /(?: Foreign domain used for publishing statements: (?<foreignDomain>[^\n]+?)\n)?/.source +
353
+ /(?: Department using the domain: (?<department>[^\n]+?)\n)?/.source +
354
+ /(?: Province or state: (?<province>[^\n]+?)\n)?/.source +
355
+ /(?: Business register number: (?<serialNumber>[^\n]+?)\n)?/.source +
356
+ /(?: City: (?<city>[^\n]+?)\n)?/.source +
357
+ /(?: Latitude: (?<latitude>[^\n]+?)\n)?/.source +
358
+ /(?: Longitude: (?<longitude>[^\n]+?)\n)?/.source +
359
+ /(?: Population: (?<population>[^\n]+?)\n)?/.source +
360
+ /(?: Logo: (?<pictureHash>[A-Za-z0-9_-]+\.[a-zA-Z0-9]+)\n)?/.source +
361
+ /(?: Employee count: (?<employeeCount>[01,+-]+?)\n)?/.source +
362
+ /(?: Public key: (?<publicKey>[A-Za-z0-9_-]+)\n)?/.source +
363
+ /(?: Reliability policy: (?<reliabilityPolicy>[^\n]+?)\n)?/.source +
364
+ /(?: Confidence: (?<confidence>[0-9.]+?))?/.source +
365
+ /\n?$/.source);
366
+ const match = content.match(organisationVerificationRegex);
367
+ if (!match || !match.groups)
368
+ throw new Error('Invalid organisation verification format: ' + content);
369
+ const { name, englishName, country, legalForm, domain, foreignDomain, department, province, serialNumber, city, latitude, longitude, population, pictureHash, employeeCount, publicKey, reliabilityPolicy, confidence, } = match.groups;
370
+ if (!(0, types_1.isLegalForm)(legalForm)) {
371
+ throw new Error('Invalid legal form after validation: ' + legalForm);
372
+ }
314
373
  return {
315
- name: m[1],
316
- englishName: m[2],
317
- country: m[3],
318
- legalForm: m[4],
319
- domain: m[5],
320
- foreignDomain: m[6],
321
- department: m[7],
322
- province: m[8],
323
- serialNumber: m[9],
324
- city: m[10],
325
- latitude: m[11] ? parseFloat(m[11]) : undefined,
326
- longitude: m[12] ? parseFloat(m[12]) : undefined,
327
- population: m[13],
328
- pictureHash: m[14],
329
- employeeCount: m[15],
330
- reliabilityPolicy: m[16],
331
- confidence: m[17] ? parseFloat(m[17]) : undefined,
374
+ name,
375
+ englishName,
376
+ country,
377
+ legalForm,
378
+ domain,
379
+ foreignDomain,
380
+ department,
381
+ province,
382
+ serialNumber,
383
+ city,
384
+ latitude: latitude ? parseFloat(latitude) : undefined,
385
+ longitude: longitude ? parseFloat(longitude) : undefined,
386
+ population: population && (0, types_1.isPeopleCountBucket)(population) ? population : undefined,
387
+ pictureHash,
388
+ employeeCount: employeeCount && (0, types_1.isPeopleCountBucket)(employeeCount) ? employeeCount : undefined,
389
+ publicKey,
390
+ reliabilityPolicy,
391
+ confidence: confidence ? parseFloat(confidence) : undefined,
332
392
  };
333
393
  };
334
394
  exports.parseOrganisationVerification = parseOrganisationVerification;
335
- const buildPersonVerificationContent = ({ name, countryOfBirth, cityOfBirth, ownDomain, foreignDomain, dateOfBirth, jobTitle, employer, verificationMethod, confidence, picture, reliabilityPolicy }) => {
395
+ const buildPersonVerificationContent = ({ name, countryOfBirth, cityOfBirth, ownDomain, foreignDomain, dateOfBirth, jobTitle, employer, verificationMethod, confidence, picture, reliabilityPolicy, publicKey, }) => {
336
396
  if (!name || !countryOfBirth || !cityOfBirth || !dateOfBirth || (!ownDomain && !foreignDomain)) {
337
- console.log("Missing required fields: ", { name, countryOfBirth, cityOfBirth, dateOfBirth, ownDomain, foreignDomain });
338
- return "";
397
+ throw new Error('Missing required fields for person verification');
398
+ }
399
+ if (publicKey && !publicKey.match(/^[A-Za-z0-9_-]+$/)) {
400
+ throw new Error('Public key must be in URL-safe base64 format (A-Z, a-z, 0-9, _, -)');
339
401
  }
340
- const [day, month, year] = dateOfBirth.toUTCString().split(' ').filter((i, j) => [1, 2, 3].includes(j));
341
- let content = "\n" +
342
- "\t" + "Type: Person verification" + "\n" +
343
- "\t" + "Description: We verified the following information about a person." + "\n" +
344
- "\t" + "Name: " + name + "\n" +
345
- "\t" + "Date of birth: " + [day.replace(/$0/, ''), month, year].join(' ') + "\n" +
346
- "\t" + "City of birth: " + cityOfBirth + "\n" +
347
- "\t" + "Country of birth: " + countryOfBirth + "\n" +
348
- (jobTitle ? "\t" + "Job title: " + jobTitle + "\n" : "") +
349
- (employer ? "\t" + "Employer: " + employer + "\n" : "") +
350
- (ownDomain ? "\t" + "Owner of the domain: " + ownDomain + "\n" : "") +
351
- (foreignDomain ? "\t" + "Foreign domain used for publishing statements: " + foreignDomain + "\n" : "") +
352
- (picture ? "\t" + "Picture: " + picture + "\n" : "") +
353
- (verificationMethod ? "\t" + "Verification method: " + verificationMethod + "\n" : "") +
354
- (confidence ? "\t" + "Confidence: " + confidence + "\n" : "") +
355
- (reliabilityPolicy ? "\t" + "Reliability policy: " + reliabilityPolicy + "\n" : "") +
356
- "";
402
+ const [day, month, year] = dateOfBirth
403
+ .toUTCString()
404
+ .split(' ')
405
+ .filter((_i, j) => [1, 2, 3].includes(j));
406
+ const content = ' Type: Person verification\n' +
407
+ ' Description: We verified the following information about a person.\n' +
408
+ ' Name: ' +
409
+ name +
410
+ '\n' +
411
+ ' Date of birth: ' +
412
+ [day.replace(/^0/, ''), month, year].join(' ') +
413
+ '\n' +
414
+ ' City of birth: ' +
415
+ cityOfBirth +
416
+ '\n' +
417
+ ' Country of birth: ' +
418
+ countryOfBirth +
419
+ '\n' +
420
+ (jobTitle ? ' Job title: ' + jobTitle + '\n' : '') +
421
+ (employer ? ' Employer: ' + employer + '\n' : '') +
422
+ (ownDomain ? ' Owner of the domain: ' + ownDomain + '\n' : '') +
423
+ (foreignDomain
424
+ ? ' Foreign domain used for publishing statements: ' + foreignDomain + '\n'
425
+ : '') +
426
+ (picture ? ' Picture: ' + picture + '\n' : '') +
427
+ (verificationMethod ? ' Verification method: ' + verificationMethod + '\n' : '') +
428
+ (publicKey ? ' Public key: ' + publicKey + '\n' : '') +
429
+ (confidence ? ' Confidence: ' + confidence + '\n' : '') +
430
+ (reliabilityPolicy ? ' Reliability policy: ' + reliabilityPolicy + '\n' : '');
357
431
  return content;
358
432
  };
359
433
  exports.buildPersonVerificationContent = buildPersonVerificationContent;
360
- const parsePersonVerification = (s) => {
361
- const domainVerificationRegex = new RegExp(''
362
- + /^\n\tType: Person verification\n/.source
363
- + /\tDescription: We verified the following information about a person.\n/.source
364
- + /\tName: (?<name>[^\n]+?)\n/.source
365
- + /\tDate of birth: (?<dateOfBirth>[^\n]+?)\n/.source
366
- + /\tCity of birth: (?<cityOfBirth>[^\n]+?)\n/.source
367
- + /\tCountry of birth: (?<countryOfBirth>[^\n]+?)\n/.source
368
- + /(?:\tJob title: (?<jobTitle>[^\n]+?)\n)?/.source
369
- + /(?:\tEmployer: (?<employer>[^\n]+?)\n)?/.source
370
- + /(?:\tOwner of the domain: (?<domain>[^\n]+?)\n)?/.source
371
- + /(?:\tForeign domain used for publishing statements: (?<foreignDomain>[^\n]+?)\n)?/.source
372
- + /(?:\tPicture: (?<picture>[^\n]+?)\n)?/.source
373
- + /(?:\tVerification method: (?<verificationMethod>[^\n]+?)\n)?/.source
374
- + /(?:\tConfidence: (?<confidence>[^\n]+?)\n)?/.source
375
- + /(?:\tReliability policy: (?<reliabilityPolicy>[^\n]+?)\n)?/.source
376
- + /$/.source);
377
- const m = s.match(domainVerificationRegex);
378
- if (!m)
379
- throw new Error("Invalid person verification format: " + s);
380
- if (m[2] && !m[2].match(utils_1.birthDateFormat))
381
- throw new Error("Invalid birth date format: " + m[2]);
382
- let { d, month, y } = m[2].match(utils_1.birthDateFormat)?.groups || {};
434
+ const parsePersonVerification = (content) => {
435
+ const domainVerificationRegex = new RegExp('' +
436
+ /^ Type: Person verification\n/.source +
437
+ / Description: We verified the following information about a person.\n/.source +
438
+ / Name: (?<name>[^\n]+?)\n/.source +
439
+ / Date of birth: (?<dateOfBirth>[^\n]+?)\n/.source +
440
+ / City of birth: (?<cityOfBirth>[^\n]+?)\n/.source +
441
+ / Country of birth: (?<countryOfBirth>[^\n]+?)\n/.source +
442
+ /(?: Job title: (?<jobTitle>[^\n]+?)\n)?/.source +
443
+ /(?: Employer: (?<employer>[^\n]+?)\n)?/.source +
444
+ /(?: Owner of the domain: (?<domain>[^\n]+?)\n)?/.source +
445
+ /(?: Foreign domain used for publishing statements: (?<foreignDomain>[^\n]+?)\n)?/.source +
446
+ /(?: Picture: (?<picture>[^\n]+?)\n)?/.source +
447
+ /(?: Verification method: (?<verificationMethod>[^\n]+?)\n)?/.source +
448
+ /(?: Public key: (?<publicKey>[A-Za-z0-9_-]+)\n)?/.source +
449
+ /(?: Confidence: (?<confidence>[^\n]+?)\n)?/.source +
450
+ /(?: Reliability policy: (?<reliabilityPolicy>[^\n]+?)\n)?/.source +
451
+ /$/.source);
452
+ const match = content.match(domainVerificationRegex);
453
+ if (!match || !match.groups)
454
+ throw new Error('Invalid person verification format: ' + content);
455
+ const { name, dateOfBirth: dateOfBirthStr, cityOfBirth, countryOfBirth, jobTitle, employer, domain, foreignDomain, picture, verificationMethod, publicKey, confidence, reliabilityPolicy, } = match.groups;
456
+ if (dateOfBirthStr && !dateOfBirthStr.match(utils_1.birthDateFormat))
457
+ throw new Error('Invalid birth date format: ' + dateOfBirthStr);
458
+ const { d, month, y } = dateOfBirthStr.match(utils_1.birthDateFormat)?.groups || {};
383
459
  if (!d || !month || !y)
384
- throw new Error("Invalid birth date format: " + m[2]);
460
+ throw new Error('Invalid birth date format: ' + dateOfBirthStr);
385
461
  return {
386
- name: m[1],
462
+ name,
387
463
  dateOfBirth: new Date(Date.UTC(parseInt(y), (0, utils_1.monthIndex)(month), parseInt(d))),
388
- cityOfBirth: m[3],
389
- countryOfBirth: m[4],
390
- jobTitle: m[5],
391
- employer: m[6],
392
- ownDomain: m[7],
393
- foreignDomain: m[8],
394
- picture: m[9],
395
- verificationMethod: m[10],
396
- confidence: m[11] ? parseFloat(m[11]) : undefined,
397
- reliabilityPolicy: m[12]
464
+ cityOfBirth,
465
+ countryOfBirth,
466
+ jobTitle,
467
+ employer,
468
+ ownDomain: domain,
469
+ foreignDomain,
470
+ picture,
471
+ verificationMethod,
472
+ publicKey,
473
+ confidence: confidence ? parseFloat(confidence) : undefined,
474
+ reliabilityPolicy,
398
475
  };
399
476
  };
400
477
  exports.parsePersonVerification = parsePersonVerification;
401
478
  const buildVoteContent = ({ pollHash, poll, vote }) => {
402
- const content = "\n" +
403
- "\t" + "Type: Vote" + "\n" +
404
- "\t" + "Poll id: " + pollHash + "\n" +
405
- "\t" + "Poll: " + poll + "\n" +
406
- "\t" + "Option: " + vote + "\n" +
407
- "";
479
+ const content = ' Type: Vote\n' +
480
+ ' Poll id: ' +
481
+ pollHash +
482
+ '\n' +
483
+ ' Poll:\n ' +
484
+ poll +
485
+ '\n' +
486
+ ' Option:\n ' +
487
+ vote +
488
+ '\n';
408
489
  return content;
409
490
  };
410
491
  exports.buildVoteContent = buildVoteContent;
411
- const parseVote = (s) => {
412
- const voteRegex = new RegExp(''
413
- + /^\n\tType: Vote\n/.source
414
- + /\tPoll id: (?<pollHash>[^\n]+?)\n/.source
415
- + /\tPoll: (?<poll>[^\n]+?)\n/.source
416
- + /\tOption: (?<vote>[^\n]+?)\n/.source
417
- + /$/.source);
418
- const m = s.match(voteRegex);
419
- if (!m)
420
- throw new Error("Invalid vote format: " + s);
421
- return {
422
- pollHash: m[1],
423
- poll: m[2],
424
- vote: m[3]
425
- };
492
+ const parseVote = (content) => {
493
+ const voteRegex = new RegExp('' +
494
+ /^ Type: Vote\n/.source +
495
+ / Poll id: (?<pollHash>[^\n]+?)\n/.source +
496
+ / Poll:\n (?<poll>[^\n]+?)\n/.source +
497
+ / Option:\n (?<vote>[^\n]+?)\n/.source +
498
+ /$/.source);
499
+ const match = content.match(voteRegex);
500
+ if (!match || !match.groups)
501
+ throw new Error('Invalid vote format: ' + content);
502
+ const { pollHash, poll, vote } = match.groups;
503
+ return { pollHash, poll, vote };
426
504
  };
427
505
  exports.parseVote = parseVote;
428
- const buildDisputeAuthenticityContent = ({ hash, confidence, reliabilityPolicy }) => {
429
- const content = "\n" +
430
- "\t" + "Type: Dispute statement authenticity" + "\n" +
431
- "\t" + "Description: We think that the referenced statement is not authentic.\n" +
432
- "\t" + "Hash of referenced statement: " + hash + "\n" +
433
- (confidence ? "\t" + "Confidence: " + confidence + "\n" : "") +
434
- (reliabilityPolicy ? "\t" + "Reliability policy: " + reliabilityPolicy + "\n" : "") +
435
- "";
506
+ const buildDisputeAuthenticityContent = ({ hash, confidence, reliabilityPolicy, }) => {
507
+ const content = ' Type: Dispute statement authenticity\n' +
508
+ ' Description: We think that the referenced statement is not authentic.\n' +
509
+ ' Hash of referenced statement: ' +
510
+ hash +
511
+ '\n' +
512
+ (confidence ? ' Confidence: ' + confidence + '\n' : '') +
513
+ (reliabilityPolicy ? ' Reliability policy: ' + reliabilityPolicy + '\n' : '');
436
514
  return content;
437
515
  };
438
516
  exports.buildDisputeAuthenticityContent = buildDisputeAuthenticityContent;
439
- const parseDisputeAuthenticity = (s) => {
440
- const disputeRegex = new RegExp(''
441
- + /^\n\tType: Dispute statement authenticity\n/.source
442
- + /\tDescription: We think that the referenced statement is not authentic.\n/.source
443
- + /\tHash of referenced statement: (?<hash>[^\n]+?)\n/.source
444
- + /(?:\tConfidence: (?<confidence>[^\n]*?)\n)?/.source
445
- + /(?:\tReliability policy: (?<reliabilityPolicy>[^\n]+?)\n)?/.source
446
- + /$/.source);
447
- const m = s.match(disputeRegex);
448
- if (!m)
449
- throw new Error("Invalid dispute authenticity format: " + s);
517
+ const parseDisputeAuthenticity = (content) => {
518
+ const disputeRegex = new RegExp('' +
519
+ /^ Type: Dispute statement authenticity\n/.source +
520
+ / Description: We think that the referenced statement is not authentic.\n/.source +
521
+ / Hash of referenced statement: (?<hash>[^\n]+?)\n/.source +
522
+ /(?: Confidence: (?<confidence>[^\n]*?)\n)?/.source +
523
+ /(?: Reliability policy: (?<reliabilityPolicy>[^\n]+?)\n)?/.source +
524
+ /$/.source);
525
+ const match = content.match(disputeRegex);
526
+ if (!match || !match.groups)
527
+ throw new Error('Invalid dispute authenticity format: ' + content);
528
+ const { hash, confidence, reliabilityPolicy } = match.groups;
450
529
  return {
451
- hash: m[1],
452
- confidence: m[2] ? parseFloat(m[2]) : undefined,
453
- reliabilityPolicy: m[3]
530
+ hash,
531
+ confidence: confidence ? parseFloat(confidence) : undefined,
532
+ reliabilityPolicy,
454
533
  };
455
534
  };
456
535
  exports.parseDisputeAuthenticity = parseDisputeAuthenticity;
457
- const buildDisputeContentContent = ({ hash, confidence, reliabilityPolicy }) => {
458
- const content = "\n" +
459
- "\t" + "Type: Dispute statement content" + "\n" +
460
- "\t" + "Description: We think that the content of the referenced statement is false.\n" +
461
- "\t" + "Hash of referenced statement: " + hash + "\n" +
462
- (confidence ? "\t" + "Confidence: " + confidence + "\n" : "") +
463
- (reliabilityPolicy ? "\t" + "Reliability policy: " + reliabilityPolicy + "\n" : "") +
464
- "";
536
+ const buildDisputeContentContent = ({ hash, confidence, reliabilityPolicy, }) => {
537
+ const content = ' Type: Dispute statement content\n' +
538
+ ' Description: We think that the content of the referenced statement is false.\n' +
539
+ ' Hash of referenced statement: ' +
540
+ hash +
541
+ '\n' +
542
+ (confidence ? ' Confidence: ' + confidence + '\n' : '') +
543
+ (reliabilityPolicy ? ' Reliability policy: ' + reliabilityPolicy + '\n' : '');
465
544
  return content;
466
545
  };
467
546
  exports.buildDisputeContentContent = buildDisputeContentContent;
468
- const parseDisputeContent = (s) => {
469
- const disputeRegex = new RegExp(''
470
- + /^\n\tType: Dispute statement content\n/.source
471
- + /\tDescription: We think that the content of the referenced statement is false.\n/.source
472
- + /\tHash of referenced statement: (?<hash>[^\n]+?)\n/.source
473
- + /(?:\tConfidence: (?<confidence>[^\n]*?)\n)?/.source
474
- + /(?:\tReliability policy: (?<reliabilityPolicy>[^\n]+?)\n)?/.source
475
- + /$/.source);
476
- const m = s.match(disputeRegex);
477
- if (!m)
478
- throw new Error("Invalid dispute content format: " + s);
547
+ const parseDisputeContent = (content) => {
548
+ const disputeRegex = new RegExp('' +
549
+ /^ Type: Dispute statement content\n/.source +
550
+ / Description: We think that the content of the referenced statement is false.\n/.source +
551
+ / Hash of referenced statement: (?<hash>[^\n]+?)\n/.source +
552
+ /(?: Confidence: (?<confidence>[^\n]*?)\n)?/.source +
553
+ /(?: Reliability policy: (?<reliabilityPolicy>[^\n]+?)\n)?/.source +
554
+ /$/.source);
555
+ const match = content.match(disputeRegex);
556
+ if (!match || !match.groups)
557
+ throw new Error('Invalid dispute content format: ' + content);
558
+ const { hash, confidence, reliabilityPolicy } = match.groups;
479
559
  return {
480
- hash: m[1],
481
- confidence: m[2] ? parseFloat(m[2]) : undefined,
482
- reliabilityPolicy: m[3]
560
+ hash,
561
+ confidence: confidence ? parseFloat(confidence) : undefined,
562
+ reliabilityPolicy,
483
563
  };
484
564
  };
485
565
  exports.parseDisputeContent = parseDisputeContent;
486
566
  const buildResponseContent = ({ hash, response }) => {
487
- const content = "\n" +
488
- "\t" + "Type: Response" + "\n" +
489
- "\t" + "Hash of referenced statement: " + hash + "\n" +
490
- "\t" + "Response: " + response + "\n" +
491
- "";
567
+ const content = ' Type: Response\n' +
568
+ ' Hash of referenced statement: ' +
569
+ hash +
570
+ '\n' +
571
+ ' Response:\n ' +
572
+ response +
573
+ '\n';
492
574
  return content;
493
575
  };
494
576
  exports.buildResponseContent = buildResponseContent;
495
- const parseResponseContent = (s) => {
496
- const disputeRegex = new RegExp(''
497
- + /^\n\tType: Response\n/.source
498
- + /\tHash of referenced statement: (?<hash>[^\n]+?)\n/.source
499
- + /\tResponse: (?<response>[^\n]*?)\n/.source
500
- + /$/.source);
501
- const m = s.match(disputeRegex);
502
- if (!m)
503
- throw new Error("Invalid response content format: " + s);
504
- return {
505
- hash: m[1],
506
- response: m[2]
507
- };
577
+ const parseResponseContent = (content) => {
578
+ const responseRegex = new RegExp('' +
579
+ /^ Type: Response\n/.source +
580
+ / Hash of referenced statement: (?<hash>[^\n]+?)\n/.source +
581
+ / Response:\n (?<response>[^\n]*?)\n/.source +
582
+ /$/.source);
583
+ const match = content.match(responseRegex);
584
+ if (!match || !match.groups)
585
+ throw new Error('Invalid response content format: ' + content);
586
+ const { hash, response } = match.groups;
587
+ return { hash, response };
508
588
  };
509
589
  exports.parseResponseContent = parseResponseContent;
510
590
  const buildPDFSigningContent = ({ hash }) => {
511
- const content = "\n" +
512
- "\t" + "Type: Sign PDF" + "\n" +
513
- "\t" + "Description: We hereby digitally sign the referenced PDF file.\n" +
514
- "\t" + "PDF file hash: " + hash + "\n" +
515
- "";
591
+ if (!hash.match(/^[A-Za-z0-9_-]+$/)) {
592
+ throw new Error('PDF file hash must be in URL-safe base64 format (A-Z, a-z, 0-9, _, -)');
593
+ }
594
+ const content = ' Type: Sign PDF\n' +
595
+ ' Description: We hereby digitally sign the referenced PDF file.\n' +
596
+ ' PDF file hash: ' +
597
+ hash +
598
+ '\n';
516
599
  return content;
517
600
  };
518
601
  exports.buildPDFSigningContent = buildPDFSigningContent;
519
- const parsePDFSigning = (s) => {
520
- const signingRegex = new RegExp(''
521
- + /^\n\tType: Sign PDF\n/.source
522
- + /\tDescription: We hereby digitally sign the referenced PDF file.\n/.source
523
- + /\tPDF file hash: (?<hash>[^\n]+?)\n/.source
524
- + /$/.source);
525
- const m = s.match(signingRegex);
526
- if (!m)
527
- throw new Error("Invalid PDF signing format: " + s);
528
- return {
529
- hash: m[1]
530
- };
602
+ const parsePDFSigning = (content) => {
603
+ const signingRegex = new RegExp('' +
604
+ /^ Type: Sign PDF\n/.source +
605
+ / Description: We hereby digitally sign the referenced PDF file.\n/.source +
606
+ / PDF file hash: (?<hash>[A-Za-z0-9_-]+)\n/.source +
607
+ /$/.source);
608
+ const match = content.match(signingRegex);
609
+ if (!match || !match.groups)
610
+ throw new Error('Invalid PDF signing format: ' + content);
611
+ const { hash } = match.groups;
612
+ return { hash };
531
613
  };
532
614
  exports.parsePDFSigning = parsePDFSigning;
533
- const buildRating = ({ subjectName, subjectType, subjectReference, documentFileHash, rating, quality, comment }) => {
615
+ const buildRating = ({ subjectName, subjectType, subjectReference, documentFileHash, rating, quality, comment, }) => {
534
616
  if (![1, 2, 3, 4, 5].includes(rating))
535
- throw new Error("Invalid rating: " + rating);
536
- const content = "\n" +
537
- "\t" + "Type: Rating" + "\n" +
538
- (subjectType ? "\t" + "Subject type: " + subjectType + "\n" : "") +
539
- "\t" + "Subject name: " + subjectName + "\n" +
540
- (subjectReference ? "\t" + "URL that identifies the subject: " + subjectReference + "\n" : "") +
541
- (documentFileHash ? "\t" + "Document file hash: " + documentFileHash + "\n" : "") +
542
- (quality ? "\t" + "Rated quality: " + quality + "\n" : "") +
543
- "\t" + "Our rating: " + rating + "/5 Stars\n" +
544
- (comment ? "\t" + "Comment: " + comment + "\n" : "") +
545
- "";
617
+ throw new Error('Invalid rating: ' + rating);
618
+ const content = ' Type: Rating\n' +
619
+ (subjectType ? ' Subject type: ' + subjectType + '\n' : '') +
620
+ ' Subject name: ' +
621
+ subjectName +
622
+ '\n' +
623
+ (subjectReference ? ' URL that identifies the subject: ' + subjectReference + '\n' : '') +
624
+ (documentFileHash ? ' Document file hash: ' + documentFileHash + '\n' : '') +
625
+ (quality ? ' Rated quality: ' + quality + '\n' : '') +
626
+ ' Our rating: ' +
627
+ rating +
628
+ '/5 Stars\n' +
629
+ (comment ? ' Comment:\n ' + comment + '\n' : '');
546
630
  return content;
547
631
  };
548
632
  exports.buildRating = buildRating;
549
- const parseRating = (s) => {
550
- const ratingRegex = new RegExp(''
551
- + /^\n\tType: Rating\n/.source
552
- + /(?:\tSubject type: (?<subjectType>[^\n]*?)\n)?/.source
553
- + /\tSubject name: (?<subjectName>[^\n]*?)\n/.source
554
- + /(?:\tURL that identifies the subject: (?<subjectReference>[^\n]*?)\n)?/.source
555
- + /(?:\tDocument file hash: (?<documentFileHash>[^\n]*?)\n)?/.source
556
- + /(?:\tRated quality: (?<quality>[^\n]*?)\n)?/.source
557
- + /\tOur rating: (?<rating>[1-5])\/5 Stars\n/.source
558
- + /(?:\tComment: (?<comment>[\s\S]+?)\n)?/.source
559
- + /$/.source);
560
- const m = s.match(ratingRegex);
561
- if (!m)
562
- throw new Error("Invalid rating format: " + s);
563
- const rating = parseInt(m[6]);
633
+ const parseRating = (content) => {
634
+ const ratingRegex = new RegExp('' +
635
+ /^ Type: Rating\n/.source +
636
+ /(?: Subject type: (?<subjectType>[^\n]*?)\n)?/.source +
637
+ / Subject name: (?<subjectName>[^\n]*?)\n/.source +
638
+ /(?: URL that identifies the subject: (?<subjectReference>[^\n]*?)\n)?/.source +
639
+ /(?: Document file hash: (?<documentFileHash>[^\n]*?)\n)?/.source +
640
+ /(?: Rated quality: (?<quality>[^\n]*?)\n)?/.source +
641
+ / Our rating: (?<rating>[1-5])\/5 Stars\n/.source +
642
+ /(?: Comment:\n (?<comment>[\s\S]+?)\n)?/.source +
643
+ /$/.source);
644
+ const match = content.match(ratingRegex);
645
+ if (!match || !match.groups)
646
+ throw new Error('Invalid rating format: ' + content);
647
+ const { subjectType, subjectName, subjectReference, documentFileHash, quality, rating: ratingStr, comment, } = match.groups;
648
+ const rating = parseInt(ratingStr);
564
649
  if (![1, 2, 3, 4, 5].includes(rating))
565
- throw new Error("Invalid rating: " + m[6]);
566
- if (m[1] && !['Organisation', 'Policy proposal', 'Regulation',
567
- 'Treaty draft', 'Product', 'Research publication'].includes(m[1]))
568
- throw new Error("Invalid subject type: " + m[1]);
569
- if (!m[2])
570
- throw new Error("Missing subject name");
650
+ throw new Error('Invalid rating: ' + ratingStr);
651
+ if (subjectType &&
652
+ ![
653
+ 'Organisation',
654
+ 'Policy proposal',
655
+ 'Regulation',
656
+ 'Treaty draft',
657
+ 'Product',
658
+ 'Research publication',
659
+ ].includes(subjectType))
660
+ throw new Error('Invalid subject type: ' + subjectType);
661
+ if (!subjectName)
662
+ throw new Error('Missing subject name');
663
+ if (!(0, types_1.isRatingValue)(rating)) {
664
+ throw new Error('Invalid rating after validation: ' + rating);
665
+ }
571
666
  return {
572
- subjectType: m[1],
573
- subjectName: m[2],
574
- subjectReference: m[3],
575
- documentFileHash: m[4],
576
- quality: m[5],
667
+ subjectType: subjectType,
668
+ subjectName,
669
+ subjectReference,
670
+ documentFileHash,
671
+ quality,
577
672
  rating,
578
- comment: m[7]
673
+ comment,
579
674
  };
580
675
  };
581
676
  exports.parseRating = parseRating;
582
- const buildBounty = ({ motivation, bounty, reward, judge, judgePay }) => {
583
- const content = "\n" +
584
- "\t" + "Type: Bounty" + "\n" +
585
- (motivation ? "\t" + "In order to: " + motivation + "\n" : "") +
586
- "\t" + "We will reward any entity that: " + bounty + "\n" +
587
- "\t" + "The reward is: " + reward + "\n" +
588
- "\t" + "In case of dispute, bounty claims are judged by: " + judge + "\n" +
589
- (judgePay ? "\t" + "The judge will be paid per investigated case with a maxium of: " + judgePay + "\n" : "") +
590
- "";
591
- return content;
592
- };
593
- exports.buildBounty = buildBounty;
594
- const parseBounty = (s) => {
595
- const bountyRegex = new RegExp(''
596
- + /^\n\tType: Bounty\n/.source
597
- + /(?:\tIn order to: (?<motivation>[^\n]*?)\n)?/.source
598
- + /\tWe will reward any entity that: (?<bounty>[^\n]*?)\n/.source
599
- + /\tThe reward is: (?<reward>[^\n]*?)\n/.source
600
- + /\tIn case of dispute, bounty claims are judged by: (?<judge>[^\n]*?)\n/.source
601
- + /(?:\tThe judge will be paid per investigated case with a maxium of: (?<judgePay>[^\n]*?)\n)?/.source
602
- + /$/.source);
603
- const m = s.match(bountyRegex);
604
- if (!m)
605
- throw new Error("Invalid bounty format: " + s);
606
- return {
607
- motivation: m[1],
608
- bounty: m[2],
609
- reward: m[3],
610
- judge: m[4],
611
- judgePay: m[5]
612
- };
613
- };
614
- exports.parseBounty = parseBounty;
615
- const buildObservation = ({ approach, confidence, reliabilityPolicy, subject, subjectReference, observationReference, property, value }) => {
616
- const content = "\n" +
617
- "\t" + "Type: Observation" + "\n" +
618
- (approach ? "\t" + "Approach: " + approach + "\n" : "") +
619
- (confidence ? "\t" + "Confidence: " + confidence + "\n" : "") +
620
- (reliabilityPolicy ? "\t" + "Reliability policy: " + reliabilityPolicy + "\n" : "") +
621
- "\t" + "Subject: " + subject + "\n" +
622
- (subjectReference ? "\t" + "Subject identity reference: " + subjectReference + "\n" : "") +
623
- (observationReference ? "\t" + "Observation reference: " + observationReference + "\n" : "") +
624
- "\t" + "Observed property: " + property + "\n" +
625
- (value ? "\t" + "Observed value: " + value + "\n" : "") +
626
- "";
627
- return content;
628
- };
629
- exports.buildObservation = buildObservation;
630
- const parseObservation = (s) => {
631
- const observationRegex = new RegExp(''
632
- + /^\n\tType: Observation\n/.source
633
- + /(?:\tApproach: (?<approach>[^\n]*?)\n)?/.source
634
- + /(?:\tConfidence: (?<confidence>[^\n]*?)\n)?/.source
635
- + /(?:\tReliability policy: (?<reliabilityPolicy>[^\n]+?)\n)?/.source
636
- + /\tSubject: (?<subject>[^\n]*?)\n/.source
637
- + /(?:\tSubject identity reference: (?<subjectReference>[^\n]*?)\n)?/.source
638
- + /(?:\tObservation reference: (?<observationReference>[^\n]*?)\n)?/.source
639
- + /\tObserved property: (?<property>[^\n]*?)\n/.source
640
- + /(?:\tObserved value: (?<value>[\s\S]+?)\n)?/.source
641
- + /$/.source);
642
- const m = s.match(observationRegex);
643
- if (!m)
644
- throw new Error("Invalid observation format: " + s);
645
- return {
646
- approach: m[1],
647
- confidence: m[2] ? parseFloat(m[2]) : undefined,
648
- reliabilityPolicy: m[3],
649
- subject: m[4],
650
- subjectReference: m[5],
651
- observationReference: m[6],
652
- property: m[7],
653
- value: m[8]
654
- };
655
- };
656
- exports.parseObservation = parseObservation;
657
- const buildBoycott = ({ description, subject, subjectReference }) => {
658
- const content = "\n" +
659
- "\t" + "Type: Boycott" + "\n" +
660
- (description ? "\t" + "Description: " + description + "\n" : "") +
661
- "\t" + "Subject: " + subject + "\n" +
662
- (subjectReference ? "\t" + "Subject identity reference: " + subjectReference + "\n" : "") +
663
- "";
664
- return content;
665
- };
666
- exports.buildBoycott = buildBoycott;
667
- const parseBoycott = (s) => {
668
- const observationRegex = new RegExp(''
669
- + /^\n\tType: Boycott\n/.source
670
- + /(?:\tDescription: (?<description>[^\n]*?)\n)?/.source
671
- + /\tSubject: (?<subject>[^\n]*?)\n/.source
672
- + /(?:\tSubject identity reference: (?<subjectReference>[^\n]*?)\n)?/.source
673
- + /$/.source);
674
- const m = s.match(observationRegex);
675
- if (!m)
676
- throw new Error("Invalid observation format: " + s);
677
- return {
678
- description: m[1],
679
- subject: m[2],
680
- subjectReference: m[3],
681
- };
682
- };
683
- exports.parseBoycott = parseBoycott;
684
677
  //# sourceMappingURL=protocol.js.map