@pdtf/schemas 3.4.1-7 → 3.5.0
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 +12 -1
- package/index.js +263 -33
- package/package.json +2 -1
- package/src/examples/v3/exampleDocumentedVouch.json +1 -0
- package/src/examples/v3/exampleVouch.json +1 -0
- package/src/schemas/v3/combined.json +1662 -165
- package/src/schemas/v3/compactSkeleton.txt +3325 -0
- package/src/schemas/v3/overlays/nts.json +4 -1
- package/src/schemas/v3/overlays/nts2.json +4 -1
- package/src/schemas/v3/overlays/ntsl.json +4 -1
- package/src/schemas/v3/overlays/ntsl2.json +4 -1
- package/src/schemas/v3/pdtf-transaction.json +2609 -4
- package/src/schemas/v3/skeleton.json +3187 -3037
- package/src/schemas/verifiedClaims/pdtf-verified-claims.json +5 -0
- package/src/tests/v3/caching.test.js +591 -0
- package/src/tests/v3/transactionSchema.test.js +78 -0
- 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/src/schemas/v3/property-transaction-milestones.json +0 -91
|
@@ -9,7 +9,18 @@
|
|
|
9
9
|
"Bash(grep:*)",
|
|
10
10
|
"Bash(rm:*)",
|
|
11
11
|
"Bash(npm test:*)",
|
|
12
|
-
"Bash(npm install:*)"
|
|
12
|
+
"Bash(npm install:*)",
|
|
13
|
+
"Bash(git merge:*)",
|
|
14
|
+
"Bash(git add:*)",
|
|
15
|
+
"Bash(git commit:*)",
|
|
16
|
+
"Bash(git push:*)",
|
|
17
|
+
"Bash(git fetch:*)",
|
|
18
|
+
"Bash(git checkout:*)",
|
|
19
|
+
"Bash(gh pr view:*)",
|
|
20
|
+
"WebFetch(domain:github.com)",
|
|
21
|
+
"Bash(tree:*)",
|
|
22
|
+
"Bash(sed:*)",
|
|
23
|
+
"Bash(find:*)"
|
|
13
24
|
],
|
|
14
25
|
"deny": []
|
|
15
26
|
}
|
package/index.js
CHANGED
|
@@ -10,6 +10,17 @@ const ajv = new Ajv({
|
|
|
10
10
|
// Adds date formats among other types to the validator.
|
|
11
11
|
addFormats(ajv);
|
|
12
12
|
|
|
13
|
+
// Enhanced caching structure for subschemas and validators
|
|
14
|
+
const schemaCache = new Map();
|
|
15
|
+
|
|
16
|
+
// Cache performance tracking
|
|
17
|
+
const cacheStats = {
|
|
18
|
+
hits: 0,
|
|
19
|
+
misses: 0,
|
|
20
|
+
totalQueries: 0,
|
|
21
|
+
cacheStartTime: Date.now()
|
|
22
|
+
};
|
|
23
|
+
|
|
13
24
|
const verifiedClaimsSchema = require("./src/schemas/verifiedClaims/pdtf-verified-claims.json");
|
|
14
25
|
const v2CoreSchema = require("./src/schemas/v2/pdtf-transaction.json");
|
|
15
26
|
const v3CoreSchema = require("./src/schemas/v3/pdtf-transaction.json");
|
|
@@ -180,32 +191,79 @@ const generateOverlayKey = (overlays) => {
|
|
|
180
191
|
return overlays.join(".");
|
|
181
192
|
};
|
|
182
193
|
|
|
194
|
+
// Enhanced caching function that stores both subschemas and validators
|
|
195
|
+
const getCachedSchemaData = (path, schemaId, overlays) => {
|
|
196
|
+
const overlayKey = generateOverlayKey(overlays);
|
|
197
|
+
const cacheKey = `${path}-${schemaId}-${overlayKey}`;
|
|
198
|
+
|
|
199
|
+
cacheStats.totalQueries++;
|
|
200
|
+
let cached = schemaCache.get(cacheKey);
|
|
201
|
+
if (!cached) {
|
|
202
|
+
cacheStats.misses++;
|
|
203
|
+
// Compute subschema using the original logic
|
|
204
|
+
const sourceSchema = getTransactionSchema(schemaId, overlays);
|
|
205
|
+
const pathArray = path.split("/").slice(1);
|
|
206
|
+
let subSchema = sourceSchema;
|
|
207
|
+
|
|
208
|
+
if (pathArray.length >= 1) {
|
|
209
|
+
subSchema = pathArray.reduce((schema, pathElement) => {
|
|
210
|
+
if (!schema) return undefined;
|
|
211
|
+
const { type, items, properties, oneOf } = schema;
|
|
212
|
+
if (type === "array") return items;
|
|
213
|
+
if (properties?.[pathElement]) return properties[pathElement];
|
|
214
|
+
if (oneOf) {
|
|
215
|
+
let matchingProperty;
|
|
216
|
+
oneOf.forEach((aOneOf) => {
|
|
217
|
+
if (aOneOf.type === "array" && !Number.isNaN(pathElement)) {
|
|
218
|
+
matchingProperty = aOneOf.items;
|
|
219
|
+
} else if (aOneOf.properties?.[pathElement]) {
|
|
220
|
+
matchingProperty = aOneOf.properties?.[pathElement];
|
|
221
|
+
}
|
|
222
|
+
});
|
|
223
|
+
if (matchingProperty) return matchingProperty;
|
|
224
|
+
}
|
|
225
|
+
return undefined;
|
|
226
|
+
}, sourceSchema);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// Add schema to AJV and get validator
|
|
230
|
+
// Only add valid schemas to AJV
|
|
231
|
+
let validator;
|
|
232
|
+
if (subSchema && typeof subSchema === 'object') {
|
|
233
|
+
// Check if schema already exists in AJV to avoid duplicates
|
|
234
|
+
validator = ajv.getSchema(cacheKey);
|
|
235
|
+
if (!validator) {
|
|
236
|
+
ajv.addSchema(subSchema, cacheKey);
|
|
237
|
+
validator = ajv.getSchema(cacheKey);
|
|
238
|
+
}
|
|
239
|
+
} else {
|
|
240
|
+
// For invalid paths, create a validator that always fails
|
|
241
|
+
validator = () => false;
|
|
242
|
+
validator.errors = [`Invalid path: schema is ${subSchema}`];
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
cached = {
|
|
246
|
+
subSchema,
|
|
247
|
+
validator,
|
|
248
|
+
cacheKey,
|
|
249
|
+
createdAt: Date.now()
|
|
250
|
+
};
|
|
251
|
+
schemaCache.set(cacheKey, cached);
|
|
252
|
+
} else {
|
|
253
|
+
cacheStats.hits++;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
return cached;
|
|
257
|
+
};
|
|
258
|
+
|
|
183
259
|
const getValidator = (schemaId, overlays) => {
|
|
184
260
|
return getSubschemaValidator("", schemaId, overlays);
|
|
185
261
|
};
|
|
186
262
|
|
|
187
|
-
// common functions for v1 and v2
|
|
263
|
+
// common functions for v1 and v2 - now with caching
|
|
188
264
|
const getSubschema = (path, schemaId, overlays) => {
|
|
189
|
-
const
|
|
190
|
-
|
|
191
|
-
if (pathArray.length < 1) return sourceSchema;
|
|
192
|
-
return pathArray.reduce((schema, pathElement) => {
|
|
193
|
-
const { type, items, properties, oneOf } = schema;
|
|
194
|
-
if (type === "array") return items;
|
|
195
|
-
if (properties?.[pathElement]) return properties[pathElement];
|
|
196
|
-
if (oneOf) {
|
|
197
|
-
let matchingProperty;
|
|
198
|
-
oneOf.forEach((aOneOf) => {
|
|
199
|
-
if (aOneOf.type === "array" && !Number.isNaN(pathElement)) {
|
|
200
|
-
matchingProperty = aOneOf.items;
|
|
201
|
-
} else if (aOneOf.properties?.[pathElement]) {
|
|
202
|
-
matchingProperty = aOneOf.properties?.[pathElement];
|
|
203
|
-
}
|
|
204
|
-
});
|
|
205
|
-
if (matchingProperty) return matchingProperty;
|
|
206
|
-
}
|
|
207
|
-
return undefined;
|
|
208
|
-
}, sourceSchema);
|
|
265
|
+
const cached = getCachedSchemaData(path, schemaId, overlays);
|
|
266
|
+
return cached.subSchema;
|
|
209
267
|
};
|
|
210
268
|
|
|
211
269
|
const isPathValid = (path, schemaId, overlays) => {
|
|
@@ -217,18 +275,8 @@ const isPathValid = (path, schemaId, overlays) => {
|
|
|
217
275
|
};
|
|
218
276
|
|
|
219
277
|
const getSubschemaValidator = (path, schemaId, overlays) => {
|
|
220
|
-
const
|
|
221
|
-
|
|
222
|
-
// see if we can retrieve the schema by path, schemaId and overlays
|
|
223
|
-
const cacheKey = `${path}-${schemaId}-${overlayKey}`;
|
|
224
|
-
let validator = ajv.getSchema(cacheKey);
|
|
225
|
-
// retrieve whole schema by $id if available
|
|
226
|
-
if (!validator && subSchema.$id) validator = ajv.getSchema(cacheKey);
|
|
227
|
-
if (!validator) {
|
|
228
|
-
ajv.addSchema(subSchema, cacheKey);
|
|
229
|
-
validator = ajv.getSchema(cacheKey);
|
|
230
|
-
}
|
|
231
|
-
return validator;
|
|
278
|
+
const cached = getCachedSchemaData(path, schemaId, overlays);
|
|
279
|
+
return cached.validator;
|
|
232
280
|
};
|
|
233
281
|
|
|
234
282
|
// v1, deprecated
|
|
@@ -312,6 +360,181 @@ const validateVerifiedClaims = (verifiedClaims, schemaId, overlays) => {
|
|
|
312
360
|
return validationErrorsArr;
|
|
313
361
|
};
|
|
314
362
|
|
|
363
|
+
// Cache management functions
|
|
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
|
+
};
|
|
401
|
+
|
|
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;
|
|
536
|
+
};
|
|
537
|
+
|
|
315
538
|
module.exports = {
|
|
316
539
|
ajv,
|
|
317
540
|
getTransactionSchema,
|
|
@@ -324,4 +547,11 @@ module.exports = {
|
|
|
324
547
|
validateVerifiedClaims,
|
|
325
548
|
overlaysMap,
|
|
326
549
|
extensionOverlays,
|
|
550
|
+
// Enhanced cache management functions
|
|
551
|
+
getCacheStats,
|
|
552
|
+
getDetailedCacheStats,
|
|
553
|
+
clearSchemaCache,
|
|
554
|
+
warmupCache,
|
|
555
|
+
pruneCacheByAge,
|
|
556
|
+
setCacheMaxSize,
|
|
327
557
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pdtf/schemas",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.5.0",
|
|
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
|
},
|