@learncard/learn-card-plugin 1.2.26 → 1.2.27

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.
@@ -1 +1 @@
1
- {"version":3,"file":"verificationPrettifier.d.ts","sourceRoot":"","sources":["../src/verificationPrettifier.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAA0B,MAAM,kBAAkB,CAAC;AA+G5E,eAAO,MAAM,wBAAwB,SAAU,gBAAgB,KAAG,gBA6CjE,CAAC;AAEF,eAAO,MAAM,yBAAyB,UAAW,gBAAgB,EAAE,KAAG,gBAAgB,EAC/C,CAAC"}
1
+ {"version":3,"file":"verificationPrettifier.d.ts","sourceRoot":"","sources":["../src/verificationPrettifier.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAA0B,MAAM,kBAAkB,CAAC;AA8H5E,eAAO,MAAM,wBAAwB,SAAU,gBAAgB,KAAG,gBAmEjE,CAAC;AAEF,eAAO,MAAM,yBAAyB,UAAW,gBAAgB,EAAE,KAAG,gBAAgB,EAC/C,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@learncard/learn-card-plugin",
3
- "version": "1.2.26",
3
+ "version": "1.2.27",
4
4
  "description": "",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
@@ -51,9 +51,9 @@
51
51
  },
52
52
  "types": "./dist/index.d.ts",
53
53
  "dependencies": {
54
- "@learncard/core": "9.4.26",
55
- "@learncard/didkit-plugin": "^1.9.6",
56
- "@learncard/types": "5.17.5",
54
+ "@learncard/core": "9.4.27",
55
+ "@learncard/didkit-plugin": "^1.9.7",
56
+ "@learncard/types": "5.17.6",
57
57
  "date-fns": "^2.28.0"
58
58
  },
59
59
  "sideEffects": false
@@ -409,4 +409,80 @@ describe('verificationPrettifier', () => {
409
409
  expect(result.every(r => r.message && r.message !== r.check)).toBe(true);
410
410
  });
411
411
  });
412
+
413
+ describe('raw resolver / diagnostic failures (LC-1946)', () => {
414
+ it('maps an in-browser did:web resolution/fetch failure to friendly copy', () => {
415
+ const result = prettifyVerificationItem({
416
+ check:
417
+ 'Unable to filter proofs: Unable to resolve: Error sending HTTP request ' +
418
+ '(https://participate.community/.well-known/did.json):',
419
+ message:
420
+ 'Unable to filter proofs: Unable to resolve: Error sending HTTP request ' +
421
+ '(https://participate.community/.well-known/did.json): error sending request: ' +
422
+ 'JsValue(TypeError: Failed to fetch)',
423
+ status: VerificationStatusEnum.Failed,
424
+ });
425
+
426
+ expect(result.check).toBe('Issuer');
427
+ expect(result.message).toBe('Could not be reached');
428
+ // Raw diagnostic must not leak through message or details.
429
+ expect(result.message).not.toMatch(/fetch|resolve|http|did\.json/i);
430
+ expect(result.details).toBeUndefined();
431
+ });
432
+
433
+ it('maps a bare WASM/stack diagnostic to a generic friendly message', () => {
434
+ const result = prettifyVerificationItem({
435
+ check: 'proof',
436
+ message:
437
+ 'at https://staging.learncard.ai/assets/didkit_wasm.wasm:wasm-function[4263]:0x683',
438
+ status: VerificationStatusEnum.Failed,
439
+ });
440
+
441
+ expect(result.check).toBe('Verification');
442
+ expect(result.message).toBe('Could not be verified');
443
+ expect(result.message).not.toMatch(/wasm|assets|0x/i);
444
+ });
445
+
446
+ it('is idempotent on a mapped resolution failure', () => {
447
+ const raw = {
448
+ check: 'Unable to resolve',
449
+ message: 'Unable to resolve: Failed to fetch',
450
+ status: VerificationStatusEnum.Failed,
451
+ };
452
+ const once = prettifyVerificationItem(raw);
453
+ const twice = prettifyVerificationItem(once);
454
+ expect(twice).toEqual(once);
455
+ expect(once.message).toBe('Could not be reached');
456
+ });
457
+
458
+ it('does not touch a humanized non-resolver failure detail', () => {
459
+ const result = prettifyVerificationItem({
460
+ check: 'status',
461
+ details: 'Status: Revoked',
462
+ status: VerificationStatusEnum.Failed,
463
+ });
464
+ expect(result.details).toBe('Status: Revoked');
465
+ expect(result.check).toBe('status');
466
+ });
467
+
468
+ it('does not mislabel a humanized "Could not retrieve/resolve …" status detail as an issuer failure', () => {
469
+ const retrieve = prettifyVerificationItem({
470
+ check: 'revocation',
471
+ details: 'Could not retrieve revocation list',
472
+ status: VerificationStatusEnum.Failed,
473
+ });
474
+ expect(retrieve.check).toBe('revocation');
475
+ expect(retrieve.details).toBe('Could not retrieve revocation list');
476
+ expect(retrieve.check).not.toBe('Issuer');
477
+
478
+ const resolve = prettifyVerificationItem({
479
+ check: 'status',
480
+ details: 'Could not resolve credential status',
481
+ status: VerificationStatusEnum.Failed,
482
+ });
483
+ expect(resolve.check).toBe('status');
484
+ expect(resolve.details).toBe('Could not resolve credential status');
485
+ expect(resolve.check).not.toBe('Issuer');
486
+ });
487
+ });
412
488
  });
@@ -44,6 +44,21 @@ const ERROR_LABELS: Record<string, Label> = {
44
44
  internal_error: { check: 'Verification', message: 'Something went wrong' },
45
45
  };
46
46
 
47
+ // Raw resolver / engine diagnostics that must never reach the UI. did:web verification
48
+ // resolves the issuer's DID document via an in-browser fetch, which fails (CORS / offline)
49
+ // with messages like "Unable to resolve: Error sending HTTP request (.../.well-known/did.json)
50
+ // ... Failed to fetch ... wasm-function[...]". Map these to friendly copy instead.
51
+ //
52
+ // Kept deliberately specific to DID-document resolution/network noise. In particular we do
53
+ // NOT match a bare "could not resolve/retrieve …", because that also appears in humanized,
54
+ // already-readable status/revocation details (e.g. "Could not retrieve revocation list")
55
+ // that must pass through untouched rather than be mislabeled as an issuer failure.
56
+ const RESOLUTION_FAILURE_RE =
57
+ /unable to resolve|error sending (?:an? )?(?:http )?request|failed to fetch|well-known\/did\.json|dereferenc|network ?error/i;
58
+
59
+ // Obvious raw stack / engine internals: WASM frames, JS source locations, JS error types.
60
+ const RAW_DIAGNOSTIC_RE = /wasm-function|\bat https?:\/\/|\.js:\d+|\bJsValue\(|\bTypeError\b/i;
61
+
47
62
  const isAlreadyPrettified = (check: string | undefined): boolean => {
48
63
  if (!check) return true;
49
64
  return /^[A-Z]/.test(check) || /\s/.test(check);
@@ -118,7 +133,29 @@ export const prettifyVerificationItem = (item: VerificationItem): VerificationIt
118
133
  // (e.g. "signature_invalid: bad signature"), which the guard would
119
134
  // otherwise treat as already-prettified and skip.
120
135
  const code = resolveErrorCode(item);
121
- if (!code) return item;
136
+ if (!code) {
137
+ // No known error code. Never surface raw resolver / WASM diagnostics to the user;
138
+ // map them to stable friendly copy. Anything else (e.g. a humanized "Status:
139
+ // Revoked" detail) passes through untouched.
140
+ const raw = [item.message, item.details, item.check].filter(Boolean).join(' ');
141
+ if (RESOLUTION_FAILURE_RE.test(raw)) {
142
+ return {
143
+ ...item,
144
+ check: 'Issuer',
145
+ message: 'Could not be reached',
146
+ details: undefined,
147
+ };
148
+ }
149
+ if (RAW_DIAGNOSTIC_RE.test(raw)) {
150
+ return {
151
+ ...item,
152
+ check: 'Verification',
153
+ message: 'Could not be verified',
154
+ details: undefined,
155
+ };
156
+ }
157
+ return item;
158
+ }
122
159
  const label = ERROR_LABELS[code];
123
160
 
124
161
  // The UI renders failed rows as `message ?? details`. If Layer 1