stated-protocol 5.2.1 → 5.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/fixtures.test.d.ts +2 -0
- package/dist/fixtures.test.d.ts.map +1 -0
- package/dist/fixtures.test.js +207 -0
- package/dist/fixtures.test.js.map +1 -0
- package/dist/hash.js +2 -2
- package/dist/hash.js.map +1 -1
- package/dist/hash.test.d.ts +2 -0
- package/dist/hash.test.d.ts.map +1 -0
- package/dist/hash.test.js +183 -0
- package/dist/hash.test.js.map +1 -0
- package/dist/observation.test.d.ts +2 -0
- package/dist/observation.test.d.ts.map +1 -0
- package/dist/observation.test.js +101 -0
- package/dist/observation.test.js.map +1 -0
- package/dist/organisation-verification.test.d.ts +2 -0
- package/dist/organisation-verification.test.d.ts.map +1 -0
- package/dist/organisation-verification.test.js +44 -0
- package/dist/organisation-verification.test.js.map +1 -0
- package/dist/person-verification.test.d.ts +2 -0
- package/dist/person-verification.test.d.ts.map +1 -0
- package/dist/person-verification.test.js +41 -0
- package/dist/person-verification.test.js.map +1 -0
- package/dist/poll.test.d.ts +2 -0
- package/dist/poll.test.d.ts.map +1 -0
- package/dist/poll.test.js +26 -0
- package/dist/poll.test.js.map +1 -0
- package/dist/protocol.d.ts +3 -1
- package/dist/protocol.d.ts.map +1 -1
- package/dist/protocol.js +53 -8
- package/dist/protocol.js.map +1 -1
- package/dist/rating.test.d.ts +2 -0
- package/dist/rating.test.d.ts.map +1 -0
- package/dist/rating.test.js +24 -0
- package/dist/rating.test.js.map +1 -0
- package/dist/signature.js +2 -2
- package/dist/signature.js.map +1 -1
- package/dist/signature.test.d.ts +2 -0
- package/dist/signature.test.d.ts.map +1 -0
- package/dist/signature.test.js +165 -0
- package/dist/signature.test.js.map +1 -0
- package/dist/statement.test.d.ts +2 -0
- package/dist/statement.test.d.ts.map +1 -0
- package/dist/statement.test.js +90 -0
- package/dist/statement.test.js.map +1 -0
- package/dist/types.d.ts +7 -2
- package/dist/types.d.ts.map +1 -1
- package/dist/utils.d.ts +1 -1
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +2 -9
- package/dist/utils.js.map +1 -1
- package/dist/utils.test.d.ts +2 -0
- package/dist/utils.test.d.ts.map +1 -0
- package/dist/utils.test.js +106 -0
- package/dist/utils.test.js.map +1 -0
- package/package.json +1 -1
- package/src/fixtures.test.ts +8 -1
- package/src/hash.ts +2 -2
- package/src/observation.test.ts +128 -0
- package/src/poll.test.ts +1 -1
- package/src/protocol.ts +56 -8
- package/src/rating.test.ts +5 -1
- package/src/signature.ts +2 -2
- package/src/types.ts +9 -2
- package/src/utils.test.ts +5 -10
- package/src/utils.ts +2 -10
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { describe, it } from 'node:test';
|
|
2
|
+
import assert from 'node:assert';
|
|
3
|
+
import { parseStatement, buildStatement } from './protocol';
|
|
4
|
+
const randomUnicodeString = () => Array.from({ length: 20 }, () => String.fromCharCode(Math.floor(Math.random() * 65536)))
|
|
5
|
+
.join('')
|
|
6
|
+
.replace(/[\n;>=<"''\\]/g, '');
|
|
7
|
+
describe('Statement building', () => {
|
|
8
|
+
it('build & parse function compatibility: input=parse(build(input))', () => {
|
|
9
|
+
const [domain, author, representative, content, supersededStatement] = Array.from({ length: 5 }, randomUnicodeString);
|
|
10
|
+
const tags = Array.from({ length: 4 }, randomUnicodeString);
|
|
11
|
+
const contentWithTrailingNewline = content + (content.match(/\n$/) ? '' : '\n');
|
|
12
|
+
const time = new Date('Sun, 04 Sep 2022 14:48:50 GMT');
|
|
13
|
+
const statementContent = buildStatement({
|
|
14
|
+
domain,
|
|
15
|
+
author,
|
|
16
|
+
time,
|
|
17
|
+
content: contentWithTrailingNewline,
|
|
18
|
+
representative,
|
|
19
|
+
supersededStatement,
|
|
20
|
+
tags,
|
|
21
|
+
});
|
|
22
|
+
const parsedStatement = parseStatement({ statement: statementContent });
|
|
23
|
+
assert.strictEqual(parsedStatement.domain, domain);
|
|
24
|
+
assert.strictEqual(parsedStatement.author, author);
|
|
25
|
+
assert.strictEqual(parsedStatement.time?.toUTCString(), time.toUTCString());
|
|
26
|
+
assert.strictEqual(parsedStatement.content, content);
|
|
27
|
+
assert.strictEqual(parsedStatement.representative, representative);
|
|
28
|
+
assert.strictEqual(parsedStatement.supersededStatement, supersededStatement);
|
|
29
|
+
assert.deepStrictEqual(parsedStatement.tags?.sort(), tags.sort());
|
|
30
|
+
});
|
|
31
|
+
it('build & parse statement with attachments', () => {
|
|
32
|
+
const domain = 'example.com';
|
|
33
|
+
const author = 'Test Author';
|
|
34
|
+
const time = new Date('Thu, 15 Jun 2023 20:01:26 GMT');
|
|
35
|
+
const content = 'Statement with attachments';
|
|
36
|
+
const attachments = ['abc123_-XYZ.pdf', 'def456-_ABC.jpg', 'xyz789_hash.docx'];
|
|
37
|
+
const statementContent = buildStatement({
|
|
38
|
+
domain,
|
|
39
|
+
author,
|
|
40
|
+
time,
|
|
41
|
+
content,
|
|
42
|
+
attachments,
|
|
43
|
+
});
|
|
44
|
+
const parsedStatement = parseStatement({ statement: statementContent });
|
|
45
|
+
assert.strictEqual(parsedStatement.domain, domain);
|
|
46
|
+
assert.strictEqual(parsedStatement.author, author);
|
|
47
|
+
assert.strictEqual(parsedStatement.content, content);
|
|
48
|
+
assert.deepStrictEqual(parsedStatement.attachments, attachments);
|
|
49
|
+
});
|
|
50
|
+
it('reject statement with more than 5 attachments', () => {
|
|
51
|
+
const domain = 'example.com';
|
|
52
|
+
const author = 'Test Author';
|
|
53
|
+
const time = new Date();
|
|
54
|
+
const content = 'Too many attachments\n';
|
|
55
|
+
const attachments = [
|
|
56
|
+
'hash1.pdf',
|
|
57
|
+
'hash2.pdf',
|
|
58
|
+
'hash3.pdf',
|
|
59
|
+
'hash4.pdf',
|
|
60
|
+
'hash5.pdf',
|
|
61
|
+
'hash6.pdf',
|
|
62
|
+
];
|
|
63
|
+
assert.throws(() => {
|
|
64
|
+
buildStatement({
|
|
65
|
+
domain,
|
|
66
|
+
author,
|
|
67
|
+
time,
|
|
68
|
+
content,
|
|
69
|
+
attachments,
|
|
70
|
+
});
|
|
71
|
+
}, /Maximum 5 attachments allowed/);
|
|
72
|
+
});
|
|
73
|
+
it('reject attachment with invalid format', () => {
|
|
74
|
+
const domain = 'example.com';
|
|
75
|
+
const author = 'Test Author';
|
|
76
|
+
const time = new Date();
|
|
77
|
+
const content = 'Invalid attachment\n';
|
|
78
|
+
const attachments = ['invalid file name.pdf'];
|
|
79
|
+
assert.throws(() => {
|
|
80
|
+
buildStatement({
|
|
81
|
+
domain,
|
|
82
|
+
author,
|
|
83
|
+
time,
|
|
84
|
+
content,
|
|
85
|
+
attachments,
|
|
86
|
+
});
|
|
87
|
+
}, /Attachment 1 must be in format 'base64hash.extension' \(URL-safe base64\)/);
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
//# sourceMappingURL=statement.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"statement.test.js","sourceRoot":"","sources":["../src/statement.test.ts"],"names":[],"mappings":"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"}
|
package/dist/types.d.ts
CHANGED
|
@@ -4,7 +4,7 @@ export type PeopleCountBucket = '0-10' | '10-100' | '100-1000' | '1000-10,000' |
|
|
|
4
4
|
export declare function isLegalForm(value: string): value is LegalForm;
|
|
5
5
|
export declare function isPeopleCountBucket(value: string): value is PeopleCountBucket;
|
|
6
6
|
export declare function isRatingValue(value: number): value is RatingValue;
|
|
7
|
-
export type StatementTypeValue = 'statement' | 'organisation_verification' | 'person_verification' | 'poll' | 'vote' | 'response' | 'dispute_statement_content' | 'dispute_statement_authenticity' | 'rating' | 'sign_pdf';
|
|
7
|
+
export type StatementTypeValue = 'statement' | 'organisation_verification' | 'person_verification' | 'poll' | 'vote' | 'response' | 'dispute_statement_content' | 'dispute_statement_authenticity' | 'rating' | 'sign_pdf' | 'observation';
|
|
8
8
|
export type Statement = {
|
|
9
9
|
domain: string;
|
|
10
10
|
author: string;
|
|
@@ -89,7 +89,7 @@ export type ResponseContent = {
|
|
|
89
89
|
hash: string;
|
|
90
90
|
response: string;
|
|
91
91
|
};
|
|
92
|
-
export type PDFSigning =
|
|
92
|
+
export type PDFSigning = Record<string, never>;
|
|
93
93
|
export type RatingSubjectTypeValue = 'Organisation' | 'Policy proposal' | 'Treaty draft' | 'Research publication' | 'Regulation' | 'Product';
|
|
94
94
|
export type RatingValue = 1 | 2 | 3 | 4 | 5;
|
|
95
95
|
export type Rating = {
|
|
@@ -108,4 +108,9 @@ export type Bounty = {
|
|
|
108
108
|
judge: string;
|
|
109
109
|
judgePay?: string;
|
|
110
110
|
};
|
|
111
|
+
export type Observation = {
|
|
112
|
+
subject: string;
|
|
113
|
+
property: string;
|
|
114
|
+
value: string;
|
|
115
|
+
};
|
|
111
116
|
//# sourceMappingURL=types.d.ts.map
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAErD,MAAM,MAAM,SAAS,GACjB,kBAAkB,GAClB,kBAAkB,GAClB,0BAA0B,GAC1B,aAAa,GACb,qBAAqB,CAAC;AAE1B,MAAM,MAAM,iBAAiB,GACzB,MAAM,GACN,QAAQ,GACR,UAAU,GACV,aAAa,GACb,gBAAgB,GAChB,UAAU,GACV,YAAY,GACZ,aAAa,CAAC;AAGlB,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,KAAK,IAAI,SAAS,CAQ7D;AAED,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,KAAK,IAAI,iBAAiB,CAW7E;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,KAAK,IAAI,WAAW,CAEjE;AAED,MAAM,MAAM,kBAAkB,GAC1B,WAAW,GACX,2BAA2B,GAC3B,qBAAqB,GACrB,MAAM,GACN,MAAM,GACN,UAAU,GACV,2BAA2B,GAC3B,gCAAgC,GAChC,QAAQ,GACR,UAAU,CAAC;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/dist/utils.d.ts
CHANGED
|
@@ -5,7 +5,7 @@ export declare const validateStatementContentHash: (statementContent: string, ex
|
|
|
5
5
|
export declare const generateStatementHash: (statement: string) => string;
|
|
6
6
|
export declare const validateStatementHash: (statement: string, expectedHash: string) => boolean;
|
|
7
7
|
export declare const generateStatementsFile: (statements: string[]) => string;
|
|
8
|
-
export declare const
|
|
8
|
+
export declare const splitStatements: (statementsFileContent: string) => string[];
|
|
9
9
|
export declare const generateStatementFilename: (statement: string) => string;
|
|
10
10
|
export declare const generateAttachmentFilename: (fileContent: Buffer | string, extension: string) => string;
|
|
11
11
|
export declare const minPeopleCountToRange: (n: number) => string | undefined;
|
package/dist/utils.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,gBAAgB,GAAI,aAAa,MAAM,GAAG,MAAM,KAAG,MAE/D,CAAC;AAEF,eAAO,MAAM,gBAAgB,GAAI,aAAa,MAAM,GAAG,MAAM,EAAE,cAAc,MAAM,KAAG,OAErF,CAAC;AAEF,eAAO,MAAM,4BAA4B,GAAI,kBAAkB,MAAM,KAAG,MAEvE,CAAC;AAEF,eAAO,MAAM,4BAA4B,GACvC,kBAAkB,MAAM,EACxB,cAAc,MAAM,KACnB,OAEF,CAAC;AAEF,eAAO,MAAM,qBAAqB,GAAI,WAAW,MAAM,KAAG,MASzD,CAAC;AAEF,eAAO,MAAM,qBAAqB,GAAI,WAAW,MAAM,EAAE,cAAc,MAAM,KAAG,OAG/E,CAAC;AAEF,eAAO,MAAM,sBAAsB,GAAI,YAAY,MAAM,EAAE,KAAG,MAE7D,CAAC;AAEF,eAAO,MAAM,eAAe,GAAI,uBAAuB,MAAM,KAAG,MAAM,EAmBrE,CAAC;AAEF,eAAO,MAAM,yBAAyB,GAAI,WAAW,MAAM,KAAG,MAG7D,CAAC;AAEF,eAAO,MAAM,0BAA0B,GACrC,aAAa,MAAM,GAAG,MAAM,EAC5B,WAAW,MAAM,KAChB,MAIF,CAAC;AAEF,eAAO,MAAM,qBAAqB,GAAI,GAAG,MAAM,KAAG,MAAM,GAAG,SAS1D,CAAC;AAEF,eAAO,MAAM,UAAU,GAAI,OAAO,MAAM,KAAG,MAGxC,CAAC;AAEJ,eAAO,MAAM,eAAe,EAAE,MAC2D,CAAC"}
|
package/dist/utils.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { peopleCountBuckets } from './constants';
|
|
2
2
|
import { sha256, verify } from './hash';
|
|
3
|
-
import { parseStatement } from './protocol';
|
|
4
3
|
export const generateFileHash = (fileContent) => {
|
|
5
4
|
return sha256(fileContent);
|
|
6
5
|
};
|
|
@@ -28,7 +27,7 @@ export const validateStatementHash = (statement, expectedHash) => {
|
|
|
28
27
|
export const generateStatementsFile = (statements) => {
|
|
29
28
|
return statements.join('\n\n');
|
|
30
29
|
};
|
|
31
|
-
export const
|
|
30
|
+
export const splitStatements = (statementsFileContent) => {
|
|
32
31
|
const statementParts = statementsFileContent.split(/\n\nStated protocol version: /);
|
|
33
32
|
const statements = [];
|
|
34
33
|
for (let i = 0; i < statementParts.length; i++) {
|
|
@@ -39,13 +38,7 @@ export const parseStatementsFile = (statementsFileContent) => {
|
|
|
39
38
|
statement = 'Stated protocol version: ' + statement;
|
|
40
39
|
}
|
|
41
40
|
statement = statement.replace(/\n+$/, '\n');
|
|
42
|
-
|
|
43
|
-
parseStatement({ statement });
|
|
44
|
-
statements.push(statement);
|
|
45
|
-
}
|
|
46
|
-
catch (error) {
|
|
47
|
-
throw new Error(`Invalid statement at index ${i}: ${error instanceof Error ? error.message : String(error)}`);
|
|
48
|
-
}
|
|
41
|
+
statements.push(statement);
|
|
49
42
|
}
|
|
50
43
|
return statements;
|
|
51
44
|
};
|
package/dist/utils.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.js","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAEjD,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;
|
|
1
|
+
{"version":3,"file":"utils.js","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAEjD,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAExC,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,WAA4B,EAAU,EAAE;IACvE,OAAO,MAAM,CAAC,WAAW,CAAC,CAAC;AAC7B,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,WAA4B,EAAE,YAAoB,EAAW,EAAE;IAC9F,OAAO,MAAM,CAAC,WAAW,EAAE,YAAY,CAAC,CAAC;AAC3C,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,4BAA4B,GAAG,CAAC,gBAAwB,EAAU,EAAE;IAC/E,OAAO,MAAM,CAAC,gBAAgB,CAAC,CAAC;AAClC,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,4BAA4B,GAAG,CAC1C,gBAAwB,EACxB,YAAoB,EACX,EAAE;IACX,OAAO,MAAM,CAAC,gBAAgB,EAAE,YAAY,CAAC,CAAC;AAChD,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,qBAAqB,GAAG,CAAC,SAAiB,EAAU,EAAE;IACjE,MAAM,cAAc,GAAG,0BAA0B,CAAC;IAClD,MAAM,KAAK,GAAG,SAAS,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC;IAE9C,IAAI,KAAK,IAAI,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;QACtB,OAAO,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IAC1B,CAAC;IAED,OAAO,MAAM,CAAC,SAAS,CAAC,CAAC;AAC3B,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,qBAAqB,GAAG,CAAC,SAAiB,EAAE,YAAoB,EAAW,EAAE;IACxF,MAAM,YAAY,GAAG,qBAAqB,CAAC,SAAS,CAAC,CAAC;IACtD,OAAO,YAAY,KAAK,YAAY,CAAC;AACvC,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,sBAAsB,GAAG,CAAC,UAAoB,EAAU,EAAE;IACrE,OAAO,UAAU,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;AACjC,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,qBAA6B,EAAY,EAAE;IACzE,MAAM,cAAc,GAAG,qBAAqB,CAAC,KAAK,CAAC,+BAA+B,CAAC,CAAC;IACpF,MAAM,UAAU,GAAa,EAAE,CAAC;IAEhC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,cAAc,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QAC/C,IAAI,SAAS,GAAG,cAAc,CAAC,CAAC,CAAC,CAAC;QAElC,IAAI,CAAC,KAAK,CAAC,IAAI,SAAS,CAAC,IAAI,EAAE,CAAC,MAAM,KAAK,CAAC;YAAE,SAAS;QAEvD,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;YACV,SAAS,GAAG,2BAA2B,GAAG,SAAS,CAAC;QACtD,CAAC;QAED,SAAS,GAAG,SAAS,CAAC,OAAO,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;QAE5C,UAAU,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IAC7B,CAAC;IAED,OAAO,UAAU,CAAC;AACpB,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,yBAAyB,GAAG,CAAC,SAAiB,EAAU,EAAE;IACrE,MAAM,IAAI,GAAG,qBAAqB,CAAC,SAAS,CAAC,CAAC;IAC9C,OAAO,GAAG,IAAI,MAAM,CAAC;AACvB,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,0BAA0B,GAAG,CACxC,WAA4B,EAC5B,SAAiB,EACT,EAAE;IACV,MAAM,IAAI,GAAG,gBAAgB,CAAC,WAAW,CAAC,CAAC;IAC3C,MAAM,GAAG,GAAG,SAAS,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IAC3E,OAAO,GAAG,IAAI,IAAI,GAAG,EAAE,CAAC;AAC1B,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,qBAAqB,GAAG,CAAC,CAAS,EAAsB,EAAE;IACrE,IAAI,CAAC,IAAI,QAAQ;QAAE,OAAO,kBAAkB,CAAC,UAAU,CAAC,CAAC;IACzD,IAAI,CAAC,IAAI,OAAO;QAAE,OAAO,kBAAkB,CAAC,SAAS,CAAC,CAAC;IACvD,IAAI,CAAC,IAAI,MAAM;QAAE,OAAO,kBAAkB,CAAC,QAAQ,CAAC,CAAC;IACrD,IAAI,CAAC,IAAI,KAAK;QAAE,OAAO,kBAAkB,CAAC,OAAO,CAAC,CAAC;IACnD,IAAI,CAAC,IAAI,IAAI;QAAE,OAAO,kBAAkB,CAAC,MAAM,CAAC,CAAC;IACjD,IAAI,CAAC,IAAI,GAAG;QAAE,OAAO,kBAAkB,CAAC,KAAK,CAAC,CAAC;IAC/C,IAAI,CAAC,IAAI,EAAE;QAAE,OAAO,kBAAkB,CAAC,IAAI,CAAC,CAAC;IAC7C,IAAI,CAAC,IAAI,CAAC;QAAE,OAAO,kBAAkB,CAAC,GAAG,CAAC,CAAC;AAC7C,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC,KAAa,EAAU,EAAE,CAClD,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC,OAAO,CAC1F,KAAK,CAAC,WAAW,EAAE,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,CACjC,CAAC;AAEJ,MAAM,CAAC,MAAM,eAAe,GAC1B,uFAAuF,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.test.d.ts","sourceRoot":"","sources":["../src/utils.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { describe, it } from 'node:test';
|
|
2
|
+
import assert from 'node:assert';
|
|
3
|
+
import { buildStatement } from './protocol';
|
|
4
|
+
import { generateFileHash, validateFileHash, generateStatementContentHash, validateStatementContentHash, generateStatementHash, validateStatementHash, generateStatementsFile, splitStatements, generateStatementFilename, generateAttachmentFilename, } from './utils';
|
|
5
|
+
describe('Statement Utils', () => {
|
|
6
|
+
const testContent = 'test content';
|
|
7
|
+
const testFileContent = Buffer.from('test file content');
|
|
8
|
+
describe('File Hash Functions', () => {
|
|
9
|
+
it('should generate file hash', () => {
|
|
10
|
+
const hash = generateFileHash(testFileContent);
|
|
11
|
+
assert.ok(hash);
|
|
12
|
+
assert.strictEqual(typeof hash, 'string');
|
|
13
|
+
});
|
|
14
|
+
it('should validate file hash', () => {
|
|
15
|
+
const hash = generateFileHash(testFileContent);
|
|
16
|
+
assert.strictEqual(validateFileHash(testFileContent, hash), true);
|
|
17
|
+
assert.strictEqual(validateFileHash(testFileContent, 'invalid-hash'), false);
|
|
18
|
+
});
|
|
19
|
+
it('should generate attachment filename', () => {
|
|
20
|
+
const filename = generateAttachmentFilename(testFileContent, 'pdf');
|
|
21
|
+
assert.ok(/^[A-Za-z0-9_-]+\.pdf$/.test(filename));
|
|
22
|
+
});
|
|
23
|
+
it('should handle extension with dot', () => {
|
|
24
|
+
const filename = generateAttachmentFilename(testFileContent, '.jpg');
|
|
25
|
+
assert.ok(/^[A-Za-z0-9_-]+\.jpg$/.test(filename));
|
|
26
|
+
});
|
|
27
|
+
});
|
|
28
|
+
describe('Statement Content Hash Functions', () => {
|
|
29
|
+
it('should generate statement content hash', () => {
|
|
30
|
+
const hash = generateStatementContentHash(testContent);
|
|
31
|
+
assert.ok(hash);
|
|
32
|
+
assert.strictEqual(typeof hash, 'string');
|
|
33
|
+
});
|
|
34
|
+
it('should validate statement content hash', () => {
|
|
35
|
+
const hash = generateStatementContentHash(testContent);
|
|
36
|
+
assert.strictEqual(validateStatementContentHash(testContent, hash), true);
|
|
37
|
+
assert.strictEqual(validateStatementContentHash(testContent, 'invalid-hash'), false);
|
|
38
|
+
});
|
|
39
|
+
});
|
|
40
|
+
describe('Statement Hash Functions', () => {
|
|
41
|
+
const statement = buildStatement({
|
|
42
|
+
domain: 'example.com',
|
|
43
|
+
author: 'Test Author',
|
|
44
|
+
time: new Date('2023-06-15T20:01:26.000Z'),
|
|
45
|
+
content: 'Test statement content',
|
|
46
|
+
});
|
|
47
|
+
it('should generate statement hash', () => {
|
|
48
|
+
const hash = generateStatementHash(statement);
|
|
49
|
+
assert.ok(hash);
|
|
50
|
+
assert.strictEqual(typeof hash, 'string');
|
|
51
|
+
});
|
|
52
|
+
it('should validate statement hash', () => {
|
|
53
|
+
const hash = generateStatementHash(statement);
|
|
54
|
+
assert.strictEqual(validateStatementHash(statement, hash), true);
|
|
55
|
+
assert.strictEqual(validateStatementHash(statement, 'invalid-hash'), false);
|
|
56
|
+
});
|
|
57
|
+
it('should generate statement filename', () => {
|
|
58
|
+
const filename = generateStatementFilename(statement);
|
|
59
|
+
assert.ok(/^[A-Za-z0-9_-]+\.txt$/.test(filename));
|
|
60
|
+
});
|
|
61
|
+
it('should exclude signature fields from hash', () => {
|
|
62
|
+
const signedStatement = statement +
|
|
63
|
+
'---\n' +
|
|
64
|
+
'Statement hash: test-hash\n' +
|
|
65
|
+
'Public key: test-key\n' +
|
|
66
|
+
'Signature: test-signature\n' +
|
|
67
|
+
'Algorithm: Ed25519\n';
|
|
68
|
+
const hash = generateStatementHash(signedStatement);
|
|
69
|
+
const hashWithoutSignature = generateStatementHash(statement);
|
|
70
|
+
assert.strictEqual(hash, hashWithoutSignature);
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
describe('Statements File Functions', () => {
|
|
74
|
+
const statement1 = buildStatement({
|
|
75
|
+
domain: 'example.com',
|
|
76
|
+
author: 'Test Author',
|
|
77
|
+
time: new Date('2023-06-15T20:01:26.000Z'),
|
|
78
|
+
content: 'First statement',
|
|
79
|
+
});
|
|
80
|
+
const statement2 = buildStatement({
|
|
81
|
+
domain: 'example.com',
|
|
82
|
+
author: 'Test Author',
|
|
83
|
+
time: new Date('2023-06-16T10:30:00.000Z'),
|
|
84
|
+
content: 'Second statement',
|
|
85
|
+
});
|
|
86
|
+
it('should generate statements file', () => {
|
|
87
|
+
const statementsFile = generateStatementsFile([statement1, statement2]);
|
|
88
|
+
assert.ok(statementsFile.includes(statement1));
|
|
89
|
+
assert.ok(statementsFile.includes(statement2));
|
|
90
|
+
assert.ok(statementsFile.includes('\n\n'));
|
|
91
|
+
});
|
|
92
|
+
it('should split statements file', () => {
|
|
93
|
+
const statementsFile = generateStatementsFile([statement1, statement2]);
|
|
94
|
+
const parsed = splitStatements(statementsFile);
|
|
95
|
+
assert.strictEqual(parsed.length, 2);
|
|
96
|
+
assert.strictEqual(parsed[0], statement1);
|
|
97
|
+
assert.strictEqual(parsed[1], statement2);
|
|
98
|
+
});
|
|
99
|
+
it('should filter empty statements when splitting', () => {
|
|
100
|
+
const fileWithEmpty = statement1 + '\n\n\n\n' + statement2;
|
|
101
|
+
const parsed = splitStatements(fileWithEmpty);
|
|
102
|
+
assert.strictEqual(parsed.length, 2);
|
|
103
|
+
});
|
|
104
|
+
});
|
|
105
|
+
});
|
|
106
|
+
//# sourceMappingURL=utils.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.test.js","sourceRoot":"","sources":["../src/utils.test.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
package/src/fixtures.test.ts
CHANGED
|
@@ -15,6 +15,7 @@ import {
|
|
|
15
15
|
buildResponseContent,
|
|
16
16
|
buildPDFSigningContent,
|
|
17
17
|
buildRating,
|
|
18
|
+
buildObservationContent,
|
|
18
19
|
} from './protocol';
|
|
19
20
|
import { verifySignedStatement } from './signature';
|
|
20
21
|
|
|
@@ -119,6 +120,12 @@ function buildContentFromInput(contentObj: any): string {
|
|
|
119
120
|
quality: contentObj.quality,
|
|
120
121
|
comment: contentObj.comment,
|
|
121
122
|
});
|
|
123
|
+
case 'observation':
|
|
124
|
+
return buildObservationContent({
|
|
125
|
+
subject: contentObj.subject,
|
|
126
|
+
property: contentObj.property,
|
|
127
|
+
value: contentObj.value,
|
|
128
|
+
});
|
|
122
129
|
default:
|
|
123
130
|
throw new Error(`Unknown content type: ${contentObj.type}`);
|
|
124
131
|
}
|
|
@@ -199,7 +206,7 @@ describe('Fixture Validation', () => {
|
|
|
199
206
|
assert.ok(parsed.domain);
|
|
200
207
|
assert.ok(parsed.author);
|
|
201
208
|
assert.ok(parsed.content);
|
|
202
|
-
assert.strictEqual(parsed.formatVersion, '5');
|
|
209
|
+
assert.strictEqual(parsed.formatVersion, '5.3');
|
|
203
210
|
});
|
|
204
211
|
|
|
205
212
|
it('round-trip: parse(output.txt) should match input.json structure', () => {
|
package/src/hash.ts
CHANGED
|
@@ -71,9 +71,9 @@ export const toUrlSafeBase64 = (base64: string): string => {
|
|
|
71
71
|
* @returns Base64 string
|
|
72
72
|
*/
|
|
73
73
|
function bytesToBase64(bytes: Uint8Array): string {
|
|
74
|
-
// Use btoa if available (browser), otherwise use Buffer (Node.js)
|
|
75
74
|
if (typeof btoa !== 'undefined') {
|
|
76
|
-
|
|
75
|
+
const binaryString = Array.from(bytes, byte => String.fromCharCode(byte)).join('');
|
|
76
|
+
return btoa(binaryString);
|
|
77
77
|
} else {
|
|
78
78
|
return Buffer.from(bytes).toString('base64');
|
|
79
79
|
}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { describe, it } from 'node:test';
|
|
2
|
+
import assert from 'node:assert';
|
|
3
|
+
import {
|
|
4
|
+
buildObservationContent,
|
|
5
|
+
parseObservation,
|
|
6
|
+
buildStatement,
|
|
7
|
+
parseStatement,
|
|
8
|
+
} from './protocol';
|
|
9
|
+
|
|
10
|
+
describe('Observation', () => {
|
|
11
|
+
it('should build observation content', () => {
|
|
12
|
+
const observation = {
|
|
13
|
+
subject: 'John_Doe@globalcoordination.johndoe.com',
|
|
14
|
+
property: 'Among the members of this network they are the most appropriate to represent',
|
|
15
|
+
value: 'Germany',
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
const content = buildObservationContent(observation);
|
|
19
|
+
|
|
20
|
+
assert.ok(content.includes('Type: Observation'));
|
|
21
|
+
assert.ok(content.includes('Subject: John_Doe@globalcoordination.johndoe.com'));
|
|
22
|
+
assert.ok(
|
|
23
|
+
content.includes(
|
|
24
|
+
'Property: Among the members of this network they are the most appropriate to represent'
|
|
25
|
+
)
|
|
26
|
+
);
|
|
27
|
+
assert.ok(content.includes('Value: Germany'));
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it('should parse observation content', () => {
|
|
31
|
+
const content =
|
|
32
|
+
' Type: Observation\n' +
|
|
33
|
+
' Subject: John_Doe@globalcoordination.johndoe.com\n' +
|
|
34
|
+
' Property: Among the members of this network they are the most appropriate to represent\n' +
|
|
35
|
+
' Value: Germany\n';
|
|
36
|
+
|
|
37
|
+
const parsed = parseObservation(content);
|
|
38
|
+
|
|
39
|
+
assert.strictEqual(parsed.subject, 'John_Doe@globalcoordination.johndoe.com');
|
|
40
|
+
assert.strictEqual(
|
|
41
|
+
parsed.property,
|
|
42
|
+
'Among the members of this network they are the most appropriate to represent'
|
|
43
|
+
);
|
|
44
|
+
assert.strictEqual(parsed.value, 'Germany');
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it('should build and parse observation in a statement', () => {
|
|
48
|
+
const observation = {
|
|
49
|
+
subject: 'John_Doe@globalcoordination.johndoe.com',
|
|
50
|
+
property: 'Among the members of this network they are the most appropriate to represent',
|
|
51
|
+
value: 'Germany',
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
const content = buildObservationContent(observation);
|
|
55
|
+
const statement = buildStatement({
|
|
56
|
+
domain: 'example.com',
|
|
57
|
+
author: 'Test Author',
|
|
58
|
+
time: new Date('2023-06-15T10:00:00Z'),
|
|
59
|
+
content,
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
const parsed = parseStatement({ statement });
|
|
63
|
+
assert.strictEqual(parsed.type, 'observation');
|
|
64
|
+
|
|
65
|
+
const parsedObservation = parseObservation(parsed.content);
|
|
66
|
+
assert.strictEqual(parsedObservation.subject, observation.subject);
|
|
67
|
+
assert.strictEqual(parsedObservation.property, observation.property);
|
|
68
|
+
assert.strictEqual(parsedObservation.value, observation.value);
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
it('should throw error when subject is missing', () => {
|
|
72
|
+
assert.throws(() => {
|
|
73
|
+
buildObservationContent({
|
|
74
|
+
subject: '',
|
|
75
|
+
property: 'Some property',
|
|
76
|
+
value: 'Some value',
|
|
77
|
+
});
|
|
78
|
+
}, /Subject is required/);
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
it('should throw error when property is missing', () => {
|
|
82
|
+
assert.throws(() => {
|
|
83
|
+
buildObservationContent({
|
|
84
|
+
subject: 'test@example.com',
|
|
85
|
+
property: '',
|
|
86
|
+
value: 'Some value',
|
|
87
|
+
});
|
|
88
|
+
}, /Property is required/);
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
it('should throw error when value is missing', () => {
|
|
92
|
+
assert.throws(() => {
|
|
93
|
+
buildObservationContent({
|
|
94
|
+
subject: 'test@example.com',
|
|
95
|
+
property: 'Some property',
|
|
96
|
+
value: '',
|
|
97
|
+
});
|
|
98
|
+
}, /Value is required/);
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
it('should handle various subject formats', () => {
|
|
102
|
+
const observations = [
|
|
103
|
+
{
|
|
104
|
+
subject: 'user@domain.com',
|
|
105
|
+
property: 'role',
|
|
106
|
+
value: 'administrator',
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
subject: 'https://example.com/resource/123',
|
|
110
|
+
property: 'status',
|
|
111
|
+
value: 'active',
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
subject: 'Organization Name',
|
|
115
|
+
property: 'headquarters',
|
|
116
|
+
value: 'Berlin, Germany',
|
|
117
|
+
},
|
|
118
|
+
];
|
|
119
|
+
|
|
120
|
+
observations.forEach((obs) => {
|
|
121
|
+
const content = buildObservationContent(obs);
|
|
122
|
+
const parsed = parseObservation(content);
|
|
123
|
+
assert.strictEqual(parsed.subject, obs.subject);
|
|
124
|
+
assert.strictEqual(parsed.property, obs.property);
|
|
125
|
+
assert.strictEqual(parsed.value, obs.value);
|
|
126
|
+
});
|
|
127
|
+
});
|
|
128
|
+
});
|
package/src/poll.test.ts
CHANGED
|
@@ -18,7 +18,7 @@ describe('Poll building', () => {
|
|
|
18
18
|
options,
|
|
19
19
|
scopeDescription,
|
|
20
20
|
});
|
|
21
|
-
const parsedPoll = parsePoll(pollContent, '5');
|
|
21
|
+
const parsedPoll = parsePoll(pollContent, '5.3');
|
|
22
22
|
assert.strictEqual(parsedPoll.poll, poll);
|
|
23
23
|
assert.strictEqual(parsedPoll.scopeDescription, scopeDescription);
|
|
24
24
|
assert.strictEqual(parsedPoll.deadline?.toUTCString(), deadline.toUTCString());
|
package/src/protocol.ts
CHANGED
|
@@ -14,10 +14,11 @@ import type {
|
|
|
14
14
|
PDFSigning,
|
|
15
15
|
Rating,
|
|
16
16
|
RatingSubjectTypeValue,
|
|
17
|
+
Observation,
|
|
17
18
|
} from './types';
|
|
18
19
|
import { isLegalForm, isPeopleCountBucket, isRatingValue } from './types';
|
|
19
20
|
|
|
20
|
-
const
|
|
21
|
+
const VERSION = '5.3';
|
|
21
22
|
|
|
22
23
|
export * from './types';
|
|
23
24
|
export * from './constants';
|
|
@@ -100,7 +101,7 @@ export const buildStatement = ({
|
|
|
100
101
|
|
|
101
102
|
const statement =
|
|
102
103
|
'Stated protocol version: ' +
|
|
103
|
-
|
|
104
|
+
VERSION +
|
|
104
105
|
'\n' +
|
|
105
106
|
'Publishing domain: ' +
|
|
106
107
|
domain +
|
|
@@ -236,9 +237,9 @@ export const parseStatement = ({
|
|
|
236
237
|
if (!parsed.content) throw new Error('Invalid statement format: statement content is required');
|
|
237
238
|
if (!parsed.formatVersion)
|
|
238
239
|
throw new Error('Invalid statement format: format version is required');
|
|
239
|
-
if (parsed.formatVersion !==
|
|
240
|
+
if (parsed.formatVersion !== VERSION)
|
|
240
241
|
throw new Error(
|
|
241
|
-
`Invalid statement format: only version
|
|
242
|
+
`Invalid statement format: only version ${VERSION} is supported, got version ${parsed.formatVersion}`
|
|
242
243
|
);
|
|
243
244
|
|
|
244
245
|
const tags = parsed.tagsStr?.split(', ');
|
|
@@ -322,7 +323,7 @@ export const buildPollContent = ({
|
|
|
322
323
|
};
|
|
323
324
|
|
|
324
325
|
export const parsePoll = (content: string, version?: string): Poll => {
|
|
325
|
-
if (version !==
|
|
326
|
+
if (version !== VERSION) throw new Error('Invalid version ' + version);
|
|
326
327
|
const pollRegex = new RegExp(
|
|
327
328
|
'' +
|
|
328
329
|
/^ Type: Poll\n/.source +
|
|
@@ -435,7 +436,8 @@ export const parseOrganisationVerification = (content: string): OrganisationVeri
|
|
|
435
436
|
const organisationVerificationRegex = new RegExp(
|
|
436
437
|
'' +
|
|
437
438
|
/^ Type: Organisation verification\n/.source +
|
|
438
|
-
/ Description: We verified the following information about an organisation\.(?:\sTheir logo may be attached to this statement\.)?\n
|
|
439
|
+
/ Description: We verified the following information about an organisation\.(?:\sTheir logo may be attached to this statement\.)?\n/
|
|
440
|
+
.source +
|
|
439
441
|
/ Name: (?<name>[^\n]+?)\n/.source +
|
|
440
442
|
/(?: English name: (?<englishName>[^\n]+?)\n)?/.source +
|
|
441
443
|
/ Country: (?<country>[^\n]+?)\n/.source +
|
|
@@ -561,7 +563,8 @@ export const parsePersonVerification = (content: string): PersonVerification =>
|
|
|
561
563
|
const domainVerificationRegex = new RegExp(
|
|
562
564
|
'' +
|
|
563
565
|
/^ Type: Person verification\n/.source +
|
|
564
|
-
/ Description: We verified the following information about a person\.(?:\sTheir profile picture may be attached to this statement\.)?\n
|
|
566
|
+
/ Description: We verified the following information about a person\.(?:\sTheir profile picture may be attached to this statement\.)?\n/
|
|
567
|
+
.source +
|
|
565
568
|
/ Name: (?<name>[^\n]+?)\n/.source +
|
|
566
569
|
/ Date of birth: (?<dateOfBirth>[^\n]+?)\n/.source +
|
|
567
570
|
/ City of birth: (?<cityOfBirth>[^\n]+?)\n/.source +
|
|
@@ -759,7 +762,8 @@ export const parsePDFSigning = (content: string): PDFSigning => {
|
|
|
759
762
|
const signingRegex = new RegExp(
|
|
760
763
|
'' +
|
|
761
764
|
/^ Type: Sign PDF\n/.source +
|
|
762
|
-
/ Description: We hereby digitally sign the attached PDF file\. The filename contains a hash of the file contents\.\n
|
|
765
|
+
/ Description: We hereby digitally sign the attached PDF file\. The filename contains a hash of the file contents\.\n/
|
|
766
|
+
.source +
|
|
763
767
|
/$/.source
|
|
764
768
|
);
|
|
765
769
|
const match = content.match(signingRegex);
|
|
@@ -850,3 +854,47 @@ export const parseRating = (content: string): Rating => {
|
|
|
850
854
|
comment,
|
|
851
855
|
};
|
|
852
856
|
};
|
|
857
|
+
|
|
858
|
+
export const buildObservationContent = ({ subject, property, value }: Observation) => {
|
|
859
|
+
if (!subject) throw new Error('Subject is required');
|
|
860
|
+
if (!property) throw new Error('Property is required');
|
|
861
|
+
if (!value) throw new Error('Value is required');
|
|
862
|
+
|
|
863
|
+
const content =
|
|
864
|
+
' Type: Observation\n' +
|
|
865
|
+
' Subject: ' +
|
|
866
|
+
subject +
|
|
867
|
+
'\n' +
|
|
868
|
+
' Property: ' +
|
|
869
|
+
property +
|
|
870
|
+
'\n' +
|
|
871
|
+
' Value: ' +
|
|
872
|
+
value +
|
|
873
|
+
'\n';
|
|
874
|
+
return content;
|
|
875
|
+
};
|
|
876
|
+
|
|
877
|
+
export const parseObservation = (content: string): Observation => {
|
|
878
|
+
const observationRegex = new RegExp(
|
|
879
|
+
'' +
|
|
880
|
+
/^ Type: Observation\n/.source +
|
|
881
|
+
/ Subject: (?<subject>[^\n]+?)\n/.source +
|
|
882
|
+
/ Property: (?<property>[^\n]+?)\n/.source +
|
|
883
|
+
/ Value: (?<value>[^\n]+?)\n/.source +
|
|
884
|
+
/$/.source
|
|
885
|
+
);
|
|
886
|
+
const match = content.match(observationRegex);
|
|
887
|
+
if (!match || !match.groups) throw new Error('Invalid observation format: ' + content);
|
|
888
|
+
|
|
889
|
+
const { subject, property, value } = match.groups;
|
|
890
|
+
|
|
891
|
+
if (!subject) throw new Error('Missing subject');
|
|
892
|
+
if (!property) throw new Error('Missing property');
|
|
893
|
+
if (!value) throw new Error('Missing value');
|
|
894
|
+
|
|
895
|
+
return {
|
|
896
|
+
subject,
|
|
897
|
+
property,
|
|
898
|
+
value,
|
|
899
|
+
};
|
|
900
|
+
};
|