@pdtf/schemas 3.6.0-4 → 3.6.0-41
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/.claude/plans/not-seeing-associated-costs.md +99 -0
- package/.claude/settings.local.json +22 -3
- package/README.md +52 -33
- package/docs/sef25-extensions-ui-spec.md +355 -0
- package/index.js +130 -66
- package/package.json +4 -2
- package/src/examples/v3/exampleTransaction.json +3 -3
- package/src/schemas/v3/combined.json +7651 -1672
- package/src/schemas/v3/compactSkeleton.txt +365 -14
- package/src/schemas/v3/overlays/baspi4.json +163 -18
- package/src/schemas/v3/overlays/baspi5.json +168 -20
- package/src/schemas/v3/overlays/extensions/as.json +4 -2
- package/src/schemas/v3/overlays/extensions/dk.json +32 -0
- package/src/schemas/v3/overlays/extensions/dr.json +9 -9
- package/src/schemas/v3/overlays/extensions/er.json +4 -2
- package/src/schemas/v3/overlays/extensions/fd.json +6 -3
- package/src/schemas/v3/overlays/extensions/hi.json +6 -3
- package/src/schemas/v3/overlays/extensions/hs.json +4 -2
- package/src/schemas/v3/overlays/extensions/ic.json +59 -0
- package/src/schemas/v3/overlays/extensions/jk.json +28 -8
- package/src/schemas/v3/overlays/extensions/la.json +4 -2
- package/src/schemas/v3/overlays/extensions/lc.json +75 -0
- package/src/schemas/v3/overlays/extensions/ma.json +6 -3
- package/src/schemas/v3/overlays/extensions/mc.json +4 -2
- package/src/schemas/v3/overlays/extensions/mi.json +69 -0
- package/src/schemas/v3/overlays/extensions/nd.json +43 -0
- package/src/schemas/v3/overlays/extensions/oa.json +4 -2
- package/src/schemas/v3/overlays/extensions/oc.json +4 -2
- package/src/schemas/v3/overlays/extensions/pc.json +37 -0
- package/src/schemas/v3/overlays/extensions/ph.json +194 -0
- package/src/schemas/v3/overlays/extensions/rw.json +68 -0
- package/src/schemas/v3/overlays/extensions/sb.json +4 -2
- package/src/schemas/v3/overlays/extensions/sc.json +129 -0
- package/src/schemas/v3/overlays/extensions/sd.json +63 -0
- package/src/schemas/v3/overlays/extensions/sf.json +4 -2
- package/src/schemas/v3/overlays/extensions/sl.json +4 -2
- package/src/schemas/v3/overlays/extensions/tf.json +6 -3
- package/src/schemas/v3/overlays/extensions/tr.json +73 -0
- package/src/schemas/v3/overlays/extensions/wg.json +350 -0
- package/src/schemas/v3/overlays/fme1.json +16 -14
- package/src/schemas/v3/overlays/lpe1.json +60 -30
- package/src/schemas/v3/overlays/nts.json +68 -1
- package/src/schemas/v3/overlays/nts2.json +68 -4
- package/src/schemas/v3/overlays/ntsl.json +68 -1
- package/src/schemas/v3/overlays/ntsl2.json +68 -4
- package/src/schemas/v3/overlays/piq.json +38 -1
- package/src/schemas/v3/overlays/rds.json +118 -6
- package/src/schemas/v3/overlays/sef25.json +1067 -0
- package/src/schemas/v3/overlays/ta6.json +46 -16
- package/src/schemas/v3/overlays/ta6ed6.json +6498 -0
- package/src/schemas/v3/overlays/ta7.json +70 -6
- package/src/schemas/v3/overlays/ta7ed5.json +1539 -0
- package/src/schemas/v3/pdtf-transaction.json +4361 -676
- package/src/schemas/v3/skeleton.json +505 -36
- package/src/tests/v3/buyerCircumstances.test.js +543 -0
- package/src/tests/v3/extensionOverlays.test.js +911 -0
- package/src/tests/v3/offers.test.js +37 -40
- package/src/tests/v3/patternProperties.test.js +149 -30
- package/src/tests/v3/ta6ed6.test.js +2317 -0
- package/src/tests/v3/ta7ed5.test.js +567 -0
- package/src/tests/v3/transactionSchema.test.js +31 -0
- package/src/utils/extractExtensionOverlays.js +216 -31
- package/src/utils/extractOverlay.js +88 -23
- 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,140 @@ 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/costOfPermit",
|
|
207
|
+
"/properties/propertyPack/properties/parking/properties/controlledParking/oneOf/1/properties/costOfPermitFrequency",
|
|
208
|
+
],
|
|
209
|
+
},
|
|
210
|
+
|
|
211
|
+
// Property hazards
|
|
212
|
+
ph: {
|
|
213
|
+
name: "propertyHazards",
|
|
214
|
+
description: "Property hazards and known issues",
|
|
215
|
+
refType: "sef25Ref",
|
|
216
|
+
paths: [
|
|
217
|
+
"/properties/propertyPack/properties/specialistIssues",
|
|
218
|
+
],
|
|
219
|
+
},
|
|
220
|
+
|
|
221
|
+
// Dropped kerb access to parking
|
|
222
|
+
dk: {
|
|
223
|
+
name: "droppedKerbAccess",
|
|
224
|
+
description: "Dropped kerb access to parking",
|
|
225
|
+
refType: "sef25Ref",
|
|
226
|
+
paths: [
|
|
227
|
+
"/properties/propertyPack/properties/parking/properties/droppedKerbAccess",
|
|
228
|
+
],
|
|
229
|
+
},
|
|
230
|
+
|
|
231
|
+
// Private right of way
|
|
232
|
+
rw: {
|
|
233
|
+
name: "privateRightOfWay",
|
|
234
|
+
description: "Private right of way",
|
|
235
|
+
refType: "sef25Ref",
|
|
236
|
+
paths: [
|
|
237
|
+
"/properties/propertyPack/properties/rightsAndInformalArrangements/properties/rightsOrArrangements/properties/privateRightOfWay",
|
|
238
|
+
],
|
|
239
|
+
},
|
|
240
|
+
|
|
241
|
+
// Storm, fire or flood damage
|
|
242
|
+
sd: {
|
|
243
|
+
name: "stormFireFloodDamage",
|
|
244
|
+
description: "Storm, fire or flood damage",
|
|
245
|
+
refType: "sef25Ref",
|
|
246
|
+
paths: [
|
|
247
|
+
"/properties/propertyPack/properties/environmentalIssues/properties/stormFireFloodDamage",
|
|
248
|
+
],
|
|
249
|
+
},
|
|
250
|
+
|
|
251
|
+
// Solar panel lease costs
|
|
252
|
+
lc: {
|
|
253
|
+
name: "solarLeaseCosts",
|
|
254
|
+
description: "Solar panel lease costs",
|
|
255
|
+
refType: "sef25Ref",
|
|
256
|
+
paths: [
|
|
257
|
+
"/properties/propertyPack/properties/electricity/properties/solarPanels/oneOf/1/properties/panelsOwnedOutright/oneOf/1/properties/associatedCost",
|
|
258
|
+
],
|
|
259
|
+
},
|
|
260
|
+
|
|
261
|
+
// Warranties and guarantees upfront
|
|
262
|
+
wg: {
|
|
263
|
+
name: "warrantiesGuarantees",
|
|
264
|
+
description: "Warranties and guarantees upfront Y/N for SEF25",
|
|
265
|
+
refType: "sef25Ref",
|
|
266
|
+
paths: [
|
|
267
|
+
"/properties/propertyPack/properties/guaranteesWarrantiesAndIndemnityInsurances",
|
|
268
|
+
],
|
|
269
|
+
},
|
|
270
|
+
|
|
271
|
+
// Insurance claims upfront
|
|
272
|
+
ic: {
|
|
273
|
+
name: "insuranceClaims",
|
|
274
|
+
description: "Insurance claims upfront for SEF25",
|
|
275
|
+
refType: "sef25Ref",
|
|
276
|
+
paths: [
|
|
277
|
+
"/properties/propertyPack/properties/insurance/oneOf/1/properties/insuranceClaims",
|
|
278
|
+
],
|
|
279
|
+
},
|
|
280
|
+
|
|
281
|
+
// Neighbour development upfront
|
|
282
|
+
nd: {
|
|
283
|
+
name: "neighbourDevelopment",
|
|
284
|
+
description: "Neighbour development upfront for SEF25",
|
|
285
|
+
refType: "sef25Ref",
|
|
286
|
+
paths: [
|
|
287
|
+
"/properties/propertyPack/properties/notices/properties/neighbourDevelopment",
|
|
288
|
+
],
|
|
289
|
+
},
|
|
290
|
+
|
|
291
|
+
// Other material issue upfront
|
|
292
|
+
mi: {
|
|
293
|
+
name: "otherMaterialIssue",
|
|
294
|
+
description: "Other material issue upfront for SEF25",
|
|
295
|
+
refType: "sef25Ref",
|
|
296
|
+
paths: [
|
|
297
|
+
"/properties/propertyPack/properties/additionalInformation/properties/otherMaterialIssue",
|
|
298
|
+
],
|
|
299
|
+
},
|
|
300
|
+
|
|
301
|
+
// Title restrictions for freehold
|
|
302
|
+
tr: {
|
|
303
|
+
name: "titleRestrictions",
|
|
304
|
+
description: "Title restrictions for freehold properties",
|
|
305
|
+
refType: "sef25Ref",
|
|
306
|
+
paths: [
|
|
307
|
+
"/properties/propertyPack/properties/ownership/properties/ownershipsToBeTransferred/items/oneOf/0/properties/titleRestrictions",
|
|
308
|
+
],
|
|
309
|
+
},
|
|
153
310
|
};
|
|
154
311
|
|
|
155
|
-
// Function to extract properties at specific paths with
|
|
156
|
-
function
|
|
312
|
+
// Function to extract properties at specific paths with given ref type
|
|
313
|
+
function extractPathsWithRef(schema, paths, refConfig) {
|
|
157
314
|
const result = {};
|
|
158
315
|
|
|
159
316
|
paths.forEach((path) => {
|
|
160
317
|
try {
|
|
161
318
|
const value = jp.get(schema, path);
|
|
162
|
-
if (value &&
|
|
319
|
+
if (value && hasRef(value, refConfig)) {
|
|
163
320
|
// Set the value at the same path in result
|
|
164
|
-
jp.set(result, path,
|
|
321
|
+
jp.set(result, path, extractRefProperties(value, refConfig));
|
|
165
322
|
}
|
|
166
323
|
} catch (err) {
|
|
167
324
|
console.warn(`Path not found: ${path}`);
|
|
@@ -171,11 +328,11 @@ function extractPathsWithNts2Ref(schema, paths) {
|
|
|
171
328
|
return result;
|
|
172
329
|
}
|
|
173
330
|
|
|
174
|
-
// Check if an object or its descendants have
|
|
175
|
-
function
|
|
331
|
+
// Check if an object or its descendants have the specified ref
|
|
332
|
+
function hasRef(obj, refConfig) {
|
|
176
333
|
let found = false;
|
|
177
334
|
traverse(obj).forEach(function (element) {
|
|
178
|
-
if (element && element.
|
|
335
|
+
if (element && element[refConfig.ref]) {
|
|
179
336
|
found = true;
|
|
180
337
|
this.stop();
|
|
181
338
|
}
|
|
@@ -183,16 +340,16 @@ function hasNts2Ref(obj) {
|
|
|
183
340
|
return found;
|
|
184
341
|
}
|
|
185
342
|
|
|
186
|
-
// Extract only
|
|
187
|
-
function
|
|
343
|
+
// Extract only ref-specific properties
|
|
344
|
+
function extractRefProperties(obj, refConfig, parentKey) {
|
|
188
345
|
const result = {};
|
|
189
346
|
|
|
190
|
-
// Copy
|
|
191
|
-
if (obj.
|
|
192
|
-
if (obj.
|
|
193
|
-
if (obj.
|
|
194
|
-
if (obj.
|
|
195
|
-
if (obj.
|
|
347
|
+
// Copy ref-specific metadata at current level
|
|
348
|
+
if (obj[refConfig.ref]) result[refConfig.outputRef] = obj[refConfig.ref];
|
|
349
|
+
if (obj[refConfig.required]) result.required = obj[refConfig.required];
|
|
350
|
+
if (obj[refConfig.title]) result.title = obj[refConfig.title];
|
|
351
|
+
if (obj[refConfig.description]) result.description = obj[refConfig.description];
|
|
352
|
+
if (obj[refConfig.enumKey]) result.enum = obj[refConfig.enumKey];
|
|
196
353
|
|
|
197
354
|
// Handle discriminator
|
|
198
355
|
if (obj.discriminator) {
|
|
@@ -204,11 +361,11 @@ function extractNts2Properties(obj, parentKey) {
|
|
|
204
361
|
result.properties = {};
|
|
205
362
|
Object.keys(obj.properties).forEach((key) => {
|
|
206
363
|
const prop = obj.properties[key];
|
|
207
|
-
if (
|
|
208
|
-
result.properties[key] =
|
|
364
|
+
if (hasRef(prop, refConfig)) {
|
|
365
|
+
result.properties[key] = extractRefProperties(prop, refConfig, key);
|
|
209
366
|
}
|
|
210
367
|
});
|
|
211
|
-
// Only keep properties if we found some with
|
|
368
|
+
// Only keep properties if we found some with ref
|
|
212
369
|
if (Object.keys(result.properties).length === 0) {
|
|
213
370
|
delete result.properties;
|
|
214
371
|
}
|
|
@@ -216,8 +373,8 @@ function extractNts2Properties(obj, parentKey) {
|
|
|
216
373
|
|
|
217
374
|
// Handle arrays
|
|
218
375
|
if (obj.items) {
|
|
219
|
-
if (
|
|
220
|
-
result.items =
|
|
376
|
+
if (hasRef(obj.items, refConfig)) {
|
|
377
|
+
result.items = extractRefProperties(obj.items, refConfig);
|
|
221
378
|
}
|
|
222
379
|
}
|
|
223
380
|
|
|
@@ -225,8 +382,8 @@ function extractNts2Properties(obj, parentKey) {
|
|
|
225
382
|
if (obj.oneOf) {
|
|
226
383
|
// Always preserve the full array structure and order
|
|
227
384
|
const processedOneOf = obj.oneOf.map((schema, index) => {
|
|
228
|
-
if (
|
|
229
|
-
const extracted =
|
|
385
|
+
if (hasRef(schema, refConfig)) {
|
|
386
|
+
const extracted = extractRefProperties(schema, refConfig);
|
|
230
387
|
// For oneOf schemas, we need to preserve discriminator enum values
|
|
231
388
|
if (obj.discriminator && schema.properties) {
|
|
232
389
|
const propName = obj.discriminator.propertyName;
|
|
@@ -234,7 +391,7 @@ function extractNts2Properties(obj, parentKey) {
|
|
|
234
391
|
if (!extracted.properties) extracted.properties = {};
|
|
235
392
|
extracted.properties[propName] = {
|
|
236
393
|
enum:
|
|
237
|
-
schema.properties[propName].
|
|
394
|
+
schema.properties[propName][refConfig.enumKey] ||
|
|
238
395
|
schema.properties[propName].enum,
|
|
239
396
|
};
|
|
240
397
|
}
|
|
@@ -258,7 +415,7 @@ function extractNts2Properties(obj, parentKey) {
|
|
|
258
415
|
}
|
|
259
416
|
return extracted;
|
|
260
417
|
} else {
|
|
261
|
-
// If this branch doesn't have
|
|
418
|
+
// If this branch doesn't have ref, preserve the base discriminator enum
|
|
262
419
|
if (obj.discriminator && schema.properties) {
|
|
263
420
|
const propName = obj.discriminator.propertyName;
|
|
264
421
|
if (schema.properties[propName]) {
|
|
@@ -281,7 +438,7 @@ function extractNts2Properties(obj, parentKey) {
|
|
|
281
438
|
// If parentKey is set, and this object has extension metadata, return a stub for the parent
|
|
282
439
|
if (
|
|
283
440
|
parentKey &&
|
|
284
|
-
(result.
|
|
441
|
+
(result[refConfig.outputRef] || result.required || result.discriminator)
|
|
285
442
|
) {
|
|
286
443
|
// This is an extension property inside a oneOf branch
|
|
287
444
|
// Return a stub for the parent property with metadata and oneOf structure
|
|
@@ -311,16 +468,16 @@ function extractNts2Properties(obj, parentKey) {
|
|
|
311
468
|
}
|
|
312
469
|
|
|
313
470
|
// Add required array at parent levels if needed
|
|
314
|
-
function addRequiredArrays(overlay, schema) {
|
|
471
|
+
function addRequiredArrays(overlay, schema, refConfig) {
|
|
315
472
|
traverse(overlay).forEach(function (node) {
|
|
316
473
|
if (node && node.properties) {
|
|
317
474
|
const schemaPath = "/" + this.path.join("/");
|
|
318
475
|
try {
|
|
319
476
|
const schemaNode = jp.get(schema, schemaPath);
|
|
320
|
-
if (schemaNode && schemaNode.
|
|
477
|
+
if (schemaNode && schemaNode[refConfig.required]) {
|
|
321
478
|
// Filter to only include properties that exist in this overlay
|
|
322
479
|
const overlayProps = Object.keys(node.properties);
|
|
323
|
-
const requiredProps = schemaNode.
|
|
480
|
+
const requiredProps = schemaNode[refConfig.required].filter((prop) =>
|
|
324
481
|
overlayProps.includes(prop)
|
|
325
482
|
);
|
|
326
483
|
if (requiredProps.length > 0) {
|
|
@@ -335,15 +492,43 @@ function addRequiredArrays(overlay, schema) {
|
|
|
335
492
|
return overlay;
|
|
336
493
|
}
|
|
337
494
|
|
|
495
|
+
// Annotate intermediate nodes with refs from the source schema
|
|
496
|
+
// This ensures every level of the overlay tree carries an ntsRef
|
|
497
|
+
function addIntermediateRefs(overlay, schema, refConfig) {
|
|
498
|
+
traverse(overlay).forEach(function (node) {
|
|
499
|
+
if (node && typeof node === "object" && !Array.isArray(node) && node.properties) {
|
|
500
|
+
// Skip if this node already has a ref
|
|
501
|
+
if (node[refConfig.outputRef]) return;
|
|
502
|
+
|
|
503
|
+
const schemaPath = "/" + this.path.join("/");
|
|
504
|
+
try {
|
|
505
|
+
const schemaNode = jp.get(schema, schemaPath);
|
|
506
|
+
if (schemaNode && schemaNode[refConfig.ref]) {
|
|
507
|
+
node[refConfig.outputRef] = schemaNode[refConfig.ref];
|
|
508
|
+
}
|
|
509
|
+
} catch (err) {
|
|
510
|
+
// Path doesn't exist in schema, skip
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
});
|
|
514
|
+
return overlay;
|
|
515
|
+
}
|
|
516
|
+
|
|
338
517
|
// Generate extension overlays
|
|
339
518
|
Object.entries(extensionMappings).forEach(([code, config]) => {
|
|
340
|
-
|
|
519
|
+
const refType = config.refType || "nts2Ref";
|
|
520
|
+
const refConfig = getRefConfig(refType);
|
|
521
|
+
|
|
522
|
+
console.log(`\nGenerating extension overlay: ${code} (${config.name}) [${refType}]`);
|
|
341
523
|
|
|
342
524
|
// Extract the specific paths
|
|
343
|
-
let overlay =
|
|
525
|
+
let overlay = extractPathsWithRef(combinedSchema, config.paths, refConfig);
|
|
344
526
|
|
|
345
527
|
// Add required arrays where needed
|
|
346
|
-
overlay = addRequiredArrays(overlay, combinedSchema);
|
|
528
|
+
overlay = addRequiredArrays(overlay, combinedSchema, refConfig);
|
|
529
|
+
|
|
530
|
+
// Add refs to intermediate nodes in the path hierarchy
|
|
531
|
+
overlay = addIntermediateRefs(overlay, combinedSchema, refConfig);
|
|
347
532
|
|
|
348
533
|
// Add schema metadata
|
|
349
534
|
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
|
|
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",
|
|
@@ -25,6 +42,7 @@ const extractFields = [
|
|
|
25
42
|
"rds",
|
|
26
43
|
"oc1",
|
|
27
44
|
"sr24",
|
|
45
|
+
"sef25",
|
|
28
46
|
];
|
|
29
47
|
|
|
30
48
|
const flattenSkeleton = (schema) => {
|
|
@@ -68,23 +86,21 @@ const flattenSkeleton = (schema) => {
|
|
|
68
86
|
});
|
|
69
87
|
}
|
|
70
88
|
|
|
71
|
-
// Handle oneOf - merge all possible properties
|
|
89
|
+
// Handle oneOf - merge all possible properties (recursively handles nested oneOf)
|
|
72
90
|
if (schema.oneOf) {
|
|
73
91
|
schema.oneOf.forEach((aOneOf) => {
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
if (returnStructure[key] && JSON.stringify(returnStructure[key]) !== JSON.stringify(
|
|
79
|
-
|
|
80
|
-
if (typeof returnStructure[key] === "string" || typeof newValue === "string") {
|
|
92
|
+
const variantResult = flattenSkeleton(aOneOf);
|
|
93
|
+
if (variantResult && typeof variantResult === "object" && !Array.isArray(variantResult)) {
|
|
94
|
+
Object.entries(variantResult).forEach(([key, value]) => {
|
|
95
|
+
if (key === "_variants") return;
|
|
96
|
+
if (returnStructure[key] && JSON.stringify(returnStructure[key]) !== JSON.stringify(value)) {
|
|
97
|
+
if (typeof returnStructure[key] === "string" || typeof value === "string") {
|
|
81
98
|
returnStructure[key] = "variant";
|
|
82
99
|
} else {
|
|
83
|
-
|
|
84
|
-
returnStructure[key] = { ...returnStructure[key], ...newValue, _variants: true };
|
|
100
|
+
returnStructure[key] = { ...returnStructure[key], ...value, _variants: true };
|
|
85
101
|
}
|
|
86
102
|
} else {
|
|
87
|
-
returnStructure[key] =
|
|
103
|
+
returnStructure[key] = value;
|
|
88
104
|
}
|
|
89
105
|
});
|
|
90
106
|
}
|
|
@@ -97,6 +113,23 @@ const flattenSkeleton = (schema) => {
|
|
|
97
113
|
|
|
98
114
|
const extractOverlay = (sourceSchema, ref) => {
|
|
99
115
|
const refName = `${ref}Ref`;
|
|
116
|
+
const extendedTagMap = {
|
|
117
|
+
Type: "type",
|
|
118
|
+
MinLength: "minLength",
|
|
119
|
+
MaxLength: "maxLength",
|
|
120
|
+
Pattern: "pattern",
|
|
121
|
+
Minimum: "minimum",
|
|
122
|
+
Maximum: "maximum",
|
|
123
|
+
MinItems: "minItems",
|
|
124
|
+
MaxItems: "maxItems",
|
|
125
|
+
UniqueItems: "uniqueItems",
|
|
126
|
+
AdditionalProperties: "additionalProperties",
|
|
127
|
+
Format: "format",
|
|
128
|
+
MinProperties: "minProperties",
|
|
129
|
+
MaxProperties: "maxProperties",
|
|
130
|
+
MultipleOf: "multipleOf",
|
|
131
|
+
Const: "const",
|
|
132
|
+
};
|
|
100
133
|
const returnSchema = {
|
|
101
134
|
$schema: "http://json-schema.org/draft-07/schema#",
|
|
102
135
|
$id: `https://trust.propdata.org.uk/schemas/v3/overlays/${ref}.json`,
|
|
@@ -107,7 +140,7 @@ const extractOverlay = (sourceSchema, ref) => {
|
|
|
107
140
|
if (element[refName]) {
|
|
108
141
|
jp.set(returnSchema, `${path}/${refName}`, element[refName]);
|
|
109
142
|
|
|
110
|
-
if (element.discriminator) {
|
|
143
|
+
if (element.discriminator && Array.isArray(element.oneOf)) {
|
|
111
144
|
jp.set(returnSchema, `${path}/discriminator`, element.discriminator);
|
|
112
145
|
const { propertyName } = element.discriminator;
|
|
113
146
|
element.oneOf.forEach((oneOf, index) => {
|
|
@@ -134,7 +167,7 @@ const extractOverlay = (sourceSchema, ref) => {
|
|
|
134
167
|
}
|
|
135
168
|
|
|
136
169
|
// also handle discriminator properties nested in items
|
|
137
|
-
if (element.items?.discriminator) {
|
|
170
|
+
if (element.items?.discriminator && Array.isArray(element.items.oneOf)) {
|
|
138
171
|
jp.set(
|
|
139
172
|
returnSchema,
|
|
140
173
|
`${path}/items/discriminator`,
|
|
@@ -184,6 +217,18 @@ const extractOverlay = (sourceSchema, ref) => {
|
|
|
184
217
|
if (element[refEnum]) {
|
|
185
218
|
jp.set(returnSchema, `${path}/enum`, element[refEnum]);
|
|
186
219
|
}
|
|
220
|
+
|
|
221
|
+
const refConst = `${ref}Const`;
|
|
222
|
+
if (Object.prototype.hasOwnProperty.call(element, refConst)) {
|
|
223
|
+
jp.set(returnSchema, `${path}/const`, element[refConst]);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
Object.entries(extendedTagMap).forEach(([suffix, schemaKey]) => {
|
|
227
|
+
const tagKey = `${ref}${suffix}`;
|
|
228
|
+
if (Object.prototype.hasOwnProperty.call(element, tagKey)) {
|
|
229
|
+
jp.set(returnSchema, `${path}/${schemaKey}`, element[tagKey]);
|
|
230
|
+
}
|
|
231
|
+
});
|
|
187
232
|
});
|
|
188
233
|
return returnSchema;
|
|
189
234
|
};
|
|
@@ -198,11 +243,15 @@ const deleteProperties = (sourceSchema, propertyNames) => {
|
|
|
198
243
|
};
|
|
199
244
|
|
|
200
245
|
const overlays = {};
|
|
246
|
+
const overlaysDir = path.join(outputDir, "overlays");
|
|
247
|
+
if (!fs.existsSync(overlaysDir)) {
|
|
248
|
+
fs.mkdirSync(overlaysDir, { recursive: true });
|
|
249
|
+
}
|
|
201
250
|
|
|
202
251
|
extractFields.forEach((key) => {
|
|
203
252
|
let overlay = extractOverlay(combinedSchema, key);
|
|
204
253
|
overlays[key] = overlay;
|
|
205
|
-
const fileName =
|
|
254
|
+
const fileName = path.join(overlaysDir, `${key}.json`);
|
|
206
255
|
fs.writeFileSync(fileName, JSON.stringify(overlay, null, 2));
|
|
207
256
|
console.log(`Overlay ${key} written to ${fileName}`);
|
|
208
257
|
});
|
|
@@ -212,14 +261,30 @@ const coreSchema = deleteProperties(combinedSchema, [
|
|
|
212
261
|
...extractFields.map((item) => `${item}Ref`),
|
|
213
262
|
...extractFields.map((item) => `${item}Required`),
|
|
214
263
|
...extractFields.map((item) => `${item}Title`),
|
|
264
|
+
...extractFields.map((item) => `${item}Description`),
|
|
215
265
|
...extractFields.map((item) => `${item}Enum`),
|
|
266
|
+
...extractFields.map((item) => `${item}Type`),
|
|
267
|
+
...extractFields.map((item) => `${item}MinLength`),
|
|
268
|
+
...extractFields.map((item) => `${item}MaxLength`),
|
|
269
|
+
...extractFields.map((item) => `${item}Pattern`),
|
|
270
|
+
...extractFields.map((item) => `${item}Minimum`),
|
|
271
|
+
...extractFields.map((item) => `${item}Maximum`),
|
|
272
|
+
...extractFields.map((item) => `${item}MinItems`),
|
|
273
|
+
...extractFields.map((item) => `${item}MaxItems`),
|
|
274
|
+
...extractFields.map((item) => `${item}UniqueItems`),
|
|
275
|
+
...extractFields.map((item) => `${item}AdditionalProperties`),
|
|
276
|
+
...extractFields.map((item) => `${item}Format`),
|
|
277
|
+
...extractFields.map((item) => `${item}MinProperties`),
|
|
278
|
+
...extractFields.map((item) => `${item}MaxProperties`),
|
|
279
|
+
...extractFields.map((item) => `${item}MultipleOf`),
|
|
280
|
+
...extractFields.map((item) => `${item}Const`),
|
|
216
281
|
]);
|
|
217
282
|
|
|
218
283
|
fs.writeFileSync(
|
|
219
|
-
"
|
|
284
|
+
path.join(outputDir, "pdtf-transaction.json"),
|
|
220
285
|
JSON.stringify(coreSchema, null, 2)
|
|
221
286
|
);
|
|
222
|
-
console.log(
|
|
287
|
+
console.log(`Core schema written to ${path.join(outputDir, "pdtf-transaction.json")}`);
|
|
223
288
|
|
|
224
289
|
const skeletonSchema = deleteProperties(coreSchema, [
|
|
225
290
|
"$schema",
|
|
@@ -238,10 +303,10 @@ const skeletonSchema = deleteProperties(coreSchema, [
|
|
|
238
303
|
const skeletonSchemaFlattened = flattenSkeleton(skeletonSchema);
|
|
239
304
|
|
|
240
305
|
fs.writeFileSync(
|
|
241
|
-
"
|
|
306
|
+
path.join(outputDir, "skeleton.json"),
|
|
242
307
|
JSON.stringify(skeletonSchemaFlattened, null, 2)
|
|
243
308
|
);
|
|
244
|
-
console.log(
|
|
309
|
+
console.log(`Flat Skeleton schema written to ${path.join(outputDir, "skeleton.json")}`);
|
|
245
310
|
|
|
246
311
|
// Generate compact skeleton format for better token efficiency
|
|
247
312
|
const toCompact = (obj, indent = "") => {
|
|
@@ -267,8 +332,8 @@ const toCompact = (obj, indent = "") => {
|
|
|
267
332
|
// Check if this is a keyed object (has "*" key)
|
|
268
333
|
if (keys.length === 1 && keys[0] === "*") {
|
|
269
334
|
const inner = toCompact(obj["*"], indent);
|
|
270
|
-
if (inner === "") {
|
|
271
|
-
return "{*}"; // Keyed object of primitives
|
|
335
|
+
if (inner === "" || inner === "{*}") {
|
|
336
|
+
return "{*}"; // Keyed object of primitives (or nested keyed objects with no structure)
|
|
272
337
|
}
|
|
273
338
|
return `{*}\n${inner}`;
|
|
274
339
|
}
|
|
@@ -308,7 +373,7 @@ const innerContent = toCompact(skeletonSchemaFlattened, "");
|
|
|
308
373
|
const compactSkeleton = innerContent ? innerContent : "";
|
|
309
374
|
|
|
310
375
|
fs.writeFileSync(
|
|
311
|
-
"
|
|
376
|
+
path.join(outputDir, "compactSkeleton.txt"),
|
|
312
377
|
compactSkeleton
|
|
313
378
|
);
|
|
314
|
-
console.log(
|
|
379
|
+
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
|
|