@learncard/credential-library 2.0.1 → 2.0.3
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/__tests__/docs-json.test.d.ts +2 -0
- package/dist/__tests__/docs-json.test.d.ts.map +1 -0
- package/dist/credential-library.cjs.development.js +145 -0
- package/dist/credential-library.cjs.development.js.map +4 -4
- package/dist/credential-library.cjs.production.min.js +1 -1
- package/dist/credential-library.cjs.production.min.js.map +4 -4
- package/dist/credential-library.esm.js +145 -0
- package/dist/credential-library.esm.js.map +4 -4
- package/dist/fixtures/clr/provisional-transcript.d.ts +38 -0
- package/dist/fixtures/clr/provisional-transcript.d.ts.map +1 -0
- package/dist/fixtures/index.d.ts +2 -1
- package/dist/fixtures/index.d.ts.map +1 -1
- package/package.json +6 -6
- package/src/__tests__/docs-json.test.ts +148 -0
- package/src/__tests__/issuance.test.ts +31 -1
- package/src/__tests__/registry.test.ts +61 -2
- package/src/fixtures/clr/provisional-transcript.ts +193 -0
- package/src/fixtures/index.ts +9 -0
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { type UnsignedVC } from '@learncard/types';
|
|
2
|
+
import type { CredentialFixture } from '../../types';
|
|
3
|
+
/**
|
|
4
|
+
* Provisional CLR 2.0 transcript with a `LearnCardCredentialRefresh2026` service, built for
|
|
5
|
+
* the managed credential refresh flow (LC-2117 / LC-2135 / LC-2136).
|
|
6
|
+
*
|
|
7
|
+
* The credential starts as a provisional transcript; `buildFinalTranscriptVariant`
|
|
8
|
+
* produces the final variant WITHOUT changing the credential ID, issuer, or subject,
|
|
9
|
+
* mirroring a registrar publishing the finalized record to the same refresh service.
|
|
10
|
+
*
|
|
11
|
+
* JSON-LD note: neither the VCDM 1.1/2.0 nor the live OBv3/CLR contexts define the
|
|
12
|
+
* term `LearnCardCredentialRefresh2026` (or the LearnCard `authorization` extension), so
|
|
13
|
+
* the fixture carries an inline context fragment — DIDKit's data-loss detection
|
|
14
|
+
* otherwise refuses to sign.
|
|
15
|
+
*/
|
|
16
|
+
/** Inline JSON-LD context fragment defining the refresh-service terms inline. */
|
|
17
|
+
export declare const REFRESH_SERVICE_INLINE_CONTEXT: {
|
|
18
|
+
readonly LearnCardCredentialRefresh2026: "https://learncard.com/refresh#LearnCardCredentialRefresh2026";
|
|
19
|
+
readonly authorization: {
|
|
20
|
+
readonly '@id': "https://purl.imsglobal.org/spec/ob/v3p0#authorization";
|
|
21
|
+
readonly '@context': {
|
|
22
|
+
readonly LearnCardDIDAuth: "https://docs.learncard.com/definitions#LearnCardDIDAuth";
|
|
23
|
+
};
|
|
24
|
+
};
|
|
25
|
+
};
|
|
26
|
+
export declare const clrProvisionalTranscript: CredentialFixture;
|
|
27
|
+
/**
|
|
28
|
+
* Builds the final transcript variant from a prepared provisional credential.
|
|
29
|
+
*
|
|
30
|
+
* Preserves the credential ID, issuer, subject, and refresh service (identity
|
|
31
|
+
* invariants the managed refresh pipeline enforces) while flipping user-visible
|
|
32
|
+
* content to its final state: name/description become final, the nested course
|
|
33
|
+
* credential shows the completed result, and `validFrom` moves forward.
|
|
34
|
+
*/
|
|
35
|
+
export declare const buildFinalTranscriptVariant: (provisional: UnsignedVC, options?: {
|
|
36
|
+
validFrom?: string;
|
|
37
|
+
}) => UnsignedVC;
|
|
38
|
+
//# sourceMappingURL=provisional-transcript.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"provisional-transcript.d.ts","sourceRoot":"","sources":["../../../src/fixtures/clr/provisional-transcript.ts"],"names":[],"mappings":"AAAA,OAAO,EAAkC,KAAK,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAEnF,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAErD;;;;;;;;;;;;GAYG;AAEH,iFAAiF;AACjF,eAAO,MAAM,8BAA8B;;;;;;;;CASjC,CAAC;AAaX,eAAO,MAAM,wBAAwB,EAAE,iBAoGtC,CAAC;AAEF;;;;;;;GAOG;AACH,eAAO,MAAM,2BAA2B,GACpC,aAAa,UAAU,EACvB,UAAS;IAAE,SAAS,CAAC,EAAE,MAAM,CAAA;CAAO,KACrC,UAsCF,CAAC"}
|
package/dist/fixtures/index.d.ts
CHANGED
|
@@ -38,8 +38,9 @@ import { vcV2LicenseCredential } from './vc-v2/license-credential';
|
|
|
38
38
|
import { clrGreatPlainsFull } from './clr/great-plains-full';
|
|
39
39
|
import { clrWestbridgeFull } from './clr/westbridge-full';
|
|
40
40
|
import { clrCompetencyAligned } from './clr/competency-aligned';
|
|
41
|
+
import { clrProvisionalTranscript, buildFinalTranscriptVariant, REFRESH_SERVICE_INLINE_CONTEXT } from './clr/provisional-transcript';
|
|
41
42
|
import { clrDemoIsdDiplomaAssessments } from './clr/demo-isd-diploma-assessments';
|
|
42
43
|
import { sdJwtVcCourseCompletion } from './sd-jwt-vc/course-completion';
|
|
43
44
|
export declare const ALL_FIXTURES: LibraryFixture[];
|
|
44
|
-
export { vcV1Basic, vcV1WithStatus, vcV2Basic, vcV2WithEvidence, vcV2MultipleSubjects, vcV2RenderMethodExample, obv3MinimalBadge, obv3FullBadge, obv3WithAlignment, obv3WithEndorsement, obv3PlugfestJff2, clrMinimal, clrMultiAchievement, boostBasic, boostId, boostWithSkills, invalidMissingContext, invalidEmptyType, invalidMissingIssuer, clrNdStudentTranscript, obv3OneedtechFull, obv3ProfessionalCert, obv3MicroCredential, obv3CourseCompletion, obv3StandaloneFullCourse, obv3K12Diploma, obv3EndorsementCredential, clrUniversityTranscript, boostCommunityAward, boostDelegate, vcV1AlumniCredential, vcV2EmploymentCredential, vcV2EducationDegree, vcV2DigitalId, vcV2MembershipCredential, vcV2LicenseCredential, clrGreatPlainsFull, clrWestbridgeFull, clrCompetencyAligned, clrDemoIsdDiplomaAssessments, sdJwtVcCourseCompletion, };
|
|
45
|
+
export { vcV1Basic, vcV1WithStatus, vcV2Basic, vcV2WithEvidence, vcV2MultipleSubjects, vcV2RenderMethodExample, obv3MinimalBadge, obv3FullBadge, obv3WithAlignment, obv3WithEndorsement, obv3PlugfestJff2, clrMinimal, clrMultiAchievement, boostBasic, boostId, boostWithSkills, invalidMissingContext, invalidEmptyType, invalidMissingIssuer, clrNdStudentTranscript, obv3OneedtechFull, obv3ProfessionalCert, obv3MicroCredential, obv3CourseCompletion, obv3StandaloneFullCourse, obv3K12Diploma, obv3EndorsementCredential, clrUniversityTranscript, boostCommunityAward, boostDelegate, vcV1AlumniCredential, vcV2EmploymentCredential, vcV2EducationDegree, vcV2DigitalId, vcV2MembershipCredential, vcV2LicenseCredential, clrGreatPlainsFull, clrWestbridgeFull, clrCompetencyAligned, clrProvisionalTranscript, buildFinalTranscriptVariant, REFRESH_SERVICE_INLINE_CONTEXT, clrDemoIsdDiplomaAssessments, sdJwtVcCourseCompletion, };
|
|
45
46
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/fixtures/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAG/C,OAAO,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAC1C,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAGrD,OAAO,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAC1C,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AACzD,OAAO,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAC;AACjE,OAAO,EAAE,uBAAuB,EAAE,MAAM,uCAAuC,CAAC;AAGhF,OAAO,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AACxD,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAClD,OAAO,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAC1D,OAAO,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAC9D,OAAO,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AACxD,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,EAAE,oBAAoB,EAAE,MAAM,0BAA0B,CAAC;AAChE,OAAO,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAC9D,OAAO,EAAE,oBAAoB,EAAE,MAAM,0BAA0B,CAAC;AAChE,OAAO,EAAE,wBAAwB,EAAE,MAAM,+BAA+B,CAAC;AACzE,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACpD,OAAO,EAAE,yBAAyB,EAAE,MAAM,oBAAoB,CAAC;AAG/D,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAC3C,OAAO,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAC9D,OAAO,EAAE,uBAAuB,EAAE,MAAM,6BAA6B,CAAC;AAGtE,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAC3C,OAAO,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAC3C,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AACtD,OAAO,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAC9D,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAGjD,OAAO,EAAE,qBAAqB,EAAE,MAAM,2BAA2B,CAAC;AAClE,OAAO,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AACxD,OAAO,EAAE,oBAAoB,EAAE,MAAM,0BAA0B,CAAC;AAEhE,OAAO,EAAE,sBAAsB,EAAE,MAAM,6BAA6B,CAAC;AAGrE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAC;AAGjE,OAAO,EAAE,wBAAwB,EAAE,MAAM,+BAA+B,CAAC;AACzE,OAAO,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AAC/D,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACnD,OAAO,EAAE,wBAAwB,EAAE,MAAM,+BAA+B,CAAC;AACzE,OAAO,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAC;AAEnE,OAAO,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAC7D,OAAO,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAC1D,OAAO,EAAE,oBAAoB,EAAE,MAAM,0BAA0B,CAAC;AAChE,OAAO,EAAE,4BAA4B,EAAE,MAAM,oCAAoC,CAAC;AAGlF,OAAO,EAAE,uBAAuB,EAAE,MAAM,+BAA+B,CAAC;AAMxE,eAAO,MAAM,YAAY,EAAE,cAAc,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/fixtures/index.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@learncard/credential-library",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.3",
|
|
4
4
|
"description": "Comprehensive library of Verifiable Credential fixtures for testing, development, and regression prevention across the VC ecosystem",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"module": "./dist/credential-library.esm.js",
|
|
@@ -26,18 +26,18 @@
|
|
|
26
26
|
"url": "https://github.com/learningeconomy/LearnCard/issues"
|
|
27
27
|
},
|
|
28
28
|
"devDependencies": {
|
|
29
|
-
"@learncard/didkit-plugin": "1.
|
|
30
|
-
"@learncard/init": "2.4.
|
|
29
|
+
"@learncard/didkit-plugin": "1.10.0",
|
|
30
|
+
"@learncard/init": "2.4.16",
|
|
31
31
|
"@types/node": "^17.0.38",
|
|
32
32
|
"esbuild": "^0.27.1",
|
|
33
33
|
"jose": "^5.9.6",
|
|
34
34
|
"shx": "^0.3.4",
|
|
35
35
|
"typescript": "5.6.2",
|
|
36
|
-
"vitest": "4.1.
|
|
36
|
+
"vitest": "4.1.11"
|
|
37
37
|
},
|
|
38
38
|
"dependencies": {
|
|
39
|
-
"@learncard/sd-jwt-vc-plugin": "0.2.
|
|
40
|
-
"@learncard/types": "5.
|
|
39
|
+
"@learncard/sd-jwt-vc-plugin": "0.2.13",
|
|
40
|
+
"@learncard/types": "5.20.0",
|
|
41
41
|
"@sd-jwt/sd-jwt-vc": "^0.19.0",
|
|
42
42
|
"zod": "4.1.13"
|
|
43
43
|
},
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import { readFileSync } from 'fs';
|
|
2
|
+
import { readFile } from 'fs/promises';
|
|
3
|
+
import { fileURLToPath } from 'url';
|
|
4
|
+
|
|
5
|
+
import { beforeAll, describe, expect, it } from 'vitest';
|
|
6
|
+
import { initLearnCard, type LearnCardFromSeed } from '@learncard/init';
|
|
7
|
+
import { AlignmentValidator, LCNBoostStatus, UnsignedVCValidator } from '@learncard/types';
|
|
8
|
+
|
|
9
|
+
const didkit = readFile(
|
|
10
|
+
require.resolve('@learncard/didkit-plugin/dist/didkit/didkit_wasm_bg.wasm')
|
|
11
|
+
);
|
|
12
|
+
|
|
13
|
+
// `initLearnCard` is overloaded; `ReturnType` would pick the no-arg EmptyLearnCard overload.
|
|
14
|
+
let wallet: LearnCardFromSeed['returnValue'];
|
|
15
|
+
let subjectDid: string;
|
|
16
|
+
|
|
17
|
+
beforeAll(async () => {
|
|
18
|
+
wallet = await initLearnCard({ seed: 'a'.repeat(64), didkit, allowRemoteContexts: true });
|
|
19
|
+
const recipient = await initLearnCard({
|
|
20
|
+
seed: 'b'.repeat(64),
|
|
21
|
+
didkit,
|
|
22
|
+
allowRemoteContexts: true,
|
|
23
|
+
});
|
|
24
|
+
subjectDid = recipient.id.did();
|
|
25
|
+
}, 30_000);
|
|
26
|
+
|
|
27
|
+
const docsRoot = new URL('../../../../docs/', import.meta.url);
|
|
28
|
+
const summary = readFileSync(new URL('SUMMARY.md', docsRoot), 'utf8');
|
|
29
|
+
const section = summary.match(/^## 🧠 Understand\s*$([\s\S]*?)^## 📖 Reference\s*$/m)?.[1];
|
|
30
|
+
if (!section) throw new Error('docs/SUMMARY.md: missing Understand / Reference boundaries');
|
|
31
|
+
const pages = [...new Set([...section.matchAll(/\(([^)]+\.md)\)/g)].map(match => match[1]!))];
|
|
32
|
+
if (!pages.length) throw new Error('docs/SUMMARY.md: Understand has no Markdown links');
|
|
33
|
+
|
|
34
|
+
const isObject = (value: unknown): value is Record<string, unknown> =>
|
|
35
|
+
typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
36
|
+
|
|
37
|
+
const entries = (value: unknown): unknown[] => (Array.isArray(value) ? value : [value]);
|
|
38
|
+
|
|
39
|
+
// Ignore ellipses within ordinary strings (including URL paths), but not an exact "..." value.
|
|
40
|
+
const isIllustrative = (source: string): boolean =>
|
|
41
|
+
source.includes('…') ||
|
|
42
|
+
[...source.matchAll(/"(?:\\.|[^"\\])*"|(\.\.\.)/g)].some(
|
|
43
|
+
match => match[0] === '"..."' || Boolean(match[1])
|
|
44
|
+
);
|
|
45
|
+
|
|
46
|
+
/** Check fragment fields wherever they occur, including inside achievement subjects. */
|
|
47
|
+
const validateFragments = (value: unknown): void => {
|
|
48
|
+
if (Array.isArray(value)) {
|
|
49
|
+
value.forEach(validateFragments);
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
if (!isObject(value)) return;
|
|
53
|
+
|
|
54
|
+
if ('credentialStatus' in value) {
|
|
55
|
+
for (const entry of entries(value.credentialStatus)) {
|
|
56
|
+
expect(entry).toEqual(
|
|
57
|
+
expect.objectContaining({
|
|
58
|
+
type: 'BitstringStatusListEntry',
|
|
59
|
+
statusPurpose: expect.stringMatching(/^(revocation|suspension)$/),
|
|
60
|
+
statusListIndex: expect.any(String),
|
|
61
|
+
statusListCredential: expect.any(String),
|
|
62
|
+
})
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
if ('alignment' in value) {
|
|
67
|
+
expect(Array.isArray(value.alignment)).toBe(true);
|
|
68
|
+
for (const entry of entries(value.alignment)) {
|
|
69
|
+
AlignmentValidator.parse(entry);
|
|
70
|
+
expect(entry).toEqual(
|
|
71
|
+
expect.objectContaining({
|
|
72
|
+
type: expect.arrayContaining(['Alignment']),
|
|
73
|
+
targetName: expect.any(String),
|
|
74
|
+
targetUrl: expect.any(String),
|
|
75
|
+
})
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
if (
|
|
80
|
+
'status' in value &&
|
|
81
|
+
!('credentialSubject' in value) &&
|
|
82
|
+
!isObject(value.status) &&
|
|
83
|
+
!Array.isArray(value.status)
|
|
84
|
+
) {
|
|
85
|
+
LCNBoostStatus.parse(value.status);
|
|
86
|
+
}
|
|
87
|
+
Object.values(value).forEach(validateFragments);
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
describe('Understand documentation JSON', () => {
|
|
91
|
+
for (const page of pages) {
|
|
92
|
+
const markdown = readFileSync(fileURLToPath(new URL(page, docsRoot)), 'utf8');
|
|
93
|
+
// Match complete fences first so json-looking text inside another fence is not tested.
|
|
94
|
+
const fences = /^[ \t]{0,3}(`{3,}|~{3,})([^\r\n]*)\r?\n([\s\S]*?)^[ \t]{0,3}\1[ \t]*\r?$/gm;
|
|
95
|
+
for (const match of markdown.matchAll(fences)) {
|
|
96
|
+
if (match[2]!.trim() !== 'json') continue;
|
|
97
|
+
const source = match[3]!;
|
|
98
|
+
const line = markdown.slice(0, match.index).split('\n').length;
|
|
99
|
+
const location = `${page}:${line}`;
|
|
100
|
+
|
|
101
|
+
it(`${location} parses`, () => {
|
|
102
|
+
expect(() => JSON.parse(source)).not.toThrow();
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
// Parsing failures belong to the named parse test, not suite collection.
|
|
106
|
+
let parsed: unknown;
|
|
107
|
+
try {
|
|
108
|
+
parsed = JSON.parse(source);
|
|
109
|
+
} catch {
|
|
110
|
+
continue;
|
|
111
|
+
}
|
|
112
|
+
if (isIllustrative(source) || !isObject(parsed)) continue;
|
|
113
|
+
// Subject-only examples are fragments; credential-level markers must not
|
|
114
|
+
// silently become fragments when their context or type is malformed.
|
|
115
|
+
const credential =
|
|
116
|
+
'@context' in parsed ||
|
|
117
|
+
('credentialSubject' in parsed && ('type' in parsed || 'issuer' in parsed));
|
|
118
|
+
|
|
119
|
+
if (credential) {
|
|
120
|
+
it(`${location} validates and issues`, async () => {
|
|
121
|
+
const prepared = JSON.parse(
|
|
122
|
+
source.includes('{{')
|
|
123
|
+
? source.replace(/\{\{[^{}]+\}\}/g, 'Example')
|
|
124
|
+
: source
|
|
125
|
+
);
|
|
126
|
+
expect(prepared['@context']).toEqual(expect.any(Array));
|
|
127
|
+
expect(prepared.type).toEqual(expect.arrayContaining(['VerifiableCredential']));
|
|
128
|
+
prepared.issuer = wallet.id.did();
|
|
129
|
+
for (const subject of entries(prepared.credentialSubject)) {
|
|
130
|
+
if (isObject(subject)) subject.id ??= subjectDid;
|
|
131
|
+
}
|
|
132
|
+
const isVcV2 = (prepared['@context'] as unknown[]).some(
|
|
133
|
+
ctx => ctx === 'https://www.w3.org/ns/credentials/v2'
|
|
134
|
+
);
|
|
135
|
+
const dateField = isVcV2 ? 'validFrom' : 'issuanceDate';
|
|
136
|
+
prepared[dateField] ??= new Date().toISOString();
|
|
137
|
+
// Docs omit issuer/date intentionally: these are supplied during issuance.
|
|
138
|
+
const unsigned = UnsignedVCValidator.parse(prepared);
|
|
139
|
+
validateFragments(prepared);
|
|
140
|
+
const signed = await wallet.invoke.issueCredential(unsigned);
|
|
141
|
+
expect(signed.proof).toBeDefined();
|
|
142
|
+
}, 15_000);
|
|
143
|
+
} else {
|
|
144
|
+
it(`${location} validates fragment fields`, () => validateFragments(parsed));
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
});
|
|
@@ -4,7 +4,13 @@ import { describe, it, expect, beforeAll } from 'vitest';
|
|
|
4
4
|
|
|
5
5
|
import { initLearnCard } from '@learncard/init';
|
|
6
6
|
|
|
7
|
-
import {
|
|
7
|
+
import {
|
|
8
|
+
getAllFixtures,
|
|
9
|
+
getFixture,
|
|
10
|
+
isCredentialFixture,
|
|
11
|
+
prepareFixture,
|
|
12
|
+
buildFinalTranscriptVariant,
|
|
13
|
+
} from '../index';
|
|
8
14
|
|
|
9
15
|
import type { CredentialFixture } from '../types';
|
|
10
16
|
|
|
@@ -69,4 +75,28 @@ describe('Credential issuance', () => {
|
|
|
69
75
|
|
|
70
76
|
expect(signed.proof).toBeDefined();
|
|
71
77
|
}, 30_000);
|
|
78
|
+
|
|
79
|
+
it('issues the provisional transcript fixture and its final variant without remote contexts', async () => {
|
|
80
|
+
const offlineWallet = await initLearnCard({
|
|
81
|
+
seed: 'c'.repeat(64),
|
|
82
|
+
didkit,
|
|
83
|
+
allowRemoteContexts: false,
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
const provisional = prepareFixture(getFixture('clr/provisional-transcript'), {
|
|
87
|
+
issuerDid: offlineWallet.id.did(),
|
|
88
|
+
subjectDid: 'did:example:test-subject-123',
|
|
89
|
+
});
|
|
90
|
+
const signedProvisional = await offlineWallet.invoke.issueCredential(provisional);
|
|
91
|
+
|
|
92
|
+
expect(signedProvisional.proof).toBeDefined();
|
|
93
|
+
|
|
94
|
+
const final = buildFinalTranscriptVariant(provisional, {
|
|
95
|
+
validFrom: new Date().toISOString(),
|
|
96
|
+
});
|
|
97
|
+
const signedFinal = await offlineWallet.invoke.issueCredential(final);
|
|
98
|
+
|
|
99
|
+
expect(signedFinal.proof).toBeDefined();
|
|
100
|
+
expect(signedFinal.id).toBe(signedProvisional.id);
|
|
101
|
+
}, 30_000);
|
|
72
102
|
});
|
|
@@ -17,6 +17,7 @@ import {
|
|
|
17
17
|
registerFixtures,
|
|
18
18
|
prepareFixture,
|
|
19
19
|
prepareFixtureById,
|
|
20
|
+
buildFinalTranscriptVariant,
|
|
20
21
|
} from '../index';
|
|
21
22
|
|
|
22
23
|
import { ALL_FIXTURES } from '../fixtures';
|
|
@@ -171,6 +172,65 @@ describe('Query API', () => {
|
|
|
171
172
|
expect(() => getFixture('nonexistent/fixture')).toThrow('not found');
|
|
172
173
|
});
|
|
173
174
|
|
|
175
|
+
// -----------------------------------------------------------------------
|
|
176
|
+
// Provisional transcript fixture (LC-2117 / LC-2135 / LC-2136)
|
|
177
|
+
// -----------------------------------------------------------------------
|
|
178
|
+
|
|
179
|
+
describe('clr/provisional-transcript', () => {
|
|
180
|
+
it('is discoverable as a valid CLR 2.0 fixture with a LearnCard managed refresh service', () => {
|
|
181
|
+
const fixture = getFixture('clr/provisional-transcript');
|
|
182
|
+
|
|
183
|
+
expect(isCredentialFixture(fixture)).toBe(true);
|
|
184
|
+
expect(fixture.spec).toBe('clr-v2');
|
|
185
|
+
expect(fixture.profile).toBe('learner-record');
|
|
186
|
+
expect(fixture.validity).toBe('valid');
|
|
187
|
+
expect(fixture.features).toContain('refresh-service');
|
|
188
|
+
|
|
189
|
+
if (!isCredentialFixture(fixture)) throw new Error('Expected W3C VC fixture');
|
|
190
|
+
|
|
191
|
+
const refreshService = (
|
|
192
|
+
fixture.credential as { refreshService?: { type?: unknown; id?: unknown } }
|
|
193
|
+
).refreshService;
|
|
194
|
+
|
|
195
|
+
expect(refreshService?.type).toBe('LearnCardCredentialRefresh2026');
|
|
196
|
+
expect(typeof refreshService?.id).toBe('string');
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
it('prepares provisional and final variants sharing one credential ID, issuer, and subject', () => {
|
|
200
|
+
const provisional = prepareFixtureById('clr/provisional-transcript', {
|
|
201
|
+
issuerDid: 'did:example:test-issuer',
|
|
202
|
+
subjectDid: 'did:example:test-holder',
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
expect(typeof provisional.id).toBe('string');
|
|
206
|
+
expect(provisional.name).toContain('Provisional');
|
|
207
|
+
|
|
208
|
+
const final = buildFinalTranscriptVariant(provisional, {
|
|
209
|
+
validFrom: '2026-06-01T00:00:00Z',
|
|
210
|
+
});
|
|
211
|
+
const provisionalIssuer =
|
|
212
|
+
typeof provisional.issuer === 'string' ? provisional.issuer : provisional.issuer.id;
|
|
213
|
+
const finalIssuer = typeof final.issuer === 'string' ? final.issuer : final.issuer.id;
|
|
214
|
+
const provisionalSubject = Array.isArray(provisional.credentialSubject)
|
|
215
|
+
? provisional.credentialSubject[0]
|
|
216
|
+
: provisional.credentialSubject;
|
|
217
|
+
const finalSubject = Array.isArray(final.credentialSubject)
|
|
218
|
+
? final.credentialSubject[0]
|
|
219
|
+
: final.credentialSubject;
|
|
220
|
+
|
|
221
|
+
// Identity stability: the final version shares the credential ID, issuer,
|
|
222
|
+
// and subject with the provisional version.
|
|
223
|
+
expect(final.id).toBe(provisional.id);
|
|
224
|
+
expect(finalIssuer).toBe(provisionalIssuer);
|
|
225
|
+
expect(finalSubject?.id).toBe(provisionalSubject?.id);
|
|
226
|
+
|
|
227
|
+
// The final variant is materially different and marked final.
|
|
228
|
+
expect(final.name).toContain('Final');
|
|
229
|
+
expect(final.validFrom).toBe('2026-06-01T00:00:00Z');
|
|
230
|
+
expect(final.refreshService).toEqual(provisional.refreshService);
|
|
231
|
+
});
|
|
232
|
+
});
|
|
233
|
+
|
|
174
234
|
it('findFixture returns undefined for unknown ID', () => {
|
|
175
235
|
expect(findFixture('nonexistent/fixture')).toBeUndefined();
|
|
176
236
|
});
|
|
@@ -528,8 +588,7 @@ describe('prepareFixture', () => {
|
|
|
528
588
|
? (programAchievement?.resultDescription as UnknownArrayRecord[])
|
|
529
589
|
: [];
|
|
530
590
|
const programResultDescriptionId = programResults[0]?.resultDescription as
|
|
531
|
-
|
|
|
532
|
-
| undefined;
|
|
591
|
+
string | undefined;
|
|
533
592
|
const programResultDescription = programResultDescriptions[0]?.id as string | undefined;
|
|
534
593
|
|
|
535
594
|
expect(programResultDescriptionId).toBe(programResultDescription);
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
import { UnsignedClrCredentialValidator, type UnsignedVC } from '@learncard/types';
|
|
2
|
+
|
|
3
|
+
import type { CredentialFixture } from '../../types';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Provisional CLR 2.0 transcript with a `LearnCardCredentialRefresh2026` service, built for
|
|
7
|
+
* the managed credential refresh flow (LC-2117 / LC-2135 / LC-2136).
|
|
8
|
+
*
|
|
9
|
+
* The credential starts as a provisional transcript; `buildFinalTranscriptVariant`
|
|
10
|
+
* produces the final variant WITHOUT changing the credential ID, issuer, or subject,
|
|
11
|
+
* mirroring a registrar publishing the finalized record to the same refresh service.
|
|
12
|
+
*
|
|
13
|
+
* JSON-LD note: neither the VCDM 1.1/2.0 nor the live OBv3/CLR contexts define the
|
|
14
|
+
* term `LearnCardCredentialRefresh2026` (or the LearnCard `authorization` extension), so
|
|
15
|
+
* the fixture carries an inline context fragment — DIDKit's data-loss detection
|
|
16
|
+
* otherwise refuses to sign.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
/** Inline JSON-LD context fragment defining the refresh-service terms inline. */
|
|
20
|
+
export const REFRESH_SERVICE_INLINE_CONTEXT = {
|
|
21
|
+
'LearnCardCredentialRefresh2026':
|
|
22
|
+
'https://learncard.com/refresh#LearnCardCredentialRefresh2026',
|
|
23
|
+
authorization: {
|
|
24
|
+
'@id': 'https://purl.imsglobal.org/spec/ob/v3p0#authorization',
|
|
25
|
+
'@context': {
|
|
26
|
+
LearnCardDIDAuth: 'https://docs.learncard.com/definitions#LearnCardDIDAuth',
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
} as const;
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Placeholder refresh service on the unsigned fixture. Callers preparing the fixture
|
|
33
|
+
* for managed issuance replace `refreshService` with the allocated service descriptor
|
|
34
|
+
* (the allocated ID is only known after `allocateCredentialRefresh`).
|
|
35
|
+
*/
|
|
36
|
+
const PLACEHOLDER_REFRESH_SERVICE = {
|
|
37
|
+
id: 'https://refresh.example.com/refresh/placeholder',
|
|
38
|
+
type: 'LearnCardCredentialRefresh2026',
|
|
39
|
+
authorization: { type: 'LearnCardDIDAuth' },
|
|
40
|
+
} as const;
|
|
41
|
+
|
|
42
|
+
export const clrProvisionalTranscript: CredentialFixture = {
|
|
43
|
+
id: 'clr/provisional-transcript',
|
|
44
|
+
name: 'Provisional Transcript (Refreshable)',
|
|
45
|
+
description:
|
|
46
|
+
'A CLR v2 provisional transcript carrying a LearnCardCredentialRefresh2026 service. Paired with buildFinalTranscriptVariant, it demonstrates the managed provisional-to-final credential refresh flow.',
|
|
47
|
+
spec: 'clr-v2',
|
|
48
|
+
profile: 'learner-record',
|
|
49
|
+
features: ['refresh-service', 'results'],
|
|
50
|
+
source: 'synthetic',
|
|
51
|
+
signed: false,
|
|
52
|
+
validity: 'valid',
|
|
53
|
+
validator: UnsignedClrCredentialValidator,
|
|
54
|
+
tags: ['transcript', 'provisional', 'refresh', 'clr', 'managed-refresh'],
|
|
55
|
+
|
|
56
|
+
credential: {
|
|
57
|
+
'@context': [
|
|
58
|
+
'https://www.w3.org/ns/credentials/v2',
|
|
59
|
+
'https://purl.imsglobal.org/spec/ob/v3p0/context-3.0.3.json',
|
|
60
|
+
'https://purl.imsglobal.org/spec/clr/v2p0/context.json',
|
|
61
|
+
REFRESH_SERVICE_INLINE_CONTEXT,
|
|
62
|
+
],
|
|
63
|
+
id: 'urn:uuid:7d2e9f41-3c58-4e21-9b6a-1f0c5d8e2a47',
|
|
64
|
+
type: ['VerifiableCredential', 'ClrCredential'],
|
|
65
|
+
name: 'Provisional Transcript — Ridgeview Community College',
|
|
66
|
+
description:
|
|
67
|
+
'Provisional record of courses completed to date. This transcript becomes final once the registrar certifies the completed term.',
|
|
68
|
+
issuer: {
|
|
69
|
+
id: 'did:web:registrar.ridgeview-example.edu',
|
|
70
|
+
type: ['Profile'],
|
|
71
|
+
name: 'Ridgeview Community College — Office of the Registrar',
|
|
72
|
+
},
|
|
73
|
+
validFrom: '2026-01-15T00:00:00Z',
|
|
74
|
+
refreshService: PLACEHOLDER_REFRESH_SERVICE,
|
|
75
|
+
credentialSubject: {
|
|
76
|
+
id: 'did:example:student-ridgeview-042',
|
|
77
|
+
type: ['ClrSubject'],
|
|
78
|
+
achievement: [
|
|
79
|
+
{
|
|
80
|
+
id: 'urn:uuid:7d2e9f41-3c58-4e21-9b6a-1f0c5d8e2a48',
|
|
81
|
+
type: ['Achievement'],
|
|
82
|
+
achievementType: 'Course',
|
|
83
|
+
name: 'BIO 150 — Principles of Biology',
|
|
84
|
+
description:
|
|
85
|
+
'Introductory biology covering cell structure, genetics, and evolution.',
|
|
86
|
+
criteria: { narrative: 'Complete all coursework and the final exam.' },
|
|
87
|
+
humanCode: 'BIO-150',
|
|
88
|
+
fieldOfStudy: 'Biology',
|
|
89
|
+
creditsAvailable: 4,
|
|
90
|
+
resultDescription: [
|
|
91
|
+
{
|
|
92
|
+
id: 'urn:uuid:7d2e9f41-3c58-4e21-9b6a-1f0c5d8e2a49',
|
|
93
|
+
type: ['ResultDescription'],
|
|
94
|
+
name: 'Course Status',
|
|
95
|
+
resultType: 'RawScore',
|
|
96
|
+
},
|
|
97
|
+
],
|
|
98
|
+
},
|
|
99
|
+
],
|
|
100
|
+
verifiableCredential: [
|
|
101
|
+
{
|
|
102
|
+
'@context': [
|
|
103
|
+
'https://www.w3.org/ns/credentials/v2',
|
|
104
|
+
'https://purl.imsglobal.org/spec/ob/v3p0/context-3.0.3.json',
|
|
105
|
+
],
|
|
106
|
+
id: 'urn:uuid:7d2e9f41-3c58-4e21-9b6a-1f0c5d8e2a50',
|
|
107
|
+
type: ['VerifiableCredential', 'AchievementCredential'],
|
|
108
|
+
name: 'BIO 150 — Principles of Biology (In Progress)',
|
|
109
|
+
issuer: {
|
|
110
|
+
id: 'did:web:registrar.ridgeview-example.edu',
|
|
111
|
+
type: ['Profile'],
|
|
112
|
+
name: 'Ridgeview Community College',
|
|
113
|
+
},
|
|
114
|
+
validFrom: '2026-01-15T00:00:00Z',
|
|
115
|
+
credentialSubject: {
|
|
116
|
+
id: 'did:example:student-ridgeview-042',
|
|
117
|
+
type: ['AchievementSubject'],
|
|
118
|
+
achievement: {
|
|
119
|
+
id: 'urn:uuid:7d2e9f41-3c58-4e21-9b6a-1f0c5d8e2a48',
|
|
120
|
+
type: ['Achievement'],
|
|
121
|
+
achievementType: 'Course',
|
|
122
|
+
name: 'BIO 150 — Principles of Biology',
|
|
123
|
+
criteria: {
|
|
124
|
+
narrative: 'Complete all coursework and the final exam.',
|
|
125
|
+
},
|
|
126
|
+
humanCode: 'BIO-150',
|
|
127
|
+
fieldOfStudy: 'Biology',
|
|
128
|
+
creditsAvailable: 4,
|
|
129
|
+
},
|
|
130
|
+
result: [
|
|
131
|
+
{
|
|
132
|
+
type: ['Result'],
|
|
133
|
+
resultDescription: 'urn:uuid:7d2e9f41-3c58-4e21-9b6a-1f0c5d8e2a49',
|
|
134
|
+
status: 'InProgress',
|
|
135
|
+
},
|
|
136
|
+
],
|
|
137
|
+
},
|
|
138
|
+
},
|
|
139
|
+
],
|
|
140
|
+
},
|
|
141
|
+
},
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Builds the final transcript variant from a prepared provisional credential.
|
|
146
|
+
*
|
|
147
|
+
* Preserves the credential ID, issuer, subject, and refresh service (identity
|
|
148
|
+
* invariants the managed refresh pipeline enforces) while flipping user-visible
|
|
149
|
+
* content to its final state: name/description become final, the nested course
|
|
150
|
+
* credential shows the completed result, and `validFrom` moves forward.
|
|
151
|
+
*/
|
|
152
|
+
export const buildFinalTranscriptVariant = (
|
|
153
|
+
provisional: UnsignedVC,
|
|
154
|
+
options: { validFrom?: string } = {}
|
|
155
|
+
): UnsignedVC => {
|
|
156
|
+
const isRecord = (value: unknown): value is Record<string, unknown> =>
|
|
157
|
+
typeof value === 'object' && value !== null;
|
|
158
|
+
|
|
159
|
+
const final = JSON.parse(JSON.stringify(provisional)) as Record<string, unknown>;
|
|
160
|
+
|
|
161
|
+
final.name = 'Final Official Transcript — Ridgeview Community College';
|
|
162
|
+
final.description =
|
|
163
|
+
'Final official transcript certified by the registrar. Supersedes the provisional record.';
|
|
164
|
+
|
|
165
|
+
if (options.validFrom) final.validFrom = options.validFrom;
|
|
166
|
+
|
|
167
|
+
const subject = final.credentialSubject;
|
|
168
|
+
|
|
169
|
+
if (isRecord(subject) && Array.isArray(subject.verifiableCredential)) {
|
|
170
|
+
subject.verifiableCredential = subject.verifiableCredential.map((nested: unknown) => {
|
|
171
|
+
if (!isRecord(nested)) return nested;
|
|
172
|
+
|
|
173
|
+
const next = { ...nested };
|
|
174
|
+
|
|
175
|
+
if (typeof next.name === 'string') {
|
|
176
|
+
next.name = next.name.replace('(In Progress)', '(Completed)');
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
if (isRecord(next.credentialSubject) && Array.isArray(next.credentialSubject.result)) {
|
|
180
|
+
next.credentialSubject = {
|
|
181
|
+
...next.credentialSubject,
|
|
182
|
+
result: next.credentialSubject.result.map((result: unknown) =>
|
|
183
|
+
isRecord(result) ? { ...result, status: 'Completed', value: 'A' } : result
|
|
184
|
+
),
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
return next;
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
return final as unknown as UnsignedVC;
|
|
193
|
+
};
|
package/src/fixtures/index.ts
CHANGED
|
@@ -56,6 +56,11 @@ import { vcV2LicenseCredential } from './vc-v2/license-credential';
|
|
|
56
56
|
import { clrGreatPlainsFull } from './clr/great-plains-full';
|
|
57
57
|
import { clrWestbridgeFull } from './clr/westbridge-full';
|
|
58
58
|
import { clrCompetencyAligned } from './clr/competency-aligned';
|
|
59
|
+
import {
|
|
60
|
+
clrProvisionalTranscript,
|
|
61
|
+
buildFinalTranscriptVariant,
|
|
62
|
+
REFRESH_SERVICE_INLINE_CONTEXT,
|
|
63
|
+
} from './clr/provisional-transcript';
|
|
59
64
|
import { clrDemoIsdDiplomaAssessments } from './clr/demo-isd-diploma-assessments';
|
|
60
65
|
|
|
61
66
|
// SD-JWT VC
|
|
@@ -120,6 +125,7 @@ export const ALL_FIXTURES: LibraryFixture[] = [
|
|
|
120
125
|
clrGreatPlainsFull,
|
|
121
126
|
clrWestbridgeFull,
|
|
122
127
|
clrCompetencyAligned,
|
|
128
|
+
clrProvisionalTranscript,
|
|
123
129
|
clrDemoIsdDiplomaAssessments,
|
|
124
130
|
|
|
125
131
|
// SD-JWT VC
|
|
@@ -167,6 +173,9 @@ export {
|
|
|
167
173
|
clrGreatPlainsFull,
|
|
168
174
|
clrWestbridgeFull,
|
|
169
175
|
clrCompetencyAligned,
|
|
176
|
+
clrProvisionalTranscript,
|
|
177
|
+
buildFinalTranscriptVariant,
|
|
178
|
+
REFRESH_SERVICE_INLINE_CONTEXT,
|
|
170
179
|
clrDemoIsdDiplomaAssessments,
|
|
171
180
|
sdJwtVcCourseCompletion,
|
|
172
181
|
};
|