smartledger-bsv 3.2.2 → 3.3.2

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 (41) hide show
  1. package/CHANGELOG.md +147 -0
  2. package/architecture_demo.js +247 -0
  3. package/bsv-gdaf.min.js +37 -0
  4. package/bsv-ltp.min.js +37 -0
  5. package/bsv-shamir.min.js +12 -0
  6. package/bsv.bundle.js +9 -9
  7. package/build/bsv-smartcontract.min.js +10 -8
  8. package/build/bsv.bundle.js +9 -9
  9. package/build/bsv.min.js +10 -8
  10. package/build/webpack.gdaf.config.js +54 -0
  11. package/build/webpack.ltp.config.js +17 -0
  12. package/bundle-entry.js +77 -1
  13. package/complete_ltp_demo.js +511 -0
  14. package/gdaf-entry.js +54 -0
  15. package/index.js +259 -0
  16. package/lib/crypto/shamir.js +360 -0
  17. package/lib/gdaf/attestation-signer.js +461 -0
  18. package/lib/gdaf/attestation-verifier.js +600 -0
  19. package/lib/gdaf/did-resolver.js +382 -0
  20. package/lib/gdaf/index.js +471 -0
  21. package/lib/gdaf/schema-validator.js +682 -0
  22. package/lib/gdaf/smartledger-anchor.js +486 -0
  23. package/lib/gdaf/zk-prover.js +507 -0
  24. package/lib/ltp/anchor.js +438 -0
  25. package/lib/ltp/claim.js +1026 -0
  26. package/lib/ltp/index.js +470 -0
  27. package/lib/ltp/obligation.js +945 -0
  28. package/lib/ltp/proof.js +828 -0
  29. package/lib/ltp/registry.js +702 -0
  30. package/lib/ltp/right.js +765 -0
  31. package/lib/smart_contract/API_REFERENCE.md +1 -1
  32. package/lib/smart_contract/EXAMPLES.md +2 -2
  33. package/lib/smart_contract/QUICK_START.md +2 -2
  34. package/lib/smart_contract/README.md +1 -1
  35. package/ltp-entry.js +92 -0
  36. package/package.json +44 -4
  37. package/shamir-entry.js +173 -0
  38. package/shamir_demo.js +121 -0
  39. package/simple_demo.js +204 -0
  40. package/test_shamir.js +221 -0
  41. package/test_standalone_shamir.html +83 -0
@@ -0,0 +1,1026 @@
1
+ 'use strict'
2
+
3
+ var bsv = require('../../')
4
+ var Hash = bsv.crypto.Hash
5
+ var $ = bsv.util.preconditions
6
+
7
+ /**
8
+ * Legal Token Protocol - Claim Preparation Primitives
9
+ *
10
+ * Provides primitives for legal claim validation, formatting, and preparation
11
+ * without direct blockchain publishing. External systems handle claim storage
12
+ * and blockchain anchoring operations.
13
+ */
14
+
15
+ /**
16
+ * Predefined Legal Claim Schemas
17
+ */
18
+ var ClaimSchemas = {
19
+
20
+ // Property Rights
21
+ PropertyTitle: {
22
+ title: 'Property Title',
23
+ description: 'Real estate property ownership title',
24
+ type: 'object',
25
+ required: ['propertyId', 'address', 'ownershipType'],
26
+ properties: {
27
+ propertyId: {
28
+ type: 'string',
29
+ description: 'Unique property identifier'
30
+ },
31
+ address: {
32
+ type: 'object',
33
+ required: ['street', 'city', 'state', 'country'],
34
+ properties: {
35
+ street: { type: 'string' },
36
+ city: { type: 'string' },
37
+ state: { type: 'string' },
38
+ country: { type: 'string' },
39
+ postalCode: { type: 'string' }
40
+ }
41
+ },
42
+ ownershipType: {
43
+ type: 'string',
44
+ enum: ['fee_simple', 'leasehold', 'joint_tenancy', 'tenancy_in_common']
45
+ },
46
+ titleNumber: { type: 'string' },
47
+ registeredDate: { type: 'string', format: 'date-time' },
48
+ legalDescription: { type: 'string' },
49
+ encumbrances: {
50
+ type: 'array',
51
+ items: {
52
+ type: 'object',
53
+ properties: {
54
+ type: { type: 'string' },
55
+ amount: { type: 'number' },
56
+ description: { type: 'string' }
57
+ }
58
+ }
59
+ }
60
+ }
61
+ },
62
+
63
+ // Vehicle Title
64
+ VehicleTitle: {
65
+ title: 'Vehicle Title',
66
+ description: 'Motor vehicle ownership title',
67
+ type: 'object',
68
+ required: ['vin', 'make', 'model', 'year'],
69
+ properties: {
70
+ vin: {
71
+ type: 'string',
72
+ pattern: '^[A-HJ-NPR-Z0-9]{17}$',
73
+ description: 'Vehicle Identification Number'
74
+ },
75
+ make: { type: 'string' },
76
+ model: { type: 'string' },
77
+ year: { type: 'integer', minimum: 1900, maximum: 2030 },
78
+ titleNumber: { type: 'string' },
79
+ registeredState: { type: 'string' },
80
+ mileage: { type: 'integer', minimum: 0 },
81
+ color: { type: 'string' },
82
+ fuelType: {
83
+ type: 'string',
84
+ enum: ['gasoline', 'diesel', 'electric', 'hybrid', 'other']
85
+ },
86
+ liens: {
87
+ type: 'array',
88
+ items: {
89
+ type: 'object',
90
+ properties: {
91
+ lienholderName: { type: 'string' },
92
+ lienAmount: { type: 'number' },
93
+ lienDate: { type: 'string', format: 'date' }
94
+ }
95
+ }
96
+ }
97
+ }
98
+ },
99
+
100
+ // Financial Instruments
101
+ PromissoryNote: {
102
+ title: 'Promissory Note',
103
+ description: 'Legal promise to pay a specified amount',
104
+ type: 'object',
105
+ required: ['principal', 'interestRate', 'maturityDate', 'borrower', 'lender'],
106
+ properties: {
107
+ principalAmount: {
108
+ type: 'number',
109
+ minimum: 0,
110
+ description: 'Principal amount in base currency units'
111
+ },
112
+ currency: { type: 'string', default: 'USD' },
113
+ interestRate: {
114
+ type: 'number',
115
+ minimum: 0,
116
+ maximum: 100,
117
+ description: 'Annual interest rate percentage'
118
+ },
119
+ maturityDate: {
120
+ type: 'string',
121
+ format: 'date',
122
+ description: 'Date when principal and interest are due'
123
+ },
124
+ payee: {
125
+ type: 'string',
126
+ description: 'DID of the payee (creditor)'
127
+ },
128
+ payor: {
129
+ type: 'string',
130
+ description: 'DID of the payor (debtor)'
131
+ },
132
+ paymentSchedule: {
133
+ type: 'string',
134
+ enum: ['lump_sum', 'monthly', 'quarterly', 'annually']
135
+ },
136
+ collateral: {
137
+ type: 'array',
138
+ items: {
139
+ type: 'object',
140
+ properties: {
141
+ description: { type: 'string' },
142
+ value: { type: 'number' },
143
+ type: { type: 'string' }
144
+ }
145
+ }
146
+ },
147
+ defaultProvisions: { type: 'string' },
148
+ governingLaw: { type: 'string' }
149
+ }
150
+ },
151
+
152
+ // Intellectual Property
153
+ IntellectualProperty: {
154
+ title: 'Intellectual Property',
155
+ description: 'Intellectual property rights and licensing',
156
+ type: 'object',
157
+ required: ['ipType', 'title', 'registrationNumber'],
158
+ properties: {
159
+ ipType: {
160
+ type: 'string',
161
+ enum: ['patent', 'trademark', 'copyright', 'trade_secret']
162
+ },
163
+ title: { type: 'string' },
164
+ creator: { type: 'string', description: 'Creator DID' },
165
+ registrationNumber: { type: 'string' },
166
+ registrationDate: { type: 'string', format: 'date' },
167
+ expirationDate: { type: 'string', format: 'date' },
168
+ jurisdiction: { type: 'string' },
169
+ description: { type: 'string' },
170
+ claims: {
171
+ type: 'array',
172
+ items: { type: 'string' }
173
+ },
174
+ priorArt: {
175
+ type: 'array',
176
+ items: {
177
+ type: 'object',
178
+ properties: {
179
+ reference: { type: 'string' },
180
+ date: { type: 'string', format: 'date' },
181
+ relevance: { type: 'string' }
182
+ }
183
+ }
184
+ }
185
+ }
186
+ },
187
+
188
+ // Professional License
189
+ ProfessionalLicense: {
190
+ title: 'Professional License',
191
+ description: 'Professional practice license or certification',
192
+ type: 'object',
193
+ required: ['licenseType', 'licenseNumber', 'issuingAuthority', 'expirationDate'],
194
+ properties: {
195
+ licenseType: { type: 'string' },
196
+ licenseNumber: { type: 'string' },
197
+ issuingAuthority: { type: 'string' },
198
+ licensee: { type: 'string', description: 'Licensee DID' },
199
+ issueDate: { type: 'string', format: 'date' },
200
+ expirationDate: { type: 'string', format: 'date' },
201
+ renewalDate: { type: 'string', format: 'date' },
202
+ jurisdiction: { type: 'string' },
203
+ scope: { type: 'string' },
204
+ restrictions: {
205
+ type: 'array',
206
+ items: { type: 'string' }
207
+ },
208
+ continuing_education: {
209
+ type: 'object',
210
+ properties: {
211
+ hours_required: { type: 'integer' },
212
+ deadline: { type: 'string', format: 'date' },
213
+ completed_hours: { type: 'integer' }
214
+ }
215
+ }
216
+ }
217
+ },
218
+
219
+ // Music License
220
+ MusicLicense: {
221
+ title: 'Music License',
222
+ description: 'Music performance and usage licensing',
223
+ type: 'object',
224
+ required: ['songTitle', 'artist', 'licenseType', 'royaltyRate'],
225
+ properties: {
226
+ workTitle: { type: 'string' },
227
+ composer: { type: 'string', description: 'Composer DID' },
228
+ performer: { type: 'string', description: 'Performer DID' },
229
+ licenseType: {
230
+ type: 'string',
231
+ enum: ['mechanical', 'performance', 'synchronization', 'master_use']
232
+ },
233
+ territory: { type: 'string' },
234
+ duration: { type: 'string' },
235
+ royaltyRate: { type: 'number', minimum: 0, maximum: 100 },
236
+ advancePayment: { type: 'number', minimum: 0 },
237
+ exclusivity: { type: 'boolean' },
238
+ mediaTypes: {
239
+ type: 'array',
240
+ items: {
241
+ type: 'string',
242
+ enum: ['cd', 'vinyl', 'digital', 'streaming', 'broadcast', 'film', 'tv']
243
+ }
244
+ },
245
+ usage_restrictions: { type: 'string' },
246
+ copyrightNotice: { type: 'string' }
247
+ }
248
+ }
249
+ }
250
+
251
+ /**
252
+ * Claim Preparation and Validation Primitives
253
+ */
254
+ var ClaimValidator = {
255
+
256
+ /**
257
+ * Prepare claim validation for external processing
258
+ * @param {Object} claim - Claim to validate
259
+ * @param {String} schemaName - Schema name
260
+ * @returns {Object} Prepared validation data
261
+ */
262
+ prepareClaimValidation: function(claim, schemaName) {
263
+ try {
264
+ $.checkArgument(claim && typeof claim === 'object', 'Invalid claim')
265
+ $.checkArgument(typeof schemaName === 'string', 'Schema name must be string')
266
+
267
+ var schema = ClaimSchemas[schemaName]
268
+ if (!schema) {
269
+ return {
270
+ success: false,
271
+ error: 'Unknown schema: ' + schemaName
272
+ }
273
+ }
274
+
275
+ var errors = []
276
+ var warnings = []
277
+
278
+ this._validateObject(claim, schema, '', errors, warnings)
279
+
280
+ var validation = {
281
+ valid: errors.length === 0,
282
+ errors: errors,
283
+ warnings: warnings,
284
+ schema: schemaName,
285
+ claimHash: this.hash(claim),
286
+ canonical: this.canonicalize(claim)
287
+ }
288
+
289
+ return {
290
+ success: true,
291
+ validation: validation,
292
+ claim: claim,
293
+ schema: schema,
294
+ externalOperations: {
295
+ store: {
296
+ endpoint: 'POST /claims/validate',
297
+ data: {
298
+ claim: claim,
299
+ validation: validation,
300
+ schema: schemaName
301
+ }
302
+ },
303
+ index: {
304
+ endpoint: 'POST /claims/index',
305
+ data: {
306
+ claimHash: validation.claimHash,
307
+ schemaName: schemaName,
308
+ valid: validation.valid
309
+ }
310
+ }
311
+ }
312
+ }
313
+
314
+ } catch (error) {
315
+ return {
316
+ success: false,
317
+ error: 'Validation preparation failed: ' + error.message
318
+ }
319
+ }
320
+ },
321
+
322
+ /**
323
+ * Prepare claim for external attestation
324
+ * @param {Object} claim - Claim to prepare for attestation
325
+ * @param {String} schemaName - Schema name
326
+ * @param {Object} attestor - Attestor information
327
+ * @returns {Object} Prepared attestation data
328
+ */
329
+ prepareClaimAttestation: function(claim, schemaName, attestor) {
330
+ try {
331
+ $.checkArgument(claim && typeof claim === 'object', 'Invalid claim')
332
+ $.checkArgument(typeof schemaName === 'string', 'Schema name must be string')
333
+ $.checkArgument(attestor && typeof attestor === 'object', 'Invalid attestor')
334
+
335
+ // Validate claim first
336
+ var validation = this.prepareClaimValidation(claim, schemaName)
337
+ if (!validation.success || !validation.validation.valid) {
338
+ return {
339
+ success: false,
340
+ error: 'Cannot attest invalid claim',
341
+ validation: validation
342
+ }
343
+ }
344
+
345
+ // Prepare attestation structure
346
+ var attestation = {
347
+ type: 'LTP_CLAIM_ATTESTATION',
348
+ claimHash: validation.validation.claimHash,
349
+ schemaName: schemaName,
350
+ attestor: {
351
+ did: attestor.did || attestor.id,
352
+ name: attestor.name || 'Unknown',
353
+ authority: attestor.authority || null,
354
+ jurisdiction: attestor.jurisdiction || null
355
+ },
356
+ attestedAt: new Date().toISOString(),
357
+ confidence: attestor.confidence || 100,
358
+ evidence: attestor.evidence || null,
359
+ notes: attestor.notes || null
360
+ }
361
+
362
+ // Create attestation hash
363
+ var attestationHash = Hash.sha256(Buffer.from(this.canonicalize(attestation))).toString('hex')
364
+ attestation.attestationHash = attestationHash
365
+
366
+ return {
367
+ success: true,
368
+ attestation: attestation,
369
+ claim: claim,
370
+ validation: validation.validation,
371
+ externalOperations: {
372
+ storeAttestation: {
373
+ endpoint: 'POST /claims/' + validation.validation.claimHash + '/attestations',
374
+ data: attestation
375
+ },
376
+ notifyStakeholders: {
377
+ endpoint: 'POST /notifications/claim-attested',
378
+ data: {
379
+ claimHash: validation.validation.claimHash,
380
+ attestorDid: attestation.attestor.did,
381
+ attestationHash: attestationHash
382
+ }
383
+ }
384
+ }
385
+ }
386
+
387
+ } catch (error) {
388
+ return {
389
+ success: false,
390
+ error: 'Attestation preparation failed: ' + error.message
391
+ }
392
+ }
393
+ },
394
+
395
+ /**
396
+ * Prepare claim dispute for external processing
397
+ * @param {String} claimHash - Hash of disputed claim
398
+ * @param {Object} disputant - Disputant information
399
+ * @param {Object} dispute - Dispute details
400
+ * @returns {Object} Prepared dispute data
401
+ */
402
+ prepareClaimDispute: function(claimHash, disputant, dispute) {
403
+ try {
404
+ $.checkArgument(typeof claimHash === 'string', 'Claim hash must be string')
405
+ $.checkArgument(disputant && typeof disputant === 'object', 'Invalid disputant')
406
+ $.checkArgument(dispute && typeof dispute === 'object', 'Invalid dispute')
407
+
408
+ var disputeRecord = {
409
+ type: 'LTP_CLAIM_DISPUTE',
410
+ claimHash: claimHash,
411
+ disputant: {
412
+ did: disputant.did || disputant.id,
413
+ name: disputant.name || 'Unknown',
414
+ jurisdiction: disputant.jurisdiction || null
415
+ },
416
+ dispute: {
417
+ reason: dispute.reason || 'UNSPECIFIED',
418
+ category: dispute.category || 'ACCURACY',
419
+ description: dispute.description || '',
420
+ evidence: dispute.evidence || null,
421
+ requestedAction: dispute.requestedAction || 'REVIEW'
422
+ },
423
+ disputedAt: new Date().toISOString(),
424
+ status: 'FILED',
425
+ priority: dispute.priority || 'NORMAL'
426
+ }
427
+
428
+ // Create dispute hash
429
+ var disputeHash = Hash.sha256(Buffer.from(this.canonicalize(disputeRecord))).toString('hex')
430
+ disputeRecord.disputeHash = disputeHash
431
+
432
+ return {
433
+ success: true,
434
+ dispute: disputeRecord,
435
+ disputeId: this._generateDisputeId(claimHash),
436
+ externalOperations: {
437
+ fileDispute: {
438
+ endpoint: 'POST /claims/' + claimHash + '/disputes',
439
+ data: disputeRecord
440
+ },
441
+ notifyStakeholders: {
442
+ endpoint: 'POST /notifications/claim-disputed',
443
+ data: {
444
+ claimHash: claimHash,
445
+ disputantDid: disputeRecord.disputant.did,
446
+ disputeHash: disputeHash
447
+ }
448
+ },
449
+ escalate: {
450
+ endpoint: 'POST /disputes/escalate',
451
+ data: {
452
+ disputeHash: disputeHash,
453
+ priority: disputeRecord.priority
454
+ }
455
+ }
456
+ }
457
+ }
458
+
459
+ } catch (error) {
460
+ return {
461
+ success: false,
462
+ error: 'Dispute preparation failed: ' + error.message
463
+ }
464
+ }
465
+ },
466
+
467
+ /**
468
+ * Prepare bulk claim validation for external processing
469
+ * @param {Array} claims - Array of claims to validate
470
+ * @param {String} schemaName - Schema name for all claims
471
+ * @returns {Object} Prepared bulk validation data
472
+ */
473
+ prepareBulkClaimValidation: function(claims, schemaName) {
474
+ try {
475
+ $.checkArgument(Array.isArray(claims), 'Claims must be array')
476
+ $.checkArgument(typeof schemaName === 'string', 'Schema name must be string')
477
+
478
+ var validations = []
479
+ var errors = []
480
+ var totalValid = 0
481
+
482
+ claims.forEach(function(claim, index) {
483
+ try {
484
+ var validation = this.prepareClaimValidation(claim, schemaName)
485
+ if (validation.success && validation.validation.valid) {
486
+ totalValid++
487
+ }
488
+ validations.push({
489
+ index: index,
490
+ result: validation
491
+ })
492
+ } catch (error) {
493
+ errors.push({
494
+ index: index,
495
+ error: error.message
496
+ })
497
+ }
498
+ }.bind(this))
499
+
500
+ var batchId = this._generateBatchId()
501
+ var batchHash = Hash.sha256(Buffer.from(JSON.stringify(validations.map(v => v.result.validation.claimHash)))).toString('hex')
502
+
503
+ return {
504
+ success: true,
505
+ batchId: batchId,
506
+ batchHash: batchHash,
507
+ totalClaims: claims.length,
508
+ validClaims: totalValid,
509
+ invalidClaims: claims.length - totalValid,
510
+ validations: validations,
511
+ errors: errors,
512
+ externalOperations: {
513
+ storeBatch: {
514
+ endpoint: 'POST /claims/batch-validate',
515
+ data: {
516
+ batchId: batchId,
517
+ batchHash: batchHash,
518
+ schemaName: schemaName,
519
+ validations: validations
520
+ }
521
+ },
522
+ generateReport: {
523
+ endpoint: 'POST /reports/bulk-validation',
524
+ data: {
525
+ batchId: batchId,
526
+ summary: {
527
+ total: claims.length,
528
+ valid: totalValid,
529
+ invalid: claims.length - totalValid
530
+ }
531
+ }
532
+ }
533
+ }
534
+ }
535
+
536
+ } catch (error) {
537
+ return {
538
+ success: false,
539
+ error: 'Bulk validation preparation failed: ' + error.message
540
+ }
541
+ }
542
+ },
543
+
544
+ /**
545
+ * Canonicalize claim for hashing
546
+ * @param {Object} claim - Claim to canonicalize
547
+ * @returns {String} Canonical JSON string
548
+ */
549
+ canonicalize: function(claim) {
550
+ $.checkArgument(claim && typeof claim === 'object', 'Invalid claim')
551
+
552
+ // Sort keys recursively
553
+ var canonical = this._sortObjectKeys(claim)
554
+
555
+ // Return deterministic JSON
556
+ return JSON.stringify(canonical)
557
+ },
558
+
559
+ /**
560
+ * Hash claim
561
+ * @param {Object} claim - Claim to hash
562
+ * @returns {String} SHA256 hash
563
+ */
564
+ hash: function(claim) {
565
+ var canonical = this.canonicalize(claim)
566
+ return Hash.sha256(Buffer.from(canonical)).toString('hex')
567
+ },
568
+
569
+ /**
570
+ * Prepare claim template for external form generation
571
+ * @param {String} schemaName - Schema name
572
+ * @param {Object} options - Template options
573
+ * @returns {Object} Prepared template data
574
+ */
575
+ prepareClaimTemplate: function(schemaName, options) {
576
+ options = options || {}
577
+
578
+ try {
579
+ var schema = ClaimSchemas[schemaName]
580
+ if (!schema) {
581
+ return {
582
+ success: false,
583
+ error: 'Unknown schema: ' + schemaName
584
+ }
585
+ }
586
+
587
+ var template = this._createTemplateFromSchema(schema)
588
+ var formStructure = this._generateFormStructure(schema)
589
+
590
+ return {
591
+ success: true,
592
+ schemaName: schemaName,
593
+ template: template,
594
+ schema: schema,
595
+ formStructure: formStructure,
596
+ validation: {
597
+ required: schema.required || [],
598
+ patterns: this._extractPatterns(schema),
599
+ enums: this._extractEnums(schema)
600
+ },
601
+ externalOperations: {
602
+ generateForm: {
603
+ endpoint: 'POST /forms/generate',
604
+ data: {
605
+ schemaName: schemaName,
606
+ formStructure: formStructure,
607
+ template: template
608
+ }
609
+ },
610
+ saveTemplate: {
611
+ endpoint: 'POST /templates/claim',
612
+ data: {
613
+ schemaName: schemaName,
614
+ template: template,
615
+ customizations: options.customizations || {}
616
+ }
617
+ }
618
+ }
619
+ }
620
+
621
+ } catch (error) {
622
+ return {
623
+ success: false,
624
+ error: 'Template preparation failed: ' + error.message
625
+ }
626
+ }
627
+ },
628
+
629
+ /**
630
+ * Get available schemas
631
+ * @returns {Object} Available schemas
632
+ */
633
+ getSchemas: function() {
634
+ return ClaimSchemas
635
+ },
636
+
637
+ /**
638
+ * Get schema names
639
+ * @returns {Array} Available schema names
640
+ */
641
+ getSchemaNames: function() {
642
+ return Object.keys(ClaimSchemas)
643
+ },
644
+
645
+ /**
646
+ * Get schema definition
647
+ * @param {String} schemaName - Schema name
648
+ * @returns {Object} Schema definition
649
+ */
650
+ getSchema: function(schemaName) {
651
+ return ClaimSchemas[schemaName] || null
652
+ },
653
+
654
+ /**
655
+ * Add custom schema
656
+ * @param {String} name - Schema name
657
+ * @param {Object} schema - Schema definition
658
+ */
659
+ addSchema: function(name, schema) {
660
+ $.checkArgument(typeof name === 'string', 'Schema name must be string')
661
+ $.checkArgument(schema && typeof schema === 'object', 'Invalid schema')
662
+
663
+ ClaimSchemas[name] = schema
664
+ },
665
+
666
+ /**
667
+ * Create claim template
668
+ * @param {String} schemaName - Schema name
669
+ * @returns {Object} Claim template
670
+ */
671
+ createTemplate: function(schemaName) {
672
+ var schema = ClaimSchemas[schemaName]
673
+ if (!schema) {
674
+ throw new Error('Unknown schema: ' + schemaName)
675
+ }
676
+
677
+ return this._createTemplateFromSchema(schema)
678
+ },
679
+
680
+ /**
681
+ * Generate dispute ID
682
+ * @private
683
+ */
684
+ _generateDisputeId: function(claimHash) {
685
+ var data = 'dispute_' + claimHash + '_' + Date.now()
686
+ return Hash.sha256(Buffer.from(data)).toString('hex').substring(0, 16)
687
+ },
688
+
689
+ /**
690
+ * Generate batch ID
691
+ * @private
692
+ */
693
+ _generateBatchId: function() {
694
+ var data = 'batch_' + Date.now() + '_' + Math.random()
695
+ return Hash.sha256(Buffer.from(data)).toString('hex').substring(0, 16)
696
+ },
697
+
698
+ /**
699
+ * Generate form structure from schema
700
+ * @private
701
+ */
702
+ _generateFormStructure: function(schema) {
703
+ var structure = {
704
+ type: 'form',
705
+ title: schema.title || 'Legal Claim Form',
706
+ description: schema.description || '',
707
+ fields: []
708
+ }
709
+
710
+ if (schema.properties) {
711
+ Object.keys(schema.properties).forEach(function(key) {
712
+ var prop = schema.properties[key]
713
+ var field = {
714
+ name: key,
715
+ label: this._humanizeFieldName(key),
716
+ type: this._mapSchemaTypeToFormType(prop),
717
+ required: schema.required && schema.required.includes(key),
718
+ validation: this._extractFieldValidation(prop)
719
+ }
720
+
721
+ if (prop.enum) {
722
+ field.options = prop.enum.map(function(value) {
723
+ return { value: value, label: this._humanizeValue(value) }
724
+ }.bind(this))
725
+ }
726
+
727
+ structure.fields.push(field)
728
+ }.bind(this))
729
+ }
730
+
731
+ return structure
732
+ },
733
+
734
+ /**
735
+ * Extract patterns from schema
736
+ * @private
737
+ */
738
+ _extractPatterns: function(schema) {
739
+ var patterns = {}
740
+
741
+ if (schema.properties) {
742
+ Object.keys(schema.properties).forEach(function(key) {
743
+ var prop = schema.properties[key]
744
+ if (prop.pattern) {
745
+ patterns[key] = prop.pattern
746
+ }
747
+ })
748
+ }
749
+
750
+ return patterns
751
+ },
752
+
753
+ /**
754
+ * Extract enums from schema
755
+ * @private
756
+ */
757
+ _extractEnums: function(schema) {
758
+ var enums = {}
759
+
760
+ if (schema.properties) {
761
+ Object.keys(schema.properties).forEach(function(key) {
762
+ var prop = schema.properties[key]
763
+ if (prop.enum) {
764
+ enums[key] = prop.enum
765
+ }
766
+ })
767
+ }
768
+
769
+ return enums
770
+ },
771
+
772
+ /**
773
+ * Map schema type to form type
774
+ * @private
775
+ */
776
+ _mapSchemaTypeToFormType: function(prop) {
777
+ switch (prop.type) {
778
+ case 'string':
779
+ if (prop.enum) return 'select'
780
+ if (prop.format === 'date') return 'date'
781
+ if (prop.format === 'date-time') return 'datetime'
782
+ return 'text'
783
+ case 'number':
784
+ case 'integer':
785
+ return 'number'
786
+ case 'boolean':
787
+ return 'checkbox'
788
+ case 'array':
789
+ return 'list'
790
+ case 'object':
791
+ return 'fieldset'
792
+ default:
793
+ return 'text'
794
+ }
795
+ },
796
+
797
+ /**
798
+ * Extract field validation rules
799
+ * @private
800
+ */
801
+ _extractFieldValidation: function(prop) {
802
+ var validation = {}
803
+
804
+ if (prop.pattern) validation.pattern = prop.pattern
805
+ if (prop.minimum !== undefined) validation.min = prop.minimum
806
+ if (prop.maximum !== undefined) validation.max = prop.maximum
807
+ if (prop.format) validation.format = prop.format
808
+
809
+ return validation
810
+ },
811
+
812
+ /**
813
+ * Humanize field name
814
+ * @private
815
+ */
816
+ _humanizeFieldName: function(fieldName) {
817
+ return fieldName
818
+ .replace(/([A-Z])/g, ' $1')
819
+ .replace(/^./, function(str) { return str.toUpperCase() })
820
+ .replace(/_/g, ' ')
821
+ },
822
+
823
+ /**
824
+ * Humanize enum value
825
+ * @private
826
+ */
827
+ _humanizeValue: function(value) {
828
+ return value
829
+ .replace(/_/g, ' ')
830
+ .replace(/\b\w/g, function(l) { return l.toUpperCase() })
831
+ },
832
+
833
+ /**
834
+ * Validate object against schema
835
+ * @private
836
+ */
837
+ _validateObject: function(obj, schema, path, errors, warnings) {
838
+ if (schema.type === 'object') {
839
+ // Check required fields
840
+ if (schema.required && Array.isArray(schema.required)) {
841
+ schema.required.forEach(function(field) {
842
+ if (!obj.hasOwnProperty(field)) {
843
+ errors.push('Missing required field: ' + this._getFieldPath(path, field))
844
+ }
845
+ }.bind(this))
846
+ }
847
+
848
+ // Validate properties
849
+ if (schema.properties) {
850
+ Object.keys(obj).forEach(function(key) {
851
+ var fieldPath = this._getFieldPath(path, key)
852
+ var fieldSchema = schema.properties[key]
853
+
854
+ if (fieldSchema) {
855
+ this._validateValue(obj[key], fieldSchema, fieldPath, errors, warnings)
856
+ } else {
857
+ warnings.push('Unknown field: ' + fieldPath)
858
+ }
859
+ }.bind(this))
860
+ }
861
+ } else {
862
+ this._validateValue(obj, schema, path, errors, warnings)
863
+ }
864
+ },
865
+
866
+ /**
867
+ * Validate value against schema
868
+ * @private
869
+ */
870
+ _validateValue: function(value, schema, path, errors, warnings) {
871
+ // Type validation
872
+ if (schema.type) {
873
+ var actualType = Array.isArray(value) ? 'array' : typeof value
874
+ if (actualType !== schema.type) {
875
+ errors.push('Invalid type for ' + path + ': expected ' + schema.type + ', got ' + actualType)
876
+ return
877
+ }
878
+ }
879
+
880
+ // String validations
881
+ if (schema.type === 'string') {
882
+ if (schema.pattern && !new RegExp(schema.pattern).test(value)) {
883
+ errors.push('Value does not match pattern for ' + path)
884
+ }
885
+
886
+ if (schema.enum && !schema.enum.includes(value)) {
887
+ errors.push('Invalid enum value for ' + path + ': ' + value)
888
+ }
889
+
890
+ if (schema.format === 'date' && !this._isValidDate(value)) {
891
+ errors.push('Invalid date format for ' + path)
892
+ }
893
+
894
+ if (schema.format === 'date-time' && !this._isValidDateTime(value)) {
895
+ errors.push('Invalid date-time format for ' + path)
896
+ }
897
+ }
898
+
899
+ // Number validations
900
+ if (schema.type === 'number' || schema.type === 'integer') {
901
+ if (schema.minimum !== undefined && value < schema.minimum) {
902
+ errors.push('Value below minimum for ' + path + ': ' + value + ' < ' + schema.minimum)
903
+ }
904
+
905
+ if (schema.maximum !== undefined && value > schema.maximum) {
906
+ errors.push('Value above maximum for ' + path + ': ' + value + ' > ' + schema.maximum)
907
+ }
908
+
909
+ if (schema.type === 'integer' && !Number.isInteger(value)) {
910
+ errors.push('Value must be integer for ' + path)
911
+ }
912
+ }
913
+
914
+ // Array validations
915
+ if (schema.type === 'array' && schema.items) {
916
+ for (var i = 0; i < value.length; i++) {
917
+ this._validateValue(value[i], schema.items, path + '[' + i + ']', errors, warnings)
918
+ }
919
+ }
920
+
921
+ // Object validations
922
+ if (schema.type === 'object') {
923
+ this._validateObject(value, schema, path, errors, warnings)
924
+ }
925
+ },
926
+
927
+ /**
928
+ * Sort object keys recursively
929
+ * @private
930
+ */
931
+ _sortObjectKeys: function(obj) {
932
+ if (Array.isArray(obj)) {
933
+ return obj.map(this._sortObjectKeys.bind(this))
934
+ } else if (obj !== null && typeof obj === 'object') {
935
+ var sorted = {}
936
+ Object.keys(obj).sort().forEach(function(key) {
937
+ sorted[key] = this._sortObjectKeys(obj[key])
938
+ }.bind(this))
939
+ return sorted
940
+ }
941
+ return obj
942
+ },
943
+
944
+ /**
945
+ * Create template from schema
946
+ * @private
947
+ */
948
+ _createTemplateFromSchema: function(schema) {
949
+ if (schema.type === 'object') {
950
+ var template = {}
951
+
952
+ if (schema.properties) {
953
+ Object.keys(schema.properties).forEach(function(key) {
954
+ var propSchema = schema.properties[key]
955
+ template[key] = this._getDefaultValue(propSchema)
956
+ }.bind(this))
957
+ }
958
+
959
+ return template
960
+ }
961
+
962
+ return this._getDefaultValue(schema)
963
+ },
964
+
965
+ /**
966
+ * Get default value for schema type
967
+ * @private
968
+ */
969
+ _getDefaultValue: function(schema) {
970
+ if (schema.default !== undefined) {
971
+ return schema.default
972
+ }
973
+
974
+ switch (schema.type) {
975
+ case 'string':
976
+ if (schema.enum) return schema.enum[0]
977
+ if (schema.format === 'date') return '2023-01-01'
978
+ if (schema.format === 'date-time') return new Date().toISOString()
979
+ return 'PLACEHOLDER_STRING'
980
+
981
+ case 'number':
982
+ case 'integer':
983
+ return schema.minimum || 0
984
+
985
+ case 'boolean':
986
+ return false
987
+
988
+ case 'array':
989
+ return []
990
+
991
+ case 'object':
992
+ return this._createTemplateFromSchema(schema)
993
+
994
+ default:
995
+ return null
996
+ }
997
+ },
998
+
999
+ /**
1000
+ * Get field path
1001
+ * @private
1002
+ */
1003
+ _getFieldPath: function(basePath, field) {
1004
+ return basePath ? basePath + '.' + field : field
1005
+ },
1006
+
1007
+ /**
1008
+ * Validate date format
1009
+ * @private
1010
+ */
1011
+ _isValidDate: function(dateString) {
1012
+ var date = new Date(dateString)
1013
+ return !isNaN(date.getTime()) && dateString.match(/^\d{4}-\d{2}-\d{2}$/)
1014
+ },
1015
+
1016
+ /**
1017
+ * Validate date-time format
1018
+ * @private
1019
+ */
1020
+ _isValidDateTime: function(dateTimeString) {
1021
+ var date = new Date(dateTimeString)
1022
+ return !isNaN(date.getTime())
1023
+ }
1024
+ }
1025
+
1026
+ module.exports = ClaimValidator