@vltpkg/vsr 0.0.0-26 → 0.0.0-28
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/dist/README.md +1 -0
- package/dist/assets/public/favicon.ico +0 -0
- package/dist/assets/public/fonts/courier-bold-italic.ttf +0 -0
- package/dist/assets/public/fonts/courier-bold.ttf +0 -0
- package/dist/assets/public/fonts/courier-italic.ttf +0 -0
- package/dist/assets/public/fonts/courier-regular.ttf +0 -0
- package/dist/assets/public/fonts/geist-mono.ttf +0 -0
- package/dist/assets/public/fonts/inter.ttf +0 -0
- package/dist/assets/public/images/bg.png +0 -0
- package/dist/assets/public/images/clients/logo-bun.png +0 -0
- package/dist/assets/public/images/clients/logo-deno.png +0 -0
- package/dist/assets/public/images/clients/logo-npm.png +0 -0
- package/dist/assets/public/images/clients/logo-pnpm.png +0 -0
- package/dist/assets/public/images/clients/logo-vlt.png +0 -0
- package/dist/assets/public/images/clients/logo-yarn.png +0 -0
- package/dist/assets/public/images/favicon/apple-touch-icon.png +0 -0
- package/dist/assets/public/images/favicon/favicon-96x96.png +0 -0
- package/dist/assets/public/images/favicon/favicon.ico +0 -0
- package/dist/assets/public/images/favicon/favicon.svg +3 -0
- package/dist/assets/public/images/favicon/site.webmanifest +21 -0
- package/dist/assets/public/images/favicon/web-app-manifest-192x192.png +0 -0
- package/dist/assets/public/images/favicon/web-app-manifest-512x512.png +0 -0
- package/dist/assets/public/index.html +70 -0
- package/dist/assets/public/index.js +1300 -0
- package/dist/assets/public/index.js.map +7 -0
- package/dist/assets/public/main.css +1 -0
- package/dist/assets/public/styles/styles.css +231 -0
- package/dist/bin/demo/package.json +6 -0
- package/dist/bin/demo/vlt.json +1 -0
- package/dist/bin/vsr.js +773 -0
- package/dist/index.js +28280 -0
- package/dist/index.js.map +8 -0
- package/package.json +6 -6
- package/scripts/build-bin.js +1 -0
- package/src/bin/vsr.ts +15 -3
- package/scripts/prepack.js +0 -27
- package/test/access.test.ts +0 -705
- package/test/audit.test.ts +0 -828
- package/test/dashboard.test.ts +0 -693
- package/test/dist-tags.test.ts +0 -678
- package/test/manifest.test.ts +0 -436
- package/test/packument.test.ts +0 -530
- package/test/ping.test.ts +0 -41
- package/test/search.test.ts +0 -472
- package/test/setup.ts +0 -130
- package/test/static.test.ts +0 -646
- package/test/tokens.test.ts +0 -389
- package/test/utils/auth.test.ts +0 -214
- package/test/utils/packages.test.ts +0 -235
- package/test/utils/response.test.ts +0 -184
- package/test/whoami.test.ts +0 -119
package/test/manifest.test.ts
DELETED
|
@@ -1,436 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from 'vitest'
|
|
2
|
-
import { app } from '../src/index.ts'
|
|
3
|
-
|
|
4
|
-
// Mock environment for testing
|
|
5
|
-
const mockEnv = {
|
|
6
|
-
DB: {
|
|
7
|
-
// Minimal D1 interface to prevent database errors
|
|
8
|
-
prepare: () => ({
|
|
9
|
-
bind: () => ({
|
|
10
|
-
get: () => Promise.resolve(null),
|
|
11
|
-
all: () => Promise.resolve({ results: [] }),
|
|
12
|
-
run: () => Promise.resolve({ success: true }),
|
|
13
|
-
raw: () => Promise.resolve([]),
|
|
14
|
-
}),
|
|
15
|
-
get: () => Promise.resolve(null),
|
|
16
|
-
all: () => Promise.resolve({ results: [] }),
|
|
17
|
-
run: () => Promise.resolve({ success: true }),
|
|
18
|
-
raw: () => Promise.resolve([]),
|
|
19
|
-
}),
|
|
20
|
-
batch: () => Promise.resolve([]),
|
|
21
|
-
exec: () => Promise.resolve(),
|
|
22
|
-
},
|
|
23
|
-
BUCKET: {
|
|
24
|
-
get: () => Promise.resolve(null),
|
|
25
|
-
put: () => Promise.resolve(),
|
|
26
|
-
delete: () => Promise.resolve(),
|
|
27
|
-
},
|
|
28
|
-
KV: {
|
|
29
|
-
get: () => Promise.resolve(null),
|
|
30
|
-
put: () => Promise.resolve(),
|
|
31
|
-
delete: () => Promise.resolve(),
|
|
32
|
-
},
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
describe('Package Manifest Endpoints', () => {
|
|
36
|
-
describe('Local/Private Package Manifests', () => {
|
|
37
|
-
describe('Packument Requests (Full Package Info)', () => {
|
|
38
|
-
it('should handle unscoped package packument request', async () => {
|
|
39
|
-
const res = await app.request('/lodash', {}, mockEnv)
|
|
40
|
-
// Should redirect to default upstream since package doesn't exist locally
|
|
41
|
-
expect(res.status).toBe(302)
|
|
42
|
-
expect(res.headers.get('location')).toBe('/local/lodash')
|
|
43
|
-
})
|
|
44
|
-
|
|
45
|
-
it('should handle scoped package packument request', async () => {
|
|
46
|
-
const res = await app.request('/@types/node', {}, mockEnv)
|
|
47
|
-
// Scoped packages should return 404 since they're not handled by the root route
|
|
48
|
-
expect(res.status).toBe(404)
|
|
49
|
-
})
|
|
50
|
-
|
|
51
|
-
it('should return 404 for invalid package names', async () => {
|
|
52
|
-
const res = await app.request(
|
|
53
|
-
'/invalid..package',
|
|
54
|
-
{},
|
|
55
|
-
mockEnv,
|
|
56
|
-
)
|
|
57
|
-
expect(res.status).toBe(404)
|
|
58
|
-
})
|
|
59
|
-
|
|
60
|
-
it('should return 404 for packages starting with dots', async () => {
|
|
61
|
-
const res = await app.request('/.hidden-package', {}, mockEnv)
|
|
62
|
-
expect(res.status).toBe(404)
|
|
63
|
-
})
|
|
64
|
-
|
|
65
|
-
it('should return 404 for packages starting with underscores', async () => {
|
|
66
|
-
const res = await app.request(
|
|
67
|
-
'/_internal-package',
|
|
68
|
-
{},
|
|
69
|
-
mockEnv,
|
|
70
|
-
)
|
|
71
|
-
expect(res.status).toBe(404)
|
|
72
|
-
})
|
|
73
|
-
})
|
|
74
|
-
|
|
75
|
-
describe('Package Version Requests (Specific Versions)', () => {
|
|
76
|
-
it('should handle unscoped package version request', async () => {
|
|
77
|
-
const res = await app.request('/lodash/4.17.21', {}, mockEnv)
|
|
78
|
-
// Should return 404 since package doesn't exist locally and version routes don't redirect
|
|
79
|
-
expect(res.status).toBe(404)
|
|
80
|
-
})
|
|
81
|
-
|
|
82
|
-
it('should handle scoped package version request', async () => {
|
|
83
|
-
const res = await app.request(
|
|
84
|
-
'/@types/node/18.0.0',
|
|
85
|
-
{},
|
|
86
|
-
mockEnv,
|
|
87
|
-
)
|
|
88
|
-
// Should return 404 since package doesn't exist locally and version routes don't redirect
|
|
89
|
-
expect(res.status).toBe(404)
|
|
90
|
-
})
|
|
91
|
-
|
|
92
|
-
it('should handle semver ranges in version requests', async () => {
|
|
93
|
-
const res = await app.request(
|
|
94
|
-
'/lodash/%3E%3D4.0.0',
|
|
95
|
-
{},
|
|
96
|
-
mockEnv,
|
|
97
|
-
)
|
|
98
|
-
// URL-encoded >=4.0.0 - should return 404 since package doesn't exist locally
|
|
99
|
-
expect(res.status).toBe(404)
|
|
100
|
-
})
|
|
101
|
-
|
|
102
|
-
it('should handle dist-tags in version requests', async () => {
|
|
103
|
-
const res = await app.request('/lodash/latest', {}, mockEnv)
|
|
104
|
-
// Should return 404 since package doesn't exist locally
|
|
105
|
-
expect(res.status).toBe(404)
|
|
106
|
-
})
|
|
107
|
-
})
|
|
108
|
-
|
|
109
|
-
describe('Package Tarball Requests', () => {
|
|
110
|
-
it('should handle unscoped package tarball request', async () => {
|
|
111
|
-
const res = await app.request(
|
|
112
|
-
'/lodash/-/lodash-4.17.21.tgz',
|
|
113
|
-
{},
|
|
114
|
-
mockEnv,
|
|
115
|
-
)
|
|
116
|
-
// Should return 404 since package doesn't exist locally and tarball routes don't redirect
|
|
117
|
-
expect(res.status).toBe(404)
|
|
118
|
-
})
|
|
119
|
-
|
|
120
|
-
it('should handle scoped package tarball request', async () => {
|
|
121
|
-
const res = await app.request(
|
|
122
|
-
'/@types/node/-/node-18.0.0.tgz',
|
|
123
|
-
{},
|
|
124
|
-
mockEnv,
|
|
125
|
-
)
|
|
126
|
-
// Should return 404 since package doesn't exist locally and tarball routes don't redirect
|
|
127
|
-
expect(res.status).toBe(404)
|
|
128
|
-
})
|
|
129
|
-
})
|
|
130
|
-
})
|
|
131
|
-
|
|
132
|
-
describe('Upstream Public Package Manifests', () => {
|
|
133
|
-
describe('NPM Registry Upstream', () => {
|
|
134
|
-
it('should handle npm packument requests', async () => {
|
|
135
|
-
const res = await app.request('/npm/lodash', {}, mockEnv)
|
|
136
|
-
// This will likely return 502 due to database mocking issues, but we test the route exists
|
|
137
|
-
expect([200, 502].includes(res.status)).toBe(true)
|
|
138
|
-
expect(res.headers.get('content-type')).toContain(
|
|
139
|
-
'application/json',
|
|
140
|
-
)
|
|
141
|
-
})
|
|
142
|
-
|
|
143
|
-
it('should handle npm scoped package requests', async () => {
|
|
144
|
-
const res = await app.request('/npm/@types/node', {}, mockEnv)
|
|
145
|
-
// This will likely return 502 due to database mocking issues, but we test the route exists
|
|
146
|
-
expect([200, 502].includes(res.status)).toBe(true)
|
|
147
|
-
expect(res.headers.get('content-type')).toContain(
|
|
148
|
-
'application/json',
|
|
149
|
-
)
|
|
150
|
-
})
|
|
151
|
-
|
|
152
|
-
it('should handle npm package version requests', async () => {
|
|
153
|
-
const res = await app.request(
|
|
154
|
-
'/npm/lodash/4.17.21',
|
|
155
|
-
{},
|
|
156
|
-
mockEnv,
|
|
157
|
-
)
|
|
158
|
-
// This should work as it's a direct version request
|
|
159
|
-
expect(res.status).toBe(200)
|
|
160
|
-
expect(res.headers.get('content-type')).toContain(
|
|
161
|
-
'application/json',
|
|
162
|
-
)
|
|
163
|
-
|
|
164
|
-
const data = (await res.json()) as any
|
|
165
|
-
expect(data).toHaveProperty('name')
|
|
166
|
-
expect(data).toHaveProperty('version')
|
|
167
|
-
expect(data).toHaveProperty('dist')
|
|
168
|
-
expect(data.dist).toHaveProperty('tarball')
|
|
169
|
-
})
|
|
170
|
-
|
|
171
|
-
it('should handle npm scoped package version requests', async () => {
|
|
172
|
-
const res = await app.request(
|
|
173
|
-
'/npm/@types/node/18.0.0',
|
|
174
|
-
{},
|
|
175
|
-
mockEnv,
|
|
176
|
-
)
|
|
177
|
-
// This should work but might return a different version if 18.0.0 doesn't exist
|
|
178
|
-
expect([200, 404].includes(res.status)).toBe(true)
|
|
179
|
-
if (res.status === 200) {
|
|
180
|
-
expect(res.headers.get('content-type')).toContain(
|
|
181
|
-
'application/json',
|
|
182
|
-
)
|
|
183
|
-
const data = (await res.json()) as any
|
|
184
|
-
expect(data).toHaveProperty('name')
|
|
185
|
-
expect(data.name).toBe('@types/node')
|
|
186
|
-
expect(data).toHaveProperty('version')
|
|
187
|
-
}
|
|
188
|
-
})
|
|
189
|
-
|
|
190
|
-
it('should rewrite tarball URLs for npm packages', async () => {
|
|
191
|
-
const res = await app.request(
|
|
192
|
-
'/npm/lodash/4.17.21',
|
|
193
|
-
{},
|
|
194
|
-
mockEnv,
|
|
195
|
-
)
|
|
196
|
-
expect(res.status).toBe(200)
|
|
197
|
-
|
|
198
|
-
const data = (await res.json()) as any
|
|
199
|
-
expect(data.dist.tarball).toMatch(
|
|
200
|
-
/^https?:\/\/.*\/npm\/lodash\/-\/lodash-4\.17\.21\.tgz$/,
|
|
201
|
-
)
|
|
202
|
-
})
|
|
203
|
-
|
|
204
|
-
it('should handle URL-encoded scoped packages', async () => {
|
|
205
|
-
const res = await app.request(
|
|
206
|
-
'/npm/@babel%2Fcore',
|
|
207
|
-
{},
|
|
208
|
-
mockEnv,
|
|
209
|
-
)
|
|
210
|
-
// This will likely return 502 due to database mocking issues, but we test the route exists
|
|
211
|
-
expect([200, 502].includes(res.status)).toBe(true)
|
|
212
|
-
expect(res.headers.get('content-type')).toContain(
|
|
213
|
-
'application/json',
|
|
214
|
-
)
|
|
215
|
-
})
|
|
216
|
-
})
|
|
217
|
-
|
|
218
|
-
describe('JSR Registry Upstream', () => {
|
|
219
|
-
it('should handle jsr packument requests', async () => {
|
|
220
|
-
const res = await app.request('/jsr/@std/fs', {}, mockEnv)
|
|
221
|
-
// JSR is not configured in the default config, should return 404
|
|
222
|
-
expect(res.status).toBe(404)
|
|
223
|
-
expect(res.headers.get('content-type')).toContain(
|
|
224
|
-
'application/json',
|
|
225
|
-
)
|
|
226
|
-
})
|
|
227
|
-
|
|
228
|
-
it('should handle jsr package version requests', async () => {
|
|
229
|
-
const res = await app.request(
|
|
230
|
-
'/jsr/@std/fs/1.0.0',
|
|
231
|
-
{},
|
|
232
|
-
mockEnv,
|
|
233
|
-
)
|
|
234
|
-
// JSR is not configured in the default config, should return 404
|
|
235
|
-
expect(res.status).toBe(404)
|
|
236
|
-
expect(res.headers.get('content-type')).toContain(
|
|
237
|
-
'application/json',
|
|
238
|
-
)
|
|
239
|
-
})
|
|
240
|
-
})
|
|
241
|
-
|
|
242
|
-
describe('Custom Registry Upstream', () => {
|
|
243
|
-
it('should handle custom upstream packument requests', async () => {
|
|
244
|
-
const res = await app.request(
|
|
245
|
-
'/custom/some-package',
|
|
246
|
-
{},
|
|
247
|
-
mockEnv,
|
|
248
|
-
)
|
|
249
|
-
// Custom upstream is not configured in the default config, should return 404
|
|
250
|
-
expect(res.status).toBe(404)
|
|
251
|
-
expect(res.headers.get('content-type')).toContain(
|
|
252
|
-
'application/json',
|
|
253
|
-
)
|
|
254
|
-
})
|
|
255
|
-
|
|
256
|
-
it('should handle custom upstream version requests', async () => {
|
|
257
|
-
const res = await app.request(
|
|
258
|
-
'/custom/some-package/1.0.0',
|
|
259
|
-
{},
|
|
260
|
-
mockEnv,
|
|
261
|
-
)
|
|
262
|
-
// Custom upstream is not configured in the default config, should return 404
|
|
263
|
-
expect(res.status).toBe(404)
|
|
264
|
-
expect(res.headers.get('content-type')).toContain(
|
|
265
|
-
'application/json',
|
|
266
|
-
)
|
|
267
|
-
})
|
|
268
|
-
})
|
|
269
|
-
})
|
|
270
|
-
|
|
271
|
-
describe('Error Handling', () => {
|
|
272
|
-
it('should return 404 for non-existent packages in upstream', async () => {
|
|
273
|
-
const res = await app.request(
|
|
274
|
-
'/npm/this-package-definitely-does-not-exist-12345',
|
|
275
|
-
{},
|
|
276
|
-
mockEnv,
|
|
277
|
-
)
|
|
278
|
-
expect(res.status).toBe(404)
|
|
279
|
-
expect(res.headers.get('content-type')).toContain(
|
|
280
|
-
'application/json',
|
|
281
|
-
)
|
|
282
|
-
|
|
283
|
-
const data = await res.json()
|
|
284
|
-
expect(data).toHaveProperty('error')
|
|
285
|
-
})
|
|
286
|
-
|
|
287
|
-
it('should return 404 for non-existent versions', async () => {
|
|
288
|
-
const res = await app.request(
|
|
289
|
-
'/npm/lodash/999.999.999',
|
|
290
|
-
{},
|
|
291
|
-
mockEnv,
|
|
292
|
-
)
|
|
293
|
-
expect(res.status).toBe(404)
|
|
294
|
-
expect(res.headers.get('content-type')).toContain(
|
|
295
|
-
'application/json',
|
|
296
|
-
)
|
|
297
|
-
|
|
298
|
-
const data = await res.json()
|
|
299
|
-
expect(data).toHaveProperty('error')
|
|
300
|
-
})
|
|
301
|
-
|
|
302
|
-
it('should return 404 for invalid upstream names', async () => {
|
|
303
|
-
const res = await app.request(
|
|
304
|
-
'/invalid-upstream/some-package',
|
|
305
|
-
{},
|
|
306
|
-
mockEnv,
|
|
307
|
-
)
|
|
308
|
-
// Invalid upstreams return 404 since they're not configured
|
|
309
|
-
expect(res.status).toBe(404)
|
|
310
|
-
expect(res.headers.get('content-type')).toContain(
|
|
311
|
-
'application/json',
|
|
312
|
-
)
|
|
313
|
-
|
|
314
|
-
const data = await res.json()
|
|
315
|
-
expect(data).toHaveProperty('error')
|
|
316
|
-
})
|
|
317
|
-
|
|
318
|
-
it('should return 404 for unknown upstream registries', async () => {
|
|
319
|
-
const res = await app.request(
|
|
320
|
-
'/unknown-registry/some-package',
|
|
321
|
-
{},
|
|
322
|
-
mockEnv,
|
|
323
|
-
)
|
|
324
|
-
expect(res.status).toBe(404)
|
|
325
|
-
expect(res.headers.get('content-type')).toContain(
|
|
326
|
-
'application/json',
|
|
327
|
-
)
|
|
328
|
-
|
|
329
|
-
const data = (await res.json()) as any
|
|
330
|
-
expect(data).toHaveProperty('error')
|
|
331
|
-
expect(data.error).toContain('Unknown upstream')
|
|
332
|
-
})
|
|
333
|
-
})
|
|
334
|
-
|
|
335
|
-
describe('Response Headers and Caching', () => {
|
|
336
|
-
it('should set appropriate content-type headers', async () => {
|
|
337
|
-
const res = await app.request('/npm/lodash', {}, mockEnv)
|
|
338
|
-
// May return 502 due to database mocking, but should have correct content-type
|
|
339
|
-
expect([200, 502].includes(res.status)).toBe(true)
|
|
340
|
-
expect(res.headers.get('content-type')).toContain(
|
|
341
|
-
'application/json',
|
|
342
|
-
)
|
|
343
|
-
})
|
|
344
|
-
|
|
345
|
-
it('should set cache-control headers for version manifests', async () => {
|
|
346
|
-
const res = await app.request(
|
|
347
|
-
'/npm/lodash/4.17.21',
|
|
348
|
-
{},
|
|
349
|
-
mockEnv,
|
|
350
|
-
)
|
|
351
|
-
expect(res.status).toBe(200)
|
|
352
|
-
expect(res.headers.get('cache-control')).toBeTruthy()
|
|
353
|
-
})
|
|
354
|
-
})
|
|
355
|
-
|
|
356
|
-
describe('Special Package Name Handling', () => {
|
|
357
|
-
it('should handle packages with special characters', async () => {
|
|
358
|
-
const res = await app.request(
|
|
359
|
-
'/npm/package-with-dashes',
|
|
360
|
-
{},
|
|
361
|
-
mockEnv,
|
|
362
|
-
)
|
|
363
|
-
// Package doesn't exist, should return 404
|
|
364
|
-
expect(res.status).toBe(404)
|
|
365
|
-
expect(res.headers.get('content-type')).toContain(
|
|
366
|
-
'application/json',
|
|
367
|
-
)
|
|
368
|
-
})
|
|
369
|
-
|
|
370
|
-
it('should handle packages with numbers', async () => {
|
|
371
|
-
const res = await app.request('/npm/package123', {}, mockEnv)
|
|
372
|
-
// Package doesn't exist, may return 502 due to database mocking
|
|
373
|
-
expect([404, 502].includes(res.status)).toBe(true)
|
|
374
|
-
expect(res.headers.get('content-type')).toContain(
|
|
375
|
-
'application/json',
|
|
376
|
-
)
|
|
377
|
-
})
|
|
378
|
-
|
|
379
|
-
it('should handle deeply scoped packages', async () => {
|
|
380
|
-
const res = await app.request(
|
|
381
|
-
'/npm/@org/sub/package',
|
|
382
|
-
{},
|
|
383
|
-
mockEnv,
|
|
384
|
-
)
|
|
385
|
-
// Package doesn't exist, should return 404
|
|
386
|
-
expect(res.status).toBe(404)
|
|
387
|
-
expect(res.headers.get('content-type')).toContain(
|
|
388
|
-
'application/json',
|
|
389
|
-
)
|
|
390
|
-
})
|
|
391
|
-
})
|
|
392
|
-
|
|
393
|
-
describe('Version Range Handling', () => {
|
|
394
|
-
it('should handle semver ranges in packument requests', async () => {
|
|
395
|
-
const res = await app.request(
|
|
396
|
-
'/npm/lodash?versionRange=%5E4.0.0',
|
|
397
|
-
{},
|
|
398
|
-
mockEnv,
|
|
399
|
-
)
|
|
400
|
-
// May return 502 due to database mocking issues
|
|
401
|
-
expect([200, 502].includes(res.status)).toBe(true)
|
|
402
|
-
expect(res.headers.get('content-type')).toContain(
|
|
403
|
-
'application/json',
|
|
404
|
-
)
|
|
405
|
-
})
|
|
406
|
-
|
|
407
|
-
it('should handle exact version ranges', async () => {
|
|
408
|
-
const res = await app.request(
|
|
409
|
-
'/npm/lodash?versionRange=4.17.21',
|
|
410
|
-
{},
|
|
411
|
-
mockEnv,
|
|
412
|
-
)
|
|
413
|
-
// May return 502 due to database mocking issues
|
|
414
|
-
expect([200, 502].includes(res.status)).toBe(true)
|
|
415
|
-
expect(res.headers.get('content-type')).toContain(
|
|
416
|
-
'application/json',
|
|
417
|
-
)
|
|
418
|
-
})
|
|
419
|
-
|
|
420
|
-
it('should return 400 for invalid semver ranges', async () => {
|
|
421
|
-
const res = await app.request(
|
|
422
|
-
'/npm/lodash?versionRange=invalid-range',
|
|
423
|
-
{},
|
|
424
|
-
mockEnv,
|
|
425
|
-
)
|
|
426
|
-
expect(res.status).toBe(400)
|
|
427
|
-
expect(res.headers.get('content-type')).toContain(
|
|
428
|
-
'application/json',
|
|
429
|
-
)
|
|
430
|
-
|
|
431
|
-
const data = (await res.json()) as any
|
|
432
|
-
expect(data).toHaveProperty('error')
|
|
433
|
-
expect(data.error).toContain('Invalid semver range')
|
|
434
|
-
})
|
|
435
|
-
})
|
|
436
|
-
})
|