@pdtf/schemas 3.6.0-2 → 3.6.0-20

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 +3 -2
  5. package/src/schemas/v3/combined.json +6997 -1310
  6. package/src/schemas/v3/compactSkeleton.txt +352 -10
  7. package/src/schemas/v3/overlays/baspi4.json +161 -17
  8. package/src/schemas/v3/overlays/baspi5.json +164 -17
  9. package/src/schemas/v3/overlays/extensions/jk.json +24 -6
  10. package/src/schemas/v3/overlays/extensions/pc.json +28 -0
  11. package/src/schemas/v3/overlays/extensions/ph.json +191 -0
  12. package/src/schemas/v3/overlays/extensions/sc.json +77 -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 +4077 -611
  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 +433 -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 +103 -31
  34. package/src/utils/extractOverlay.js +87 -23
  35. package/src/utils/pathSkeleton.js +1 -1
@@ -5,6 +5,41 @@ const path = require("path");
5
5
 
6
6
  const combinedSchema = require("../schemas/v3/combined.json");
7
7
 
8
+ // Derive ref-related field names from a refType
9
+ function getRefConfig(refType) {
10
+ if (refType === "nts2Ref") {
11
+ // Legacy mapping: nts2Ref -> ntsRef in output
12
+ return {
13
+ ref: "nts2Ref",
14
+ outputRef: "ntsRef",
15
+ required: "nts2Required",
16
+ title: "nts2Title",
17
+ description: "nts2Description",
18
+ enumKey: "nts2Enum",
19
+ };
20
+ }
21
+ if (refType === "sef25Ref") {
22
+ // SEF25 extensions also appear as ntsRef in output
23
+ return {
24
+ ref: "sef25Ref",
25
+ outputRef: "ntsRef",
26
+ required: "sef25Required",
27
+ title: "sef25Title",
28
+ description: "sef25Description",
29
+ enumKey: "sef25Enum",
30
+ };
31
+ }
32
+ // For other ref types, derive keys by replacing "Ref" suffix
33
+ return {
34
+ ref: refType,
35
+ outputRef: refType,
36
+ required: refType.replace("Ref", "Required"),
37
+ title: refType.replace("Ref", "Title"),
38
+ description: refType.replace("Ref", "Description"),
39
+ enumKey: refType.replace("Ref", "Enum"),
40
+ };
41
+ }
42
+
8
43
  // Define the extension overlay mappings
9
44
  const extensionMappings = {
10
45
  // Outside areas
@@ -150,18 +185,52 @@ const extensionMappings = {
150
185
  "/properties/propertyPack/properties/completionAndMoving/properties/otherPropertyInChain",
151
186
  ],
152
187
  },
188
+
189
+ // Private supply costs (water & sewerage)
190
+ sc: {
191
+ name: "supplyCosts",
192
+ description: "Associated costs for private water and sewerage supply",
193
+ refType: "sef25Ref",
194
+ paths: [
195
+ "/properties/propertyPack/properties/waterAndDrainage/properties/water/properties/mainsWater/oneOf/0/properties/associatedCost",
196
+ "/properties/propertyPack/properties/waterAndDrainage/properties/drainage/properties/mainsFoulDrainage/oneOf/2/properties/associatedCost",
197
+ ],
198
+ },
199
+
200
+ // Parking permit costs
201
+ pc: {
202
+ name: "parkingPermitCost",
203
+ description: "Parking permit cost and frequency",
204
+ refType: "sef25Ref",
205
+ paths: [
206
+ "/properties/propertyPack/properties/parking/properties/controlledParking/oneOf/1/properties/costOfPermitFrequency",
207
+ ],
208
+ },
209
+
210
+ // Property hazards
211
+ ph: {
212
+ name: "propertyHazards",
213
+ description: "Property hazards and known issues",
214
+ refType: "sef25Ref",
215
+ paths: [
216
+ "/properties/propertyPack/properties/specialistIssues/properties/wellsDitchesShaft",
217
+ "/properties/propertyPack/properties/specialistIssues/properties/damagedOrExposedElectrics",
218
+ "/properties/propertyPack/properties/specialistIssues/properties/damageToFlooringOrStaircases",
219
+ "/properties/propertyPack/properties/specialistIssues/properties/knownAreasInPoorCondition",
220
+ ],
221
+ },
153
222
  };
154
223
 
155
- // Function to extract properties at specific paths with nts2Ref
156
- function extractPathsWithNts2Ref(schema, paths) {
224
+ // Function to extract properties at specific paths with given ref type
225
+ function extractPathsWithRef(schema, paths, refConfig) {
157
226
  const result = {};
158
227
 
159
228
  paths.forEach((path) => {
160
229
  try {
161
230
  const value = jp.get(schema, path);
162
- if (value && hasNts2Ref(value)) {
231
+ if (value && hasRef(value, refConfig)) {
163
232
  // Set the value at the same path in result
164
- jp.set(result, path, extractNts2Properties(value));
233
+ jp.set(result, path, extractRefProperties(value, refConfig));
165
234
  }
166
235
  } catch (err) {
167
236
  console.warn(`Path not found: ${path}`);
@@ -171,11 +240,11 @@ function extractPathsWithNts2Ref(schema, paths) {
171
240
  return result;
172
241
  }
173
242
 
174
- // Check if an object or its descendants have nts2Ref
175
- function hasNts2Ref(obj) {
243
+ // Check if an object or its descendants have the specified ref
244
+ function hasRef(obj, refConfig) {
176
245
  let found = false;
177
246
  traverse(obj).forEach(function (element) {
178
- if (element && element.nts2Ref) {
247
+ if (element && element[refConfig.ref]) {
179
248
  found = true;
180
249
  this.stop();
181
250
  }
@@ -183,16 +252,16 @@ function hasNts2Ref(obj) {
183
252
  return found;
184
253
  }
185
254
 
186
- // Extract only NTS2-specific properties (with nts2Ref)
187
- function extractNts2Properties(obj, parentKey) {
255
+ // Extract only ref-specific properties
256
+ function extractRefProperties(obj, refConfig, parentKey) {
188
257
  const result = {};
189
258
 
190
- // Copy nts2-specific metadata at current level
191
- if (obj.nts2Ref) result.ntsRef = obj.nts2Ref;
192
- if (obj.nts2Required) result.required = obj.nts2Required;
193
- if (obj.nts2Title) result.title = obj.nts2Title;
194
- if (obj.nts2Description) result.description = obj.nts2Description;
195
- if (obj.nts2Enum) result.enum = obj.nts2Enum;
259
+ // Copy ref-specific metadata at current level
260
+ if (obj[refConfig.ref]) result[refConfig.outputRef] = obj[refConfig.ref];
261
+ if (obj[refConfig.required]) result.required = obj[refConfig.required];
262
+ if (obj[refConfig.title]) result.title = obj[refConfig.title];
263
+ if (obj[refConfig.description]) result.description = obj[refConfig.description];
264
+ if (obj[refConfig.enumKey]) result.enum = obj[refConfig.enumKey];
196
265
 
197
266
  // Handle discriminator
198
267
  if (obj.discriminator) {
@@ -204,11 +273,11 @@ function extractNts2Properties(obj, parentKey) {
204
273
  result.properties = {};
205
274
  Object.keys(obj.properties).forEach((key) => {
206
275
  const prop = obj.properties[key];
207
- if (hasNts2Ref(prop)) {
208
- result.properties[key] = extractNts2Properties(prop, key);
276
+ if (hasRef(prop, refConfig)) {
277
+ result.properties[key] = extractRefProperties(prop, refConfig, key);
209
278
  }
210
279
  });
211
- // Only keep properties if we found some with nts2Ref
280
+ // Only keep properties if we found some with ref
212
281
  if (Object.keys(result.properties).length === 0) {
213
282
  delete result.properties;
214
283
  }
@@ -216,8 +285,8 @@ function extractNts2Properties(obj, parentKey) {
216
285
 
217
286
  // Handle arrays
218
287
  if (obj.items) {
219
- if (hasNts2Ref(obj.items)) {
220
- result.items = extractNts2Properties(obj.items);
288
+ if (hasRef(obj.items, refConfig)) {
289
+ result.items = extractRefProperties(obj.items, refConfig);
221
290
  }
222
291
  }
223
292
 
@@ -225,8 +294,8 @@ function extractNts2Properties(obj, parentKey) {
225
294
  if (obj.oneOf) {
226
295
  // Always preserve the full array structure and order
227
296
  const processedOneOf = obj.oneOf.map((schema, index) => {
228
- if (hasNts2Ref(schema)) {
229
- const extracted = extractNts2Properties(schema);
297
+ if (hasRef(schema, refConfig)) {
298
+ const extracted = extractRefProperties(schema, refConfig);
230
299
  // For oneOf schemas, we need to preserve discriminator enum values
231
300
  if (obj.discriminator && schema.properties) {
232
301
  const propName = obj.discriminator.propertyName;
@@ -234,7 +303,7 @@ function extractNts2Properties(obj, parentKey) {
234
303
  if (!extracted.properties) extracted.properties = {};
235
304
  extracted.properties[propName] = {
236
305
  enum:
237
- schema.properties[propName].nts2Enum ||
306
+ schema.properties[propName][refConfig.enumKey] ||
238
307
  schema.properties[propName].enum,
239
308
  };
240
309
  }
@@ -258,7 +327,7 @@ function extractNts2Properties(obj, parentKey) {
258
327
  }
259
328
  return extracted;
260
329
  } else {
261
- // If this branch doesn't have nts2Ref, preserve the base discriminator enum
330
+ // If this branch doesn't have ref, preserve the base discriminator enum
262
331
  if (obj.discriminator && schema.properties) {
263
332
  const propName = obj.discriminator.propertyName;
264
333
  if (schema.properties[propName]) {
@@ -281,7 +350,7 @@ function extractNts2Properties(obj, parentKey) {
281
350
  // If parentKey is set, and this object has extension metadata, return a stub for the parent
282
351
  if (
283
352
  parentKey &&
284
- (result.ntsRef || result.required || result.discriminator)
353
+ (result[refConfig.outputRef] || result.required || result.discriminator)
285
354
  ) {
286
355
  // This is an extension property inside a oneOf branch
287
356
  // Return a stub for the parent property with metadata and oneOf structure
@@ -311,16 +380,16 @@ function extractNts2Properties(obj, parentKey) {
311
380
  }
312
381
 
313
382
  // Add required array at parent levels if needed
314
- function addRequiredArrays(overlay, schema) {
383
+ function addRequiredArrays(overlay, schema, refConfig) {
315
384
  traverse(overlay).forEach(function (node) {
316
385
  if (node && node.properties) {
317
386
  const schemaPath = "/" + this.path.join("/");
318
387
  try {
319
388
  const schemaNode = jp.get(schema, schemaPath);
320
- if (schemaNode && schemaNode.nts2Required) {
389
+ if (schemaNode && schemaNode[refConfig.required]) {
321
390
  // Filter to only include properties that exist in this overlay
322
391
  const overlayProps = Object.keys(node.properties);
323
- const requiredProps = schemaNode.nts2Required.filter((prop) =>
392
+ const requiredProps = schemaNode[refConfig.required].filter((prop) =>
324
393
  overlayProps.includes(prop)
325
394
  );
326
395
  if (requiredProps.length > 0) {
@@ -337,13 +406,16 @@ function addRequiredArrays(overlay, schema) {
337
406
 
338
407
  // Generate extension overlays
339
408
  Object.entries(extensionMappings).forEach(([code, config]) => {
340
- console.log(`\nGenerating extension overlay: ${code} (${config.name})`);
409
+ const refType = config.refType || "nts2Ref";
410
+ const refConfig = getRefConfig(refType);
411
+
412
+ console.log(`\nGenerating extension overlay: ${code} (${config.name}) [${refType}]`);
341
413
 
342
414
  // Extract the specific paths
343
- let overlay = extractPathsWithNts2Ref(combinedSchema, config.paths);
415
+ let overlay = extractPathsWithRef(combinedSchema, config.paths, refConfig);
344
416
 
345
417
  // Add required arrays where needed
346
- overlay = addRequiredArrays(overlay, combinedSchema);
418
+ overlay = addRequiredArrays(overlay, combinedSchema, refConfig);
347
419
 
348
420
  // Add schema metadata
349
421
  overlay.$schema = "http://json-schema.org/draft-07/schema#";
@@ -2,9 +2,24 @@ const { dereference } = require("@jdw/jst");
2
2
  const traverse = require("traverse");
3
3
  const jp = require("jsonpointer");
4
4
  const fs = require("fs");
5
+ const path = require("path");
5
6
  const merge = require("deepmerge");
6
7
 
7
- const combinedSchema = require("../schemas/v3/combined.json");
8
+ const combinedPath =
9
+ process.env.COMBINED_PATH ||
10
+ path.resolve(__dirname, "../schemas/v3/combined.json");
11
+ const outputDir =
12
+ process.env.OUTPUT_DIR ||
13
+ path.resolve(__dirname, "../schemas/v3");
14
+
15
+ if (!fs.existsSync(combinedPath)) {
16
+ throw new Error(`Combined schema not found: ${combinedPath}`);
17
+ }
18
+ if (!fs.existsSync(outputDir)) {
19
+ fs.mkdirSync(outputDir, { recursive: true });
20
+ }
21
+
22
+ const combinedSchema = JSON.parse(fs.readFileSync(combinedPath, "utf8"));
8
23
 
9
24
  const extractFields = [
10
25
  "baspi4",
@@ -14,7 +29,9 @@ const extractFields = [
14
29
  "ntsl",
15
30
  "ntsl2",
16
31
  "ta6",
32
+ "ta6ed6",
17
33
  "ta7",
34
+ "ta7ed5",
18
35
  "ta10",
19
36
  "lpe1",
20
37
  "fme1",
@@ -68,23 +85,21 @@ const flattenSkeleton = (schema) => {
68
85
  });
69
86
  }
70
87
 
71
- // Handle oneOf - merge all possible properties
88
+ // Handle oneOf - merge all possible properties (recursively handles nested oneOf)
72
89
  if (schema.oneOf) {
73
90
  schema.oneOf.forEach((aOneOf) => {
74
- if (aOneOf.properties) {
75
- Object.entries(aOneOf.properties).forEach(([key, value]) => {
76
- // If property already exists and differs, mark as variant
77
- const newValue = flattenSkeleton(value);
78
- if (returnStructure[key] && JSON.stringify(returnStructure[key]) !== JSON.stringify(newValue)) {
79
- // For primitive types, just mark as variant type
80
- if (typeof returnStructure[key] === "string" || typeof newValue === "string") {
91
+ const variantResult = flattenSkeleton(aOneOf);
92
+ if (variantResult && typeof variantResult === "object" && !Array.isArray(variantResult)) {
93
+ Object.entries(variantResult).forEach(([key, value]) => {
94
+ if (key === "_variants") return;
95
+ if (returnStructure[key] && JSON.stringify(returnStructure[key]) !== JSON.stringify(value)) {
96
+ if (typeof returnStructure[key] === "string" || typeof value === "string") {
81
97
  returnStructure[key] = "variant";
82
98
  } else {
83
- // For complex types, merge properties
84
- returnStructure[key] = { ...returnStructure[key], ...newValue, _variants: true };
99
+ returnStructure[key] = { ...returnStructure[key], ...value, _variants: true };
85
100
  }
86
101
  } else {
87
- returnStructure[key] = newValue;
102
+ returnStructure[key] = value;
88
103
  }
89
104
  });
90
105
  }
@@ -97,6 +112,23 @@ const flattenSkeleton = (schema) => {
97
112
 
98
113
  const extractOverlay = (sourceSchema, ref) => {
99
114
  const refName = `${ref}Ref`;
115
+ const extendedTagMap = {
116
+ Type: "type",
117
+ MinLength: "minLength",
118
+ MaxLength: "maxLength",
119
+ Pattern: "pattern",
120
+ Minimum: "minimum",
121
+ Maximum: "maximum",
122
+ MinItems: "minItems",
123
+ MaxItems: "maxItems",
124
+ UniqueItems: "uniqueItems",
125
+ AdditionalProperties: "additionalProperties",
126
+ Format: "format",
127
+ MinProperties: "minProperties",
128
+ MaxProperties: "maxProperties",
129
+ MultipleOf: "multipleOf",
130
+ Const: "const",
131
+ };
100
132
  const returnSchema = {
101
133
  $schema: "http://json-schema.org/draft-07/schema#",
102
134
  $id: `https://trust.propdata.org.uk/schemas/v3/overlays/${ref}.json`,
@@ -107,7 +139,7 @@ const extractOverlay = (sourceSchema, ref) => {
107
139
  if (element[refName]) {
108
140
  jp.set(returnSchema, `${path}/${refName}`, element[refName]);
109
141
 
110
- if (element.discriminator) {
142
+ if (element.discriminator && Array.isArray(element.oneOf)) {
111
143
  jp.set(returnSchema, `${path}/discriminator`, element.discriminator);
112
144
  const { propertyName } = element.discriminator;
113
145
  element.oneOf.forEach((oneOf, index) => {
@@ -134,7 +166,7 @@ const extractOverlay = (sourceSchema, ref) => {
134
166
  }
135
167
 
136
168
  // also handle discriminator properties nested in items
137
- if (element.items?.discriminator) {
169
+ if (element.items?.discriminator && Array.isArray(element.items.oneOf)) {
138
170
  jp.set(
139
171
  returnSchema,
140
172
  `${path}/items/discriminator`,
@@ -184,6 +216,18 @@ const extractOverlay = (sourceSchema, ref) => {
184
216
  if (element[refEnum]) {
185
217
  jp.set(returnSchema, `${path}/enum`, element[refEnum]);
186
218
  }
219
+
220
+ const refConst = `${ref}Const`;
221
+ if (Object.prototype.hasOwnProperty.call(element, refConst)) {
222
+ jp.set(returnSchema, `${path}/const`, element[refConst]);
223
+ }
224
+
225
+ Object.entries(extendedTagMap).forEach(([suffix, schemaKey]) => {
226
+ const tagKey = `${ref}${suffix}`;
227
+ if (Object.prototype.hasOwnProperty.call(element, tagKey)) {
228
+ jp.set(returnSchema, `${path}/${schemaKey}`, element[tagKey]);
229
+ }
230
+ });
187
231
  });
188
232
  return returnSchema;
189
233
  };
@@ -198,11 +242,15 @@ const deleteProperties = (sourceSchema, propertyNames) => {
198
242
  };
199
243
 
200
244
  const overlays = {};
245
+ const overlaysDir = path.join(outputDir, "overlays");
246
+ if (!fs.existsSync(overlaysDir)) {
247
+ fs.mkdirSync(overlaysDir, { recursive: true });
248
+ }
201
249
 
202
250
  extractFields.forEach((key) => {
203
251
  let overlay = extractOverlay(combinedSchema, key);
204
252
  overlays[key] = overlay;
205
- const fileName = `../schemas/v3/overlays/${key}.json`;
253
+ const fileName = path.join(overlaysDir, `${key}.json`);
206
254
  fs.writeFileSync(fileName, JSON.stringify(overlay, null, 2));
207
255
  console.log(`Overlay ${key} written to ${fileName}`);
208
256
  });
@@ -212,14 +260,30 @@ const coreSchema = deleteProperties(combinedSchema, [
212
260
  ...extractFields.map((item) => `${item}Ref`),
213
261
  ...extractFields.map((item) => `${item}Required`),
214
262
  ...extractFields.map((item) => `${item}Title`),
263
+ ...extractFields.map((item) => `${item}Description`),
215
264
  ...extractFields.map((item) => `${item}Enum`),
265
+ ...extractFields.map((item) => `${item}Type`),
266
+ ...extractFields.map((item) => `${item}MinLength`),
267
+ ...extractFields.map((item) => `${item}MaxLength`),
268
+ ...extractFields.map((item) => `${item}Pattern`),
269
+ ...extractFields.map((item) => `${item}Minimum`),
270
+ ...extractFields.map((item) => `${item}Maximum`),
271
+ ...extractFields.map((item) => `${item}MinItems`),
272
+ ...extractFields.map((item) => `${item}MaxItems`),
273
+ ...extractFields.map((item) => `${item}UniqueItems`),
274
+ ...extractFields.map((item) => `${item}AdditionalProperties`),
275
+ ...extractFields.map((item) => `${item}Format`),
276
+ ...extractFields.map((item) => `${item}MinProperties`),
277
+ ...extractFields.map((item) => `${item}MaxProperties`),
278
+ ...extractFields.map((item) => `${item}MultipleOf`),
279
+ ...extractFields.map((item) => `${item}Const`),
216
280
  ]);
217
281
 
218
282
  fs.writeFileSync(
219
- "../schemas/v3/pdtf-transaction.json",
283
+ path.join(outputDir, "pdtf-transaction.json"),
220
284
  JSON.stringify(coreSchema, null, 2)
221
285
  );
222
- console.log("Core schema written to ../schemas/v3/pdtf-transaction.json");
286
+ console.log(`Core schema written to ${path.join(outputDir, "pdtf-transaction.json")}`);
223
287
 
224
288
  const skeletonSchema = deleteProperties(coreSchema, [
225
289
  "$schema",
@@ -238,10 +302,10 @@ const skeletonSchema = deleteProperties(coreSchema, [
238
302
  const skeletonSchemaFlattened = flattenSkeleton(skeletonSchema);
239
303
 
240
304
  fs.writeFileSync(
241
- "../schemas/v3/skeleton.json",
305
+ path.join(outputDir, "skeleton.json"),
242
306
  JSON.stringify(skeletonSchemaFlattened, null, 2)
243
307
  );
244
- console.log("Flat Skeleton schema written to ../schemas/v3/skeleton.json");
308
+ console.log(`Flat Skeleton schema written to ${path.join(outputDir, "skeleton.json")}`);
245
309
 
246
310
  // Generate compact skeleton format for better token efficiency
247
311
  const toCompact = (obj, indent = "") => {
@@ -267,8 +331,8 @@ const toCompact = (obj, indent = "") => {
267
331
  // Check if this is a keyed object (has "*" key)
268
332
  if (keys.length === 1 && keys[0] === "*") {
269
333
  const inner = toCompact(obj["*"], indent);
270
- if (inner === "") {
271
- return "{*}"; // Keyed object of primitives
334
+ if (inner === "" || inner === "{*}") {
335
+ return "{*}"; // Keyed object of primitives (or nested keyed objects with no structure)
272
336
  }
273
337
  return `{*}\n${inner}`;
274
338
  }
@@ -308,7 +372,7 @@ const innerContent = toCompact(skeletonSchemaFlattened, "");
308
372
  const compactSkeleton = innerContent ? innerContent : "";
309
373
 
310
374
  fs.writeFileSync(
311
- "../schemas/v3/compactSkeleton.txt",
375
+ path.join(outputDir, "compactSkeleton.txt"),
312
376
  compactSkeleton
313
377
  );
314
- console.log("Compact Skeleton written to ../schemas/v3/compactSkeleton.txt");
378
+ console.log(`Compact Skeleton written to ${path.join(outputDir, "compactSkeleton.txt")}`);
@@ -6,7 +6,7 @@ const combinedSchema = require("../schemas/v3/combined.json");
6
6
  // Extract fields that have overlay-specific properties
7
7
  const extractFields = [
8
8
  "baspi4", "baspi5", "nts", "nts2", "ntsl", "ntsl2",
9
- "ta6", "ta7", "ta10", "lpe1", "fme1", "piq",
9
+ "ta6", "ta7", "ta7ed5", "ta10", "lpe1", "fme1", "piq",
10
10
  "con29R", "con29DW", "llc1", "rds", "oc1", "sr24",
11
11
  ];
12
12