@learncard/credential-library 2.0.3 → 2.0.4

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/README.md CHANGED
@@ -251,7 +251,7 @@ See the [Credential Viewer README](../../examples/credential-viewer/README.md) f
251
251
  - `vc-v2/membership-credential` — Professional association membership
252
252
  - `vc-v2/license-credential` — Professional nursing license
253
253
 
254
- ### Open Badges v3 (11)
254
+ ### Open Badges v3 (12)
255
255
 
256
256
  - `obv3/minimal-badge` — Minimal OBv3 achievement credential
257
257
  - `obv3/full-badge` — Full-featured badge with image, evidence, alignment, results, expiration
@@ -264,6 +264,7 @@ See the [Credential Viewer README](../../examples/credential-viewer/README.md) f
264
264
  - `obv3/course-completion` — Introduction to Machine Learning course completion
265
265
  - `obv3/k12-diploma` — High school diploma
266
266
  - `obv3/endorsement-credential` — Program accreditation endorsement
267
+ - `obv3/rubric-aligned-badge` — Open Skills Alignment result with rubric levels and CTDL alignment
267
268
 
268
269
  ### CLR v2 (4)
269
270
 
@@ -68,6 +68,7 @@ __export(index_exports, {
68
68
  obv3OneedtechFull: () => obv3OneedtechFull,
69
69
  obv3PlugfestJff2: () => obv3PlugfestJff2,
70
70
  obv3ProfessionalCert: () => obv3ProfessionalCert,
71
+ obv3RubricAlignedBadge: () => obv3RubricAlignedBadge,
71
72
  obv3StandaloneFullCourse: () => obv3StandaloneFullCourse,
72
73
  obv3WithAlignment: () => obv3WithAlignment,
73
74
  obv3WithEndorsement: () => obv3WithEndorsement,
@@ -1585,8 +1586,107 @@ var obv3EndorsementCredential = {
1585
1586
  }
1586
1587
  };
1587
1588
 
1588
- // src/fixtures/clr/minimal.ts
1589
+ // src/fixtures/obv3/rubric-aligned-badge.ts
1589
1590
  var import_types13 = require("@learncard/types");
1591
+ var RESULT_DESCRIPTION_ID = "urn:uuid:7cc1519e-87f4-4cae-b7a5-786ec15db769";
1592
+ var PROFICIENT_LEVEL_ID = "urn:uuid:eb753d86-9277-478d-b9c4-3d589c82aff8";
1593
+ var obv3RubricAlignedBadge = {
1594
+ id: "obv3/rubric-aligned-badge",
1595
+ name: "Rubric-Aligned OBv3 Badge",
1596
+ description: "Open Badges v3 credential with rubric levels, an achieved level, and a CTDL result alignment",
1597
+ spec: "obv3",
1598
+ profile: "badge",
1599
+ features: ["alignment", "results"],
1600
+ source: "synthetic",
1601
+ signed: false,
1602
+ validity: "valid",
1603
+ validator: import_types13.UnsignedAchievementCredentialValidator,
1604
+ tags: ["rubric", "open-skill-alignment"],
1605
+ credential: {
1606
+ "@context": [
1607
+ "https://www.w3.org/ns/credentials/v2",
1608
+ "https://purl.imsglobal.org/spec/ob/v3p0/context-3.0.3.json"
1609
+ ],
1610
+ id: "urn:uuid:b3e5babd-f68e-4095-979d-3e910a444e73",
1611
+ type: ["VerifiableCredential", "OpenBadgeCredential"],
1612
+ name: "Applied Data Analysis",
1613
+ description: "Recognizes applied data analysis at the proficient rubric level.",
1614
+ issuer: {
1615
+ id: "did:example:issuer123",
1616
+ type: ["Profile"],
1617
+ name: "Example Skills Institute",
1618
+ url: "https://example.org"
1619
+ },
1620
+ validFrom: "2026-01-15T00:00:00Z",
1621
+ credentialSubject: {
1622
+ id: "did:example:learner456",
1623
+ type: ["AchievementSubject"],
1624
+ achievement: {
1625
+ id: "urn:uuid:e736a10c-aa36-466f-99af-e52440f32d8e",
1626
+ type: ["Achievement"],
1627
+ achievementType: "Badge",
1628
+ name: "Applied Data Analysis",
1629
+ description: "Analyze a real-world dataset and communicate supported conclusions.",
1630
+ criteria: {
1631
+ narrative: "Submit an analysis that meets at least the proficient rubric level."
1632
+ },
1633
+ resultDescription: [
1634
+ {
1635
+ id: RESULT_DESCRIPTION_ID,
1636
+ type: ["ResultDescription"],
1637
+ name: "Data Analysis Rubric",
1638
+ resultType: "RubricCriterionLevel",
1639
+ valueMin: "1",
1640
+ valueMax: "4",
1641
+ rubricCriterionLevel: [
1642
+ {
1643
+ id: "urn:uuid:a4993932-d453-4417-b5aa-52b1bc1ea80e",
1644
+ type: ["RubricCriterionLevel"],
1645
+ name: "Developing",
1646
+ level: "2",
1647
+ points: "2"
1648
+ },
1649
+ {
1650
+ id: PROFICIENT_LEVEL_ID,
1651
+ type: ["RubricCriterionLevel"],
1652
+ name: "Proficient",
1653
+ level: "3",
1654
+ points: "3"
1655
+ },
1656
+ {
1657
+ id: "urn:uuid:217fc04f-383b-48a0-a2e5-1cf1b016f7d4",
1658
+ type: ["RubricCriterionLevel"],
1659
+ name: "Advanced",
1660
+ level: "4",
1661
+ points: "4"
1662
+ }
1663
+ ],
1664
+ alignment: [
1665
+ {
1666
+ type: ["Alignment"],
1667
+ targetName: "Analyze data using appropriate methods",
1668
+ targetUrl: "https://credentialengineregistry.org/resources/ce-2f5dce8d-29ad-40cb-a9e1-5a5517ff79a7",
1669
+ targetType: "CTDL",
1670
+ targetFramework: "Credential Engine Registry"
1671
+ }
1672
+ ]
1673
+ }
1674
+ ]
1675
+ },
1676
+ result: [
1677
+ {
1678
+ type: ["Result"],
1679
+ resultDescription: RESULT_DESCRIPTION_ID,
1680
+ value: "3",
1681
+ achievedLevel: PROFICIENT_LEVEL_ID
1682
+ }
1683
+ ]
1684
+ }
1685
+ }
1686
+ };
1687
+
1688
+ // src/fixtures/clr/minimal.ts
1689
+ var import_types14 = require("@learncard/types");
1590
1690
  var clrMinimal = {
1591
1691
  id: "clr/minimal",
1592
1692
  name: "Minimal CLR v2 Credential",
@@ -1597,7 +1697,7 @@ var clrMinimal = {
1597
1697
  source: "spec-example",
1598
1698
  signed: false,
1599
1699
  validity: "valid",
1600
- validator: import_types13.UnsignedClrCredentialValidator,
1700
+ validator: import_types14.UnsignedClrCredentialValidator,
1601
1701
  credential: {
1602
1702
  "@context": [
1603
1703
  "https://www.w3.org/ns/credentials/v2",
@@ -1636,7 +1736,7 @@ var clrMinimal = {
1636
1736
  };
1637
1737
 
1638
1738
  // src/fixtures/clr/multi-achievement.ts
1639
- var import_types14 = require("@learncard/types");
1739
+ var import_types15 = require("@learncard/types");
1640
1740
  var clrMultiAchievement = {
1641
1741
  id: "clr/multi-achievement",
1642
1742
  name: "CLR v2 with Multiple Achievements and Associations",
@@ -1647,7 +1747,7 @@ var clrMultiAchievement = {
1647
1747
  source: "synthetic",
1648
1748
  signed: false,
1649
1749
  validity: "valid",
1650
- validator: import_types14.UnsignedClrCredentialValidator,
1750
+ validator: import_types15.UnsignedClrCredentialValidator,
1651
1751
  tags: ["comprehensive"],
1652
1752
  credential: {
1653
1753
  "@context": [
@@ -2082,7 +2182,7 @@ var clrUniversityTranscript = {
2082
2182
  };
2083
2183
 
2084
2184
  // src/fixtures/boost/basic.ts
2085
- var import_types15 = require("@learncard/types");
2185
+ var import_types16 = require("@learncard/types");
2086
2186
  var boostBasic = {
2087
2187
  id: "boost/basic",
2088
2188
  name: "Basic LearnCard Boost",
@@ -2093,7 +2193,7 @@ var boostBasic = {
2093
2193
  source: "synthetic",
2094
2194
  signed: false,
2095
2195
  validity: "valid",
2096
- validator: import_types15.UnsignedAchievementCredentialValidator,
2196
+ validator: import_types16.UnsignedAchievementCredentialValidator,
2097
2197
  tags: ["learncard", "boost"],
2098
2198
  credential: {
2099
2199
  "@context": [
@@ -2129,7 +2229,7 @@ var boostBasic = {
2129
2229
  };
2130
2230
 
2131
2231
  // src/fixtures/boost/boost-id.ts
2132
- var import_types16 = require("@learncard/types");
2232
+ var import_types17 = require("@learncard/types");
2133
2233
  var boostId = {
2134
2234
  id: "boost/boost-id",
2135
2235
  name: "LearnCard Boost ID",
@@ -2140,7 +2240,7 @@ var boostId = {
2140
2240
  source: "synthetic",
2141
2241
  signed: false,
2142
2242
  validity: "valid",
2143
- validator: import_types16.UnsignedAchievementCredentialValidator,
2243
+ validator: import_types17.UnsignedAchievementCredentialValidator,
2144
2244
  tags: ["learncard", "boost", "id"],
2145
2245
  credential: {
2146
2246
  "@context": [
@@ -2185,7 +2285,7 @@ var boostId = {
2185
2285
  };
2186
2286
 
2187
2287
  // src/fixtures/boost/with-skills.ts
2188
- var import_types17 = require("@learncard/types");
2288
+ var import_types18 = require("@learncard/types");
2189
2289
  var boostWithSkills = {
2190
2290
  id: "boost/with-skills",
2191
2291
  name: "LearnCard Boost with Skills",
@@ -2196,7 +2296,7 @@ var boostWithSkills = {
2196
2296
  source: "synthetic",
2197
2297
  signed: false,
2198
2298
  validity: "valid",
2199
- validator: import_types17.UnsignedAchievementCredentialValidator,
2299
+ validator: import_types18.UnsignedAchievementCredentialValidator,
2200
2300
  tags: ["learncard", "boost", "skills"],
2201
2301
  credential: {
2202
2302
  "@context": [
@@ -2406,7 +2506,7 @@ var boostDelegate = {
2406
2506
  };
2407
2507
 
2408
2508
  // src/fixtures/invalid/missing-context.ts
2409
- var import_types18 = require("@learncard/types");
2509
+ var import_types19 = require("@learncard/types");
2410
2510
  var invalidMissingContext = {
2411
2511
  id: "invalid/missing-context",
2412
2512
  name: "Missing @context",
@@ -2417,7 +2517,7 @@ var invalidMissingContext = {
2417
2517
  source: "synthetic",
2418
2518
  signed: false,
2419
2519
  validity: "invalid",
2420
- validator: import_types18.UnsignedVCValidator,
2520
+ validator: import_types19.UnsignedVCValidator,
2421
2521
  tags: ["negative-test"],
2422
2522
  credential: {
2423
2523
  // Intentionally missing '@context'
@@ -2430,7 +2530,7 @@ var invalidMissingContext = {
2430
2530
  };
2431
2531
 
2432
2532
  // src/fixtures/invalid/empty-type.ts
2433
- var import_types19 = require("@learncard/types");
2533
+ var import_types20 = require("@learncard/types");
2434
2534
  var invalidEmptyType = {
2435
2535
  id: "invalid/empty-type",
2436
2536
  name: "Empty type Array",
@@ -2441,7 +2541,7 @@ var invalidEmptyType = {
2441
2541
  source: "synthetic",
2442
2542
  signed: false,
2443
2543
  validity: "invalid",
2444
- validator: import_types19.UnsignedVCValidator,
2544
+ validator: import_types20.UnsignedVCValidator,
2445
2545
  tags: ["negative-test"],
2446
2546
  credential: {
2447
2547
  "@context": ["https://www.w3.org/ns/credentials/v2"],
@@ -2454,7 +2554,7 @@ var invalidEmptyType = {
2454
2554
  };
2455
2555
 
2456
2556
  // src/fixtures/invalid/missing-issuer.ts
2457
- var import_types20 = require("@learncard/types");
2557
+ var import_types21 = require("@learncard/types");
2458
2558
  var invalidMissingIssuer = {
2459
2559
  id: "invalid/missing-issuer",
2460
2560
  name: "Missing Issuer",
@@ -2465,7 +2565,7 @@ var invalidMissingIssuer = {
2465
2565
  source: "synthetic",
2466
2566
  signed: false,
2467
2567
  validity: "invalid",
2468
- validator: import_types20.UnsignedVCValidator,
2568
+ validator: import_types21.UnsignedVCValidator,
2469
2569
  tags: ["negative-test"],
2470
2570
  credential: {
2471
2571
  "@context": ["https://www.w3.org/ns/credentials/v2"],
@@ -14330,8 +14430,9 @@ var clrCompetencyAligned = {
14330
14430
  };
14331
14431
 
14332
14432
  // src/fixtures/clr/provisional-transcript.ts
14333
- var import_types21 = require("@learncard/types");
14433
+ var import_types22 = require("@learncard/types");
14334
14434
  var REFRESH_SERVICE_INLINE_CONTEXT = {
14435
+ partial: "https://purl.imsglobal.org/spec/clr/v2p0#partial",
14335
14436
  "LearnCardCredentialRefresh2026": "https://learncard.com/refresh#LearnCardCredentialRefresh2026",
14336
14437
  authorization: {
14337
14438
  "@id": "https://purl.imsglobal.org/spec/ob/v3p0#authorization",
@@ -14355,7 +14456,7 @@ var clrProvisionalTranscript = {
14355
14456
  source: "synthetic",
14356
14457
  signed: false,
14357
14458
  validity: "valid",
14358
- validator: import_types21.UnsignedClrCredentialValidator,
14459
+ validator: import_types22.UnsignedClrCredentialValidator,
14359
14460
  tags: ["transcript", "provisional", "refresh", "clr", "managed-refresh"],
14360
14461
  credential: {
14361
14462
  "@context": [
@@ -14374,6 +14475,8 @@ var clrProvisionalTranscript = {
14374
14475
  name: "Ridgeview Community College \u2014 Office of the Registrar"
14375
14476
  },
14376
14477
  validFrom: "2026-01-15T00:00:00Z",
14478
+ validUntil: "2026-05-31T00:00:00Z",
14479
+ partial: true,
14377
14480
  refreshService: PLACEHOLDER_REFRESH_SERVICE,
14378
14481
  credentialSubject: {
14379
14482
  id: "did:example:student-ridgeview-042",
@@ -14394,7 +14497,8 @@ var clrProvisionalTranscript = {
14394
14497
  id: "urn:uuid:7d2e9f41-3c58-4e21-9b6a-1f0c5d8e2a49",
14395
14498
  type: ["ResultDescription"],
14396
14499
  name: "Course Status",
14397
- resultType: "RawScore"
14500
+ resultType: "Status",
14501
+ allowedValue: ["InProgress", "Provisional", "Completed"]
14398
14502
  }
14399
14503
  ]
14400
14504
  }
@@ -14448,6 +14552,8 @@ var buildFinalTranscriptVariant = /* @__PURE__ */ __name((provisional, options =
14448
14552
  final.name = "Final Official Transcript \u2014 Ridgeview Community College";
14449
14553
  final.description = "Final official transcript certified by the registrar. Supersedes the provisional record.";
14450
14554
  if (options.validFrom) final.validFrom = options.validFrom;
14555
+ delete final.validUntil;
14556
+ delete final.partial;
14451
14557
  const subject = final.credentialSubject;
14452
14558
  if (isRecord(subject) && Array.isArray(subject.verifiableCredential)) {
14453
14559
  subject.verifiableCredential = subject.verifiableCredential.map((nested) => {
@@ -14460,7 +14566,7 @@ var buildFinalTranscriptVariant = /* @__PURE__ */ __name((provisional, options =
14460
14566
  next.credentialSubject = {
14461
14567
  ...next.credentialSubject,
14462
14568
  result: next.credentialSubject.result.map(
14463
- (result) => isRecord(result) ? { ...result, status: "Completed", value: "A" } : result
14569
+ (result) => isRecord(result) ? { ...result, status: "Completed", value: "Completed" } : result
14464
14570
  )
14465
14571
  };
14466
14572
  }
@@ -23024,6 +23130,7 @@ var ALL_FIXTURES = [
23024
23130
  obv3StandaloneFullCourse,
23025
23131
  obv3K12Diploma,
23026
23132
  obv3EndorsementCredential,
23133
+ obv3RubricAlignedBadge,
23027
23134
  // CLR v2
23028
23135
  clrMinimal,
23029
23136
  clrMultiAchievement,
@@ -23274,6 +23381,7 @@ var patchSubject = /* @__PURE__ */ __name((subject, subjectDid) => {
23274
23381
  }
23275
23382
  return subject;
23276
23383
  }, "patchSubject");
23384
+ var FIXTURE_VALIDITY_MS = 180 * 24 * 60 * 60 * 1e3;
23277
23385
  var prepareFixture = /* @__PURE__ */ __name((fixture, options) => {
23278
23386
  if (isSdJwtVcFixture(fixture)) {
23279
23387
  throw new Error(
@@ -23310,6 +23418,17 @@ var prepareFixture = /* @__PURE__ */ __name((fixture, options) => {
23310
23418
  } else {
23311
23419
  credential.validUntil = validUntil;
23312
23420
  }
23421
+ } else {
23422
+ const from = new Date(
23423
+ credential.validFrom ?? credential.issuanceDate
23424
+ ).getTime();
23425
+ const baseline = Math.max(from, Date.now());
23426
+ for (const field of ["validUntil", "expirationDate"]) {
23427
+ const expiry = credential[field];
23428
+ if (typeof expiry === "string" && new Date(expiry).getTime() <= baseline) {
23429
+ credential[field] = new Date(baseline + FIXTURE_VALIDITY_MS).toISOString();
23430
+ }
23431
+ }
23313
23432
  }
23314
23433
  return credential;
23315
23434
  }, "prepareFixture");