@pdtf/schemas 3.6.0-2 → 3.6.0-21

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.
Files changed (35) hide show
  1. package/.claude/settings.local.json +17 -2
  2. package/docs/sef25-extensions-ui-spec.md +146 -0
  3. package/index.js +97 -58
  4. package/package.json +4 -2
  5. package/src/schemas/v3/combined.json +7013 -1313
  6. package/src/schemas/v3/compactSkeleton.txt +353 -11
  7. package/src/schemas/v3/overlays/baspi4.json +161 -17
  8. package/src/schemas/v3/overlays/baspi5.json +166 -19
  9. package/src/schemas/v3/overlays/extensions/jk.json +24 -6
  10. package/src/schemas/v3/overlays/extensions/pc.json +36 -0
  11. package/src/schemas/v3/overlays/extensions/ph.json +197 -0
  12. package/src/schemas/v3/overlays/extensions/sc.json +83 -0
  13. package/src/schemas/v3/overlays/fme1.json +16 -14
  14. package/src/schemas/v3/overlays/lpe1.json +60 -30
  15. package/src/schemas/v3/overlays/nts.json +68 -1
  16. package/src/schemas/v3/overlays/nts2.json +68 -1
  17. package/src/schemas/v3/overlays/ntsl.json +68 -1
  18. package/src/schemas/v3/overlays/ntsl2.json +68 -1
  19. package/src/schemas/v3/overlays/piq.json +38 -1
  20. package/src/schemas/v3/overlays/rds.json +118 -6
  21. package/src/schemas/v3/overlays/ta6.json +40 -14
  22. package/src/schemas/v3/overlays/ta6ed6.json +6436 -0
  23. package/src/schemas/v3/overlays/ta7.json +70 -6
  24. package/src/schemas/v3/overlays/ta7ed5.json +1539 -0
  25. package/src/schemas/v3/pdtf-transaction.json +4098 -615
  26. package/src/schemas/v3/skeleton.json +491 -34
  27. package/src/tests/v3/buyerCircumstances.test.js +543 -0
  28. package/src/tests/v3/extensionOverlays.test.js +436 -0
  29. package/src/tests/v3/offers.test.js +37 -40
  30. package/src/tests/v3/patternProperties.test.js +149 -30
  31. package/src/tests/v3/ta6ed6.test.js +2356 -0
  32. package/src/tests/v3/ta7ed5.test.js +567 -0
  33. package/src/utils/extractExtensionOverlays.js +104 -31
  34. package/src/utils/extractOverlay.js +87 -23
  35. package/src/utils/pathSkeleton.js +1 -1
@@ -20,7 +20,7 @@ describe("Offers object validation", () => {
20
20
  "550e8400-e29b-41d4-a716-446655440000": {
21
21
  amount: 250000,
22
22
  currency: "GBP",
23
- status: "pending",
23
+ status: "Pending",
24
24
  },
25
25
  },
26
26
  };
@@ -44,7 +44,7 @@ describe("Offers object validation", () => {
44
44
  "did:example:123456789abcdefghi": {
45
45
  amount: 300000,
46
46
  currency: "GBP",
47
- status: "accepted",
47
+ status: "Accepted",
48
48
  },
49
49
  },
50
50
  };
@@ -68,7 +68,7 @@ describe("Offers object validation", () => {
68
68
  "did:web:example.com:users:alice": {
69
69
  amount: 275000,
70
70
  currency: "GBP",
71
- status: "pending",
71
+ status: "Pending",
72
72
  },
73
73
  },
74
74
  };
@@ -92,17 +92,17 @@ describe("Offers object validation", () => {
92
92
  "550e8400-e29b-41d4-a716-446655440000": {
93
93
  amount: 250000,
94
94
  currency: "GBP",
95
- status: "pending",
95
+ status: "Pending",
96
96
  },
97
97
  "did:example:buyer123": {
98
98
  amount: 260000,
99
99
  currency: "GBP",
100
- status: "accepted",
100
+ status: "Accepted",
101
101
  },
102
102
  "offer-abc123": {
103
103
  amount: 240000,
104
104
  currency: "GBP",
105
- status: "rejected",
105
+ status: "Rejected",
106
106
  },
107
107
  },
108
108
  };
@@ -128,7 +128,7 @@ describe("Offers object validation", () => {
128
128
  "-invalid-key": {
129
129
  amount: 250000,
130
130
  currency: "GBP",
131
- status: "pending",
131
+ status: "Pending",
132
132
  },
133
133
  },
134
134
  };
@@ -153,7 +153,7 @@ describe("Offers object validation", () => {
153
153
  "invalid key with spaces": {
154
154
  amount: 250000,
155
155
  currency: "GBP",
156
- status: "pending",
156
+ status: "Pending",
157
157
  },
158
158
  },
159
159
  };
@@ -177,7 +177,7 @@ describe("Offers object validation", () => {
177
177
  "invalid@key#here": {
178
178
  amount: 250000,
179
179
  currency: "GBP",
180
- status: "pending",
180
+ status: "Pending",
181
181
  },
182
182
  },
183
183
  };
@@ -203,7 +203,7 @@ describe("Offers object validation", () => {
203
203
  "offer-1": {
204
204
  amount: 250000,
205
205
  currency: "GBP",
206
- status: "pending",
206
+ status: "Pending",
207
207
  },
208
208
  },
209
209
  };
@@ -227,7 +227,7 @@ describe("Offers object validation", () => {
227
227
  "offer-1": {
228
228
  amount: 250000,
229
229
  currency: "GBP",
230
- status: "accepted",
230
+ status: "Accepted",
231
231
  inclusions: ["curtains", "carpets", "light fixtures"],
232
232
  exclusions: ["garden furniture", "shed"],
233
233
  conditions: [
@@ -245,11 +245,11 @@ describe("Offers object validation", () => {
245
245
  test("accepts all valid status values", () => {
246
246
  const validator = getValidator(schemaId);
247
247
  const validStatuses = [
248
- "pending",
249
- "withdrawn",
250
- "rejected",
251
- "accepted",
252
- "noteOfInterest",
248
+ "Pending",
249
+ "Withdrawn",
250
+ "Rejected",
251
+ "Accepted",
252
+ "Note of Interest",
253
253
  ];
254
254
 
255
255
  validStatuses.forEach((status) => {
@@ -294,7 +294,7 @@ describe("Offers object validation", () => {
294
294
  "offer-1": {
295
295
  amount: 250000,
296
296
  currency: currency,
297
- status: "pending",
297
+ status: "Pending",
298
298
  },
299
299
  },
300
300
  };
@@ -319,7 +319,7 @@ describe("Offers object validation", () => {
319
319
  "offer-1": {
320
320
  amount: 0,
321
321
  currency: "GBP",
322
- status: "noteOfInterest",
322
+ status: "Note of Interest",
323
323
  },
324
324
  },
325
325
  };
@@ -330,7 +330,7 @@ describe("Offers object validation", () => {
330
330
  });
331
331
 
332
332
  describe("Invalid offer contents", () => {
333
- test("rejects offer missing required amount field", () => {
333
+ test("accepts offer without amount field (no required fields in schema)", () => {
334
334
  const validator = getValidator(schemaId);
335
335
  const transaction = {
336
336
  transactionId: "123e4567-e89b-12d3-a456-426614174000",
@@ -344,19 +344,16 @@ describe("Offers object validation", () => {
344
344
  offers: {
345
345
  "offer-1": {
346
346
  currency: "GBP",
347
- status: "pending",
347
+ status: "Pending",
348
348
  },
349
349
  },
350
350
  };
351
351
 
352
352
  const isValid = validator(transaction);
353
- expect(isValid).toBe(false);
354
- expect(
355
- validator.errors.some((e) => e.message.includes("required property"))
356
- ).toBe(true);
353
+ expect(isValid).toBe(true);
357
354
  });
358
355
 
359
- test("rejects offer missing required currency field", () => {
356
+ test("accepts offer without currency field (no required fields in schema)", () => {
360
357
  const validator = getValidator(schemaId);
361
358
  const transaction = {
362
359
  transactionId: "123e4567-e89b-12d3-a456-426614174000",
@@ -370,16 +367,16 @@ describe("Offers object validation", () => {
370
367
  offers: {
371
368
  "offer-1": {
372
369
  amount: 250000,
373
- status: "pending",
370
+ status: "Pending",
374
371
  },
375
372
  },
376
373
  };
377
374
 
378
375
  const isValid = validator(transaction);
379
- expect(isValid).toBe(false);
376
+ expect(isValid).toBe(true);
380
377
  });
381
378
 
382
- test("rejects offer missing required status field", () => {
379
+ test("accepts offer without status field (no required fields in schema)", () => {
383
380
  const validator = getValidator(schemaId);
384
381
  const transaction = {
385
382
  transactionId: "123e4567-e89b-12d3-a456-426614174000",
@@ -399,7 +396,7 @@ describe("Offers object validation", () => {
399
396
  };
400
397
 
401
398
  const isValid = validator(transaction);
402
- expect(isValid).toBe(false);
399
+ expect(isValid).toBe(true);
403
400
  });
404
401
 
405
402
  test("rejects offer with invalid status value", () => {
@@ -441,7 +438,7 @@ describe("Offers object validation", () => {
441
438
  "offer-1": {
442
439
  amount: -100000,
443
440
  currency: "GBP",
444
- status: "pending",
441
+ status: "Pending",
445
442
  },
446
443
  },
447
444
  };
@@ -465,7 +462,7 @@ describe("Offers object validation", () => {
465
462
  "offer-1": {
466
463
  amount: 250000,
467
464
  currency: "gb",
468
- status: "pending",
465
+ status: "Pending",
469
466
  },
470
467
  },
471
468
  };
@@ -489,7 +486,7 @@ describe("Offers object validation", () => {
489
486
  "offer-1": {
490
487
  amount: 250000,
491
488
  currency: "gbp",
492
- status: "pending",
489
+ status: "Pending",
493
490
  },
494
491
  },
495
492
  };
@@ -513,7 +510,7 @@ describe("Offers object validation", () => {
513
510
  "offer-1": {
514
511
  amount: 250000,
515
512
  currency: "GBP",
516
- status: "pending",
513
+ status: "Pending",
517
514
  inclusions: "curtains",
518
515
  },
519
516
  },
@@ -538,7 +535,7 @@ describe("Offers object validation", () => {
538
535
  "offer-1": {
539
536
  amount: 250000,
540
537
  currency: "GBP",
541
- status: "pending",
538
+ status: "Pending",
542
539
  inclusions: ["curtains", 123, "carpets"],
543
540
  },
544
541
  },
@@ -563,7 +560,7 @@ describe("Offers object validation", () => {
563
560
  "offer-1": {
564
561
  amount: 250000,
565
562
  currency: "GBP",
566
- status: "pending",
563
+ status: "Pending",
567
564
  exclusions: { item: "garden furniture" },
568
565
  },
569
566
  },
@@ -588,7 +585,7 @@ describe("Offers object validation", () => {
588
585
  "offer-1": {
589
586
  amount: 250000,
590
587
  currency: "GBP",
591
- status: "pending",
588
+ status: "Pending",
592
589
  conditions: "Subject to survey",
593
590
  },
594
591
  },
@@ -653,7 +650,7 @@ describe("Offers object validation", () => {
653
650
  "550e8400-e29b-41d4-a716-446655440000": {
654
651
  amount: 250000,
655
652
  currency: "GBP",
656
- status: "accepted",
653
+ status: "Accepted",
657
654
  },
658
655
  },
659
656
  };
@@ -678,7 +675,7 @@ describe("Offers object validation", () => {
678
675
  "did:example:buyer123": {
679
676
  amount: 260000,
680
677
  currency: "GBP",
681
- status: "accepted",
678
+ status: "Accepted",
682
679
  },
683
680
  },
684
681
  };
@@ -766,12 +763,12 @@ describe("Offers object validation", () => {
766
763
  "offer-1": {
767
764
  amount: 250000,
768
765
  currency: "GBP",
769
- status: "pending",
766
+ status: "Pending",
770
767
  },
771
768
  "offer-2": {
772
769
  amount: 260000,
773
770
  currency: "GBP",
774
- status: "accepted",
771
+ status: "Accepted",
775
772
  },
776
773
  },
777
774
  };
@@ -87,11 +87,11 @@ describe("patternProperties support in path validation", () => {
87
87
 
88
88
  expect(subschema).toBeDefined();
89
89
  expect(subschema.type).toBe("string");
90
- expect(subschema.enum).toContain("pending");
91
- expect(subschema.enum).toContain("accepted");
92
- expect(subschema.enum).toContain("rejected");
93
- expect(subschema.enum).toContain("withdrawn");
94
- expect(subschema.enum).toContain("noteOfInterest");
90
+ expect(subschema.enum).toContain("Pending");
91
+ expect(subschema.enum).toContain("Accepted");
92
+ expect(subschema.enum).toContain("Rejected");
93
+ expect(subschema.enum).toContain("Withdrawn");
94
+ expect(subschema.enum).toContain("Note of Interest");
95
95
  });
96
96
 
97
97
  test("returns correct subschema for currency pattern", () => {
@@ -132,7 +132,7 @@ describe("patternProperties support in path validation", () => {
132
132
  const validOffer = {
133
133
  amount: 500000,
134
134
  currency: "GBP",
135
- status: "pending",
135
+ status: "Pending",
136
136
  };
137
137
 
138
138
  const isValid = validator(validOffer);
@@ -146,7 +146,7 @@ describe("patternProperties support in path validation", () => {
146
146
  const validOffer = {
147
147
  amount: 300000,
148
148
  currency: "EUR",
149
- status: "accepted",
149
+ status: "Accepted",
150
150
  inclusions: ["carpets", "curtains"],
151
151
  exclusions: ["garden furniture"],
152
152
  conditions: ["Subject to survey"],
@@ -156,20 +156,17 @@ describe("patternProperties support in path validation", () => {
156
156
  expect(isValid).toBe(true);
157
157
  });
158
158
 
159
- test("validator correctly rejects invalid offer data - missing required field", () => {
159
+ test("validator accepts offer data without status (no required fields in schema)", () => {
160
160
  const path = "/offers/offer-789";
161
161
  const validator = getSubschemaValidator(path, schemaId);
162
162
 
163
- const invalidOffer = {
163
+ const offer = {
164
164
  amount: 400000,
165
165
  currency: "GBP",
166
- // missing required status field
167
166
  };
168
167
 
169
- const isValid = validator(invalidOffer);
170
- expect(isValid).toBe(false);
171
- expect(validator.errors).toBeDefined();
172
- expect(validator.errors.length).toBeGreaterThan(0);
168
+ const isValid = validator(offer);
169
+ expect(isValid).toBe(true);
173
170
  });
174
171
 
175
172
  test("validator correctly rejects invalid offer data - negative amount", () => {
@@ -179,7 +176,7 @@ describe("patternProperties support in path validation", () => {
179
176
  const invalidOffer = {
180
177
  amount: -100000,
181
178
  currency: "GBP",
182
- status: "pending",
179
+ status: "Pending",
183
180
  };
184
181
 
185
182
  const isValid = validator(invalidOffer);
@@ -225,7 +222,7 @@ describe("patternProperties support in path validation", () => {
225
222
  "/offers/offer-001": {
226
223
  amount: 500000,
227
224
  currency: "GBP",
228
- status: "pending",
225
+ status: "Pending",
229
226
  },
230
227
  },
231
228
  };
@@ -245,7 +242,7 @@ describe("patternProperties support in path validation", () => {
245
242
  "/offers/1sorzJCFHbWVlHA2tYXA": {
246
243
  amount: 750000,
247
244
  currency: "GBP",
248
- status: "accepted",
245
+ status: "Accepted",
249
246
  },
250
247
  },
251
248
  };
@@ -265,7 +262,7 @@ describe("patternProperties support in path validation", () => {
265
262
  "/offers/did:example:buyer123": {
266
263
  amount: 600000,
267
264
  currency: "EUR",
268
- status: "noteOfInterest",
265
+ status: "Note of Interest",
269
266
  },
270
267
  },
271
268
  };
@@ -301,7 +298,7 @@ describe("patternProperties support in path validation", () => {
301
298
  "/offers/offer-complete": {
302
299
  amount: 550000,
303
300
  currency: "GBP",
304
- status: "accepted",
301
+ status: "Accepted",
305
302
  inclusions: ["carpets", "curtains", "light fixtures"],
306
303
  exclusions: ["garden shed"],
307
304
  conditions: ["Subject to survey", "Subject to mortgage approval"],
@@ -313,7 +310,7 @@ describe("patternProperties support in path validation", () => {
313
310
  expect(errors).toHaveLength(0);
314
311
  });
315
312
 
316
- test("rejects claim with invalid offer data - missing required field", () => {
313
+ test("accepts claim with offer data without status (no required fields in schema)", () => {
317
314
  const claim = {
318
315
  timestamp: 1643115903108,
319
316
  verification: {
@@ -321,16 +318,15 @@ describe("patternProperties support in path validation", () => {
321
318
  time: "2025-01-15T12:00:00Z",
322
319
  },
323
320
  claims: {
324
- "/offers/offer-invalid": {
321
+ "/offers/offer-no-status": {
325
322
  amount: 500000,
326
323
  currency: "GBP",
327
- // missing required status field
328
324
  },
329
325
  },
330
326
  };
331
327
 
332
328
  const errors = validateVerifiedClaims([claim], schemaId);
333
- expect(errors.length).toBeGreaterThan(0);
329
+ expect(errors).toHaveLength(0);
334
330
  });
335
331
 
336
332
  test("rejects claim with invalid offer data - negative amount", () => {
@@ -344,7 +340,7 @@ describe("patternProperties support in path validation", () => {
344
340
  "/offers/offer-negative": {
345
341
  amount: -100000,
346
342
  currency: "GBP",
347
- status: "pending",
343
+ status: "Pending",
348
344
  },
349
345
  },
350
346
  };
@@ -384,7 +380,7 @@ describe("patternProperties support in path validation", () => {
384
380
  "/offers/offer-bad-currency": {
385
381
  amount: 500000,
386
382
  currency: "gbp", // should be uppercase
387
- status: "pending",
383
+ status: "Pending",
388
384
  },
389
385
  },
390
386
  };
@@ -405,7 +401,7 @@ describe("patternProperties support in path validation", () => {
405
401
  "/offers/offer-1": {
406
402
  amount: 500000,
407
403
  currency: "GBP",
408
- status: "pending",
404
+ status: "Pending",
409
405
  },
410
406
  },
411
407
  },
@@ -419,7 +415,7 @@ describe("patternProperties support in path validation", () => {
419
415
  "/offers/offer-2": {
420
416
  amount: 520000,
421
417
  currency: "GBP",
422
- status: "accepted",
418
+ status: "Accepted",
423
419
  },
424
420
  },
425
421
  },
@@ -440,14 +436,14 @@ describe("patternProperties support in path validation", () => {
440
436
  "/offers/offer-1": {
441
437
  amount: 500000,
442
438
  currency: "GBP",
443
- status: "pending",
439
+ status: "Pending",
444
440
  },
445
441
  "/offers/offer-2": {
446
442
  amount: 510000,
447
443
  currency: "GBP",
448
- status: "withdrawn",
444
+ status: "Withdrawn",
449
445
  },
450
- "/offers/offer-3/status": "rejected",
446
+ "/offers/offer-3/status": "Rejected",
451
447
  },
452
448
  };
453
449
 
@@ -534,6 +530,129 @@ describe("patternProperties support in path validation", () => {
534
530
  });
535
531
  });
536
532
 
533
+ describe("externalIds patternProperties paths", () => {
534
+ test("accepts claim for /externalIds with a nested key path", () => {
535
+ const claim = {
536
+ timestamp: 1643115903108,
537
+ verification: {
538
+ trust_framework: "uk_pdtf",
539
+ time: "2025-01-15T12:00:00Z",
540
+ },
541
+ claims: {
542
+ "/externalIds/nptn/selectedQuoteId": "12345577",
543
+ },
544
+ };
545
+
546
+ const errors = validateVerifiedClaims([claim], schemaId);
547
+ expect(errors).toHaveLength(0);
548
+ });
549
+
550
+ test("accepts claim for /externalIds with a simple string value", () => {
551
+ const claim = {
552
+ timestamp: 1643115903108,
553
+ verification: {
554
+ trust_framework: "uk_pdtf",
555
+ time: "2025-01-15T12:00:00Z",
556
+ },
557
+ claims: {
558
+ "/externalIds/lms": "abc-123",
559
+ },
560
+ };
561
+
562
+ const errors = validateVerifiedClaims([claim], schemaId);
563
+ expect(errors).toHaveLength(0);
564
+ });
565
+
566
+ test("accepts claim for /externalIds with a numeric value", () => {
567
+ const claim = {
568
+ timestamp: 1643115903108,
569
+ verification: {
570
+ trust_framework: "uk_pdtf",
571
+ time: "2025-01-15T12:00:00Z",
572
+ },
573
+ claims: {
574
+ "/externalIds/systemRef": 98765,
575
+ },
576
+ };
577
+
578
+ const errors = validateVerifiedClaims([claim], schemaId);
579
+ expect(errors).toHaveLength(0);
580
+ });
581
+
582
+ test("accepts claim for /externalIds with a boolean value", () => {
583
+ const claim = {
584
+ timestamp: 1643115903108,
585
+ verification: {
586
+ trust_framework: "uk_pdtf",
587
+ time: "2025-01-15T12:00:00Z",
588
+ },
589
+ claims: {
590
+ "/externalIds/isActive": true,
591
+ },
592
+ };
593
+
594
+ const errors = validateVerifiedClaims([claim], schemaId);
595
+ expect(errors).toHaveLength(0);
596
+ });
597
+
598
+ test("accepts claim for /externalIds with an object value", () => {
599
+ const claim = {
600
+ timestamp: 1643115903108,
601
+ verification: {
602
+ trust_framework: "uk_pdtf",
603
+ time: "2025-01-15T12:00:00Z",
604
+ },
605
+ claims: {
606
+ "/externalIds/nptn": {
607
+ selectedQuoteId: "12345577",
608
+ caseRef: "CASE-001",
609
+ },
610
+ },
611
+ };
612
+
613
+ const errors = validateVerifiedClaims([claim], schemaId);
614
+ expect(errors).toHaveLength(0);
615
+ });
616
+
617
+ test("accepts claim with multiple externalIds paths in same claim", () => {
618
+ const claim = {
619
+ timestamp: 1643115903108,
620
+ verification: {
621
+ trust_framework: "uk_pdtf",
622
+ time: "2025-01-15T12:00:00Z",
623
+ },
624
+ claims: {
625
+ "/externalIds/nptn/selectedQuoteId": "12345577",
626
+ "/externalIds/lms": "LMS-REF-001",
627
+ },
628
+ };
629
+
630
+ const errors = validateVerifiedClaims([claim], schemaId);
631
+ expect(errors).toHaveLength(0);
632
+ });
633
+
634
+ test("accepts claim for /externalIds root object", () => {
635
+ const claim = {
636
+ timestamp: 1643115903108,
637
+ verification: {
638
+ trust_framework: "uk_pdtf",
639
+ time: "2025-01-15T12:00:00Z",
640
+ },
641
+ claims: {
642
+ "/externalIds": {
643
+ nptn: {
644
+ selectedQuoteId: "12345577",
645
+ },
646
+ lms: "LMS-REF-001",
647
+ },
648
+ },
649
+ };
650
+
651
+ const errors = validateVerifiedClaims([claim], schemaId);
652
+ expect(errors).toHaveLength(0);
653
+ });
654
+ });
655
+
537
656
  describe("edge cases", () => {
538
657
  test("handles offer ID with dots", () => {
539
658
  const path = "/offers/offer.123.abc";