@cooperation/vc-storage 1.0.32 → 1.0.34
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/models/CredentialEngine.js +38 -22
- package/dist/models/GoogleDriveStorage.js +36 -0
- package/dist/tests/email.test.js +19 -1
- package/dist/types/models/CredentialEngine.d.ts +6 -3
- package/dist/types/models/GoogleDriveStorage.d.ts +9 -0
- package/dist/types/utils/context.d.ts +73 -0
- package/dist/types/utils/credential.d.ts +171 -1
- package/dist/utils/context.js +76 -0
- package/dist/utils/credential.js +105 -0
- package/package.json +1 -1
@@ -2,7 +2,7 @@ import { Ed25519VerificationKey2020 } from '@digitalbazaar/ed25519-verification-
|
|
2
2
|
import { Ed25519Signature2020 } from '@digitalbazaar/ed25519-signature-2020';
|
3
3
|
import * as dbVc from '@digitalbazaar/vc';
|
4
4
|
import { v4 as uuidv4 } from 'uuid';
|
5
|
-
import { extractKeyPairFromCredential, generateDIDSchema, generateUnsignedRecommendation, generateUnsignedVC, } from '../utils/credential.js';
|
5
|
+
import { extractKeyPairFromCredential, generateDIDSchema, generateUnsignedEmployment, generateUnsignedPerformanceReview, generateUnsignedRecommendation, generateUnsignedVC, generateUnsignedVolunteering, } from '../utils/credential.js';
|
6
6
|
import { customDocumentLoader } from '../utils/digitalbazaar.js';
|
7
7
|
import { saveToGoogleDrive } from '../utils/google.js';
|
8
8
|
function delay(ms) {
|
@@ -135,28 +135,39 @@ export class CredentialEngine {
|
|
135
135
|
* @throws Will throw an error if VC signing fails.
|
136
136
|
*/
|
137
137
|
async signVC({ data, type, keyPair, issuerId, vcFileId }) {
|
138
|
-
let credential
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
138
|
+
let credential;
|
139
|
+
switch (type) {
|
140
|
+
case 'VC':
|
141
|
+
credential = generateUnsignedVC({ formData: data, issuerDid: issuerId });
|
142
|
+
break;
|
143
|
+
case 'RECOMMENDATION':
|
144
|
+
if (!vcFileId)
|
145
|
+
throw new Error('vcFileId is required for recommendation');
|
146
|
+
credential = generateUnsignedRecommendation({ vcId: vcFileId, recommendation: data, issuerDid: issuerId });
|
147
|
+
break;
|
148
|
+
case 'EMPLOYMENT':
|
149
|
+
credential = generateUnsignedEmployment({ formData: data, issuerDid: issuerId });
|
150
|
+
break;
|
151
|
+
case 'VOLUNTEERING':
|
152
|
+
credential = generateUnsignedVolunteering({ formData: data, issuerDid: issuerId });
|
153
|
+
break;
|
154
|
+
case 'PERFORMANCE_REVIEW':
|
155
|
+
credential = generateUnsignedPerformanceReview({ formData: data, issuerDid: issuerId });
|
156
|
+
break;
|
157
|
+
default:
|
158
|
+
throw new Error(`Unsupported credential type: ${type}`);
|
159
159
|
}
|
160
|
+
const suite = new Ed25519Signature2020({ key: keyPair, verificationMethod: keyPair.id });
|
161
|
+
return dbVc.issue({ credential, suite, documentLoader: customDocumentLoader });
|
162
|
+
}
|
163
|
+
async signEmploymentCredential(data, keyPair, issuerId) {
|
164
|
+
return this.signVC({ data, type: 'EMPLOYMENT', keyPair, issuerId });
|
165
|
+
}
|
166
|
+
async signVolunteeringCredential(data, keyPair, issuerId) {
|
167
|
+
return this.signVC({ data, type: 'VOLUNTEERING', keyPair, issuerId });
|
168
|
+
}
|
169
|
+
async signPerformanceReviewCredential(data, keyPair, issuerId) {
|
170
|
+
return this.signVC({ data, type: 'PERFORMANCE_REVIEW', keyPair, issuerId });
|
160
171
|
}
|
161
172
|
/**
|
162
173
|
* Verify a Verifiable Credential (VC)
|
@@ -248,6 +259,11 @@ export class CredentialEngine {
|
|
248
259
|
try {
|
249
260
|
let keyPair;
|
250
261
|
let didDocument;
|
262
|
+
// Check if VC already exists
|
263
|
+
const existing = await this.storage.checkEmailExists(email);
|
264
|
+
if (existing) {
|
265
|
+
return { signedVC: existing.data, fileId: existing.id };
|
266
|
+
}
|
251
267
|
// Require SEED from environment
|
252
268
|
if (!encodedSeed) {
|
253
269
|
throw new Error('SEED environment variable not set. Cannot generate or use any DID.');
|
@@ -641,4 +641,40 @@ export class GoogleDriveStorage {
|
|
641
641
|
return [];
|
642
642
|
}
|
643
643
|
}
|
644
|
+
/**
|
645
|
+
* Check if an email VC exists and return its content
|
646
|
+
* @param email - The email address to check
|
647
|
+
* @returns {Promise<{data: any, id: string} | null>} - The email VC content and ID if exists, null otherwise
|
648
|
+
*/
|
649
|
+
async checkEmailExists(email) {
|
650
|
+
try {
|
651
|
+
// Get root folders
|
652
|
+
const rootFolders = await this.findFolders();
|
653
|
+
// Find Credentials folder
|
654
|
+
const credentialsFolder = rootFolders.find((f) => f.name === 'Credentials');
|
655
|
+
if (!credentialsFolder) {
|
656
|
+
console.log('Credentials folder not found');
|
657
|
+
return null;
|
658
|
+
}
|
659
|
+
// Find EMAIL_VC subfolder
|
660
|
+
const subfolders = await this.findFolders(credentialsFolder.id);
|
661
|
+
const emailVcFolder = subfolders.find((f) => f.name === 'EMAIL_VC');
|
662
|
+
if (!emailVcFolder) {
|
663
|
+
console.log('EMAIL_VC folder not found');
|
664
|
+
return null;
|
665
|
+
}
|
666
|
+
// Search for file with exact email name (no extension)
|
667
|
+
const files = await this.searchFiles(`'${emailVcFolder.id}' in parents and name='${email}' and mimeType='application/json'`);
|
668
|
+
if (files.length === 0) {
|
669
|
+
return null;
|
670
|
+
}
|
671
|
+
// Get the content of the email VC
|
672
|
+
const emailVC = await this.retrieve(files[0].id);
|
673
|
+
return emailVC;
|
674
|
+
}
|
675
|
+
catch (error) {
|
676
|
+
console.error('Error checking email existence:', error);
|
677
|
+
return null;
|
678
|
+
}
|
679
|
+
}
|
644
680
|
}
|
package/dist/tests/email.test.js
CHANGED
@@ -11,7 +11,12 @@ async function testEmailVC() {
|
|
11
11
|
const testEmail = 'test@example.com';
|
12
12
|
console.log('Starting email VC generation test...');
|
13
13
|
console.log('Test email:', testEmail);
|
14
|
-
|
14
|
+
// First check if email VC exists
|
15
|
+
console.log('\nChecking if email VC exists...');
|
16
|
+
const existingVC = await storage.checkEmailExists(testEmail);
|
17
|
+
console.log('🚀 ~ testEmailVC ~ existingVC:', existingVC);
|
18
|
+
console.log('Existing VC check:', existingVC ? 'Found' : 'Not found');
|
19
|
+
const encodedSeed = 'z1AdiEjvNdC18HdruehySWKe4HnsXdUqCXMYPEs1fQ8cY2S'; // Replace with your actual encoded seed
|
15
20
|
// Generate and sign the email VC
|
16
21
|
const result = await engine.generateAndSignEmailVC(testEmail, encodedSeed);
|
17
22
|
console.log('\nTest Results:');
|
@@ -22,6 +27,19 @@ async function testEmailVC() {
|
|
22
27
|
console.log('\nRetrieving VC from storage...');
|
23
28
|
const retrievedVC = await storage.retrieve(result.fileId);
|
24
29
|
console.log('Retrieved VC:', retrievedVC ? 'Success' : 'Failed');
|
30
|
+
// Test checkEmailExists again after creation
|
31
|
+
console.log('\nChecking if email VC exists after creation...');
|
32
|
+
const newExistingVC = await storage.checkEmailExists(testEmail);
|
33
|
+
console.log('Existing VC check after creation:', newExistingVC ? 'Found' : 'Not found');
|
34
|
+
if (newExistingVC) {
|
35
|
+
console.log('VC Content:', JSON.stringify(newExistingVC.data, null, 2));
|
36
|
+
console.log('VC ID:', newExistingVC.id);
|
37
|
+
}
|
38
|
+
// Test with non-existent email
|
39
|
+
console.log('\nTesting with non-existent email...');
|
40
|
+
const nonExistentEmail = 'nonexistent@example.com';
|
41
|
+
const nonExistentVC = await storage.checkEmailExists(nonExistentEmail);
|
42
|
+
console.log('Non-existent email check:', nonExistentVC ? 'Found (unexpected)' : 'Not found (expected)');
|
25
43
|
console.log('\nTest completed successfully!');
|
26
44
|
}
|
27
45
|
catch (error) {
|
@@ -1,8 +1,8 @@
|
|
1
|
-
import { DidDocument, KeyPair, FormDataI, RecommendationFormDataI, VerifiableCredential } from '../../types/credential.js';
|
1
|
+
import { DidDocument, KeyPair, FormDataI, RecommendationFormDataI, VerifiableCredential, EmploymentFormDataI, PerformanceReviewFormDataI, VolunteeringFormDataI } from '../../types/credential.js';
|
2
2
|
import { GoogleDriveStorage } from './GoogleDriveStorage.js';
|
3
3
|
interface SignPropsI {
|
4
|
-
data: FormDataI | RecommendationFormDataI;
|
5
|
-
type: 'VC' | 'RECOMMENDATION';
|
4
|
+
data: FormDataI | RecommendationFormDataI | EmploymentFormDataI | VolunteeringFormDataI | PerformanceReviewFormDataI;
|
5
|
+
type: 'VC' | 'RECOMMENDATION' | 'EMPLOYMENT' | 'VOLUNTEERING' | 'PERFORMANCE_REVIEW';
|
6
6
|
keyPair: KeyPair;
|
7
7
|
issuerId: string;
|
8
8
|
vcFileId?: string;
|
@@ -59,6 +59,9 @@ export declare class CredentialEngine {
|
|
59
59
|
* @throws Will throw an error if VC signing fails.
|
60
60
|
*/
|
61
61
|
signVC({ data, type, keyPair, issuerId, vcFileId }: SignPropsI): Promise<any>;
|
62
|
+
signEmploymentCredential(data: EmploymentFormDataI, keyPair: KeyPair, issuerId: string): Promise<any>;
|
63
|
+
signVolunteeringCredential(data: VolunteeringFormDataI, keyPair: KeyPair, issuerId: string): Promise<any>;
|
64
|
+
signPerformanceReviewCredential(data: PerformanceReviewFormDataI, keyPair: KeyPair, issuerId: string): Promise<any>;
|
62
65
|
/**
|
63
66
|
* Verify a Verifiable Credential (VC)
|
64
67
|
* @param {object} credential - The Verifiable Credential to verify.
|
@@ -90,5 +90,14 @@ export declare class GoogleDriveStorage {
|
|
90
90
|
update(fileId: string, data: any): Promise<any>;
|
91
91
|
getFileIdsFromAppDataFolder(): Promise<any>;
|
92
92
|
getAllFilesData(): Promise<any>;
|
93
|
+
/**
|
94
|
+
* Check if an email VC exists and return its content
|
95
|
+
* @param email - The email address to check
|
96
|
+
* @returns {Promise<{data: any, id: string} | null>} - The email VC content and ID if exists, null otherwise
|
97
|
+
*/
|
98
|
+
checkEmailExists(email: string): Promise<{
|
99
|
+
data: any;
|
100
|
+
id: string;
|
101
|
+
} | null>;
|
93
102
|
}
|
94
103
|
export {};
|
@@ -104,6 +104,79 @@ export declare const inlineResumeContext: {
|
|
104
104
|
Resume: string;
|
105
105
|
};
|
106
106
|
};
|
107
|
+
export declare const employmentCredentialContext: {
|
108
|
+
'@context': {
|
109
|
+
'@vocab': string;
|
110
|
+
fullName: string;
|
111
|
+
persons: string;
|
112
|
+
credentialName: string;
|
113
|
+
credentialDuration: string;
|
114
|
+
credentialDescription: string;
|
115
|
+
portfolio: {
|
116
|
+
'@id': string;
|
117
|
+
'@container': string;
|
118
|
+
};
|
119
|
+
name: string;
|
120
|
+
url: string;
|
121
|
+
evidenceLink: string;
|
122
|
+
evidenceDescription: string;
|
123
|
+
company: string;
|
124
|
+
role: string;
|
125
|
+
};
|
126
|
+
};
|
127
|
+
export declare const volunteeringCredentialContext: {
|
128
|
+
'@context': {
|
129
|
+
'@vocab': string;
|
130
|
+
fullName: string;
|
131
|
+
persons: string;
|
132
|
+
volunteerWork: string;
|
133
|
+
volunteerOrg: string;
|
134
|
+
volunteerDescription: string;
|
135
|
+
skillsGained: {
|
136
|
+
'@id': string;
|
137
|
+
'@container': string;
|
138
|
+
};
|
139
|
+
duration: string;
|
140
|
+
volunteerDates: string;
|
141
|
+
portfolio: {
|
142
|
+
'@id': string;
|
143
|
+
'@container': string;
|
144
|
+
};
|
145
|
+
name: string;
|
146
|
+
url: string;
|
147
|
+
evidenceLink: string;
|
148
|
+
evidenceDescription: string;
|
149
|
+
};
|
150
|
+
};
|
151
|
+
export declare const performanceReviewCredentialContext: {
|
152
|
+
'@context': {
|
153
|
+
'@vocab': string;
|
154
|
+
fullName: string;
|
155
|
+
persons: string;
|
156
|
+
employeeName: string;
|
157
|
+
employeeJobTitle: string;
|
158
|
+
company: string;
|
159
|
+
role: string;
|
160
|
+
reviewStartDate: string;
|
161
|
+
reviewEndDate: string;
|
162
|
+
reviewDuration: string;
|
163
|
+
jobKnowledgeRating: string;
|
164
|
+
teamworkRating: string;
|
165
|
+
initiativeRating: string;
|
166
|
+
communicationRating: string;
|
167
|
+
overallRating: string;
|
168
|
+
reviewComments: string;
|
169
|
+
goalsNext: string;
|
170
|
+
portfolio: {
|
171
|
+
'@id': string;
|
172
|
+
'@container': string;
|
173
|
+
};
|
174
|
+
name: string;
|
175
|
+
url: string;
|
176
|
+
evidenceLink: string;
|
177
|
+
evidenceDescription: string;
|
178
|
+
};
|
179
|
+
};
|
107
180
|
declare const localOBContext: {
|
108
181
|
'@context': {
|
109
182
|
'@protected': boolean;
|
@@ -1,4 +1,4 @@
|
|
1
|
-
import { KeyPair, DidDocument, FormDataI, RecommendationCredential, Credential, RecommendationFormDataI, VerifiableCredential } from '../../types/credential';
|
1
|
+
import { KeyPair, DidDocument, FormDataI, RecommendationCredential, Credential, RecommendationFormDataI, VerifiableCredential, EmploymentFormDataI, VolunteeringFormDataI, PerformanceReviewFormDataI } from '../../types/credential';
|
2
2
|
/**
|
3
3
|
* Create a DID document using the provided key pair.
|
4
4
|
* @param {KeyPair} keyPair - The key pair used to create the DID document.
|
@@ -34,6 +34,176 @@ export declare function generateUnsignedRecommendation({ vcId, recommendation, i
|
|
34
34
|
recommendation: RecommendationFormDataI;
|
35
35
|
issuerDid: string;
|
36
36
|
}): RecommendationCredential;
|
37
|
+
/**
|
38
|
+
* Generate an unsigned Employment Credential.
|
39
|
+
*/
|
40
|
+
export declare function generateUnsignedEmployment({ formData, issuerDid }: {
|
41
|
+
formData: EmploymentFormDataI;
|
42
|
+
issuerDid: string;
|
43
|
+
}): {
|
44
|
+
'@context': (string | {
|
45
|
+
'@vocab': string;
|
46
|
+
fullName: string;
|
47
|
+
persons: string;
|
48
|
+
credentialName: string;
|
49
|
+
credentialDuration: string;
|
50
|
+
credentialDescription: string;
|
51
|
+
portfolio: {
|
52
|
+
'@id': string;
|
53
|
+
'@container': string;
|
54
|
+
};
|
55
|
+
name: string;
|
56
|
+
url: string;
|
57
|
+
evidenceLink: string;
|
58
|
+
evidenceDescription: string;
|
59
|
+
company: string;
|
60
|
+
role: string;
|
61
|
+
})[];
|
62
|
+
id: string;
|
63
|
+
type: string[];
|
64
|
+
issuer: {
|
65
|
+
id: string;
|
66
|
+
type: string[];
|
67
|
+
};
|
68
|
+
issuanceDate: string;
|
69
|
+
credentialSubject: {
|
70
|
+
type: string[];
|
71
|
+
fullName: string;
|
72
|
+
persons: string;
|
73
|
+
credentialName: string;
|
74
|
+
credentialDuration: string;
|
75
|
+
credentialDescription: string;
|
76
|
+
portfolio: {
|
77
|
+
name: string;
|
78
|
+
url: string;
|
79
|
+
}[];
|
80
|
+
evidenceLink: string;
|
81
|
+
evidenceDescription: string;
|
82
|
+
company: string;
|
83
|
+
role: string;
|
84
|
+
};
|
85
|
+
};
|
86
|
+
/**
|
87
|
+
* Generate an unsigned Volunteering Credential.
|
88
|
+
*/
|
89
|
+
export declare function generateUnsignedVolunteering({ formData, issuerDid }: {
|
90
|
+
formData: VolunteeringFormDataI;
|
91
|
+
issuerDid: string;
|
92
|
+
}): {
|
93
|
+
'@context': (string | {
|
94
|
+
'@vocab': string;
|
95
|
+
fullName: string;
|
96
|
+
persons: string;
|
97
|
+
volunteerWork: string;
|
98
|
+
volunteerOrg: string;
|
99
|
+
volunteerDescription: string;
|
100
|
+
skillsGained: {
|
101
|
+
'@id': string;
|
102
|
+
'@container': string;
|
103
|
+
};
|
104
|
+
duration: string;
|
105
|
+
volunteerDates: string;
|
106
|
+
portfolio: {
|
107
|
+
'@id': string;
|
108
|
+
'@container': string;
|
109
|
+
};
|
110
|
+
name: string;
|
111
|
+
url: string;
|
112
|
+
evidenceLink: string;
|
113
|
+
evidenceDescription: string;
|
114
|
+
})[];
|
115
|
+
id: string;
|
116
|
+
type: string[];
|
117
|
+
issuer: {
|
118
|
+
id: string;
|
119
|
+
type: string[];
|
120
|
+
};
|
121
|
+
issuanceDate: string;
|
122
|
+
credentialSubject: {
|
123
|
+
type: string[];
|
124
|
+
fullName: string;
|
125
|
+
persons: string;
|
126
|
+
volunteerWork: string;
|
127
|
+
volunteerOrg: string;
|
128
|
+
volunteerDescription: string;
|
129
|
+
skillsGained: string[];
|
130
|
+
duration: string;
|
131
|
+
volunteerDates: string;
|
132
|
+
portfolio: {
|
133
|
+
name: string;
|
134
|
+
url: string;
|
135
|
+
}[];
|
136
|
+
evidenceLink: string;
|
137
|
+
evidenceDescription: string;
|
138
|
+
};
|
139
|
+
};
|
140
|
+
/**
|
141
|
+
* Generate an unsigned Performance Review Credential.
|
142
|
+
*/
|
143
|
+
export declare function generateUnsignedPerformanceReview({ formData, issuerDid }: {
|
144
|
+
formData: PerformanceReviewFormDataI;
|
145
|
+
issuerDid: string;
|
146
|
+
}): {
|
147
|
+
'@context': (string | {
|
148
|
+
'@vocab': string;
|
149
|
+
fullName: string;
|
150
|
+
persons: string;
|
151
|
+
employeeName: string;
|
152
|
+
employeeJobTitle: string;
|
153
|
+
company: string;
|
154
|
+
role: string;
|
155
|
+
reviewStartDate: string;
|
156
|
+
reviewEndDate: string;
|
157
|
+
reviewDuration: string;
|
158
|
+
jobKnowledgeRating: string;
|
159
|
+
teamworkRating: string;
|
160
|
+
initiativeRating: string;
|
161
|
+
communicationRating: string;
|
162
|
+
overallRating: string;
|
163
|
+
reviewComments: string;
|
164
|
+
goalsNext: string;
|
165
|
+
portfolio: {
|
166
|
+
'@id': string;
|
167
|
+
'@container': string;
|
168
|
+
};
|
169
|
+
name: string;
|
170
|
+
url: string;
|
171
|
+
evidenceLink: string;
|
172
|
+
evidenceDescription: string;
|
173
|
+
})[];
|
174
|
+
id: string;
|
175
|
+
type: string[];
|
176
|
+
issuer: {
|
177
|
+
id: string;
|
178
|
+
type: string[];
|
179
|
+
};
|
180
|
+
issuanceDate: string;
|
181
|
+
credentialSubject: {
|
182
|
+
type: string[];
|
183
|
+
fullName: string;
|
184
|
+
persons: string;
|
185
|
+
employeeName: string;
|
186
|
+
employeeJobTitle: string;
|
187
|
+
company: string;
|
188
|
+
role: string;
|
189
|
+
reviewStartDate: string;
|
190
|
+
reviewEndDate: string;
|
191
|
+
reviewDuration: string;
|
192
|
+
jobKnowledgeRating: string;
|
193
|
+
teamworkRating: string;
|
194
|
+
initiativeRating: string;
|
195
|
+
communicationRating: string;
|
196
|
+
overallRating: string;
|
197
|
+
reviewComments: string;
|
198
|
+
goalsNext: string;
|
199
|
+
portfolio: {
|
200
|
+
name: string;
|
201
|
+
url: string;
|
202
|
+
}[];
|
203
|
+
evidenceLink: string;
|
204
|
+
evidenceDescription: string;
|
205
|
+
};
|
206
|
+
};
|
37
207
|
/**
|
38
208
|
* Extracts the keypair from a Verifiable Credential
|
39
209
|
* @param {Object} credential - The signed Verifiable Credential
|
package/dist/utils/context.js
CHANGED
@@ -120,6 +120,82 @@ export const inlineResumeContext = {
|
|
120
120
|
Resume: 'https://schema.hropenstandards.org/4.4#Resume',
|
121
121
|
},
|
122
122
|
};
|
123
|
+
// 1. Employment Credential Context
|
124
|
+
export const employmentCredentialContext = {
|
125
|
+
'@context': {
|
126
|
+
'@vocab': 'https://schema.hropenstandards.org/4.4/',
|
127
|
+
fullName: 'https://schema.org/name',
|
128
|
+
persons: 'https://schema.org/name',
|
129
|
+
credentialName: 'https://schema.org/jobTitle',
|
130
|
+
credentialDuration: 'https://schema.org/duration',
|
131
|
+
credentialDescription: 'https://schema.org/description',
|
132
|
+
portfolio: {
|
133
|
+
'@id': 'https://schema.org/hasPart',
|
134
|
+
'@container': '@list'
|
135
|
+
},
|
136
|
+
name: 'https://schema.org/name',
|
137
|
+
url: 'https://schema.org/url',
|
138
|
+
evidenceLink: 'https://schema.org/url',
|
139
|
+
evidenceDescription: 'https://schema.org/description',
|
140
|
+
company: 'https://schema.org/worksFor',
|
141
|
+
role: 'https://schema.org/jobTitle'
|
142
|
+
}
|
143
|
+
};
|
144
|
+
// 2. Volunteering Credential Context
|
145
|
+
export const volunteeringCredentialContext = {
|
146
|
+
'@context': {
|
147
|
+
'@vocab': 'https://schema.hropenstandards.org/4.4/',
|
148
|
+
fullName: 'https://schema.org/name',
|
149
|
+
persons: 'https://schema.org/name',
|
150
|
+
volunteerWork: 'https://schema.org/roleName',
|
151
|
+
volunteerOrg: 'https://schema.org/organization',
|
152
|
+
volunteerDescription: 'https://schema.org/description',
|
153
|
+
skillsGained: {
|
154
|
+
'@id': 'https://schema.org/skills',
|
155
|
+
'@container': '@list'
|
156
|
+
},
|
157
|
+
duration: 'https://schema.org/duration',
|
158
|
+
volunteerDates: 'https://schema.org/temporalCoverage',
|
159
|
+
portfolio: {
|
160
|
+
'@id': 'https://schema.org/hasPart',
|
161
|
+
'@container': '@list'
|
162
|
+
},
|
163
|
+
name: 'https://schema.org/name',
|
164
|
+
url: 'https://schema.org/url',
|
165
|
+
evidenceLink: 'https://schema.org/url',
|
166
|
+
evidenceDescription: 'https://schema.org/description'
|
167
|
+
}
|
168
|
+
};
|
169
|
+
// 3. Performance Review Credential Context
|
170
|
+
export const performanceReviewCredentialContext = {
|
171
|
+
'@context': {
|
172
|
+
'@vocab': 'https://schema.hropenstandards.org/4.4/',
|
173
|
+
fullName: 'https://schema.org/name',
|
174
|
+
persons: 'https://schema.org/name',
|
175
|
+
employeeName: 'https://schema.org/name',
|
176
|
+
employeeJobTitle: 'https://schema.org/jobTitle',
|
177
|
+
company: 'https://schema.org/worksFor',
|
178
|
+
role: 'https://schema.org/jobTitle',
|
179
|
+
reviewStartDate: 'https://schema.org/startDate',
|
180
|
+
reviewEndDate: 'https://schema.org/endDate',
|
181
|
+
reviewDuration: 'https://schema.org/duration',
|
182
|
+
jobKnowledgeRating: 'https://schema.org/assessmentScore',
|
183
|
+
teamworkRating: 'https://schema.org/assessmentScore',
|
184
|
+
initiativeRating: 'https://schema.org/assessmentScore',
|
185
|
+
communicationRating: 'https://schema.org/assessmentScore',
|
186
|
+
overallRating: 'https://schema.org/aggregateRating',
|
187
|
+
reviewComments: 'https://schema.org/comment',
|
188
|
+
goalsNext: 'https://schema.hropenstandards.org/4.4/goalsNext',
|
189
|
+
portfolio: {
|
190
|
+
'@id': 'https://schema.org/hasPart',
|
191
|
+
'@container': '@list'
|
192
|
+
},
|
193
|
+
name: 'https://schema.org/name',
|
194
|
+
url: 'https://schema.org/url',
|
195
|
+
evidenceLink: 'https://schema.org/url',
|
196
|
+
evidenceDescription: 'https://schema.org/description'
|
197
|
+
}
|
198
|
+
};
|
123
199
|
const localOBContext = {
|
124
200
|
'@context': {
|
125
201
|
'@protected': true,
|
package/dist/utils/credential.js
CHANGED
@@ -1,7 +1,9 @@
|
|
1
1
|
import { Ed25519VerificationKey2020 } from '@digitalbazaar/ed25519-verification-key-2020';
|
2
2
|
import { v4 as uuidv4 } from 'uuid';
|
3
3
|
import CryptoJS from 'crypto-js';
|
4
|
+
import { employmentCredentialContext, volunteeringCredentialContext, performanceReviewCredentialContext } from './context.js';
|
4
5
|
/**
|
6
|
+
*
|
5
7
|
* Utility function to generate a hashed ID for a credential.
|
6
8
|
* Excludes the `id` field when hashing.
|
7
9
|
* @param {object} credential - The credential object to hash.
|
@@ -169,6 +171,109 @@ export function generateUnsignedRecommendation({ vcId, recommendation, issuerDid
|
|
169
171
|
};
|
170
172
|
return unsignedRecommendation;
|
171
173
|
}
|
174
|
+
/**
|
175
|
+
* Generate an unsigned Employment Credential.
|
176
|
+
*/
|
177
|
+
export function generateUnsignedEmployment({ formData, issuerDid }) {
|
178
|
+
const issuanceDate = new Date().toISOString();
|
179
|
+
const unsignedCredential = {
|
180
|
+
'@context': [
|
181
|
+
'https://www.w3.org/2018/credentials/v1',
|
182
|
+
employmentCredentialContext['@context'],
|
183
|
+
],
|
184
|
+
id: '',
|
185
|
+
type: ['VerifiableCredential', 'EmploymentCredential'],
|
186
|
+
issuer: { id: issuerDid, type: ['Profile'] },
|
187
|
+
issuanceDate,
|
188
|
+
credentialSubject: {
|
189
|
+
type: ['WorkExperience'],
|
190
|
+
fullName: formData.fullName,
|
191
|
+
persons: formData.persons,
|
192
|
+
credentialName: formData.credentialName,
|
193
|
+
credentialDuration: formData.credentialDuration,
|
194
|
+
credentialDescription: formData.credentialDescription,
|
195
|
+
portfolio: formData.portfolio.map(item => ({ name: item.name, url: item.url })),
|
196
|
+
evidenceLink: formData.evidenceLink,
|
197
|
+
evidenceDescription: formData.evidenceDescription,
|
198
|
+
company: formData.company,
|
199
|
+
role: formData.role,
|
200
|
+
},
|
201
|
+
};
|
202
|
+
unsignedCredential.id = 'urn:' + generateHashedId(unsignedCredential);
|
203
|
+
return unsignedCredential;
|
204
|
+
}
|
205
|
+
/**
|
206
|
+
* Generate an unsigned Volunteering Credential.
|
207
|
+
*/
|
208
|
+
export function generateUnsignedVolunteering({ formData, issuerDid }) {
|
209
|
+
const issuanceDate = new Date().toISOString();
|
210
|
+
const unsignedCredential = {
|
211
|
+
'@context': [
|
212
|
+
'https://www.w3.org/2018/credentials/v1',
|
213
|
+
volunteeringCredentialContext['@context'],
|
214
|
+
],
|
215
|
+
id: '',
|
216
|
+
type: ['VerifiableCredential', 'VolunteeringCredential'],
|
217
|
+
issuer: { id: issuerDid, type: ['Profile'] },
|
218
|
+
issuanceDate,
|
219
|
+
credentialSubject: {
|
220
|
+
type: ['VolunteerRole'],
|
221
|
+
fullName: formData.fullName,
|
222
|
+
persons: formData.persons,
|
223
|
+
volunteerWork: formData.volunteerWork,
|
224
|
+
volunteerOrg: formData.volunteerOrg,
|
225
|
+
volunteerDescription: formData.volunteerDescription,
|
226
|
+
skillsGained: formData.skillsGained ? formData.skillsGained.split(',').map(s => s.trim()) : undefined,
|
227
|
+
duration: formData.duration,
|
228
|
+
volunteerDates: formData.volunteerDates,
|
229
|
+
portfolio: formData.portfolio.map(item => ({ name: item.name, url: item.url })),
|
230
|
+
evidenceLink: formData.evidenceLink,
|
231
|
+
evidenceDescription: formData.evidenceDescription,
|
232
|
+
},
|
233
|
+
};
|
234
|
+
unsignedCredential.id = 'urn:' + generateHashedId(unsignedCredential);
|
235
|
+
return unsignedCredential;
|
236
|
+
}
|
237
|
+
/**
|
238
|
+
* Generate an unsigned Performance Review Credential.
|
239
|
+
*/
|
240
|
+
export function generateUnsignedPerformanceReview({ formData, issuerDid }) {
|
241
|
+
const issuanceDate = new Date().toISOString();
|
242
|
+
const unsignedCredential = {
|
243
|
+
'@context': [
|
244
|
+
'https://www.w3.org/2018/credentials/v1',
|
245
|
+
performanceReviewCredentialContext['@context'],
|
246
|
+
],
|
247
|
+
id: '',
|
248
|
+
type: ['VerifiableCredential', 'PerformanceReviewCredential'],
|
249
|
+
issuer: { id: issuerDid, type: ['Profile'] },
|
250
|
+
issuanceDate,
|
251
|
+
credentialSubject: {
|
252
|
+
type: ['EndorsementSubject'],
|
253
|
+
fullName: formData.fullName,
|
254
|
+
persons: formData.persons,
|
255
|
+
employeeName: formData.employeeName,
|
256
|
+
employeeJobTitle: formData.employeeJobTitle,
|
257
|
+
company: formData.company,
|
258
|
+
role: formData.role,
|
259
|
+
reviewStartDate: formData.reviewStartDate,
|
260
|
+
reviewEndDate: formData.reviewEndDate,
|
261
|
+
reviewDuration: formData.reviewDuration,
|
262
|
+
jobKnowledgeRating: formData.jobKnowledgeRating,
|
263
|
+
teamworkRating: formData.teamworkRating,
|
264
|
+
initiativeRating: formData.initiativeRating,
|
265
|
+
communicationRating: formData.communicationRating,
|
266
|
+
overallRating: formData.overallRating,
|
267
|
+
reviewComments: formData.reviewComments,
|
268
|
+
goalsNext: formData.goalsNext,
|
269
|
+
portfolio: formData.portfolio.map(item => ({ name: item.name, url: item.url })),
|
270
|
+
evidenceLink: formData.evidenceLink,
|
271
|
+
evidenceDescription: formData.evidenceDescription,
|
272
|
+
},
|
273
|
+
};
|
274
|
+
unsignedCredential.id = 'urn:' + generateHashedId(unsignedCredential);
|
275
|
+
return unsignedCredential;
|
276
|
+
}
|
172
277
|
/**
|
173
278
|
* Extracts the keypair from a Verifiable Credential
|
174
279
|
* @param {Object} credential - The signed Verifiable Credential
|