@pdtf/schemas 3.6.0-9 → 3.6.0-dev.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.
Files changed (191) hide show
  1. package/CLAUDE.md +101 -0
  2. package/README.md +57 -33
  3. package/docs/branch-release-workflow.md +138 -0
  4. package/docs/sef25-extensions-ui-spec.md +355 -0
  5. package/index.js +131 -75
  6. package/package.json +6 -2
  7. package/pensive-tesla-d0d355/CLAUDE.md +101 -0
  8. package/pensive-tesla-d0d355/LICENSE +21 -0
  9. package/pensive-tesla-d0d355/README.md +435 -0
  10. package/pensive-tesla-d0d355/docs/branch-release-workflow.md +138 -0
  11. package/pensive-tesla-d0d355/docs/sef25-extensions-ui-spec.md +355 -0
  12. package/pensive-tesla-d0d355/index.js +654 -0
  13. package/pensive-tesla-d0d355/package-lock.json +7297 -0
  14. package/pensive-tesla-d0d355/package.json +44 -0
  15. package/pensive-tesla-d0d355/src/examples/v1/exampleAddParticipantVouch.json +32 -0
  16. package/pensive-tesla-d0d355/src/examples/v1/exampleDocumentedVouch.json +40 -0
  17. package/pensive-tesla-d0d355/src/examples/v1/exampleElectronicRecord.json +30 -0
  18. package/pensive-tesla-d0d355/src/examples/v1/exampleTransaction.json +878 -0
  19. package/pensive-tesla-d0d355/src/examples/v1/exampleVouch.json +25 -0
  20. package/pensive-tesla-d0d355/src/examples/v2/exampleTransaction.json +901 -0
  21. package/pensive-tesla-d0d355/src/examples/v3/exampleAddParticipantVouch.json +38 -0
  22. package/pensive-tesla-d0d355/src/examples/v3/exampleCustomOverlay.json +55 -0
  23. package/pensive-tesla-d0d355/src/examples/v3/exampleCustomOverlay2.json +93 -0
  24. package/pensive-tesla-d0d355/src/examples/v3/exampleDocumentedVouch.json +47 -0
  25. package/pensive-tesla-d0d355/src/examples/v3/exampleElectronicRecord.json +36 -0
  26. package/pensive-tesla-d0d355/src/examples/v3/exampleTransaction.json +1038 -0
  27. package/pensive-tesla-d0d355/src/examples/v3/exampleVouch.json +32 -0
  28. package/pensive-tesla-d0d355/src/mappings/SendPropertyDetailsRequestSchema.json +1588 -0
  29. package/pensive-tesla-d0d355/src/mappings/adf.js +198 -0
  30. package/pensive-tesla-d0d355/src/schemas/v1/GeoJSON.json +1279 -0
  31. package/pensive-tesla-d0d355/src/schemas/v1/baspiNotes.html +102 -0
  32. package/pensive-tesla-d0d355/src/schemas/v1/energy-performance-certificate.json +124 -0
  33. package/pensive-tesla-d0d355/src/schemas/v1/freehold-information.json +2051 -0
  34. package/pensive-tesla-d0d355/src/schemas/v1/leasehold-information.json +2064 -0
  35. package/pensive-tesla-d0d355/src/schemas/v1/legal-information.json +1045 -0
  36. package/pensive-tesla-d0d355/src/schemas/v1/material-facts.json +2109 -0
  37. package/pensive-tesla-d0d355/src/schemas/v1/pdtf-transaction-search-results.json +40 -0
  38. package/pensive-tesla-d0d355/src/schemas/v1/pdtf-transaction.json +191 -0
  39. package/pensive-tesla-d0d355/src/schemas/v1/searches/drainage-and-water.json +261 -0
  40. package/pensive-tesla-d0d355/src/schemas/v1/searches/local-land-charges.json +69 -0
  41. package/pensive-tesla-d0d355/src/schemas/v1/searches/local-searches-required.json +857 -0
  42. package/pensive-tesla-d0d355/src/schemas/v1/searches.json +237 -0
  43. package/pensive-tesla-d0d355/src/schemas/v1/title-deed.json +841 -0
  44. package/pensive-tesla-d0d355/src/schemas/v2/baspiNotes.html +102 -0
  45. package/pensive-tesla-d0d355/src/schemas/v2/combined.json +37731 -0
  46. package/pensive-tesla-d0d355/src/schemas/v2/overlays/baspi.json +8132 -0
  47. package/pensive-tesla-d0d355/src/schemas/v2/overlays/con29DW.json +705 -0
  48. package/pensive-tesla-d0d355/src/schemas/v2/overlays/con29R.json +2956 -0
  49. package/pensive-tesla-d0d355/src/schemas/v2/overlays/fme1.json +1259 -0
  50. package/pensive-tesla-d0d355/src/schemas/v2/overlays/llc1.json +25 -0
  51. package/pensive-tesla-d0d355/src/schemas/v2/overlays/lpe1.json +2171 -0
  52. package/pensive-tesla-d0d355/src/schemas/v2/overlays/nts.json +326 -0
  53. package/pensive-tesla-d0d355/src/schemas/v2/overlays/oc1.json +2799 -0
  54. package/pensive-tesla-d0d355/src/schemas/v2/overlays/piq.json +3419 -0
  55. package/pensive-tesla-d0d355/src/schemas/v2/overlays/rds.json +3736 -0
  56. package/pensive-tesla-d0d355/src/schemas/v2/overlays/ta10.json +5306 -0
  57. package/pensive-tesla-d0d355/src/schemas/v2/overlays/ta6.json +4533 -0
  58. package/pensive-tesla-d0d355/src/schemas/v2/overlays/ta7.json +1045 -0
  59. package/pensive-tesla-d0d355/src/schemas/v2/pdtf-transaction.json +34351 -0
  60. package/pensive-tesla-d0d355/src/schemas/v2/skeleton.json +3973 -0
  61. package/pensive-tesla-d0d355/src/schemas/v3/combined.json +48469 -0
  62. package/pensive-tesla-d0d355/src/schemas/v3/compactSkeleton.txt +4519 -0
  63. package/pensive-tesla-d0d355/src/schemas/v3/overlays/README.md +299 -0
  64. package/pensive-tesla-d0d355/src/schemas/v3/overlays/baspi4.json +8025 -0
  65. package/pensive-tesla-d0d355/src/schemas/v3/overlays/baspi5.json +8175 -0
  66. package/pensive-tesla-d0d355/src/schemas/v3/overlays/con29DW.json +716 -0
  67. package/pensive-tesla-d0d355/src/schemas/v3/overlays/con29R.json +2981 -0
  68. package/pensive-tesla-d0d355/src/schemas/v3/overlays/extensions/ac.json +333 -0
  69. package/pensive-tesla-d0d355/src/schemas/v3/overlays/extensions/as.json +74 -0
  70. package/pensive-tesla-d0d355/src/schemas/v3/overlays/extensions/dk.json +32 -0
  71. package/pensive-tesla-d0d355/src/schemas/v3/overlays/extensions/dr.json +73 -0
  72. package/pensive-tesla-d0d355/src/schemas/v3/overlays/extensions/er.json +85 -0
  73. package/pensive-tesla-d0d355/src/schemas/v3/overlays/extensions/fd.json +75 -0
  74. package/pensive-tesla-d0d355/src/schemas/v3/overlays/extensions/hi.json +51 -0
  75. package/pensive-tesla-d0d355/src/schemas/v3/overlays/extensions/hs.json +73 -0
  76. package/pensive-tesla-d0d355/src/schemas/v3/overlays/extensions/ic.json +59 -0
  77. package/pensive-tesla-d0d355/src/schemas/v3/overlays/extensions/jk.json +96 -0
  78. package/pensive-tesla-d0d355/src/schemas/v3/overlays/extensions/la.json +85 -0
  79. package/pensive-tesla-d0d355/src/schemas/v3/overlays/extensions/lc.json +75 -0
  80. package/pensive-tesla-d0d355/src/schemas/v3/overlays/extensions/ma.json +90 -0
  81. package/pensive-tesla-d0d355/src/schemas/v3/overlays/extensions/mc.json +38 -0
  82. package/pensive-tesla-d0d355/src/schemas/v3/overlays/extensions/mi.json +69 -0
  83. package/pensive-tesla-d0d355/src/schemas/v3/overlays/extensions/nd.json +43 -0
  84. package/pensive-tesla-d0d355/src/schemas/v3/overlays/extensions/oa.json +24 -0
  85. package/pensive-tesla-d0d355/src/schemas/v3/overlays/extensions/oc.json +57 -0
  86. package/pensive-tesla-d0d355/src/schemas/v3/overlays/extensions/pc.json +37 -0
  87. package/pensive-tesla-d0d355/src/schemas/v3/overlays/extensions/ph.json +194 -0
  88. package/pensive-tesla-d0d355/src/schemas/v3/overlays/extensions/rw.json +68 -0
  89. package/pensive-tesla-d0d355/src/schemas/v3/overlays/extensions/sb.json +73 -0
  90. package/pensive-tesla-d0d355/src/schemas/v3/overlays/extensions/sc.json +129 -0
  91. package/pensive-tesla-d0d355/src/schemas/v3/overlays/extensions/sd.json +63 -0
  92. package/pensive-tesla-d0d355/src/schemas/v3/overlays/extensions/sf.json +74 -0
  93. package/pensive-tesla-d0d355/src/schemas/v3/overlays/extensions/sl.json +68 -0
  94. package/pensive-tesla-d0d355/src/schemas/v3/overlays/extensions/ta.json +252 -0
  95. package/pensive-tesla-d0d355/src/schemas/v3/overlays/extensions/tf.json +103 -0
  96. package/pensive-tesla-d0d355/src/schemas/v3/overlays/extensions/tr.json +73 -0
  97. package/pensive-tesla-d0d355/src/schemas/v3/overlays/extensions/wg.json +350 -0
  98. package/pensive-tesla-d0d355/src/schemas/v3/overlays/fme1.json +1266 -0
  99. package/pensive-tesla-d0d355/src/schemas/v3/overlays/llc1.json +25 -0
  100. package/pensive-tesla-d0d355/src/schemas/v3/overlays/lpe1.json +2199 -0
  101. package/pensive-tesla-d0d355/src/schemas/v3/overlays/nts.json +2157 -0
  102. package/pensive-tesla-d0d355/src/schemas/v3/overlays/nts2.json +2742 -0
  103. package/pensive-tesla-d0d355/src/schemas/v3/overlays/ntsl.json +1813 -0
  104. package/pensive-tesla-d0d355/src/schemas/v3/overlays/ntsl2.json +2217 -0
  105. package/pensive-tesla-d0d355/src/schemas/v3/overlays/oc1.json +2817 -0
  106. package/pensive-tesla-d0d355/src/schemas/v3/overlays/piq.json +3322 -0
  107. package/pensive-tesla-d0d355/src/schemas/v3/overlays/rds.json +3665 -0
  108. package/pensive-tesla-d0d355/src/schemas/v3/overlays/sef25.json +1639 -0
  109. package/pensive-tesla-d0d355/src/schemas/v3/overlays/sr24.json +38 -0
  110. package/pensive-tesla-d0d355/src/schemas/v3/overlays/ta10.json +4654 -0
  111. package/pensive-tesla-d0d355/src/schemas/v3/overlays/ta6.json +4568 -0
  112. package/pensive-tesla-d0d355/src/schemas/v3/overlays/ta6ed6.json +6534 -0
  113. package/pensive-tesla-d0d355/src/schemas/v3/overlays/ta7.json +1537 -0
  114. package/pensive-tesla-d0d355/src/schemas/v3/overlays/ta7ed5.json +1551 -0
  115. package/pensive-tesla-d0d355/src/schemas/v3/pdtf-transaction.json +41573 -0
  116. package/pensive-tesla-d0d355/src/schemas/v3/skeleton.json +5767 -0
  117. package/pensive-tesla-d0d355/src/schemas/verifiedClaims/README.md +21 -0
  118. package/pensive-tesla-d0d355/src/schemas/verifiedClaims/pdtf-verified-claims.json +447 -0
  119. package/pensive-tesla-d0d355/src/utils/compactSkeleton.js +84 -0
  120. package/pensive-tesla-d0d355/src/utils/countTokens.js +67 -0
  121. package/pensive-tesla-d0d355/src/utils/extractExtensionOverlays.js +624 -0
  122. package/pensive-tesla-d0d355/src/utils/extractOverlay.js +381 -0
  123. package/pensive-tesla-d0d355/src/utils/extractProperties.js +44 -0
  124. package/pensive-tesla-d0d355/src/utils/listPathTitles.js +36 -0
  125. package/pensive-tesla-d0d355/src/utils/minimalSkeleton.js +85 -0
  126. package/pensive-tesla-d0d355/src/utils/pathList.csv +903 -0
  127. package/pensive-tesla-d0d355/src/utils/pathSkeleton.js +181 -0
  128. package/src/examples/v3/exampleTransaction.json +3 -3
  129. package/src/schemas/v3/combined.json +8047 -1982
  130. package/src/schemas/v3/compactSkeleton.txt +379 -5
  131. package/src/schemas/v3/overlays/baspi4.json +164 -21
  132. package/src/schemas/v3/overlays/baspi5.json +169 -23
  133. package/src/schemas/v3/overlays/extensions/ac.json +333 -0
  134. package/src/schemas/v3/overlays/extensions/as.json +4 -2
  135. package/src/schemas/v3/overlays/extensions/dk.json +32 -0
  136. package/src/schemas/v3/overlays/extensions/dr.json +9 -9
  137. package/src/schemas/v3/overlays/extensions/er.json +4 -2
  138. package/src/schemas/v3/overlays/extensions/fd.json +6 -3
  139. package/src/schemas/v3/overlays/extensions/hi.json +6 -3
  140. package/src/schemas/v3/overlays/extensions/hs.json +4 -2
  141. package/src/schemas/v3/overlays/extensions/ic.json +59 -0
  142. package/src/schemas/v3/overlays/extensions/jk.json +28 -8
  143. package/src/schemas/v3/overlays/extensions/la.json +4 -2
  144. package/src/schemas/v3/overlays/extensions/lc.json +75 -0
  145. package/src/schemas/v3/overlays/extensions/ma.json +6 -3
  146. package/src/schemas/v3/overlays/extensions/mc.json +4 -2
  147. package/src/schemas/v3/overlays/extensions/mi.json +69 -0
  148. package/src/schemas/v3/overlays/extensions/nd.json +43 -0
  149. package/src/schemas/v3/overlays/extensions/oa.json +4 -2
  150. package/src/schemas/v3/overlays/extensions/oc.json +4 -2
  151. package/src/schemas/v3/overlays/extensions/pc.json +37 -0
  152. package/src/schemas/v3/overlays/extensions/ph.json +194 -0
  153. package/src/schemas/v3/overlays/extensions/rw.json +68 -0
  154. package/src/schemas/v3/overlays/extensions/sb.json +4 -2
  155. package/src/schemas/v3/overlays/extensions/sc.json +129 -0
  156. package/src/schemas/v3/overlays/extensions/sd.json +63 -0
  157. package/src/schemas/v3/overlays/extensions/sf.json +4 -2
  158. package/src/schemas/v3/overlays/extensions/sl.json +4 -2
  159. package/src/schemas/v3/overlays/extensions/ta.json +204 -0
  160. package/src/schemas/v3/overlays/extensions/tf.json +6 -3
  161. package/src/schemas/v3/overlays/extensions/tr.json +73 -0
  162. package/src/schemas/v3/overlays/extensions/wg.json +350 -0
  163. package/src/schemas/v3/overlays/fme1.json +16 -14
  164. package/src/schemas/v3/overlays/lpe1.json +60 -30
  165. package/src/schemas/v3/overlays/nts.json +68 -1
  166. package/src/schemas/v3/overlays/nts2.json +68 -4
  167. package/src/schemas/v3/overlays/ntsl.json +68 -1
  168. package/src/schemas/v3/overlays/ntsl2.json +68 -4
  169. package/src/schemas/v3/overlays/piq.json +39 -4
  170. package/src/schemas/v3/overlays/rds.json +117 -8
  171. package/src/schemas/v3/overlays/sef25.json +1639 -0
  172. package/src/schemas/v3/overlays/ta6.json +46 -16
  173. package/src/schemas/v3/overlays/ta6ed6.json +6534 -0
  174. package/src/schemas/v3/overlays/ta7.json +70 -6
  175. package/src/schemas/v3/overlays/ta7ed5.json +1551 -0
  176. package/src/schemas/v3/pdtf-transaction.json +4565 -1021
  177. package/src/schemas/v3/skeleton.json +510 -38
  178. package/src/utils/extractExtensionOverlays.js +293 -31
  179. package/src/utils/extractOverlay.js +94 -27
  180. package/src/utils/pathSkeleton.js +1 -1
  181. package/.claude/settings.local.json +0 -42
  182. package/.github/workflows/pages.yml +0 -50
  183. package/CNAME +0 -1
  184. package/_config.yml +0 -1
  185. package/src/tests/v3/caching.test.js +0 -591
  186. package/src/tests/v3/enquiries.test.js +0 -1096
  187. package/src/tests/v3/extensionOverlays.test.js +0 -525
  188. package/src/tests/v3/offers.test.js +0 -823
  189. package/src/tests/v3/patternProperties.test.js +0 -688
  190. package/src/tests/v3/transactionSchema.test.js +0 -615
  191. package/src/tests/v3/verifiedClaimsValidator.test.js +0 -195
package/index.js CHANGED
@@ -18,7 +18,7 @@ const cacheStats = {
18
18
  hits: 0,
19
19
  misses: 0,
20
20
  totalQueries: 0,
21
- cacheStartTime: Date.now()
21
+ cacheStartTime: Date.now(),
22
22
  };
23
23
 
24
24
  const verifiedClaimsSchema = require("./src/schemas/verifiedClaims/pdtf-verified-claims.json");
@@ -52,6 +52,24 @@ const extensionOverlays = {
52
52
 
53
53
  // Transaction Extensions
54
54
  oc: require("./src/schemas/v3/overlays/extensions/oc.json"),
55
+
56
+ // SEF25 Extensions
57
+ sc: require("./src/schemas/v3/overlays/extensions/sc.json"),
58
+ pc: require("./src/schemas/v3/overlays/extensions/pc.json"),
59
+ ph: require("./src/schemas/v3/overlays/extensions/ph.json"),
60
+ dk: require("./src/schemas/v3/overlays/extensions/dk.json"),
61
+ rw: require("./src/schemas/v3/overlays/extensions/rw.json"),
62
+ sd: require("./src/schemas/v3/overlays/extensions/sd.json"),
63
+ lc: require("./src/schemas/v3/overlays/extensions/lc.json"),
64
+ wg: require("./src/schemas/v3/overlays/extensions/wg.json"),
65
+ ic: require("./src/schemas/v3/overlays/extensions/ic.json"),
66
+ nd: require("./src/schemas/v3/overlays/extensions/nd.json"),
67
+ mi: require("./src/schemas/v3/overlays/extensions/mi.json"),
68
+ tr: require("./src/schemas/v3/overlays/extensions/tr.json"),
69
+ ac: require("./src/schemas/v3/overlays/extensions/ac.json"),
70
+
71
+ // Compatibility Extensions
72
+ ta: require("./src/schemas/v3/overlays/extensions/ta.json"),
55
73
  };
56
74
 
57
75
  const overlaysMap = {
@@ -75,7 +93,9 @@ const overlaysMap = {
75
93
  baspiV4: require("./src/schemas/v3/overlays/baspi4.json"),
76
94
  baspiV5: require("./src/schemas/v3/overlays/baspi5.json"),
77
95
  ta6ed4: require("./src/schemas/v3/overlays/ta6.json"),
96
+ ta6ed6: require("./src/schemas/v3/overlays/ta6ed6.json"),
78
97
  ta7ed3: require("./src/schemas/v3/overlays/ta7.json"),
98
+ ta7ed5: require("./src/schemas/v3/overlays/ta7ed5.json"),
79
99
  ta10ed3: require("./src/schemas/v3/overlays/ta10.json"),
80
100
  lpe1ed4: require("./src/schemas/v3/overlays/lpe1.json"),
81
101
  fme1ed2: require("./src/schemas/v3/overlays/fme1.json"),
@@ -117,21 +137,27 @@ const arrayMerge = (target, source, options) => {
117
137
  // Detect if this is a required array by checking the parent key
118
138
  // Required arrays are property names (camelCase, no spaces)
119
139
  // Enum arrays are values (can contain spaces, capitals, etc.)
120
- const isRequiredArray = target.every(
121
- (item) =>
122
- typeof item === "string" &&
123
- item.length > 0 &&
124
- // Required field names are typically camelCase without spaces
125
- !item.includes(" ") &&
126
- // First character is usually lowercase for field names
127
- (item[0] === item[0].toLowerCase() || item.startsWith("is") || item.startsWith("has"))
128
- ) && source.every(
129
- (item) =>
130
- typeof item === "string" &&
131
- item.length > 0 &&
132
- !item.includes(" ") &&
133
- (item[0] === item[0].toLowerCase() || item.startsWith("is") || item.startsWith("has"))
134
- );
140
+ const isRequiredArray =
141
+ target.every(
142
+ (item) =>
143
+ typeof item === "string" &&
144
+ item.length > 0 &&
145
+ // Required field names are typically camelCase without spaces
146
+ !item.includes(" ") &&
147
+ // First character is usually lowercase for field names
148
+ (item[0] === item[0].toLowerCase() ||
149
+ item.startsWith("is") ||
150
+ item.startsWith("has")),
151
+ ) &&
152
+ source.every(
153
+ (item) =>
154
+ typeof item === "string" &&
155
+ item.length > 0 &&
156
+ !item.includes(" ") &&
157
+ (item[0] === item[0].toLowerCase() ||
158
+ item.startsWith("is") ||
159
+ item.startsWith("has")),
160
+ );
135
161
 
136
162
  if (isRequiredArray) {
137
163
  // Combine required arrays
@@ -176,7 +202,7 @@ const arrayMerge = (target, source, options) => {
176
202
 
177
203
  const getTransactionSchema = (
178
204
  schemaId = "https://trust.propdata.org.uk/schemas/v3/pdtf-transaction.json",
179
- overlays
205
+ overlays,
180
206
  ) => {
181
207
  const sourceSchema = transactionSchemas[schemaId];
182
208
  if (!overlays || overlays.length < 1) return sourceSchema;
@@ -184,7 +210,7 @@ const getTransactionSchema = (
184
210
  let mergedSchema = JSON.parse(JSON.stringify(sourceSchema)); // Deep copy
185
211
  overlays.forEach((overlay) => {
186
212
  const overlaySchema = JSON.parse(
187
- JSON.stringify(overlaysMap[schemaId][overlay] || {})
213
+ JSON.stringify(overlaysMap[schemaId][overlay] || {}),
188
214
  ); // Deep copy
189
215
  mergedSchema = merge(mergedSchema, overlaySchema, { arrayMerge });
190
216
  });
@@ -211,7 +237,7 @@ const getCachedSchemaData = (path, schemaId, overlays) => {
211
237
  const sourceSchema = getTransactionSchema(schemaId, overlays);
212
238
  const pathArray = path.split("/").slice(1);
213
239
  let subSchema = sourceSchema;
214
-
240
+
215
241
  if (pathArray.length >= 1) {
216
242
  subSchema = pathArray.reduce((schema, pathElement) => {
217
243
  if (!schema) return undefined;
@@ -233,33 +259,52 @@ const getCachedSchemaData = (path, schemaId, overlays) => {
233
259
  }
234
260
  } catch (e) {
235
261
  // Invalid regex pattern in schema - skip it
236
- console.warn(`Invalid regex pattern in patternProperties: ${pattern}`, e);
262
+ console.warn(
263
+ `Invalid regex pattern in patternProperties: ${pattern}`,
264
+ e,
265
+ );
237
266
  }
238
267
  }
239
268
  }
240
269
 
241
- // Check oneOf discriminators
270
+ // Check oneOf discriminators (recursively for nested oneOf)
242
271
  if (oneOf) {
243
- let matchingProperty;
244
- oneOf.forEach((aOneOf) => {
245
- if (aOneOf.type === "array" && !Number.isNaN(pathElement)) {
246
- matchingProperty = aOneOf.items;
247
- } else if (aOneOf.properties?.[pathElement]) {
248
- matchingProperty = aOneOf.properties?.[pathElement];
249
- } else if (aOneOf.patternProperties) {
250
- for (const pattern in aOneOf.patternProperties) {
251
- try {
252
- const regex = new RegExp(pattern);
253
- if (regex.test(pathElement)) {
254
- matchingProperty = aOneOf.patternProperties[pattern];
255
- break;
272
+ const findInOneOf = (branches) => {
273
+ let arrayFallback;
274
+ for (const branch of branches) {
275
+ if (!branch) continue;
276
+ // Track array matches as fallback (prefer property matches)
277
+ if (
278
+ branch.type === "array" &&
279
+ !Number.isNaN(pathElement) &&
280
+ !arrayFallback
281
+ ) {
282
+ arrayFallback = branch.items;
283
+ }
284
+ if (branch.properties?.[pathElement]) {
285
+ return branch.properties[pathElement];
286
+ }
287
+ if (branch.patternProperties) {
288
+ for (const pattern in branch.patternProperties) {
289
+ try {
290
+ const regex = new RegExp(pattern);
291
+ if (regex.test(pathElement)) {
292
+ return branch.patternProperties[pattern];
293
+ }
294
+ } catch (e) {
295
+ // Invalid regex pattern - skip
256
296
  }
257
- } catch (e) {
258
- // Invalid regex pattern - skip
259
297
  }
260
298
  }
299
+ // Recurse into nested oneOf branches
300
+ if (branch.oneOf) {
301
+ const nested = findInOneOf(branch.oneOf);
302
+ if (nested) return nested;
303
+ }
261
304
  }
262
- });
305
+ return arrayFallback;
306
+ };
307
+ const matchingProperty = findInOneOf(oneOf);
263
308
  if (matchingProperty) return matchingProperty;
264
309
  }
265
310
 
@@ -270,7 +315,7 @@ const getCachedSchemaData = (path, schemaId, overlays) => {
270
315
  // Add schema to AJV and get validator
271
316
  // Only add valid schemas to AJV
272
317
  let validator;
273
- if (subSchema && typeof subSchema === 'object') {
318
+ if (subSchema && typeof subSchema === "object") {
274
319
  // Check if schema already exists in AJV to avoid duplicates
275
320
  validator = ajv.getSchema(cacheKey);
276
321
  if (!validator) {
@@ -287,7 +332,7 @@ const getCachedSchemaData = (path, schemaId, overlays) => {
287
332
  subSchema,
288
333
  validator,
289
334
  cacheKey,
290
- createdAt: Date.now()
335
+ createdAt: Date.now(),
291
336
  };
292
337
  schemaCache.set(cacheKey, cached);
293
338
  } else {
@@ -353,13 +398,13 @@ const getTitleAtPath = (schema, path, rootPath = path) => {
353
398
  if (oneOfs) {
354
399
  // only single dependency discriminator, oneOf keyword is supported
355
400
  const matchingOneOf = oneOfs.find(
356
- (oneOf) => oneOf["properties"][propertyName]
401
+ (oneOf) => oneOf["properties"][propertyName],
357
402
  );
358
403
  if (matchingOneOf)
359
404
  return getTitleAtPath(
360
405
  matchingOneOf["properties"][propertyName],
361
406
  subPath,
362
- rootPath
407
+ rootPath,
363
408
  );
364
409
  }
365
410
  };
@@ -392,7 +437,7 @@ const validateVerifiedClaims = (verifiedClaims, schemaId, overlays) => {
392
437
  }
393
438
  } else {
394
439
  validationErrorsArr.push(
395
- `Path ${path} is not a valid PDTF schema path`
440
+ `Path ${path} is not a valid PDTF schema path`,
396
441
  );
397
442
  }
398
443
  }
@@ -404,8 +449,11 @@ const validateVerifiedClaims = (verifiedClaims, schemaId, overlays) => {
404
449
  // Cache management functions
405
450
  const getCacheStats = () => {
406
451
  const runtime = Date.now() - cacheStats.cacheStartTime;
407
- const hitRate = cacheStats.totalQueries > 0 ? (cacheStats.hits / cacheStats.totalQueries * 100) : 0;
408
-
452
+ const hitRate =
453
+ cacheStats.totalQueries > 0
454
+ ? (cacheStats.hits / cacheStats.totalQueries) * 100
455
+ : 0;
456
+
409
457
  return {
410
458
  totalEntries: schemaCache.size,
411
459
  hits: cacheStats.hits,
@@ -417,8 +465,8 @@ const getCacheStats = () => {
417
465
  memoryUsage: {
418
466
  entriesCount: schemaCache.size,
419
467
  // Rough estimate of memory usage per entry
420
- estimatedSizeKB: Math.round((schemaCache.size * 2) / 1024 * 100) / 100 // Rough estimate
421
- }
468
+ estimatedSizeKB: Math.round(((schemaCache.size * 2) / 1024) * 100) / 100, // Rough estimate
469
+ },
422
470
  };
423
471
  };
424
472
 
@@ -428,15 +476,17 @@ const getDetailedCacheStats = () => {
428
476
  key,
429
477
  createdAt: value.createdAt,
430
478
  age: Date.now() - value.createdAt,
431
- hasValidator: typeof value.validator === 'function',
432
- hasSubSchema: value.subSchema !== undefined
479
+ hasValidator: typeof value.validator === "function",
480
+ hasSubSchema: value.subSchema !== undefined,
433
481
  }));
434
482
 
435
483
  return {
436
484
  ...baseStats,
437
485
  entries: entries.sort((a, b) => b.createdAt - a.createdAt), // Most recent first
438
- oldestEntry: entries.length > 0 ? Math.max(...entries.map(e => e.age)) : 0,
439
- newestEntry: entries.length > 0 ? Math.min(...entries.map(e => e.age)) : 0
486
+ oldestEntry:
487
+ entries.length > 0 ? Math.max(...entries.map((e) => e.age)) : 0,
488
+ newestEntry:
489
+ entries.length > 0 ? Math.min(...entries.map((e) => e.age)) : 0,
440
490
  };
441
491
  };
442
492
 
@@ -449,17 +499,17 @@ const clearSchemaCache = (pattern) => {
449
499
  keysToDelete.push(key);
450
500
  }
451
501
  });
452
- keysToDelete.forEach(key => schemaCache.delete(key));
453
-
502
+ keysToDelete.forEach((key) => schemaCache.delete(key));
503
+
454
504
  // Also remove matching schemas from AJV
455
- keysToDelete.forEach(key => {
505
+ keysToDelete.forEach((key) => {
456
506
  try {
457
507
  ajv.removeSchema(key);
458
508
  } catch (e) {
459
509
  // Schema might not exist in AJV, ignore
460
510
  }
461
511
  });
462
-
512
+
463
513
  return keysToDelete.length;
464
514
  } else {
465
515
  // Clear all cache
@@ -470,20 +520,24 @@ const clearSchemaCache = (pattern) => {
470
520
  cacheStats.misses = 0;
471
521
  cacheStats.totalQueries = 0;
472
522
  cacheStats.cacheStartTime = Date.now();
473
-
523
+
474
524
  // Also clear AJV's internal cache to prevent duplicates
475
525
  ajv.removeSchema();
476
-
526
+
477
527
  return entriesCleared;
478
528
  }
479
529
  };
480
530
 
481
- const warmupCache = (paths, schemaId = "https://trust.propdata.org.uk/schemas/v3/pdtf-transaction.json", overlaysList = []) => {
531
+ const warmupCache = (
532
+ paths,
533
+ schemaId = "https://trust.propdata.org.uk/schemas/v3/pdtf-transaction.json",
534
+ overlaysList = [],
535
+ ) => {
482
536
  const warmupStats = {
483
537
  totalAttempted: 0,
484
538
  successful: 0,
485
539
  failed: 0,
486
- errors: []
540
+ errors: [],
487
541
  };
488
542
 
489
543
  // Default common paths if none provided
@@ -495,7 +549,7 @@ const warmupCache = (paths, schemaId = "https://trust.propdata.org.uk/schemas/v3
495
549
  "/propertyPack/valuations",
496
550
  "/propertyPack/waterAndDrainage",
497
551
  "/propertyPack/ownership",
498
- "/propertyPack/notices"
552
+ "/propertyPack/notices",
499
553
  ];
500
554
 
501
555
  // Default common overlays if none provided
@@ -504,14 +558,15 @@ const warmupCache = (paths, schemaId = "https://trust.propdata.org.uk/schemas/v3
504
558
  ["baspiV5"],
505
559
  ["ta6ed4"],
506
560
  ["nts2023"],
507
- ["baspiV5", "ta6ed4"]
561
+ ["baspiV5", "ta6ed4"],
508
562
  ];
509
563
 
510
564
  const pathsToWarm = paths || defaultPaths;
511
- const overlaysToWarm = overlaysList.length > 0 ? overlaysList : defaultOverlays;
565
+ const overlaysToWarm =
566
+ overlaysList.length > 0 ? overlaysList : defaultOverlays;
512
567
 
513
- pathsToWarm.forEach(path => {
514
- overlaysToWarm.forEach(overlays => {
568
+ pathsToWarm.forEach((path) => {
569
+ overlaysToWarm.forEach((overlays) => {
515
570
  warmupStats.totalAttempted++;
516
571
  try {
517
572
  // This will populate the cache
@@ -523,7 +578,7 @@ const warmupCache = (paths, schemaId = "https://trust.propdata.org.uk/schemas/v3
523
578
  warmupStats.errors.push({
524
579
  path,
525
580
  overlays,
526
- error: error.message
581
+ error: error.message,
527
582
  });
528
583
  }
529
584
  });
@@ -535,14 +590,14 @@ const warmupCache = (paths, schemaId = "https://trust.propdata.org.uk/schemas/v3
535
590
  const pruneCacheByAge = (maxAgeMs) => {
536
591
  const now = Date.now();
537
592
  const keysToDelete = [];
538
-
593
+
539
594
  schemaCache.forEach((value, key) => {
540
595
  if (now - value.createdAt > maxAgeMs) {
541
596
  keysToDelete.push(key);
542
597
  }
543
598
  });
544
-
545
- keysToDelete.forEach(key => {
599
+
600
+ keysToDelete.forEach((key) => {
546
601
  schemaCache.delete(key);
547
602
  try {
548
603
  ajv.removeSchema(key);
@@ -550,21 +605,22 @@ const pruneCacheByAge = (maxAgeMs) => {
550
605
  // Schema might not exist in AJV, ignore
551
606
  }
552
607
  });
553
-
608
+
554
609
  return keysToDelete.length;
555
610
  };
556
611
 
557
612
  const setCacheMaxSize = (maxSize) => {
558
613
  if (schemaCache.size <= maxSize) return 0;
559
-
614
+
560
615
  // Get entries sorted by creation time (oldest first)
561
- const entries = Array.from(schemaCache.entries())
562
- .sort((a, b) => a[1].createdAt - b[1].createdAt);
563
-
616
+ const entries = Array.from(schemaCache.entries()).sort(
617
+ (a, b) => a[1].createdAt - b[1].createdAt,
618
+ );
619
+
564
620
  const toRemove = schemaCache.size - maxSize;
565
621
  const keysToDelete = entries.slice(0, toRemove).map(([key]) => key);
566
-
567
- keysToDelete.forEach(key => {
622
+
623
+ keysToDelete.forEach((key) => {
568
624
  schemaCache.delete(key);
569
625
  try {
570
626
  ajv.removeSchema(key);
@@ -572,7 +628,7 @@ const setCacheMaxSize = (maxSize) => {
572
628
  // Schema might not exist in AJV, ignore
573
629
  }
574
630
  });
575
-
631
+
576
632
  return keysToDelete.length;
577
633
  };
578
634
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pdtf/schemas",
3
- "version": "3.6.0-9",
3
+ "version": "3.6.0-dev.3",
4
4
  "description": "Property Data Trust Framework Schemas and Utilities",
5
5
  "main": "index.js",
6
6
  "directories": {
@@ -9,7 +9,11 @@
9
9
  "scripts": {
10
10
  "test": "jest",
11
11
  "test:watch": "jest --watch",
12
- "extract-overlays": "cd src/utils && node extractOverlay.js"
12
+ "extract-overlays": "cd src/utils && node extractOverlay.js",
13
+ "extract-extension-overlays": "cd src/utils && node extractExtensionOverlays.js",
14
+ "publish:dev": "npm publish --tag dev",
15
+ "publish:next": "npm publish --tag next",
16
+ "publish:latest": "npm publish --tag latest"
13
17
  },
14
18
  "repository": {
15
19
  "type": "git",
@@ -0,0 +1,101 @@
1
+ # CLAUDE.md
2
+
3
+ This file provides guidance for AI coding agents when working with code in this repository.
4
+
5
+ ## Overview
6
+
7
+ This repository contains the Property Data Trust Framework (PDTF) schemas - JSON Schema-based definitions for digital residential property data exchange in England and Wales. The framework enables frictionless exchange of property data between software products and services.
8
+
9
+ ## Commands
10
+
11
+ ### Testing
12
+ ```bash
13
+ npm test # Run all tests
14
+ npm run test:watch # Run tests in watch mode
15
+ ```
16
+
17
+ ### Development
18
+ ```bash
19
+ npm install # Install dependencies
20
+ npm run extract-overlays # Regenerate v3 schemas and overlays from combined.json
21
+ npm run extract-extension-overlays # Regenerate v3 extension overlays from combined.json
22
+ npm run publish:dev # Publish the dev branch package to npm tag dev
23
+ npm run publish:next # Publish the next branch package to npm tag next
24
+ npm run publish:latest # Publish the main branch package to npm tag latest
25
+ ```
26
+
27
+ **Important**: `src/schemas/v3/combined.json` is the source of truth for v3 schema changes. Never edit generated schema artifacts directly; there are no exceptions. Utility/source files may be edited normally. After changing `combined.json`, always run both overlay extraction commands so derived files stay in sync.
28
+
29
+ ## Architecture
30
+
31
+ ### Schema System
32
+ The PDTF uses a flexible overlay system where a base transaction schema can be extended with form-specific overlays:
33
+
34
+ - **Base Schema**: Core transaction schema at `/src/schemas/v3/pdtf-transaction.json`
35
+ - **Overlays**: Form-specific extensions in `/src/schemas/v3/overlays/` (e.g., baspi5.json, ta6.json)
36
+ - **Merging**: Overlays are merged with the base schema using deepmerge with custom merge strategies
37
+ - **Generated Files**: `pdtf-transaction.json`, `skeleton.json`, `compactSkeleton.txt`, `src/schemas/v3/overlays/*.json`, and `src/schemas/v3/overlays/extensions/*.json` are generated from `combined.json`
38
+
39
+ ### Key Components
40
+
41
+ 1. **index.js**: Main module exposing schema utilities
42
+ - `getTransactionSchema(schemaId, overlays)`: Merges base schema with overlays
43
+ - `getValidator(schemaId, overlays)`: Returns AJV validator for schema
44
+ - `validateVerifiedClaims()`: Validates verified claims structure
45
+
46
+ 2. **Schema Versions**: Currently on v3, with v2 still supported
47
+ - Use `https://trust.propdata.org.uk/schemas/v3/pdtf-transaction.json` for v3
48
+
49
+ 3. **Overlay System**: Supports multiple UK property forms
50
+ - BASPI (v4 and v5)
51
+ - Law Society TA forms (TA6, TA7, TA10)
52
+ - NTS forms (2023 and 2025 versions)
53
+ - CON29R, PIQ, RDS, and others
54
+
55
+ ### Testing Approach
56
+ Tests in `/src/tests/` validate:
57
+ - Schema structure with different overlay combinations
58
+ - Path validation for nested properties
59
+ - Custom overlay merging
60
+ - Verified claims validation
61
+
62
+ Use existing test patterns when adding new tests.
63
+
64
+ ## Branch And Release Workflow
65
+
66
+ This repository publishes `@pdtf/schemas` through three branches and npm
67
+ dist-tags:
68
+
69
+ - `main` -> npm `latest`: stable PDTF releases for broad industry use.
70
+ - `next` -> npm `next`: tested capabilities proposed for the next managed PDTF
71
+ point release.
72
+ - `dev` -> npm `dev`: the unstable coal face for faster-moving integration work
73
+ and early adopters.
74
+
75
+ PRs may be merged into `dev` to prove out work before it is ready for the managed
76
+ release path. Once a `dev` PR has been tested and is suitable for standards
77
+ review, promote the same change into `next` via a focused PR. Do not treat `dev`
78
+ as the standard, and do not publish `dev` or `next` builds to npm `latest`.
79
+
80
+ Promotion is automatic by default: after a PR is merged into `dev`,
81
+ `.github/workflows/promote-dev-to-next.yml` creates a matching PR into `next` by
82
+ cherry-picking the merged dev PR. Add the `no-promote-next` label before merging
83
+ a dev PR only when it should stay out of the `next` review queue. Reviewers
84
+ should still inspect the promotion PR, resolve conflicts, and adjust
85
+ release/version metadata if needed before merging to `next`.
86
+
87
+ Each npm publish needs a unique semver version. Use `-dev.N` versions on `dev`,
88
+ prerelease versions on `next`, and normal release versions on `main`.
89
+
90
+ Consumer-specific compatibility profiles belong in the consuming application or
91
+ service unless the capability becomes generic PDTF package functionality.
92
+ Consumers should pin/hash supplied schemas and use their own target-profile
93
+ configuration for API calls and webhooks.
94
+
95
+ See `docs/branch-release-workflow.md` for the full workflow.
96
+
97
+ ## Important Notes
98
+
99
+ - All schemas use JSON Schema Draft 07
100
+ - The repository publishes to npm as `@pdtf/schemas`
101
+ - Overlays may contain fields from licensed forms (BASPI, PIQ, Law Society) - ensure compliance when rendering data
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 Property Data Trust Framework
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.