@pdtf/schemas 3.4.1-6 → 3.4.1-8
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/settings.local.json +6 -1
- package/index.js +190 -12
- package/package.json +2 -1
- package/src/schemas/v3/combined.json +11 -0
- package/src/schemas/v3/compactSkeleton.txt +3340 -0
- package/src/schemas/v3/overlays/ntsl2.json +11 -0
- package/src/schemas/v3/skeleton.json +3208 -3036
- package/src/tests/v3/caching.test.js +274 -0
- package/src/tests/v3/transactionSchema.test.js +60 -14
- package/src/utils/compactSkeleton.js +84 -0
- package/src/utils/countTokens.js +67 -0
- package/src/utils/extractOverlay.js +96 -5
- package/src/utils/minimalSkeleton.js +85 -0
- package/src/utils/pathSkeleton.js +181 -0
package/index.js
CHANGED
|
@@ -13,6 +13,14 @@ addFormats(ajv);
|
|
|
13
13
|
// Enhanced caching structure for subschemas and validators
|
|
14
14
|
const schemaCache = new Map();
|
|
15
15
|
|
|
16
|
+
// Cache performance tracking
|
|
17
|
+
const cacheStats = {
|
|
18
|
+
hits: 0,
|
|
19
|
+
misses: 0,
|
|
20
|
+
totalQueries: 0,
|
|
21
|
+
cacheStartTime: Date.now()
|
|
22
|
+
};
|
|
23
|
+
|
|
16
24
|
const verifiedClaimsSchema = require("./src/schemas/verifiedClaims/pdtf-verified-claims.json");
|
|
17
25
|
const v2CoreSchema = require("./src/schemas/v2/pdtf-transaction.json");
|
|
18
26
|
const v3CoreSchema = require("./src/schemas/v3/pdtf-transaction.json");
|
|
@@ -188,8 +196,10 @@ const getCachedSchemaData = (path, schemaId, overlays) => {
|
|
|
188
196
|
const overlayKey = generateOverlayKey(overlays);
|
|
189
197
|
const cacheKey = `${path}-${schemaId}-${overlayKey}`;
|
|
190
198
|
|
|
199
|
+
cacheStats.totalQueries++;
|
|
191
200
|
let cached = schemaCache.get(cacheKey);
|
|
192
201
|
if (!cached) {
|
|
202
|
+
cacheStats.misses++;
|
|
193
203
|
// Compute subschema using the original logic
|
|
194
204
|
const sourceSchema = getTransactionSchema(schemaId, overlays);
|
|
195
205
|
const pathArray = path.split("/").slice(1);
|
|
@@ -235,9 +245,12 @@ const getCachedSchemaData = (path, schemaId, overlays) => {
|
|
|
235
245
|
cached = {
|
|
236
246
|
subSchema,
|
|
237
247
|
validator,
|
|
238
|
-
cacheKey
|
|
248
|
+
cacheKey,
|
|
249
|
+
createdAt: Date.now()
|
|
239
250
|
};
|
|
240
251
|
schemaCache.set(cacheKey, cached);
|
|
252
|
+
} else {
|
|
253
|
+
cacheStats.hits++;
|
|
241
254
|
}
|
|
242
255
|
|
|
243
256
|
return cached;
|
|
@@ -348,17 +361,178 @@ const validateVerifiedClaims = (verifiedClaims, schemaId, overlays) => {
|
|
|
348
361
|
};
|
|
349
362
|
|
|
350
363
|
// Cache management functions
|
|
351
|
-
const getCacheStats = () =>
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
364
|
+
const getCacheStats = () => {
|
|
365
|
+
const runtime = Date.now() - cacheStats.cacheStartTime;
|
|
366
|
+
const hitRate = cacheStats.totalQueries > 0 ? (cacheStats.hits / cacheStats.totalQueries * 100) : 0;
|
|
367
|
+
|
|
368
|
+
return {
|
|
369
|
+
totalEntries: schemaCache.size,
|
|
370
|
+
hits: cacheStats.hits,
|
|
371
|
+
misses: cacheStats.misses,
|
|
372
|
+
totalQueries: cacheStats.totalQueries,
|
|
373
|
+
hitRate: parseFloat(hitRate.toFixed(2)),
|
|
374
|
+
runtimeMs: runtime,
|
|
375
|
+
cacheKeys: Array.from(schemaCache.keys()),
|
|
376
|
+
memoryUsage: {
|
|
377
|
+
entriesCount: schemaCache.size,
|
|
378
|
+
// Rough estimate of memory usage per entry
|
|
379
|
+
estimatedSizeKB: Math.round((schemaCache.size * 2) / 1024 * 100) / 100 // Rough estimate
|
|
380
|
+
}
|
|
381
|
+
};
|
|
382
|
+
};
|
|
383
|
+
|
|
384
|
+
const getDetailedCacheStats = () => {
|
|
385
|
+
const baseStats = getCacheStats();
|
|
386
|
+
const entries = Array.from(schemaCache.entries()).map(([key, value]) => ({
|
|
387
|
+
key,
|
|
388
|
+
createdAt: value.createdAt,
|
|
389
|
+
age: Date.now() - value.createdAt,
|
|
390
|
+
hasValidator: typeof value.validator === 'function',
|
|
391
|
+
hasSubSchema: value.subSchema !== undefined
|
|
392
|
+
}));
|
|
393
|
+
|
|
394
|
+
return {
|
|
395
|
+
...baseStats,
|
|
396
|
+
entries: entries.sort((a, b) => b.createdAt - a.createdAt), // Most recent first
|
|
397
|
+
oldestEntry: entries.length > 0 ? Math.max(...entries.map(e => e.age)) : 0,
|
|
398
|
+
newestEntry: entries.length > 0 ? Math.min(...entries.map(e => e.age)) : 0
|
|
399
|
+
};
|
|
400
|
+
};
|
|
355
401
|
|
|
356
|
-
const clearSchemaCache = () => {
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
402
|
+
const clearSchemaCache = (pattern) => {
|
|
403
|
+
if (pattern) {
|
|
404
|
+
// Clear entries matching pattern
|
|
405
|
+
const keysToDelete = [];
|
|
406
|
+
schemaCache.forEach((value, key) => {
|
|
407
|
+
if (key.includes(pattern)) {
|
|
408
|
+
keysToDelete.push(key);
|
|
409
|
+
}
|
|
410
|
+
});
|
|
411
|
+
keysToDelete.forEach(key => schemaCache.delete(key));
|
|
412
|
+
|
|
413
|
+
// Also remove matching schemas from AJV
|
|
414
|
+
keysToDelete.forEach(key => {
|
|
415
|
+
try {
|
|
416
|
+
ajv.removeSchema(key);
|
|
417
|
+
} catch (e) {
|
|
418
|
+
// Schema might not exist in AJV, ignore
|
|
419
|
+
}
|
|
420
|
+
});
|
|
421
|
+
|
|
422
|
+
return keysToDelete.length;
|
|
423
|
+
} else {
|
|
424
|
+
// Clear all cache
|
|
425
|
+
const entriesCleared = schemaCache.size;
|
|
426
|
+
schemaCache.clear();
|
|
427
|
+
// Reset stats
|
|
428
|
+
cacheStats.hits = 0;
|
|
429
|
+
cacheStats.misses = 0;
|
|
430
|
+
cacheStats.totalQueries = 0;
|
|
431
|
+
cacheStats.cacheStartTime = Date.now();
|
|
432
|
+
|
|
433
|
+
// Also clear AJV's internal cache to prevent duplicates
|
|
434
|
+
ajv.removeSchema();
|
|
435
|
+
|
|
436
|
+
return entriesCleared;
|
|
437
|
+
}
|
|
438
|
+
};
|
|
439
|
+
|
|
440
|
+
const warmupCache = (paths, schemaId = "https://trust.propdata.org.uk/schemas/v3/pdtf-transaction.json", overlaysList = []) => {
|
|
441
|
+
const warmupStats = {
|
|
442
|
+
totalAttempted: 0,
|
|
443
|
+
successful: 0,
|
|
444
|
+
failed: 0,
|
|
445
|
+
errors: []
|
|
446
|
+
};
|
|
447
|
+
|
|
448
|
+
// Default common paths if none provided
|
|
449
|
+
const defaultPaths = [
|
|
450
|
+
"/propertyPack",
|
|
451
|
+
"/participants",
|
|
452
|
+
"/status",
|
|
453
|
+
"/propertyPack/surveys",
|
|
454
|
+
"/propertyPack/valuations",
|
|
455
|
+
"/propertyPack/waterAndDrainage",
|
|
456
|
+
"/propertyPack/ownership",
|
|
457
|
+
"/propertyPack/notices"
|
|
458
|
+
];
|
|
459
|
+
|
|
460
|
+
// Default common overlays if none provided
|
|
461
|
+
const defaultOverlays = [
|
|
462
|
+
[],
|
|
463
|
+
["baspiV5"],
|
|
464
|
+
["ta6ed4"],
|
|
465
|
+
["nts2023"],
|
|
466
|
+
["baspiV5", "ta6ed4"]
|
|
467
|
+
];
|
|
468
|
+
|
|
469
|
+
const pathsToWarm = paths || defaultPaths;
|
|
470
|
+
const overlaysToWarm = overlaysList.length > 0 ? overlaysList : defaultOverlays;
|
|
471
|
+
|
|
472
|
+
pathsToWarm.forEach(path => {
|
|
473
|
+
overlaysToWarm.forEach(overlays => {
|
|
474
|
+
warmupStats.totalAttempted++;
|
|
475
|
+
try {
|
|
476
|
+
// This will populate the cache
|
|
477
|
+
getSubschema(path, schemaId, overlays);
|
|
478
|
+
getSubschemaValidator(path, schemaId, overlays);
|
|
479
|
+
warmupStats.successful++;
|
|
480
|
+
} catch (error) {
|
|
481
|
+
warmupStats.failed++;
|
|
482
|
+
warmupStats.errors.push({
|
|
483
|
+
path,
|
|
484
|
+
overlays,
|
|
485
|
+
error: error.message
|
|
486
|
+
});
|
|
487
|
+
}
|
|
488
|
+
});
|
|
489
|
+
});
|
|
490
|
+
|
|
491
|
+
return warmupStats;
|
|
492
|
+
};
|
|
493
|
+
|
|
494
|
+
const pruneCacheByAge = (maxAgeMs) => {
|
|
495
|
+
const now = Date.now();
|
|
496
|
+
const keysToDelete = [];
|
|
497
|
+
|
|
498
|
+
schemaCache.forEach((value, key) => {
|
|
499
|
+
if (now - value.createdAt > maxAgeMs) {
|
|
500
|
+
keysToDelete.push(key);
|
|
501
|
+
}
|
|
502
|
+
});
|
|
503
|
+
|
|
504
|
+
keysToDelete.forEach(key => {
|
|
505
|
+
schemaCache.delete(key);
|
|
506
|
+
try {
|
|
507
|
+
ajv.removeSchema(key);
|
|
508
|
+
} catch (e) {
|
|
509
|
+
// Schema might not exist in AJV, ignore
|
|
510
|
+
}
|
|
511
|
+
});
|
|
512
|
+
|
|
513
|
+
return keysToDelete.length;
|
|
514
|
+
};
|
|
515
|
+
|
|
516
|
+
const setCacheMaxSize = (maxSize) => {
|
|
517
|
+
if (schemaCache.size <= maxSize) return 0;
|
|
518
|
+
|
|
519
|
+
// Get entries sorted by creation time (oldest first)
|
|
520
|
+
const entries = Array.from(schemaCache.entries())
|
|
521
|
+
.sort((a, b) => a[1].createdAt - b[1].createdAt);
|
|
522
|
+
|
|
523
|
+
const toRemove = schemaCache.size - maxSize;
|
|
524
|
+
const keysToDelete = entries.slice(0, toRemove).map(([key]) => key);
|
|
525
|
+
|
|
526
|
+
keysToDelete.forEach(key => {
|
|
527
|
+
schemaCache.delete(key);
|
|
528
|
+
try {
|
|
529
|
+
ajv.removeSchema(key);
|
|
530
|
+
} catch (e) {
|
|
531
|
+
// Schema might not exist in AJV, ignore
|
|
532
|
+
}
|
|
533
|
+
});
|
|
534
|
+
|
|
535
|
+
return keysToDelete.length;
|
|
362
536
|
};
|
|
363
537
|
|
|
364
538
|
module.exports = {
|
|
@@ -373,7 +547,11 @@ module.exports = {
|
|
|
373
547
|
validateVerifiedClaims,
|
|
374
548
|
overlaysMap,
|
|
375
549
|
extensionOverlays,
|
|
376
|
-
//
|
|
550
|
+
// Enhanced cache management functions
|
|
377
551
|
getCacheStats,
|
|
552
|
+
getDetailedCacheStats,
|
|
378
553
|
clearSchemaCache,
|
|
554
|
+
warmupCache,
|
|
555
|
+
pruneCacheByAge,
|
|
556
|
+
setCacheMaxSize,
|
|
379
557
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pdtf/schemas",
|
|
3
|
-
"version": "3.4.1-
|
|
3
|
+
"version": "3.4.1-8",
|
|
4
4
|
"description": "Property Data Trust Framework Schemas and Utilities",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"directories": {
|
|
@@ -29,6 +29,7 @@
|
|
|
29
29
|
"ajv": "^8.12.0",
|
|
30
30
|
"ajv-formats": "^2.1.1",
|
|
31
31
|
"deepmerge": "^4.3.1",
|
|
32
|
+
"gpt-3-encoder": "^1.1.4",
|
|
32
33
|
"jsonpointer": "^5.0.0",
|
|
33
34
|
"traverse": "^0.6.7"
|
|
34
35
|
},
|
|
@@ -9228,6 +9228,12 @@
|
|
|
9228
9228
|
"controlledParking",
|
|
9229
9229
|
"electricVehicleChargingPoint"
|
|
9230
9230
|
],
|
|
9231
|
+
"ntsl2Required": [
|
|
9232
|
+
"parkingArrangements",
|
|
9233
|
+
"disabledParking",
|
|
9234
|
+
"controlledParking",
|
|
9235
|
+
"electricVehicleChargingPoint"
|
|
9236
|
+
],
|
|
9231
9237
|
"ta6Required": ["parkingArrangements", "controlledParking"],
|
|
9232
9238
|
"properties": {
|
|
9233
9239
|
"parkingArrangements": {
|
|
@@ -9456,6 +9462,11 @@
|
|
|
9456
9462
|
"isConservationArea",
|
|
9457
9463
|
"hasTreePreservationOrder"
|
|
9458
9464
|
],
|
|
9465
|
+
"ntsl2Required": [
|
|
9466
|
+
"isListed",
|
|
9467
|
+
"isConservationArea",
|
|
9468
|
+
"hasTreePreservationOrder"
|
|
9469
|
+
],
|
|
9459
9470
|
"baspi4Required": [
|
|
9460
9471
|
"isListed",
|
|
9461
9472
|
"isConservationArea",
|