Skip to content

Commit 68cc43f

Browse files
author
Corey B
committed
fix: restore template rendering by matching data keys and adding dot-notation support
1 parent 4cbaae4 commit 68cc43f

4 files changed

Lines changed: 101 additions & 41 deletions

File tree

examples/bank_statement_v2.fdd

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,36 +9,47 @@
99
"VerifiableCredential"
1010
],
1111
"issuer": "did:web:spuds0588.github.io:open-fdd",
12-
"issuanceDate": "2026-04-12T04:55:18.670Z",
12+
"issuanceDate": "2026-04-12T04:59:53.662Z",
1313
"credentialSubject": {
14+
"bankName": "First National Digital",
1415
"accountHolder": "Jane Doe",
16+
"accountType": "Premium Checking",
1517
"accountNumber": "••••4242",
16-
"balance": "4,200.00",
17-
"statementDate": "2026-04-11",
18+
"statementPeriod": "April 1 - April 11, 2026",
19+
"openingBalance": "1,006.50",
20+
"totalCredits": "5,000.00",
21+
"totalDebits": "1,806.50",
22+
"closingBalance": "4,200.00",
1823
"transactions": [
1924
{
2025
"date": "2026-04-01",
2126
"description": "Deposit - Remote Work",
22-
"amount": "+$5,000.00"
27+
"type": "credit",
28+
"amount": "+$5,000.00",
29+
"balance": "6,006.50"
2330
},
2431
{
2532
"date": "2026-04-02",
2633
"description": "Rent Payment",
27-
"amount": "-$1,800.00"
34+
"type": "debit",
35+
"amount": "-$1,800.00",
36+
"balance": "4,206.50"
2837
},
2938
{
3039
"date": "2026-04-05",
3140
"description": "Coffee Shop",
32-
"amount": "-$6.50"
41+
"type": "debit",
42+
"amount": "-$6.50",
43+
"balance": "4,200.00"
3344
}
3445
]
3546
},
3647
"proof": {
3748
"type": "Ed25519Signature2020",
38-
"created": "2026-04-12T04:55:18.672Z",
49+
"created": "2026-04-12T04:59:53.664Z",
3950
"proofPurpose": "assertionMethod",
4051
"verificationMethod": "did:web:spuds0588.github.io:open-fdd#key-1",
41-
"signatureValue": "2ZRCWwerI3rpV2q5WnSvbEqL8IKjBYlUy35lr9FZEd7NRg5OakkS2y3n7GQtxhVerBQQ5pShYhu5VsBeaEg5Bg=="
52+
"signatureValue": "C5aabdPHoG8r42OhKODyD3bqglnV3GpU2+XbnpeD1vpMI/7zS4VmrFTpjbD04RnKQprihQgFgz8XzTXX7ne4Dg=="
4253
}
4354
}
4455
</script>

examples/legal_contract.fdd

Lines changed: 38 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,22 +9,51 @@
99
"VerifiableCredential"
1010
],
1111
"issuer": "did:web:spuds0588.github.io:open-fdd",
12-
"issuanceDate": "2026-04-12T04:55:18.680Z",
12+
"issuanceDate": "2026-04-12T04:59:53.668Z",
1313
"credentialSubject": {
14-
"contractType": "Master Service Agreement",
15-
"contractId": "ID-88-2026",
14+
"documentTitle": "Master Service Agreement",
15+
"documentId": "MSA-2026-88-SF",
1616
"effectiveDate": "2026-04-12",
17-
"clientName": "Cyberdyne Systems",
18-
"providerName": "DigiCore Solutions",
19-
"projectScope": "Cloud Migration Phase 1",
20-
"budget": "$250,000.00"
17+
"expirationDate": "2027-04-12",
18+
"party1": {
19+
"role": "Service Provider",
20+
"name": "DigiCore Solutions Inc.",
21+
"representative": "Sarah Chen",
22+
"address": "101 Silicon Way, San Francisco, CA"
23+
},
24+
"party2": {
25+
"role": "Client",
26+
"name": "Cyberdyne Systems",
27+
"representative": "John Connor",
28+
"address": "500 SkyNet Blvd, Los Angeles, CA"
29+
},
30+
"scopeOfServices": "Cloud Infrastructure Migration (Phase 1: AWS to Azure), ongoing 24/7 monitoring, and security auditing.",
31+
"compensation": "Fixed fee of $250,000.00 payable in monthly installments upon milestone completion.",
32+
"clauses": [
33+
{
34+
"number": "1",
35+
"title": "Intellectual Property",
36+
"text": "All work product created under this agreement remains the sole property of the Client."
37+
},
38+
{
39+
"number": "2",
40+
"title": "Confidentiality",
41+
"text": "Both parties agree to maintain strict confidentiality regarding proprietary architectural data."
42+
},
43+
{
44+
"number": "3",
45+
"title": "Termination",
46+
"text": "This agreement may be terminated by either party with 30 days written notice."
47+
}
48+
],
49+
"governingLaw": "State of California"
2150
},
2251
"proof": {
2352
"type": "Ed25519Signature2020",
24-
"created": "2026-04-12T04:55:18.681Z",
53+
"created": "2026-04-12T04:59:53.668Z",
2554
"proofPurpose": "assertionMethod",
2655
"verificationMethod": "did:web:spuds0588.github.io:open-fdd#key-1",
27-
"signatureValue": "0ptkS/EnLrPiYOhojS23KpPk83Iu5Em8L8cCP6e/CDpKawOBN6RvWDyIVuZODCjP1LwQFt29iWa46D031lOmAA=="
56+
"signatureValue": "Aq5fc+w2RuIwzvr3yh3KY9ZZtKIlgzX5AoRM4MIJBW4rH2hFLARi2DIQOgL8/dMv4Mr9wd6532xL5e5eIQNZCw=="
2857
}
2958
}
3059
</script>

examples/medical_lab_result.fdd

Lines changed: 18 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -9,45 +9,46 @@
99
"VerifiableCredential"
1010
],
1111
"issuer": "did:web:spuds0588.github.io:open-fdd",
12-
"issuanceDate": "2026-04-12T04:55:18.677Z",
12+
"issuanceDate": "2026-04-12T04:59:53.666Z",
1313
"credentialSubject": {
14-
"patientName": "Alexandra Rivera",
15-
"dateOfBirth": "1987-03-22",
16-
"patientId": "VM-2026-00419",
17-
"orderingPhysician": "Dr. Samuel Wong, MD",
1814
"labName": "Valley Medical Diagnostics",
1915
"collectionDate": "2026-04-08",
2016
"reportDate": "2026-04-10",
17+
"patientId": "VM-2026-00419",
18+
"patientName": "Alexandra Rivera",
19+
"dateOfBirth": "1987-03-22",
20+
"orderingPhysician": "Dr. Samuel Wong, MD",
2121
"results": [
2222
{
2323
"test": "Glucose, Fasting",
24-
"result": "94",
24+
"value": "94",
2525
"unit": "mg/dL",
26-
"range": "70-99",
27-
"status": "Normal"
26+
"referenceRange": "70-99",
27+
"flag": "Normal"
2828
},
2929
{
3030
"test": "Hemoglobin A1c",
31-
"result": "5.4",
31+
"value": "5.4",
3232
"unit": "%",
33-
"range": "< 5.7",
34-
"status": "Normal"
33+
"referenceRange": "< 5.7",
34+
"flag": "Normal"
3535
},
3636
{
3737
"test": "Cholesterol, Total",
38-
"result": "212",
38+
"value": "212",
3939
"unit": "mg/dL",
40-
"range": "< 200",
41-
"status": "Borderline High"
40+
"referenceRange": "< 200",
41+
"flag": "Borderline"
4242
}
43-
]
43+
],
44+
"physicianNotes": "Patient exhibits borderline high cholesterol. Recommend dietary adjustment and follow-up in 6 months."
4445
},
4546
"proof": {
4647
"type": "Ed25519Signature2020",
47-
"created": "2026-04-12T04:55:18.678Z",
48+
"created": "2026-04-12T04:59:53.667Z",
4849
"proofPurpose": "assertionMethod",
4950
"verificationMethod": "did:web:spuds0588.github.io:open-fdd#key-1",
50-
"signatureValue": "DyYV2nnqZNTr1h5jM7p791MGQ53i3kg4uTTbH2LEdghg3mgguPUSdXo97KexvCbg6dmghT0U4/QK0ra3vMfGBw=="
51+
"signatureValue": "8THz8sVgcfRoNzC0vg17nhkMgf7047URSMk1uiQuY+hSfYhycneqF/VlJVlXX+XrYBkLAyZAuN8j1Xc2wgXyCA=="
5152
}
5253
}
5354
</script>

site/sandbox.html

Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -496,18 +496,37 @@ <h1>Statement for {{accountHolder}}</h1>
496496
set('render','run', N('Merging credentialSubject + mutableData into template mustache bindings…'));
497497
const ctx = {...(vc.credentialSubject||{}), ...mutable};
498498
let html = tmpl;
499-
html = html.replace(/\{\{#each\s+(.*?)\}\}([\s\S]*?)\{\{\/each\}\}/g, (_,k,body) => {
500-
const arr = ctx[k.trim()]; if (!Array.isArray(arr)) return '';
501-
return arr.map((item,i) => {
499+
500+
// Helper to resolve nested paths like "party1.name"
501+
const getVal = (obj, path) => {
502+
try {
503+
return path.split('.').reduce((o, i) => o[i], obj) ?? '';
504+
} catch { return ''; }
505+
};
506+
507+
// 1. Handle {{#each key}} ... {{/each}}
508+
html = html.replace(/\{\{#each\s+(.*?)\}\}([\s\S]*?)\{\{\/each\}\}/g, (_, k, body) => {
509+
const arr = getVal(ctx, k.trim());
510+
if (!Array.isArray(arr)) return '';
511+
return arr.map((item, i) => {
502512
let out = body.replace(/\{\{\s*@index\s*\}\}/g, i);
503-
Object.keys(item).forEach(key => { out = out.replace(new RegExp(`\\{\\{\\s*${key}\\s*\\}\\}`, 'g'), item[key]??''); });
513+
// Item-level replacement
514+
const matches = out.match(/\{\{\s*(.*?)\s*\}\}/g) || [];
515+
matches.forEach(m => {
516+
const path = m.replace(/[\{\}\s]/g, '');
517+
if (path === '@index') return;
518+
out = out.replace(m, getVal(item, path));
519+
});
504520
return out;
505521
}).join('');
506522
});
507-
Object.keys(ctx).forEach(k => {
508-
if (typeof ctx[k] !== 'object') html = html.replace(new RegExp(`\\{\\{\\s*${k}\\s*\\}\\}`, 'g'), ctx[k]??'');
523+
524+
// 2. Handle simple {{key.path}}
525+
const topMatches = html.match(/\{\{\s*(.*?)\s*\}\}/g) || [];
526+
topMatches.forEach(m => {
527+
const path = m.replace(/[\{\}\s]/g, '');
528+
html = html.replace(m, getVal(ctx, path));
509529
});
510-
html = html.replace(/\{\{.*?\}\}/g, '');
511530

512531
// Render into iframe
513532
const iframe = document.getElementById('reader-iframe');

0 commit comments

Comments
 (0)