@xrplkit/xls26 2.4.0 → 2.6.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/package.json +1 -1
- package/readme.md +44 -54
- package/xls26.js +183 -102
package/package.json
CHANGED
package/readme.md
CHANGED
|
@@ -22,29 +22,27 @@ console.log(xls26Data)
|
|
|
22
22
|
|
|
23
23
|
```toml
|
|
24
24
|
[[ISSUERS]]
|
|
25
|
-
address = "
|
|
26
|
-
name = "
|
|
27
|
-
|
|
28
|
-
[[ISSUERS.WEBLINKS]]
|
|
29
|
-
url = "https://aesthetes.art"
|
|
30
|
-
type = "info"
|
|
31
|
-
title = "Official Website"
|
|
32
|
-
|
|
33
|
-
[[ISSUERS.WEBLINKS]]
|
|
34
|
-
url = "https://twitter.com/aesthetes_art"
|
|
35
|
-
type = "socialmedia"
|
|
25
|
+
address = "rMxCKbEDwqr76QuheSUMdEGf4B9xJ8m5De"
|
|
26
|
+
name = "Ripple"
|
|
27
|
+
desc = "We're building the Internet of Value."
|
|
36
28
|
|
|
37
29
|
[[TOKENS]]
|
|
38
|
-
issuer = "
|
|
39
|
-
currency = "
|
|
40
|
-
name = "
|
|
41
|
-
desc = "
|
|
42
|
-
icon = "https://
|
|
43
|
-
asset_class = "
|
|
30
|
+
issuer = "rMxCKbEDwqr76QuheSUMdEGf4B9xJ8m5De"
|
|
31
|
+
currency = "RLUSD"
|
|
32
|
+
name = "Ripple USD"
|
|
33
|
+
desc = "Ripple USD (RLUSD) is natively issued on the XRP Ledger and Ethereum blockchains and is enabled with a number of features to ensure strict adherence to compliance standards, flexibility for developers, and security for holders."
|
|
34
|
+
icon = "https://ripple.com/assets/rlusd-logo.png"
|
|
35
|
+
asset_class = "rwa"
|
|
36
|
+
asset_subclass = "stablecoin"
|
|
37
|
+
|
|
38
|
+
[[TOKENS.URLS]]
|
|
39
|
+
url = "https://ripple.com"
|
|
40
|
+
type = "website"
|
|
41
|
+
title = "Official Website"
|
|
44
42
|
|
|
45
|
-
[[TOKENS.
|
|
46
|
-
url = "https://
|
|
47
|
-
type = "
|
|
43
|
+
[[TOKENS.URLS]]
|
|
44
|
+
url = "https://x.com/ripple"
|
|
45
|
+
type = "social"
|
|
48
46
|
```
|
|
49
47
|
|
|
50
48
|
|
|
@@ -52,40 +50,32 @@ type = "community"
|
|
|
52
50
|
|
|
53
51
|
```javascript
|
|
54
52
|
{
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
tokens: [
|
|
53
|
+
issuers: [
|
|
54
|
+
{
|
|
55
|
+
address: 'rMxCKbEDwqr76QuheSUMdEGf4B9xJ8m5De',
|
|
56
|
+
name: 'Ripple',
|
|
57
|
+
desc: "We're building the Internet of Value."
|
|
58
|
+
}
|
|
59
|
+
],
|
|
60
|
+
tokens: [
|
|
61
|
+
{
|
|
62
|
+
currency: 'RLUSD',
|
|
63
|
+
issuer: 'rMxCKbEDwqr76QuheSUMdEGf4B9xJ8m5De',
|
|
64
|
+
name: 'Ripple USD',
|
|
65
|
+
desc: 'Ripple USD (RLUSD) is natively issued on the XRP Ledger and Ethereum blockchains and is enabled with a number of features to ensure strict adherence to compliance standards, flexibility for developers, and security for holders.',
|
|
66
|
+
icon: 'https://ripple.com/assets/rlusd-logo.png',
|
|
67
|
+
asset_class: 'rwa',
|
|
68
|
+
asset_subclass: 'stablecoin',
|
|
69
|
+
urls: [
|
|
73
70
|
{
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
type: 'community'
|
|
84
|
-
}
|
|
85
|
-
]
|
|
86
|
-
}
|
|
87
|
-
],
|
|
88
|
-
issues: []
|
|
71
|
+
url: 'https://ripple.com',
|
|
72
|
+
type: 'website',
|
|
73
|
+
title: 'Official Website'
|
|
74
|
+
},
|
|
75
|
+
{ url: 'https://x.com/ripple', type: 'social' }
|
|
76
|
+
]
|
|
77
|
+
}
|
|
78
|
+
],
|
|
79
|
+
issues: []
|
|
89
80
|
}
|
|
90
|
-
|
|
91
81
|
```
|
package/xls26.js
CHANGED
|
@@ -1,19 +1,50 @@
|
|
|
1
1
|
// The XLS-26 standard adds additional asset metadata fields to the existing xrp-ledger.toml standard,
|
|
2
2
|
// https://github.com/XRPLF/XRPL-Standards/discussions/71
|
|
3
3
|
// This package provides an implementation for a parser according to this standard.
|
|
4
|
+
// Version 5 from 2025-06-06.
|
|
4
5
|
|
|
5
6
|
|
|
6
7
|
import { parse as parseToml } from '@xrplkit/toml'
|
|
7
8
|
|
|
8
|
-
const
|
|
9
|
-
|
|
10
|
-
'
|
|
11
|
-
'
|
|
12
|
-
'
|
|
13
|
-
'
|
|
14
|
-
'
|
|
9
|
+
const validUrlRegex = /^(https?)|(ipfs):\/\/.*$/
|
|
10
|
+
const validUrlTypes = {
|
|
11
|
+
website: 'website',
|
|
12
|
+
social: 'social',
|
|
13
|
+
docs: 'docs',
|
|
14
|
+
other: 'other',
|
|
15
|
+
info: 'website',
|
|
16
|
+
socialmedia: 'social',
|
|
17
|
+
community: 'social',
|
|
18
|
+
support: 'website',
|
|
19
|
+
whitepaper: 'docs',
|
|
20
|
+
certificate: 'docs',
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const validAssetClasses = [
|
|
24
|
+
'rwa',
|
|
25
|
+
'memes',
|
|
26
|
+
'wrapped',
|
|
27
|
+
'gaming',
|
|
28
|
+
'defi',
|
|
29
|
+
'other'
|
|
15
30
|
]
|
|
16
31
|
|
|
32
|
+
const validAssetSubClasses = [
|
|
33
|
+
'stablecoin',
|
|
34
|
+
'commodity',
|
|
35
|
+
'real_estate',
|
|
36
|
+
'private_credit',
|
|
37
|
+
'equity',
|
|
38
|
+
'treasury',
|
|
39
|
+
'other'
|
|
40
|
+
]
|
|
41
|
+
|
|
42
|
+
const legacyAssetClasses = {
|
|
43
|
+
fiat: { asset_class: 'rwa', asset_subclass: 'stablecoin' },
|
|
44
|
+
commodity: { asset_class: 'rwa', asset_subclass: 'commodity' },
|
|
45
|
+
equity: { asset_class: 'rwa', asset_subclass: 'equity' }
|
|
46
|
+
}
|
|
47
|
+
|
|
17
48
|
const validAdvisoryTypes = [
|
|
18
49
|
'scam',
|
|
19
50
|
'spam',
|
|
@@ -22,17 +53,10 @@ const validAdvisoryTypes = [
|
|
|
22
53
|
'hijacked'
|
|
23
54
|
]
|
|
24
55
|
|
|
25
|
-
const validAssetClasses = [
|
|
26
|
-
'fiat',
|
|
27
|
-
'commodity',
|
|
28
|
-
'equity',
|
|
29
|
-
'cryptocurrency'
|
|
30
|
-
]
|
|
31
|
-
|
|
32
56
|
const issuerFields = [
|
|
33
57
|
{
|
|
34
58
|
key: 'address',
|
|
35
|
-
|
|
59
|
+
required: true,
|
|
36
60
|
validate: v => {
|
|
37
61
|
if(!/^[rpshnaf39wBUDNEGHJKLM4PQRST7VWXYZ2bcdeCg65jkm8oFqi1tuvAxyz]{25,35}$/.test(v))
|
|
38
62
|
throw 'is not a valid XRPL address'
|
|
@@ -42,49 +66,49 @@ const issuerFields = [
|
|
|
42
66
|
key: 'name',
|
|
43
67
|
validate: v => {
|
|
44
68
|
if(typeof v !== 'string' || v.length === 0)
|
|
45
|
-
throw '
|
|
69
|
+
throw 'must be a non empty string'
|
|
46
70
|
}
|
|
47
71
|
},
|
|
48
72
|
{
|
|
49
|
-
key: '
|
|
50
|
-
alternativeKeys: ['
|
|
73
|
+
key: 'desc',
|
|
74
|
+
alternativeKeys: ['description'],
|
|
51
75
|
validate: v => {
|
|
52
76
|
if(typeof v !== 'string' || v.length === 0)
|
|
53
|
-
throw '
|
|
77
|
+
throw 'must be a non empty string'
|
|
54
78
|
}
|
|
55
79
|
},
|
|
56
80
|
{
|
|
57
81
|
key: 'domain',
|
|
58
82
|
validate: v => {
|
|
59
83
|
if(typeof v !== 'string' || v.length === 0)
|
|
60
|
-
throw '
|
|
84
|
+
throw 'must be a non empty string'
|
|
61
85
|
}
|
|
62
86
|
},
|
|
63
87
|
{
|
|
64
88
|
key: 'icon',
|
|
65
89
|
alternativeKeys: ['avatar'],
|
|
66
90
|
validate: v => {
|
|
67
|
-
if(
|
|
68
|
-
throw '
|
|
91
|
+
if(!validUrlRegex.test(v))
|
|
92
|
+
throw 'must be a valid URL that starts with "http" or "ipfs"'
|
|
69
93
|
}
|
|
70
94
|
},
|
|
71
95
|
{
|
|
72
96
|
key: 'trust_level',
|
|
73
97
|
validate: v => {
|
|
74
98
|
if(v !== parseInt(v))
|
|
75
|
-
throw '
|
|
99
|
+
throw 'must be a integer'
|
|
76
100
|
|
|
77
101
|
if(v < 0 || v > 3)
|
|
78
|
-
throw '
|
|
102
|
+
throw 'must be between 0 and 3'
|
|
79
103
|
}
|
|
80
104
|
}
|
|
81
105
|
]
|
|
82
106
|
|
|
83
|
-
const
|
|
107
|
+
const iouTokenFields = [
|
|
84
108
|
{
|
|
85
109
|
key: 'currency',
|
|
86
110
|
alternativeKeys: ['code'],
|
|
87
|
-
|
|
111
|
+
required: true,
|
|
88
112
|
validate: v => {
|
|
89
113
|
if(typeof v !== 'string' && v.length < 3)
|
|
90
114
|
throw 'is not a valid XRPL currency code'
|
|
@@ -92,7 +116,7 @@ const tokenFields = [
|
|
|
92
116
|
},
|
|
93
117
|
{
|
|
94
118
|
key: 'issuer',
|
|
95
|
-
|
|
119
|
+
required: true,
|
|
96
120
|
validate: v => {
|
|
97
121
|
if(!/^[rpshnaf39wBUDNEGHJKLM4PQRST7VWXYZ2bcdeCg65jkm8oFqi1tuvAxyz]{25,35}$/.test(v))
|
|
98
122
|
throw 'is not a valid XRPL address'
|
|
@@ -102,65 +126,97 @@ const tokenFields = [
|
|
|
102
126
|
key: 'name',
|
|
103
127
|
validate: v => {
|
|
104
128
|
if(typeof v !== 'string' || v.length === 0)
|
|
105
|
-
throw '
|
|
129
|
+
throw 'must be a non empty string'
|
|
106
130
|
}
|
|
107
131
|
},
|
|
108
132
|
{
|
|
109
|
-
key: '
|
|
110
|
-
alternativeKeys: ['
|
|
133
|
+
key: 'desc',
|
|
134
|
+
alternativeKeys: ['description'],
|
|
111
135
|
validate: v => {
|
|
112
136
|
if(typeof v !== 'string' || v.length === 0)
|
|
113
|
-
throw '
|
|
137
|
+
throw 'must be a non empty string'
|
|
114
138
|
}
|
|
115
139
|
},
|
|
116
140
|
{
|
|
117
141
|
key: 'icon',
|
|
118
142
|
alternativeKeys: ['avatar'],
|
|
119
143
|
validate: v => {
|
|
120
|
-
if(
|
|
121
|
-
throw '
|
|
144
|
+
if(!validUrlRegex.test(v))
|
|
145
|
+
throw 'must be a valid URL starting with "http" or "ipfs"'
|
|
122
146
|
}
|
|
123
147
|
},
|
|
124
148
|
{
|
|
125
149
|
key: 'trust_level',
|
|
126
150
|
validate: v => {
|
|
127
151
|
if(v !== parseInt(v))
|
|
128
|
-
throw '
|
|
152
|
+
throw 'must be a integer'
|
|
129
153
|
|
|
130
154
|
if(v < 0 || v > 3)
|
|
131
|
-
throw '
|
|
155
|
+
throw 'must be between 0 and 3'
|
|
132
156
|
}
|
|
133
157
|
},
|
|
134
158
|
{
|
|
135
159
|
key: 'asset_class',
|
|
136
160
|
validate: v => {
|
|
137
|
-
if(!validAssetClasses.includes(v))
|
|
138
|
-
throw `
|
|
161
|
+
if(!legacyAssetClasses[v] && !validAssetClasses.includes(v))
|
|
162
|
+
throw `must be one of: ${validAssetClasses.join(', ')}`
|
|
163
|
+
}
|
|
164
|
+
},
|
|
165
|
+
{
|
|
166
|
+
key: 'asset_subclass',
|
|
167
|
+
validate: v => {
|
|
168
|
+
if(!validAssetSubClasses.includes(v))
|
|
169
|
+
throw `must be one of: ${validAssetSubClasses.join(', ')}`
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
]
|
|
173
|
+
|
|
174
|
+
const mpTokenFields = [
|
|
175
|
+
{
|
|
176
|
+
key: 'mpt_issuance_id',
|
|
177
|
+
required: true,
|
|
178
|
+
validate: v => {
|
|
179
|
+
if(!/^[0-9a-fA-F]{48}$/.test(v))
|
|
180
|
+
throw 'is not a valid mpt_issuance_id'
|
|
181
|
+
}
|
|
182
|
+
},
|
|
183
|
+
{
|
|
184
|
+
key: 'trust_level',
|
|
185
|
+
required: true,
|
|
186
|
+
validate: v => {
|
|
187
|
+
if(v !== parseInt(v))
|
|
188
|
+
throw 'must be a integer'
|
|
189
|
+
|
|
190
|
+
if(v < 0 || v > 3)
|
|
191
|
+
throw 'must be between 0 and 3'
|
|
139
192
|
}
|
|
140
193
|
}
|
|
141
194
|
]
|
|
142
195
|
|
|
143
|
-
const
|
|
196
|
+
const urlFields = [
|
|
144
197
|
{
|
|
145
198
|
key: 'url',
|
|
146
|
-
|
|
199
|
+
required: true,
|
|
147
200
|
validate: v => {
|
|
148
|
-
if(
|
|
149
|
-
throw '
|
|
201
|
+
if(!validUrlRegex.test(v))
|
|
202
|
+
throw 'must be a valid URL starting with "http" or "ipfs"'
|
|
150
203
|
}
|
|
151
204
|
},
|
|
152
205
|
{
|
|
153
206
|
key: 'type',
|
|
154
207
|
validate: v => {
|
|
155
|
-
if(!
|
|
156
|
-
throw `
|
|
208
|
+
if(!validUrlTypes[v])
|
|
209
|
+
throw `must be one of: ${Array.from(new Set(Object.values(validUrlTypes))).join(', ')}`
|
|
210
|
+
},
|
|
211
|
+
transform: v => {
|
|
212
|
+
return validUrlTypes[v]
|
|
157
213
|
}
|
|
158
214
|
},
|
|
159
215
|
{
|
|
160
216
|
key: 'title',
|
|
161
217
|
validate: v => {
|
|
162
218
|
if(typeof v !== 'string' || v.length === 0)
|
|
163
|
-
throw '
|
|
219
|
+
throw 'must be a non empty string'
|
|
164
220
|
}
|
|
165
221
|
},
|
|
166
222
|
]
|
|
@@ -168,7 +224,7 @@ const weblinkFields = [
|
|
|
168
224
|
const advisoryFields = [
|
|
169
225
|
{
|
|
170
226
|
key: 'address',
|
|
171
|
-
|
|
227
|
+
required: true,
|
|
172
228
|
validate: v => {
|
|
173
229
|
if(!/^[rpshnaf39wBUDNEGHJKLM4PQRST7VWXYZ2bcdeCg65jkm8oFqi1tuvAxyz]{25,35}$/.test(v))
|
|
174
230
|
throw 'is not a valid XRPL address'
|
|
@@ -178,7 +234,7 @@ const advisoryFields = [
|
|
|
178
234
|
key: 'type',
|
|
179
235
|
validate: v => {
|
|
180
236
|
if(!validAdvisoryTypes.includes(v))
|
|
181
|
-
throw `
|
|
237
|
+
throw `must be one of: ${validAdvisoryTypes.join(', ')}`
|
|
182
238
|
}
|
|
183
239
|
},
|
|
184
240
|
{
|
|
@@ -186,7 +242,7 @@ const advisoryFields = [
|
|
|
186
242
|
alternativeKeys: ['desc'],
|
|
187
243
|
validate: v => {
|
|
188
244
|
if(typeof v !== 'string' || v.length === 0)
|
|
189
|
-
throw '
|
|
245
|
+
throw 'must be a non empty string'
|
|
190
246
|
}
|
|
191
247
|
}
|
|
192
248
|
]
|
|
@@ -204,71 +260,67 @@ export function parse(str){
|
|
|
204
260
|
let advisories = []
|
|
205
261
|
|
|
206
262
|
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
let { valid, parsed: issuer, issues: issuerIssues } = parseStanza(stanza, issuerFields)
|
|
263
|
+
for(let stanza of (toml.ISSUERS || toml.ACCOUNTS || [])){
|
|
264
|
+
let { valid, parsed: issuer, issues: issuerIssues } = parseStanza(stanza, issuerFields)
|
|
210
265
|
|
|
211
|
-
|
|
212
|
-
|
|
266
|
+
issues.push(
|
|
267
|
+
...issuerIssues.map(
|
|
268
|
+
issue => `[[ISSUERS]] ${issue}`
|
|
269
|
+
)
|
|
270
|
+
)
|
|
271
|
+
|
|
272
|
+
if(valid)
|
|
273
|
+
issuers.push(issuer)
|
|
274
|
+
else
|
|
275
|
+
continue
|
|
276
|
+
|
|
277
|
+
for(let substanza of (stanza.URLS || stanza.WEBLINKS || [])){
|
|
278
|
+
let { valid, parsed: url, issues: urlIssues } = parseStanza(substanza, urlFields)
|
|
279
|
+
|
|
280
|
+
if(valid){
|
|
281
|
+
issuer.urls = [
|
|
282
|
+
...(issuer.urls || []),
|
|
283
|
+
url
|
|
284
|
+
]
|
|
285
|
+
}
|
|
213
286
|
|
|
214
287
|
issues.push(
|
|
215
|
-
...
|
|
216
|
-
issue => `[[ISSUERS]] ${issue}`
|
|
288
|
+
...urlIssues.map(
|
|
289
|
+
issue => `[[ISSUERS.URLS]] ${issue}`
|
|
217
290
|
)
|
|
218
291
|
)
|
|
219
|
-
|
|
220
|
-
if(valid && stanza.WEBLINKS){
|
|
221
|
-
for(let substanza of stanza.WEBLINKS){
|
|
222
|
-
let { valid, parsed: weblink, issues: weblinkIssues } = parseStanza(substanza, weblinkFields)
|
|
223
|
-
|
|
224
|
-
if(valid){
|
|
225
|
-
issuer.weblinks = [
|
|
226
|
-
...(issuer.weblinks || []),
|
|
227
|
-
weblink
|
|
228
|
-
]
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
issues.push(
|
|
232
|
-
...weblinkIssues.map(
|
|
233
|
-
issue => `[[WEBLINK]] ${issue}`
|
|
234
|
-
)
|
|
235
|
-
)
|
|
236
|
-
}
|
|
237
|
-
}
|
|
238
292
|
}
|
|
239
293
|
}
|
|
240
294
|
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
let { valid, parsed: token, issues: tokenIssues } = parseStanza(stanza, tokenFields)
|
|
295
|
+
for(let stanza of (toml.TOKENS || toml.CURRENCIES || [])){
|
|
296
|
+
let { valid, parsed: token, issues: tokenIssues } = parseStanza(stanza, stanza['mpt_issuance_id'] != null ? mpTokenFields : iouTokenFields)
|
|
244
297
|
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
issues.push(
|
|
249
|
-
...tokenIssues.map(
|
|
250
|
-
issue => `[[TOKENS]] ${issue}`
|
|
251
|
-
)
|
|
298
|
+
issues.push(
|
|
299
|
+
...tokenIssues.map(
|
|
300
|
+
issue => `[[TOKENS]] ${issue}`
|
|
252
301
|
)
|
|
302
|
+
)
|
|
253
303
|
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
issue => `[[WEBLINK]] ${issue}`
|
|
268
|
-
)
|
|
269
|
-
)
|
|
270
|
-
}
|
|
304
|
+
if(valid)
|
|
305
|
+
tokens.push(token)
|
|
306
|
+
else
|
|
307
|
+
continue
|
|
308
|
+
|
|
309
|
+
for(let substanza of (stanza.URLS || stanza.WEBLINKS || [])){
|
|
310
|
+
let { valid, parsed: url, issues: urlIssues } = parseStanza(substanza, urlFields)
|
|
311
|
+
|
|
312
|
+
if(valid){
|
|
313
|
+
token.urls = [
|
|
314
|
+
...(token.urls || []),
|
|
315
|
+
url
|
|
316
|
+
]
|
|
271
317
|
}
|
|
318
|
+
|
|
319
|
+
issues.push(
|
|
320
|
+
...urlIssues.map(
|
|
321
|
+
issue => `[[TOKENS.URLS]] ${issue}`
|
|
322
|
+
)
|
|
323
|
+
)
|
|
272
324
|
}
|
|
273
325
|
}
|
|
274
326
|
|
|
@@ -287,6 +339,32 @@ export function parse(str){
|
|
|
287
339
|
}
|
|
288
340
|
}
|
|
289
341
|
|
|
342
|
+
// Issuer URLs have been dropped since Version 5
|
|
343
|
+
// Issuer URLs now get mapped to respective tokens
|
|
344
|
+
|
|
345
|
+
for(let issuer of issuers){
|
|
346
|
+
if(!issuer.urls)
|
|
347
|
+
continue
|
|
348
|
+
|
|
349
|
+
for(let token of tokens){
|
|
350
|
+
if(token.issuer !== issuer.address)
|
|
351
|
+
continue
|
|
352
|
+
|
|
353
|
+
token.urls = [
|
|
354
|
+
...issuer.urls,
|
|
355
|
+
...(token.urls || [])
|
|
356
|
+
]
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
delete issuer.urls
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
for(let token of tokens){
|
|
363
|
+
if(!legacyAssetClasses[token.asset_class])
|
|
364
|
+
continue
|
|
365
|
+
|
|
366
|
+
Object.assign(token, legacyAssetClasses[token.asset_class])
|
|
367
|
+
}
|
|
290
368
|
|
|
291
369
|
return {
|
|
292
370
|
issuers,
|
|
@@ -301,7 +379,7 @@ function parseStanza(stanza, schemas){
|
|
|
301
379
|
let issues = []
|
|
302
380
|
let valid = true
|
|
303
381
|
|
|
304
|
-
for(let { key, alternativeKeys,
|
|
382
|
+
for(let { key, alternativeKeys, required, validate, transform } of schemas){
|
|
305
383
|
let keys = [key]
|
|
306
384
|
|
|
307
385
|
if(alternativeKeys)
|
|
@@ -322,11 +400,14 @@ function parseStanza(stanza, schemas){
|
|
|
322
400
|
}
|
|
323
401
|
}
|
|
324
402
|
|
|
403
|
+
if(transform)
|
|
404
|
+
value = transform(value)
|
|
405
|
+
|
|
325
406
|
parsed[key] = value
|
|
326
407
|
break
|
|
327
408
|
}
|
|
328
409
|
|
|
329
|
-
if(
|
|
410
|
+
if(required && parsed[key] === undefined){
|
|
330
411
|
issues.push(`${key} field missing: skipping stanza`)
|
|
331
412
|
valid = false
|
|
332
413
|
}
|