@cooperation/vc-storage 1.0.43 → 1.0.45
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/index.js +1 -0
- package/dist/models/CredentialEngine.js +38 -10
- package/dist/models/ResumeVC.js +6 -7
- package/dist/types/index.d.ts +1 -0
- package/dist/types/models/CredentialEngine.d.ts +17 -2
- package/dist/types/models/ResumeVC.d.ts +2 -1
- package/dist/types/models/WASStorage.d.ts +2 -2
- package/dist/types/utils/Ed25519Signer.d.ts +25 -0
- package/dist/types/utils/context.d.ts +30 -538
- package/dist/types/utils/credential.d.ts +21 -10
- package/dist/types/utils/customDocumentLoader.d.ts +6 -0
- package/dist/types/utils/digitalbazaar.d.ts +1 -1
- package/dist/types/utils/getOrCreateAppDID.d.ts +1 -1
- package/dist/utils/Ed25519Signer.js +71 -0
- package/dist/utils/context.js +31 -538
- package/dist/utils/credential.js +65 -41
- package/dist/utils/customDocumentLoader.js +20 -0
- package/dist/utils/decodedSeed.js +1 -1
- package/dist/utils/digitalbazaar.js +25 -11
- package/dist/utils/getOrCreateAppDID.js +1 -2
- package/package.json +7 -5
package/dist/utils/credential.js
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
import { Ed25519VerificationKey2020 } from '@
|
|
1
|
+
import { Ed25519VerificationKey2020 } from '@digitalcredentials/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
|
+
import { employmentCredentialContext, volunteeringCredentialContext, performanceReviewCredentialContext, recommendationCredentialContexts, skillClaimCredentialContexts, } from './context.js';
|
|
5
5
|
/**
|
|
6
|
-
*
|
|
7
6
|
* Utility function to generate a hashed ID for a credential.
|
|
8
7
|
* Excludes the `id` field when hashing.
|
|
9
8
|
* @param {object} credential - The credential object to hash.
|
|
@@ -124,54 +123,38 @@ export function generateUnsignedVC({ formData, issuerDid }) {
|
|
|
124
123
|
return unsignedCredential;
|
|
125
124
|
}
|
|
126
125
|
/**
|
|
127
|
-
* Generate an unsigned Recommendation Credential.
|
|
128
|
-
* Uses the
|
|
129
|
-
* @param {object} params
|
|
130
|
-
* @param {IVerifiableCredential} params.vc - The Verifiable Credential to base the recommendation on.
|
|
131
|
-
* @param {RecommendationFormDataI} params.recommendation - The recommendation form data.
|
|
132
|
-
* @param {string} params.issuerDid - The DID of the issuer.
|
|
133
|
-
* @returns {IVerifiableCredential} The created unsigned Recommendation Credential.
|
|
134
|
-
* @throws Will throw an error if the recommendation creation fails or if issuance date exceeds expiration date.
|
|
126
|
+
* Generate an unsigned Recommendation Credential (VC Data Model v2).
|
|
127
|
+
* Uses the target skill-claim VC id on credentialSubject.id.
|
|
135
128
|
*/
|
|
136
|
-
export function generateUnsignedRecommendation({ vcId, recommendation, issuerDid, }) {
|
|
137
|
-
const issuanceDate = new Date().toISOString();
|
|
138
|
-
if (issuanceDate > recommendation.expirationDate)
|
|
139
|
-
throw new Error('issuanceDate cannot be after expirationDate');
|
|
129
|
+
export function generateUnsignedRecommendation({ vcId, recommendation, issuerDid, evidence = [], }) {
|
|
140
130
|
const unsignedRecommendation = {
|
|
141
|
-
'@context':
|
|
142
|
-
|
|
143
|
-
'https://purl.imsglobal.org/spec/ob/v3p0/context-3.0.3.json',
|
|
144
|
-
{
|
|
145
|
-
howKnow: 'https://schema.org/howKnow',
|
|
146
|
-
recommendationText: 'https://schema.org/recommendationText',
|
|
147
|
-
qualifications: 'https://schema.org/qualifications',
|
|
148
|
-
explainAnswer: 'https://schema.org/explainAnswer',
|
|
149
|
-
portfolio: 'https://schema.org/portfolio',
|
|
150
|
-
},
|
|
151
|
-
],
|
|
152
|
-
id: ``,
|
|
131
|
+
'@context': recommendationCredentialContexts,
|
|
132
|
+
id: `urn:uuid:${uuidv4()}`,
|
|
153
133
|
type: ['VerifiableCredential', 'https://schema.org/RecommendationCredential'],
|
|
154
|
-
issuer: {
|
|
155
|
-
|
|
156
|
-
type: ['Profile'],
|
|
157
|
-
},
|
|
158
|
-
issuanceDate,
|
|
159
|
-
expirationDate: recommendation.expirationDate,
|
|
134
|
+
issuer: { id: issuerDid, type: ['Profile'] },
|
|
135
|
+
validFrom: new Date().toISOString(),
|
|
160
136
|
credentialSubject: {
|
|
161
137
|
id: vcId,
|
|
162
138
|
name: recommendation.fullName,
|
|
139
|
+
...(recommendation.recipientName ? { recipientName: recommendation.recipientName } : {}),
|
|
163
140
|
howKnow: recommendation.howKnow,
|
|
164
141
|
recommendationText: recommendation.recommendationText,
|
|
165
|
-
qualifications: recommendation.qualifications,
|
|
166
|
-
explainAnswer: recommendation.explainAnswer,
|
|
167
|
-
portfolio: recommendation.portfolio
|
|
168
|
-
|
|
169
|
-
url: item.url,
|
|
170
|
-
})),
|
|
142
|
+
...(recommendation.qualifications ? { qualifications: recommendation.qualifications } : {}),
|
|
143
|
+
...(recommendation.explainAnswer ? { explainAnswer: recommendation.explainAnswer } : {}),
|
|
144
|
+
...(recommendation.portfolio?.length ? { portfolio: recommendation.portfolio } : {}),
|
|
145
|
+
...(recommendation.skillsEndorsed?.length ? { skillsEndorsed: recommendation.skillsEndorsed } : {}),
|
|
171
146
|
},
|
|
147
|
+
...(evidence.length
|
|
148
|
+
? {
|
|
149
|
+
evidence: evidence.map((e) => ({
|
|
150
|
+
id: e.id,
|
|
151
|
+
type: Array.isArray(e.type) ? e.type[0] : e.type || 'Evidence',
|
|
152
|
+
name: e.name,
|
|
153
|
+
description: e.description || '',
|
|
154
|
+
})),
|
|
155
|
+
}
|
|
156
|
+
: {}),
|
|
172
157
|
};
|
|
173
|
-
// Generate the hashed ID
|
|
174
|
-
unsignedRecommendation.id = 'urn:' + generateHashedId(unsignedRecommendation);
|
|
175
158
|
return unsignedRecommendation;
|
|
176
159
|
}
|
|
177
160
|
/**
|
|
@@ -268,6 +251,47 @@ export function generateUnsignedPerformanceReview({ formData, issuerDid }) {
|
|
|
268
251
|
unsignedCredential.id = 'urn:' + generateHashedId(unsignedCredential);
|
|
269
252
|
return unsignedCredential;
|
|
270
253
|
}
|
|
254
|
+
/**
|
|
255
|
+
* Generate an unsigned SkillClaimCredential (HR Context / VC Data Model v2).
|
|
256
|
+
*
|
|
257
|
+
* Key differences from the legacy OpenBadgeCredential:
|
|
258
|
+
* - Uses `https://www.w3.org/ns/credentials/v2` and `https://w3id.org/hr/v1` contexts
|
|
259
|
+
* - VC subtype is `SkillClaimCredential` (not `OpenBadgeCredential`)
|
|
260
|
+
* - `credentialSubject.type` is `SkillClaim` with a `person` object and `skill` array
|
|
261
|
+
* - Evidence lives at the credential root (not inside `credentialSubject`)
|
|
262
|
+
* - No `issuanceDate`/`expirationDate` set by the author
|
|
263
|
+
* - Issuer is a plain DID string (no `type` wrapper)
|
|
264
|
+
*/
|
|
265
|
+
export function generateUnsignedSkillClaim({ formData, issuerDid, }) {
|
|
266
|
+
const unsignedCredential = {
|
|
267
|
+
'@context': skillClaimCredentialContexts,
|
|
268
|
+
id: `urn:uuid:${uuidv4()}`,
|
|
269
|
+
type: ['VerifiableCredential', 'SkillClaimCredential'],
|
|
270
|
+
issuer: issuerDid,
|
|
271
|
+
credentialSubject: {
|
|
272
|
+
type: ['SkillClaim'],
|
|
273
|
+
person: {
|
|
274
|
+
id: formData.personId || issuerDid,
|
|
275
|
+
name: formData.personName,
|
|
276
|
+
},
|
|
277
|
+
skill: formData.skills.map((s) => ({
|
|
278
|
+
id: `urn:uuid:${uuidv4()}`,
|
|
279
|
+
name: s.name,
|
|
280
|
+
description: s.description,
|
|
281
|
+
durationPerformed: s.durationPerformed,
|
|
282
|
+
narrative: s.narrative,
|
|
283
|
+
image: s.image,
|
|
284
|
+
})),
|
|
285
|
+
},
|
|
286
|
+
evidence: formData.evidence?.length ? formData.evidence.map((e) => ({
|
|
287
|
+
id: e.id,
|
|
288
|
+
type: e.type || 'Evidence',
|
|
289
|
+
name: e.name,
|
|
290
|
+
description: e.description || "",
|
|
291
|
+
})) : [],
|
|
292
|
+
};
|
|
293
|
+
return unsignedCredential;
|
|
294
|
+
}
|
|
271
295
|
/**
|
|
272
296
|
* Extracts the keypair from a Verifiable Credential
|
|
273
297
|
* @param {Object} credential - The signed Verifiable Credential
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Document loader using @digitalcredentials/security-document-loader.
|
|
3
|
+
* Adds hr-context for SkillClaimCredential support.
|
|
4
|
+
* @see https://github.com/digitalcredentials/security-document-loader
|
|
5
|
+
*/
|
|
6
|
+
import { securityLoader } from '@digitalcredentials/security-document-loader';
|
|
7
|
+
import hrContext from 'hr-context';
|
|
8
|
+
const loader = securityLoader();
|
|
9
|
+
loader.addStatic(hrContext.CONTEXT_URL_V1, hrContext.CONTEXT_V1);
|
|
10
|
+
loader.addStatic('https://w3id.org/hr/v1', hrContext.CONTEXT_V1);
|
|
11
|
+
const builtLoader = loader.build();
|
|
12
|
+
/** Document loader compatible with @digitalcredentials/vc */
|
|
13
|
+
export const customDocumentLoader = async (url) => {
|
|
14
|
+
const result = await builtLoader(url);
|
|
15
|
+
return {
|
|
16
|
+
contextUrl: result.contextUrl ?? null,
|
|
17
|
+
documentUrl: result.documentUrl ?? url,
|
|
18
|
+
document: result.document,
|
|
19
|
+
};
|
|
20
|
+
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Ed25519VerificationKey2020 } from '@
|
|
1
|
+
import { Ed25519VerificationKey2020 } from '@digitalcredentials/ed25519-verification-key-2020';
|
|
2
2
|
import { base58btc } from 'multiformats/bases/base58';
|
|
3
3
|
export async function decodeSeed(encodedSeed) {
|
|
4
4
|
try {
|
|
@@ -1,26 +1,40 @@
|
|
|
1
1
|
import { Ed25519VerificationKey2020 } from '@digitalbazaar/ed25519-verification-key-2020';
|
|
2
|
+
import { suiteContext as ed25519Context } from '@digitalbazaar/ed25519-signature-2020';
|
|
2
3
|
import { driver as didKeyDriver } from '@digitalbazaar/did-method-key';
|
|
3
|
-
import { defaultDocumentLoader } from '@
|
|
4
|
-
import {
|
|
4
|
+
import { defaultDocumentLoader } from '@digitalcredentials/vc';
|
|
5
|
+
import { contexts as obContexts } from '@digitalcredentials/open-badges-context';
|
|
6
|
+
import { contexts as credV1Contexts } from 'credentials-context';
|
|
7
|
+
import { contexts as credV2Contexts } from '@digitalcredentials/credentials-v2-context';
|
|
8
|
+
import hrContext from 'hr-context';
|
|
5
9
|
// Initialize the DID method key driver
|
|
6
10
|
const didKeyDriverInstance = didKeyDriver();
|
|
7
11
|
didKeyDriverInstance.use({
|
|
8
12
|
multibaseMultikeyHeader: 'z6Mk',
|
|
9
13
|
fromMultibase: Ed25519VerificationKey2020.from,
|
|
10
14
|
});
|
|
15
|
+
// Patch the upstream hr-context socCode bug: "@type":"@set" -> "@container":"@set"
|
|
16
|
+
const hrCtxData = structuredClone(hrContext.CONTEXT_V1);
|
|
17
|
+
if (hrCtxData?.['@context']?.socCode) {
|
|
18
|
+
delete hrCtxData['@context'].socCode['@type'];
|
|
19
|
+
hrCtxData['@context'].socCode['@container'] = '@set';
|
|
20
|
+
}
|
|
21
|
+
// Build context map from installed packages
|
|
22
|
+
const contextMap = new Map([
|
|
23
|
+
...credV1Contexts,
|
|
24
|
+
...credV2Contexts,
|
|
25
|
+
...obContexts,
|
|
26
|
+
...ed25519Context.contexts,
|
|
27
|
+
[hrContext.CONTEXT_URL_V1, hrCtxData],
|
|
28
|
+
['https://w3id.org/hr/v1', hrCtxData],
|
|
29
|
+
]);
|
|
11
30
|
// Custom document loader
|
|
12
31
|
export const customDocumentLoader = async (url) => {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
'https://purl.imsglobal.org/spec/ob/v3p0/context-3.0.3.json': localOBContext,
|
|
16
|
-
'https://w3id.org/security/suites/ed25519-2020/v1': localED25519Context,
|
|
17
|
-
};
|
|
18
|
-
// Return local context if it matches the URL
|
|
19
|
-
if (contextMap[url]) {
|
|
32
|
+
const context = contextMap.get(url);
|
|
33
|
+
if (context) {
|
|
20
34
|
return {
|
|
21
35
|
contextUrl: null,
|
|
22
36
|
documentUrl: url,
|
|
23
|
-
document:
|
|
37
|
+
document: context,
|
|
24
38
|
};
|
|
25
39
|
}
|
|
26
40
|
// Handle did:key resolution
|
|
@@ -32,6 +46,6 @@ export const customDocumentLoader = async (url) => {
|
|
|
32
46
|
document: didDocument,
|
|
33
47
|
};
|
|
34
48
|
}
|
|
35
|
-
// Fallback to the default document loader
|
|
49
|
+
// Fallback to the default document loader
|
|
36
50
|
return defaultDocumentLoader(url);
|
|
37
51
|
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Ed25519VerificationKey2020 } from '@
|
|
1
|
+
import { Ed25519VerificationKey2020 } from '@digitalcredentials/ed25519-verification-key-2020';
|
|
2
2
|
const LOCAL_STORAGE_KEY = 'AppInstanceDID';
|
|
3
3
|
export async function getOrCreateAppInstanceDid() {
|
|
4
4
|
const stored = localStorage.getItem(LOCAL_STORAGE_KEY);
|
|
@@ -10,7 +10,6 @@ export async function getOrCreateAppInstanceDid() {
|
|
|
10
10
|
const keyPair = await Ed25519VerificationKey2020.generate();
|
|
11
11
|
keyPair.controller = `did:key:${keyPair.publicKeyMultibase}`;
|
|
12
12
|
keyPair.id = `${keyPair.controller}#${keyPair.publicKeyMultibase}`;
|
|
13
|
-
keyPair.revoked = false;
|
|
14
13
|
const did = keyPair.controller;
|
|
15
14
|
localStorage.setItem(LOCAL_STORAGE_KEY, JSON.stringify({
|
|
16
15
|
controller: keyPair.controller,
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cooperation/vc-storage",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "1.0.
|
|
4
|
+
"version": "1.0.45",
|
|
5
5
|
"description": "Sign and store your verifiable credentials.",
|
|
6
6
|
"main": "dist/index.js",
|
|
7
7
|
"types": "dist/types/index.d.ts",
|
|
@@ -12,26 +12,27 @@
|
|
|
12
12
|
"scripts": {
|
|
13
13
|
"build": "tsc",
|
|
14
14
|
"test": "vitest",
|
|
15
|
+
"test:all-vcs": "npm run build && node src/scripts/testAllVcs.js",
|
|
15
16
|
"version": "npm version patch"
|
|
16
17
|
},
|
|
17
18
|
"author": "cooperation",
|
|
18
19
|
"license": "ISC",
|
|
19
20
|
"dependencies": {
|
|
20
21
|
"@did.coop/did-key-ed25519": "^0.0.13",
|
|
21
|
-
"@
|
|
22
|
-
"@digitalbazaar/ed25519-signature-2020": "^5.4.0",
|
|
23
|
-
"@digitalbazaar/ed25519-verification-key-2020": "^4.1.0",
|
|
24
|
-
"@digitalbazaar/vc": "^6.3.0",
|
|
22
|
+
"@digitalcredentials/did-method-key": "^3.0.0",
|
|
25
23
|
"@digitalcredentials/ed25519-signature-2020": "^5.0.0",
|
|
26
24
|
"@digitalcredentials/ed25519-verification-key-2020": "^5.0.0-beta.2",
|
|
27
25
|
"@digitalcredentials/ezcap": "^5.1.0",
|
|
26
|
+
"@digitalcredentials/security-document-loader": "^8.0.0",
|
|
28
27
|
"@digitalcredentials/ssi": "^5.1.0",
|
|
28
|
+
"@digitalcredentials/vc": "^10.0.2",
|
|
29
29
|
"@wallet.storage/fetch-client": "^1.2.0",
|
|
30
30
|
"add": "^2.0.6",
|
|
31
31
|
"bnid": "^3.0.0",
|
|
32
32
|
"crypto-js": "^4.2.0",
|
|
33
33
|
"crypto-ld": "^7.0.0",
|
|
34
34
|
"ethers": "^6.13.2",
|
|
35
|
+
"hr-context": "^0.1.6",
|
|
35
36
|
"jest": "^29.7.0",
|
|
36
37
|
"multiformats": "^13.3.6",
|
|
37
38
|
"ts-jest": "^29.2.5",
|
|
@@ -42,6 +43,7 @@
|
|
|
42
43
|
"devDependencies": {
|
|
43
44
|
"@types/fs-extra": "^11.0.4",
|
|
44
45
|
"@types/jest": "^29.5.14",
|
|
46
|
+
"@types/node": "^25.9.0",
|
|
45
47
|
"@types/uuid": "^10.0.0",
|
|
46
48
|
"babel-jest": "^29.7.0",
|
|
47
49
|
"typescript": "^5.8.3",
|