mpx-db 1.0.3 → 1.1.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 +135 -2
- package/package.json +8 -2
- package/src/cli.js +53 -15
- package/src/commands/connections.js +63 -10
- package/src/commands/data.js +20 -7
- package/src/commands/migrate.js +142 -36
- package/src/commands/query.js +44 -14
- package/src/commands/schema.js +120 -50
- package/src/mcp.js +307 -0
- package/src/schema.js +581 -0
package/src/schema.js
ADDED
|
@@ -0,0 +1,581 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Schema Module
|
|
3
|
+
*
|
|
4
|
+
* Returns a machine-readable JSON schema describing all commands,
|
|
5
|
+
* flags, inputs, and outputs for AI agent discovery.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { readFileSync } from 'fs';
|
|
9
|
+
import { fileURLToPath } from 'url';
|
|
10
|
+
import { dirname, join } from 'path';
|
|
11
|
+
|
|
12
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
13
|
+
const __dirname = dirname(__filename);
|
|
14
|
+
const pkg = JSON.parse(readFileSync(join(__dirname, '../package.json'), 'utf-8'));
|
|
15
|
+
|
|
16
|
+
export function getSchema() {
|
|
17
|
+
return {
|
|
18
|
+
tool: 'mpx-db',
|
|
19
|
+
version: pkg.version,
|
|
20
|
+
description: pkg.description,
|
|
21
|
+
homepage: pkg.homepage,
|
|
22
|
+
commands: {
|
|
23
|
+
connect: {
|
|
24
|
+
description: 'Test and optionally save a database connection',
|
|
25
|
+
usage: 'mpx-db connect <url> [options]',
|
|
26
|
+
arguments: {
|
|
27
|
+
url: {
|
|
28
|
+
type: 'string',
|
|
29
|
+
required: true,
|
|
30
|
+
description: 'Connection URL (sqlite://, postgres://, mysql://)'
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
flags: {
|
|
34
|
+
'--save': {
|
|
35
|
+
type: 'string',
|
|
36
|
+
description: 'Save connection with a name'
|
|
37
|
+
},
|
|
38
|
+
'--json': {
|
|
39
|
+
type: 'boolean',
|
|
40
|
+
default: false,
|
|
41
|
+
description: 'Output results as structured JSON'
|
|
42
|
+
},
|
|
43
|
+
'--quiet': {
|
|
44
|
+
type: 'boolean',
|
|
45
|
+
default: false,
|
|
46
|
+
description: 'Suppress non-essential output'
|
|
47
|
+
}
|
|
48
|
+
},
|
|
49
|
+
output: {
|
|
50
|
+
json: {
|
|
51
|
+
schema: {
|
|
52
|
+
type: 'object',
|
|
53
|
+
properties: {
|
|
54
|
+
success: { type: 'boolean' },
|
|
55
|
+
connection: {
|
|
56
|
+
type: 'object',
|
|
57
|
+
properties: {
|
|
58
|
+
type: { type: 'string', enum: ['sqlite', 'postgres', 'mysql'] },
|
|
59
|
+
database: { type: 'string' },
|
|
60
|
+
path: { type: 'string' }
|
|
61
|
+
}
|
|
62
|
+
},
|
|
63
|
+
saved: { type: 'boolean' },
|
|
64
|
+
name: { type: 'string' }
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
},
|
|
69
|
+
exitCodes: {
|
|
70
|
+
0: 'Connection successful',
|
|
71
|
+
1: 'Connection failed'
|
|
72
|
+
}
|
|
73
|
+
},
|
|
74
|
+
'connections list': {
|
|
75
|
+
description: 'List all saved connections',
|
|
76
|
+
usage: 'mpx-db connections list [--json]',
|
|
77
|
+
flags: {
|
|
78
|
+
'--json': {
|
|
79
|
+
type: 'boolean',
|
|
80
|
+
default: false,
|
|
81
|
+
description: 'Output as JSON array'
|
|
82
|
+
},
|
|
83
|
+
'--quiet': {
|
|
84
|
+
type: 'boolean',
|
|
85
|
+
default: false,
|
|
86
|
+
description: 'Suppress non-essential output'
|
|
87
|
+
}
|
|
88
|
+
},
|
|
89
|
+
output: {
|
|
90
|
+
json: {
|
|
91
|
+
schema: {
|
|
92
|
+
type: 'object',
|
|
93
|
+
properties: {
|
|
94
|
+
connections: {
|
|
95
|
+
type: 'array',
|
|
96
|
+
items: {
|
|
97
|
+
type: 'object',
|
|
98
|
+
properties: {
|
|
99
|
+
name: { type: 'string' },
|
|
100
|
+
type: { type: 'string' },
|
|
101
|
+
createdAt: { type: 'string', format: 'date-time' }
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
},
|
|
110
|
+
'connections remove': {
|
|
111
|
+
description: 'Remove a saved connection',
|
|
112
|
+
usage: 'mpx-db connections remove <name>',
|
|
113
|
+
arguments: {
|
|
114
|
+
name: {
|
|
115
|
+
type: 'string',
|
|
116
|
+
required: true,
|
|
117
|
+
description: 'Connection name'
|
|
118
|
+
}
|
|
119
|
+
},
|
|
120
|
+
flags: {
|
|
121
|
+
'--json': {
|
|
122
|
+
type: 'boolean',
|
|
123
|
+
default: false,
|
|
124
|
+
description: 'Output results as JSON'
|
|
125
|
+
}
|
|
126
|
+
},
|
|
127
|
+
output: {
|
|
128
|
+
json: {
|
|
129
|
+
schema: {
|
|
130
|
+
type: 'object',
|
|
131
|
+
properties: {
|
|
132
|
+
success: { type: 'boolean' },
|
|
133
|
+
name: { type: 'string' },
|
|
134
|
+
message: { type: 'string' }
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
},
|
|
140
|
+
query: {
|
|
141
|
+
description: 'Execute a SQL query',
|
|
142
|
+
usage: 'mpx-db query <target> <sql> [--json]',
|
|
143
|
+
arguments: {
|
|
144
|
+
target: {
|
|
145
|
+
type: 'string',
|
|
146
|
+
required: true,
|
|
147
|
+
description: 'Connection name or URL'
|
|
148
|
+
},
|
|
149
|
+
sql: {
|
|
150
|
+
type: 'string',
|
|
151
|
+
required: true,
|
|
152
|
+
description: 'SQL query or statement to execute'
|
|
153
|
+
}
|
|
154
|
+
},
|
|
155
|
+
flags: {
|
|
156
|
+
'--json': {
|
|
157
|
+
type: 'boolean',
|
|
158
|
+
default: false,
|
|
159
|
+
description: 'Output results as JSON'
|
|
160
|
+
},
|
|
161
|
+
'--quiet': {
|
|
162
|
+
type: 'boolean',
|
|
163
|
+
default: false,
|
|
164
|
+
description: 'Suppress non-essential output'
|
|
165
|
+
}
|
|
166
|
+
},
|
|
167
|
+
output: {
|
|
168
|
+
json: {
|
|
169
|
+
schema: {
|
|
170
|
+
type: 'object',
|
|
171
|
+
properties: {
|
|
172
|
+
success: { type: 'boolean' },
|
|
173
|
+
type: { type: 'string', enum: ['query', 'statement'] },
|
|
174
|
+
rows: { type: 'array', description: 'For SELECT queries' },
|
|
175
|
+
affectedRows: { type: 'number', description: 'For INSERT/UPDATE/DELETE' },
|
|
176
|
+
insertId: { type: 'number', description: 'For INSERT statements' },
|
|
177
|
+
duration: { type: 'number', description: 'Execution time in milliseconds' }
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
},
|
|
182
|
+
exitCodes: {
|
|
183
|
+
0: 'Query executed successfully',
|
|
184
|
+
1: 'Query failed'
|
|
185
|
+
}
|
|
186
|
+
},
|
|
187
|
+
info: {
|
|
188
|
+
description: 'Show database information',
|
|
189
|
+
usage: 'mpx-db info <target> [--json]',
|
|
190
|
+
arguments: {
|
|
191
|
+
target: {
|
|
192
|
+
type: 'string',
|
|
193
|
+
required: true,
|
|
194
|
+
description: 'Connection name or URL'
|
|
195
|
+
}
|
|
196
|
+
},
|
|
197
|
+
flags: {
|
|
198
|
+
'--json': {
|
|
199
|
+
type: 'boolean',
|
|
200
|
+
default: false,
|
|
201
|
+
description: 'Output as JSON'
|
|
202
|
+
},
|
|
203
|
+
'--quiet': {
|
|
204
|
+
type: 'boolean',
|
|
205
|
+
default: false,
|
|
206
|
+
description: 'Suppress non-essential output'
|
|
207
|
+
}
|
|
208
|
+
},
|
|
209
|
+
output: {
|
|
210
|
+
json: {
|
|
211
|
+
schema: {
|
|
212
|
+
type: 'object',
|
|
213
|
+
properties: {
|
|
214
|
+
type: { type: 'string' },
|
|
215
|
+
version: { type: 'string' },
|
|
216
|
+
database: { type: 'string' },
|
|
217
|
+
path: { type: 'string' },
|
|
218
|
+
size: { type: 'number' },
|
|
219
|
+
encoding: { type: 'string' },
|
|
220
|
+
tableCount: { type: 'number' }
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
},
|
|
226
|
+
tables: {
|
|
227
|
+
description: 'List all tables',
|
|
228
|
+
usage: 'mpx-db tables <target> [--json]',
|
|
229
|
+
arguments: {
|
|
230
|
+
target: {
|
|
231
|
+
type: 'string',
|
|
232
|
+
required: true,
|
|
233
|
+
description: 'Connection name or URL'
|
|
234
|
+
}
|
|
235
|
+
},
|
|
236
|
+
flags: {
|
|
237
|
+
'--json': {
|
|
238
|
+
type: 'boolean',
|
|
239
|
+
default: false,
|
|
240
|
+
description: 'Output as JSON array'
|
|
241
|
+
},
|
|
242
|
+
'--quiet': {
|
|
243
|
+
type: 'boolean',
|
|
244
|
+
default: false,
|
|
245
|
+
description: 'Suppress non-essential output'
|
|
246
|
+
}
|
|
247
|
+
},
|
|
248
|
+
output: {
|
|
249
|
+
json: {
|
|
250
|
+
schema: {
|
|
251
|
+
type: 'object',
|
|
252
|
+
properties: {
|
|
253
|
+
tables: {
|
|
254
|
+
type: 'array',
|
|
255
|
+
items: {
|
|
256
|
+
type: 'object',
|
|
257
|
+
properties: {
|
|
258
|
+
name: { type: 'string' },
|
|
259
|
+
rowCount: { type: 'number' }
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
},
|
|
268
|
+
describe: {
|
|
269
|
+
description: 'Show table schema',
|
|
270
|
+
usage: 'mpx-db describe <target> <table> [--json]',
|
|
271
|
+
arguments: {
|
|
272
|
+
target: {
|
|
273
|
+
type: 'string',
|
|
274
|
+
required: true,
|
|
275
|
+
description: 'Connection name or URL'
|
|
276
|
+
},
|
|
277
|
+
table: {
|
|
278
|
+
type: 'string',
|
|
279
|
+
required: true,
|
|
280
|
+
description: 'Table name'
|
|
281
|
+
}
|
|
282
|
+
},
|
|
283
|
+
flags: {
|
|
284
|
+
'--json': {
|
|
285
|
+
type: 'boolean',
|
|
286
|
+
default: false,
|
|
287
|
+
description: 'Output as JSON'
|
|
288
|
+
},
|
|
289
|
+
'--quiet': {
|
|
290
|
+
type: 'boolean',
|
|
291
|
+
default: false,
|
|
292
|
+
description: 'Suppress non-essential output'
|
|
293
|
+
}
|
|
294
|
+
},
|
|
295
|
+
output: {
|
|
296
|
+
json: {
|
|
297
|
+
schema: {
|
|
298
|
+
type: 'object',
|
|
299
|
+
properties: {
|
|
300
|
+
table: { type: 'string' },
|
|
301
|
+
columns: {
|
|
302
|
+
type: 'array',
|
|
303
|
+
items: {
|
|
304
|
+
type: 'object',
|
|
305
|
+
properties: {
|
|
306
|
+
name: { type: 'string' },
|
|
307
|
+
type: { type: 'string' },
|
|
308
|
+
nullable: { type: 'boolean' },
|
|
309
|
+
defaultValue: { type: 'string' },
|
|
310
|
+
primaryKey: { type: 'boolean' }
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
},
|
|
319
|
+
'schema dump': {
|
|
320
|
+
description: 'Dump database schema as SQL',
|
|
321
|
+
usage: 'mpx-db schema dump <target>',
|
|
322
|
+
arguments: {
|
|
323
|
+
target: {
|
|
324
|
+
type: 'string',
|
|
325
|
+
required: true,
|
|
326
|
+
description: 'Connection name or URL'
|
|
327
|
+
}
|
|
328
|
+
},
|
|
329
|
+
flags: {
|
|
330
|
+
'--json': {
|
|
331
|
+
type: 'boolean',
|
|
332
|
+
default: false,
|
|
333
|
+
description: 'Output as JSON with SQL content'
|
|
334
|
+
},
|
|
335
|
+
'--quiet': {
|
|
336
|
+
type: 'boolean',
|
|
337
|
+
default: false,
|
|
338
|
+
description: 'Suppress non-essential output'
|
|
339
|
+
}
|
|
340
|
+
},
|
|
341
|
+
output: {
|
|
342
|
+
json: {
|
|
343
|
+
schema: {
|
|
344
|
+
type: 'object',
|
|
345
|
+
properties: {
|
|
346
|
+
sql: { type: 'string', description: 'Full schema SQL' }
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
},
|
|
352
|
+
'migrate init': {
|
|
353
|
+
description: 'Initialize migrations directory',
|
|
354
|
+
usage: 'mpx-db migrate init',
|
|
355
|
+
flags: {
|
|
356
|
+
'--json': {
|
|
357
|
+
type: 'boolean',
|
|
358
|
+
default: false,
|
|
359
|
+
description: 'Output as JSON'
|
|
360
|
+
}
|
|
361
|
+
},
|
|
362
|
+
output: {
|
|
363
|
+
json: {
|
|
364
|
+
schema: {
|
|
365
|
+
type: 'object',
|
|
366
|
+
properties: {
|
|
367
|
+
success: { type: 'boolean' },
|
|
368
|
+
directory: { type: 'string' },
|
|
369
|
+
message: { type: 'string' }
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
},
|
|
375
|
+
'migrate create': {
|
|
376
|
+
description: 'Create a new migration file',
|
|
377
|
+
usage: 'mpx-db migrate create <description>',
|
|
378
|
+
arguments: {
|
|
379
|
+
description: {
|
|
380
|
+
type: 'string',
|
|
381
|
+
required: true,
|
|
382
|
+
description: 'Migration description (e.g., "add_users_table")'
|
|
383
|
+
}
|
|
384
|
+
},
|
|
385
|
+
flags: {
|
|
386
|
+
'--json': {
|
|
387
|
+
type: 'boolean',
|
|
388
|
+
default: false,
|
|
389
|
+
description: 'Output as JSON'
|
|
390
|
+
}
|
|
391
|
+
},
|
|
392
|
+
output: {
|
|
393
|
+
json: {
|
|
394
|
+
schema: {
|
|
395
|
+
type: 'object',
|
|
396
|
+
properties: {
|
|
397
|
+
success: { type: 'boolean' },
|
|
398
|
+
filename: { type: 'string' },
|
|
399
|
+
path: { type: 'string' }
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
},
|
|
405
|
+
'migrate status': {
|
|
406
|
+
description: 'Show migration status',
|
|
407
|
+
usage: 'mpx-db migrate status <target> [--json]',
|
|
408
|
+
arguments: {
|
|
409
|
+
target: {
|
|
410
|
+
type: 'string',
|
|
411
|
+
required: true,
|
|
412
|
+
description: 'Connection name or URL'
|
|
413
|
+
}
|
|
414
|
+
},
|
|
415
|
+
flags: {
|
|
416
|
+
'--json': {
|
|
417
|
+
type: 'boolean',
|
|
418
|
+
default: false,
|
|
419
|
+
description: 'Output as JSON'
|
|
420
|
+
},
|
|
421
|
+
'--quiet': {
|
|
422
|
+
type: 'boolean',
|
|
423
|
+
default: false,
|
|
424
|
+
description: 'Suppress non-essential output'
|
|
425
|
+
}
|
|
426
|
+
},
|
|
427
|
+
output: {
|
|
428
|
+
json: {
|
|
429
|
+
schema: {
|
|
430
|
+
type: 'object',
|
|
431
|
+
properties: {
|
|
432
|
+
migrations: {
|
|
433
|
+
type: 'array',
|
|
434
|
+
items: {
|
|
435
|
+
type: 'object',
|
|
436
|
+
properties: {
|
|
437
|
+
name: { type: 'string' },
|
|
438
|
+
status: { type: 'string', enum: ['pending', 'applied'] },
|
|
439
|
+
appliedAt: { type: 'string', format: 'date-time' }
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
},
|
|
448
|
+
'migrate up': {
|
|
449
|
+
description: 'Run pending migrations',
|
|
450
|
+
usage: 'mpx-db migrate up <target> [--json]',
|
|
451
|
+
arguments: {
|
|
452
|
+
target: {
|
|
453
|
+
type: 'string',
|
|
454
|
+
required: true,
|
|
455
|
+
description: 'Connection name or URL'
|
|
456
|
+
}
|
|
457
|
+
},
|
|
458
|
+
flags: {
|
|
459
|
+
'--json': {
|
|
460
|
+
type: 'boolean',
|
|
461
|
+
default: false,
|
|
462
|
+
description: 'Output as JSON'
|
|
463
|
+
},
|
|
464
|
+
'--quiet': {
|
|
465
|
+
type: 'boolean',
|
|
466
|
+
default: false,
|
|
467
|
+
description: 'Suppress non-essential output'
|
|
468
|
+
}
|
|
469
|
+
},
|
|
470
|
+
output: {
|
|
471
|
+
json: {
|
|
472
|
+
schema: {
|
|
473
|
+
type: 'object',
|
|
474
|
+
properties: {
|
|
475
|
+
success: { type: 'boolean' },
|
|
476
|
+
applied: {
|
|
477
|
+
type: 'array',
|
|
478
|
+
items: { type: 'string' }
|
|
479
|
+
},
|
|
480
|
+
count: { type: 'number' }
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
},
|
|
486
|
+
'migrate down': {
|
|
487
|
+
description: 'Rollback last migration',
|
|
488
|
+
usage: 'mpx-db migrate down <target> [--json]',
|
|
489
|
+
arguments: {
|
|
490
|
+
target: {
|
|
491
|
+
type: 'string',
|
|
492
|
+
required: true,
|
|
493
|
+
description: 'Connection name or URL'
|
|
494
|
+
}
|
|
495
|
+
},
|
|
496
|
+
flags: {
|
|
497
|
+
'--json': {
|
|
498
|
+
type: 'boolean',
|
|
499
|
+
default: false,
|
|
500
|
+
description: 'Output as JSON'
|
|
501
|
+
},
|
|
502
|
+
'--quiet': {
|
|
503
|
+
type: 'boolean',
|
|
504
|
+
default: false,
|
|
505
|
+
description: 'Suppress non-essential output'
|
|
506
|
+
}
|
|
507
|
+
},
|
|
508
|
+
output: {
|
|
509
|
+
json: {
|
|
510
|
+
schema: {
|
|
511
|
+
type: 'object',
|
|
512
|
+
properties: {
|
|
513
|
+
success: { type: 'boolean' },
|
|
514
|
+
rolledBack: { type: 'string' }
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
},
|
|
520
|
+
export: {
|
|
521
|
+
description: 'Export table data',
|
|
522
|
+
usage: 'mpx-db export <target> <table> [--format json|csv] [--output file.json]',
|
|
523
|
+
arguments: {
|
|
524
|
+
target: {
|
|
525
|
+
type: 'string',
|
|
526
|
+
required: true,
|
|
527
|
+
description: 'Connection name or URL'
|
|
528
|
+
},
|
|
529
|
+
table: {
|
|
530
|
+
type: 'string',
|
|
531
|
+
required: true,
|
|
532
|
+
description: 'Table name'
|
|
533
|
+
}
|
|
534
|
+
},
|
|
535
|
+
flags: {
|
|
536
|
+
'--format': {
|
|
537
|
+
type: 'string',
|
|
538
|
+
enum: ['json', 'csv'],
|
|
539
|
+
default: 'json',
|
|
540
|
+
description: 'Output format'
|
|
541
|
+
},
|
|
542
|
+
'--output': {
|
|
543
|
+
type: 'string',
|
|
544
|
+
description: 'Output file path'
|
|
545
|
+
},
|
|
546
|
+
'--quiet': {
|
|
547
|
+
type: 'boolean',
|
|
548
|
+
default: false,
|
|
549
|
+
description: 'Suppress non-essential output'
|
|
550
|
+
}
|
|
551
|
+
},
|
|
552
|
+
output: {
|
|
553
|
+
description: 'Exports data to stdout or specified file'
|
|
554
|
+
}
|
|
555
|
+
},
|
|
556
|
+
mcp: {
|
|
557
|
+
description: 'Start MCP (Model Context Protocol) stdio server for AI agent integration',
|
|
558
|
+
usage: 'mpx-db mcp',
|
|
559
|
+
flags: {},
|
|
560
|
+
examples: [
|
|
561
|
+
{ command: 'mpx-db mcp', description: 'Start MCP stdio server' }
|
|
562
|
+
]
|
|
563
|
+
}
|
|
564
|
+
},
|
|
565
|
+
mcpConfig: {
|
|
566
|
+
description: 'Add to your MCP client configuration to use mpx-db as an AI tool',
|
|
567
|
+
config: {
|
|
568
|
+
mcpServers: {
|
|
569
|
+
'mpx-db': {
|
|
570
|
+
command: 'npx',
|
|
571
|
+
args: ['mpx-db', 'mcp']
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
},
|
|
576
|
+
exitCodes: {
|
|
577
|
+
0: 'Success',
|
|
578
|
+
1: 'Error (connection failed, query failed, etc.)'
|
|
579
|
+
}
|
|
580
|
+
};
|
|
581
|
+
}
|