@metalabel/dfos-protocol 0.52.1 → 0.54.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/README.md +7 -0
- package/dist/chain/index.d.ts +33 -5
- package/dist/chain/index.js +6 -4
- package/dist/{chunk-BMIOY5W5.js → chunk-ISHAX5WN.js} +1 -1
- package/dist/{chunk-KAW4MT4P.js → chunk-NUIIVAZ3.js} +6 -1
- package/dist/{chunk-Q7FREWMY.js → chunk-QIM3KZXZ.js} +14 -8
- package/dist/{chunk-FZHHSC5N.js → chunk-RPA44ND4.js} +117 -20
- package/dist/credentials/index.d.ts +95 -14
- package/dist/credentials/index.js +14 -2
- package/dist/crypto/index.js +1 -1
- package/dist/{dfos-credential-j-kHtFvG.d.ts → dfos-credential-DgT8XVTm.d.ts} +37 -7
- package/dist/index.d.ts +3 -3
- package/dist/index.js +18 -4
- package/dist/key-proof/index.js +2 -2
- package/examples/api-resource-coverage.json +413 -0
- package/package.json +1 -1
|
@@ -0,0 +1,413 @@
|
|
|
1
|
+
{
|
|
2
|
+
"description": "Reference vectors for the api: resource family — parse, coverage, attenuation, and request matching. Consumed by the TS credentials tests and the Go delegation tests (both twins read this file; neither inlines a row). Space ids are 31 characters of the did:dfos identifier alphabet [2346789acdefhknrtvz]; hosts are lowercase authorities, port included when non-default.",
|
|
3
|
+
"ids": {
|
|
4
|
+
"spaceA": "9ctvrdn9vedda7efetrhcdakfh4cr2k",
|
|
5
|
+
"spaceB": "cv7n8vkvr64cctf3294h9k4eanhff8z"
|
|
6
|
+
},
|
|
7
|
+
"parse": [
|
|
8
|
+
{ "resource": "api:api.dfos.com", "valid": true, "host": "api.dfos.com", "spaceId": null },
|
|
9
|
+
{
|
|
10
|
+
"resource": "api:api.example.org:8443",
|
|
11
|
+
"valid": true,
|
|
12
|
+
"host": "api.example.org:8443",
|
|
13
|
+
"spaceId": null
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
"resource": "api:api.dfos.com/spaces/9ctvrdn9vedda7efetrhcdakfh4cr2k",
|
|
17
|
+
"valid": true,
|
|
18
|
+
"host": "api.dfos.com",
|
|
19
|
+
"spaceId": "9ctvrdn9vedda7efetrhcdakfh4cr2k"
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
"resource": "api:api.example.org:8443/spaces/9ctvrdn9vedda7efetrhcdakfh4cr2k",
|
|
23
|
+
"valid": true,
|
|
24
|
+
"host": "api.example.org:8443",
|
|
25
|
+
"spaceId": "9ctvrdn9vedda7efetrhcdakfh4cr2k"
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
"resource": "api:*",
|
|
29
|
+
"valid": true,
|
|
30
|
+
"host": "*",
|
|
31
|
+
"spaceId": null,
|
|
32
|
+
"note": "an ordinary id covering only itself; never a served host"
|
|
33
|
+
},
|
|
34
|
+
{ "resource": "api:", "valid": false, "note": "empty host" },
|
|
35
|
+
{
|
|
36
|
+
"resource": "api:/spaces/9ctvrdn9vedda7efetrhcdakfh4cr2k",
|
|
37
|
+
"valid": false,
|
|
38
|
+
"note": "empty host"
|
|
39
|
+
},
|
|
40
|
+
{ "resource": "api:api.dfos.com/", "valid": false, "note": "trailing separator, no child" },
|
|
41
|
+
{
|
|
42
|
+
"resource": "api:api.dfos.com/spaces",
|
|
43
|
+
"valid": false,
|
|
44
|
+
"note": "child segment without an id"
|
|
45
|
+
},
|
|
46
|
+
{ "resource": "api:api.dfos.com/spaces/", "valid": false, "note": "empty id" },
|
|
47
|
+
{
|
|
48
|
+
"resource": "api:api.dfos.com/spaces/9ctvrdn9vedda7efetrhcdakfh4cr2",
|
|
49
|
+
"valid": false,
|
|
50
|
+
"note": "30-character id"
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
"resource": "api:api.dfos.com/spaces/9ctvrdn9vedda7efetrhcdakfh4cr2k2",
|
|
54
|
+
"valid": false,
|
|
55
|
+
"note": "32-character id"
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
"resource": "api:api.dfos.com/spaces/9ctvrdn9vedda7efetrhcdakfh4cr2K",
|
|
59
|
+
"valid": false,
|
|
60
|
+
"note": "uppercase is outside the identifier alphabet"
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
"resource": "api:api.dfos.com/spaces/9ctvrdn9vedda7efetrhcdakfh4cr1b",
|
|
64
|
+
"valid": false,
|
|
65
|
+
"note": "1 and b are outside the identifier alphabet"
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
"resource": "api:api.dfos.com/spaces/9ctvrdn9vedda7efetrhcdakfh4cr2k/extra",
|
|
69
|
+
"valid": false,
|
|
70
|
+
"note": "the hierarchy is one level deep"
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
"resource": "api:api.dfos.com/topics/9ctvrdn9vedda7efetrhcdakfh4cr2k",
|
|
74
|
+
"valid": false,
|
|
75
|
+
"note": "topics is not a registered child"
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
"resource": "api:api.dfos.com/SPACES/9ctvrdn9vedda7efetrhcdakfh4cr2k",
|
|
79
|
+
"valid": false,
|
|
80
|
+
"note": "the child segment is lowercase"
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
"resource": "api:api.dfos.com/spaces/did:dfos:9ctvrdn9vedda7efetrhcdakfh4cr2k",
|
|
84
|
+
"valid": false,
|
|
85
|
+
"note": "the id is prefix-stripped, never a full DID"
|
|
86
|
+
}
|
|
87
|
+
],
|
|
88
|
+
"covers": [
|
|
89
|
+
{
|
|
90
|
+
"entry": "api:api.dfos.com",
|
|
91
|
+
"required": "api:api.dfos.com",
|
|
92
|
+
"covers": true,
|
|
93
|
+
"note": "bare covers bare"
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
"entry": "api:api.dfos.com",
|
|
97
|
+
"required": "api:api.dfos.com/spaces/9ctvrdn9vedda7efetrhcdakfh4cr2k",
|
|
98
|
+
"covers": true,
|
|
99
|
+
"note": "bare host is the registered ancestor of every space at that host"
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
"entry": "api:api.dfos.com/spaces/9ctvrdn9vedda7efetrhcdakfh4cr2k",
|
|
103
|
+
"required": "api:api.dfos.com/spaces/9ctvrdn9vedda7efetrhcdakfh4cr2k",
|
|
104
|
+
"covers": true,
|
|
105
|
+
"note": "child covers itself"
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
"entry": "api:api.dfos.com/spaces/9ctvrdn9vedda7efetrhcdakfh4cr2k",
|
|
109
|
+
"required": "api:api.dfos.com",
|
|
110
|
+
"covers": false,
|
|
111
|
+
"note": "child never covers the bare host"
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
"entry": "api:api.dfos.com/spaces/9ctvrdn9vedda7efetrhcdakfh4cr2k",
|
|
115
|
+
"required": "api:api.dfos.com/spaces/cv7n8vkvr64cctf3294h9k4eanhff8z",
|
|
116
|
+
"covers": false,
|
|
117
|
+
"note": "sibling space"
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
"entry": "api:api.dfos.com",
|
|
121
|
+
"required": "api:api.example.org/spaces/9ctvrdn9vedda7efetrhcdakfh4cr2k",
|
|
122
|
+
"covers": false,
|
|
123
|
+
"note": "coverage never crosses hosts"
|
|
124
|
+
},
|
|
125
|
+
{
|
|
126
|
+
"entry": "api:api.dfos.com",
|
|
127
|
+
"required": "api:api.dfos.com:8443",
|
|
128
|
+
"covers": false,
|
|
129
|
+
"note": "a non-default port is part of the host"
|
|
130
|
+
},
|
|
131
|
+
{
|
|
132
|
+
"entry": "api:api.dfos.com:8443",
|
|
133
|
+
"required": "api:api.dfos.com:8443/spaces/9ctvrdn9vedda7efetrhcdakfh4cr2k",
|
|
134
|
+
"covers": true,
|
|
135
|
+
"note": "the port travels with the host into the child"
|
|
136
|
+
},
|
|
137
|
+
{
|
|
138
|
+
"entry": "api:*",
|
|
139
|
+
"required": "api:api.dfos.com",
|
|
140
|
+
"covers": false,
|
|
141
|
+
"note": "api:* is an ordinary id, not a wildcard"
|
|
142
|
+
},
|
|
143
|
+
{
|
|
144
|
+
"entry": "api:*",
|
|
145
|
+
"required": "api:*",
|
|
146
|
+
"covers": true,
|
|
147
|
+
"note": "an ordinary id covers itself"
|
|
148
|
+
},
|
|
149
|
+
{
|
|
150
|
+
"entry": "api:api.dfos.com/topics/9ctvrdn9vedda7efetrhcdakfh4cr2k",
|
|
151
|
+
"required": "api:api.dfos.com/topics/9ctvrdn9vedda7efetrhcdakfh4cr2k",
|
|
152
|
+
"covers": false,
|
|
153
|
+
"note": "a malformed api: resource never covers a request, even itself"
|
|
154
|
+
},
|
|
155
|
+
{
|
|
156
|
+
"entry": "api:api.dfos.com",
|
|
157
|
+
"required": "api:api.dfos.com/topics/9ctvrdn9vedda7efetrhcdakfh4cr2k",
|
|
158
|
+
"covers": false,
|
|
159
|
+
"note": "a malformed required resource is never covered"
|
|
160
|
+
},
|
|
161
|
+
{
|
|
162
|
+
"entry": "chain:*",
|
|
163
|
+
"required": "api:api.dfos.com",
|
|
164
|
+
"covers": false,
|
|
165
|
+
"note": "coverage never crosses resource types"
|
|
166
|
+
},
|
|
167
|
+
{
|
|
168
|
+
"entry": "api:api.dfos.com",
|
|
169
|
+
"required": "chain:9ctvrdn9vedda7efetrhcdakfh4cr2k",
|
|
170
|
+
"covers": false,
|
|
171
|
+
"note": "coverage never crosses resource types"
|
|
172
|
+
}
|
|
173
|
+
],
|
|
174
|
+
"attenuation": [
|
|
175
|
+
{
|
|
176
|
+
"note": "bare host narrows to one space",
|
|
177
|
+
"parent": [{ "resource": "api:api.dfos.com", "action": "read:posts,write:comments" }],
|
|
178
|
+
"child": [
|
|
179
|
+
{
|
|
180
|
+
"resource": "api:api.dfos.com/spaces/9ctvrdn9vedda7efetrhcdakfh4cr2k",
|
|
181
|
+
"action": "read:posts"
|
|
182
|
+
}
|
|
183
|
+
],
|
|
184
|
+
"valid": true
|
|
185
|
+
},
|
|
186
|
+
{
|
|
187
|
+
"note": "bare host narrows to two spaces",
|
|
188
|
+
"parent": [
|
|
189
|
+
{ "resource": "api:api.dfos.com", "action": "read:posts,write:upvotes,write:comments" }
|
|
190
|
+
],
|
|
191
|
+
"child": [
|
|
192
|
+
{
|
|
193
|
+
"resource": "api:api.dfos.com/spaces/9ctvrdn9vedda7efetrhcdakfh4cr2k",
|
|
194
|
+
"action": "read:posts,write:upvotes"
|
|
195
|
+
},
|
|
196
|
+
{
|
|
197
|
+
"resource": "api:api.dfos.com/spaces/cv7n8vkvr64cctf3294h9k4eanhff8z",
|
|
198
|
+
"action": "read:posts"
|
|
199
|
+
}
|
|
200
|
+
],
|
|
201
|
+
"valid": true
|
|
202
|
+
},
|
|
203
|
+
{
|
|
204
|
+
"note": "bare host to bare host, fewer actions",
|
|
205
|
+
"parent": [{ "resource": "api:api.dfos.com", "action": "read:profile,read:email" }],
|
|
206
|
+
"child": [{ "resource": "api:api.dfos.com", "action": "read:profile" }],
|
|
207
|
+
"valid": true
|
|
208
|
+
},
|
|
209
|
+
{
|
|
210
|
+
"note": "child widening to the bare host is refused",
|
|
211
|
+
"parent": [
|
|
212
|
+
{
|
|
213
|
+
"resource": "api:api.dfos.com/spaces/9ctvrdn9vedda7efetrhcdakfh4cr2k",
|
|
214
|
+
"action": "read:posts"
|
|
215
|
+
}
|
|
216
|
+
],
|
|
217
|
+
"child": [{ "resource": "api:api.dfos.com", "action": "read:posts" }],
|
|
218
|
+
"valid": false
|
|
219
|
+
},
|
|
220
|
+
{
|
|
221
|
+
"note": "sibling space is refused",
|
|
222
|
+
"parent": [
|
|
223
|
+
{
|
|
224
|
+
"resource": "api:api.dfos.com/spaces/9ctvrdn9vedda7efetrhcdakfh4cr2k",
|
|
225
|
+
"action": "read:posts"
|
|
226
|
+
}
|
|
227
|
+
],
|
|
228
|
+
"child": [
|
|
229
|
+
{
|
|
230
|
+
"resource": "api:api.dfos.com/spaces/cv7n8vkvr64cctf3294h9k4eanhff8z",
|
|
231
|
+
"action": "read:posts"
|
|
232
|
+
}
|
|
233
|
+
],
|
|
234
|
+
"valid": false
|
|
235
|
+
},
|
|
236
|
+
{
|
|
237
|
+
"note": "child to the same child is exact",
|
|
238
|
+
"parent": [
|
|
239
|
+
{
|
|
240
|
+
"resource": "api:api.dfos.com/spaces/9ctvrdn9vedda7efetrhcdakfh4cr2k",
|
|
241
|
+
"action": "read:posts,write:comments"
|
|
242
|
+
}
|
|
243
|
+
],
|
|
244
|
+
"child": [
|
|
245
|
+
{
|
|
246
|
+
"resource": "api:api.dfos.com/spaces/9ctvrdn9vedda7efetrhcdakfh4cr2k",
|
|
247
|
+
"action": "write:comments"
|
|
248
|
+
}
|
|
249
|
+
],
|
|
250
|
+
"valid": true
|
|
251
|
+
},
|
|
252
|
+
{
|
|
253
|
+
"note": "resource narrows but the action widens",
|
|
254
|
+
"parent": [{ "resource": "api:api.dfos.com", "action": "read:posts" }],
|
|
255
|
+
"child": [
|
|
256
|
+
{
|
|
257
|
+
"resource": "api:api.dfos.com/spaces/9ctvrdn9vedda7efetrhcdakfh4cr2k",
|
|
258
|
+
"action": "read:posts,write:posts"
|
|
259
|
+
}
|
|
260
|
+
],
|
|
261
|
+
"valid": false
|
|
262
|
+
},
|
|
263
|
+
{
|
|
264
|
+
"note": "another host is refused",
|
|
265
|
+
"parent": [{ "resource": "api:api.dfos.com", "action": "read:posts" }],
|
|
266
|
+
"child": [
|
|
267
|
+
{
|
|
268
|
+
"resource": "api:api.example.org/spaces/9ctvrdn9vedda7efetrhcdakfh4cr2k",
|
|
269
|
+
"action": "read:posts"
|
|
270
|
+
}
|
|
271
|
+
],
|
|
272
|
+
"valid": false
|
|
273
|
+
},
|
|
274
|
+
{
|
|
275
|
+
"note": "an account-level token narrows onto a child; the entry is inert, not refused",
|
|
276
|
+
"parent": [{ "resource": "api:api.dfos.com", "action": "read:profile,read:posts" }],
|
|
277
|
+
"child": [
|
|
278
|
+
{
|
|
279
|
+
"resource": "api:api.dfos.com/spaces/9ctvrdn9vedda7efetrhcdakfh4cr2k",
|
|
280
|
+
"action": "read:profile"
|
|
281
|
+
}
|
|
282
|
+
],
|
|
283
|
+
"valid": true
|
|
284
|
+
},
|
|
285
|
+
{
|
|
286
|
+
"note": "a malformed api: id is carried only by byte-identical parent",
|
|
287
|
+
"parent": [
|
|
288
|
+
{
|
|
289
|
+
"resource": "api:api.dfos.com/topics/9ctvrdn9vedda7efetrhcdakfh4cr2k",
|
|
290
|
+
"action": "read:posts"
|
|
291
|
+
}
|
|
292
|
+
],
|
|
293
|
+
"child": [
|
|
294
|
+
{
|
|
295
|
+
"resource": "api:api.dfos.com/topics/9ctvrdn9vedda7efetrhcdakfh4cr2k",
|
|
296
|
+
"action": "read:posts"
|
|
297
|
+
}
|
|
298
|
+
],
|
|
299
|
+
"valid": true
|
|
300
|
+
},
|
|
301
|
+
{
|
|
302
|
+
"note": "a bare host does not carry a malformed api: id",
|
|
303
|
+
"parent": [{ "resource": "api:api.dfos.com", "action": "read:posts" }],
|
|
304
|
+
"child": [
|
|
305
|
+
{
|
|
306
|
+
"resource": "api:api.dfos.com/topics/9ctvrdn9vedda7efetrhcdakfh4cr2k",
|
|
307
|
+
"action": "read:posts"
|
|
308
|
+
}
|
|
309
|
+
],
|
|
310
|
+
"valid": false
|
|
311
|
+
},
|
|
312
|
+
{
|
|
313
|
+
"note": "mailbox: stays exact — the hierarchy is api:-only",
|
|
314
|
+
"parent": [{ "resource": "mailbox:9ctvrdn9vedda7efetrhcdakfh4cr2k", "action": "deposit" }],
|
|
315
|
+
"child": [
|
|
316
|
+
{
|
|
317
|
+
"resource": "mailbox:9ctvrdn9vedda7efetrhcdakfh4cr2k/spaces/cv7n8vkvr64cctf3294h9k4eanhff8z",
|
|
318
|
+
"action": "deposit"
|
|
319
|
+
}
|
|
320
|
+
],
|
|
321
|
+
"valid": false
|
|
322
|
+
}
|
|
323
|
+
],
|
|
324
|
+
"matches": [
|
|
325
|
+
{
|
|
326
|
+
"note": "bare host entry satisfies a space-addressed demand",
|
|
327
|
+
"att": [{ "resource": "api:api.dfos.com", "action": "read:posts,write:comments" }],
|
|
328
|
+
"resource": "api:api.dfos.com/spaces/9ctvrdn9vedda7efetrhcdakfh4cr2k",
|
|
329
|
+
"action": "write:comments",
|
|
330
|
+
"covers": true
|
|
331
|
+
},
|
|
332
|
+
{
|
|
333
|
+
"note": "child entry satisfies its own space",
|
|
334
|
+
"att": [
|
|
335
|
+
{
|
|
336
|
+
"resource": "api:api.dfos.com/spaces/9ctvrdn9vedda7efetrhcdakfh4cr2k",
|
|
337
|
+
"action": "read:posts"
|
|
338
|
+
}
|
|
339
|
+
],
|
|
340
|
+
"resource": "api:api.dfos.com/spaces/9ctvrdn9vedda7efetrhcdakfh4cr2k",
|
|
341
|
+
"action": "read:posts",
|
|
342
|
+
"covers": true
|
|
343
|
+
},
|
|
344
|
+
{
|
|
345
|
+
"note": "child entry does not satisfy the bare host",
|
|
346
|
+
"att": [
|
|
347
|
+
{
|
|
348
|
+
"resource": "api:api.dfos.com/spaces/9ctvrdn9vedda7efetrhcdakfh4cr2k",
|
|
349
|
+
"action": "read:profile"
|
|
350
|
+
}
|
|
351
|
+
],
|
|
352
|
+
"resource": "api:api.dfos.com",
|
|
353
|
+
"action": "read:profile",
|
|
354
|
+
"covers": false
|
|
355
|
+
},
|
|
356
|
+
{
|
|
357
|
+
"note": "child entry does not satisfy a sibling space",
|
|
358
|
+
"att": [
|
|
359
|
+
{
|
|
360
|
+
"resource": "api:api.dfos.com/spaces/9ctvrdn9vedda7efetrhcdakfh4cr2k",
|
|
361
|
+
"action": "read:posts"
|
|
362
|
+
}
|
|
363
|
+
],
|
|
364
|
+
"resource": "api:api.dfos.com/spaces/cv7n8vkvr64cctf3294h9k4eanhff8z",
|
|
365
|
+
"action": "read:posts",
|
|
366
|
+
"covers": false
|
|
367
|
+
},
|
|
368
|
+
{
|
|
369
|
+
"note": "the action must be in the covering entry's set",
|
|
370
|
+
"att": [{ "resource": "api:api.dfos.com", "action": "read:posts" }],
|
|
371
|
+
"resource": "api:api.dfos.com/spaces/9ctvrdn9vedda7efetrhcdakfh4cr2k",
|
|
372
|
+
"action": "write:comments",
|
|
373
|
+
"covers": false
|
|
374
|
+
},
|
|
375
|
+
{
|
|
376
|
+
"note": "any one entry may cover; the scan does not stop at the first api: entry",
|
|
377
|
+
"att": [
|
|
378
|
+
{
|
|
379
|
+
"resource": "api:api.dfos.com/spaces/cv7n8vkvr64cctf3294h9k4eanhff8z",
|
|
380
|
+
"action": "read:posts"
|
|
381
|
+
},
|
|
382
|
+
{
|
|
383
|
+
"resource": "api:api.dfos.com/spaces/9ctvrdn9vedda7efetrhcdakfh4cr2k",
|
|
384
|
+
"action": "read:posts,write:upvotes"
|
|
385
|
+
}
|
|
386
|
+
],
|
|
387
|
+
"resource": "api:api.dfos.com/spaces/9ctvrdn9vedda7efetrhcdakfh4cr2k",
|
|
388
|
+
"action": "write:upvotes",
|
|
389
|
+
"covers": true
|
|
390
|
+
},
|
|
391
|
+
{
|
|
392
|
+
"note": "host mismatch",
|
|
393
|
+
"att": [{ "resource": "api:api.dfos.com", "action": "read:posts" }],
|
|
394
|
+
"resource": "api:api.example.org/spaces/9ctvrdn9vedda7efetrhcdakfh4cr2k",
|
|
395
|
+
"action": "read:posts",
|
|
396
|
+
"covers": false
|
|
397
|
+
},
|
|
398
|
+
{
|
|
399
|
+
"note": "existing bare-host behavior is unchanged: exact host, exact action",
|
|
400
|
+
"att": [{ "resource": "api:api.dfos.com", "action": "read:profile,read:email" }],
|
|
401
|
+
"resource": "api:api.dfos.com",
|
|
402
|
+
"action": "read:email",
|
|
403
|
+
"covers": true
|
|
404
|
+
},
|
|
405
|
+
{
|
|
406
|
+
"note": "read:* is a literal token and matches no real route",
|
|
407
|
+
"att": [{ "resource": "api:api.dfos.com", "action": "read:*" }],
|
|
408
|
+
"resource": "api:api.dfos.com/spaces/9ctvrdn9vedda7efetrhcdakfh4cr2k",
|
|
409
|
+
"action": "read:posts",
|
|
410
|
+
"covers": false
|
|
411
|
+
}
|
|
412
|
+
]
|
|
413
|
+
}
|