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.
- package/CHANGELOG.md +147 -0
- package/architecture_demo.js +247 -0
- package/bsv-gdaf.min.js +37 -0
- package/bsv-ltp.min.js +37 -0
- package/bsv-shamir.min.js +12 -0
- package/bsv.bundle.js +9 -9
- package/build/bsv-smartcontract.min.js +10 -8
- package/build/bsv.bundle.js +9 -9
- package/build/bsv.min.js +10 -8
- package/build/webpack.gdaf.config.js +54 -0
- package/build/webpack.ltp.config.js +17 -0
- package/bundle-entry.js +77 -1
- package/complete_ltp_demo.js +511 -0
- package/gdaf-entry.js +54 -0
- package/index.js +259 -0
- package/lib/crypto/shamir.js +360 -0
- package/lib/gdaf/attestation-signer.js +461 -0
- package/lib/gdaf/attestation-verifier.js +600 -0
- package/lib/gdaf/did-resolver.js +382 -0
- package/lib/gdaf/index.js +471 -0
- package/lib/gdaf/schema-validator.js +682 -0
- package/lib/gdaf/smartledger-anchor.js +486 -0
- package/lib/gdaf/zk-prover.js +507 -0
- package/lib/ltp/anchor.js +438 -0
- package/lib/ltp/claim.js +1026 -0
- package/lib/ltp/index.js +470 -0
- package/lib/ltp/obligation.js +945 -0
- package/lib/ltp/proof.js +828 -0
- package/lib/ltp/registry.js +702 -0
- package/lib/ltp/right.js +765 -0
- package/lib/smart_contract/API_REFERENCE.md +1 -1
- package/lib/smart_contract/EXAMPLES.md +2 -2
- package/lib/smart_contract/QUICK_START.md +2 -2
- package/lib/smart_contract/README.md +1 -1
- package/ltp-entry.js +92 -0
- package/package.json +44 -4
- package/shamir-entry.js +173 -0
- package/shamir_demo.js +121 -0
- package/simple_demo.js +204 -0
- package/test_shamir.js +221 -0
- package/test_standalone_shamir.html +83 -0
package/lib/ltp/claim.js
ADDED
|
@@ -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
|