@ea-lab/reactive-json-docs 2.2.0 → 2.3.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
CHANGED
|
@@ -266,15 +266,112 @@ additionalDataSource:
|
|
|
266
266
|
```
|
|
267
267
|
|
|
268
268
|
### Properties
|
|
269
|
-
- **`src`** (required): URL of the data source. Can be a **string** (used as-is) or an **array of segments**
|
|
270
|
-
- **`path`** (optional): Path where to place the data (template syntax)
|
|
271
|
-
- **`method`** (optional): HTTP method (GET, POST, etc.)
|
|
272
|
-
- **`dataMapping`** (optional): Configure selective data dispatch using mapping processors
|
|
273
|
-
- **`blocking`** (optional): If `true`, waits for loading before displaying
|
|
269
|
+
- **`src`** (required): URL of the data source. Can be a **string** (used as-is) or an **array of segments** — see [Dynamic URLs](#dynamic-urls-with-src-as-array) below.
|
|
270
|
+
- **`path`** (optional): Path where to place the data (template syntax).
|
|
271
|
+
- **`method`** (optional): HTTP method (GET, POST, etc.).
|
|
272
|
+
- **`dataMapping`** (optional): Configure selective data dispatch using mapping processors.
|
|
273
|
+
- **`blocking`** (optional): If `true`, waits for loading before displaying.
|
|
274
|
+
- **`fallbackDataSource`** (optional): An alternate source tried when the primary fails — see [Fallback Sources](#fallback-sources) below.
|
|
274
275
|
|
|
275
276
|
### Dynamic URLs with `src` as Array
|
|
276
277
|
|
|
277
|
-
When `src` is an array, each
|
|
278
|
+
When `src` is an array, each element is processed individually and the results are assembled into the final URL. The array can mix three kinds of elements:
|
|
279
|
+
|
|
280
|
+
#### 1. Plain strings and store references
|
|
281
|
+
|
|
282
|
+
A plain string is used as a literal. A string starting with `~~.` or `~.` is resolved from the root store data (both notations are equivalent in this context).
|
|
283
|
+
|
|
284
|
+
```yaml
|
|
285
|
+
additionalDataSource:
|
|
286
|
+
- src:
|
|
287
|
+
- "/api/items/"
|
|
288
|
+
- ~~.itemId # resolved from root data
|
|
289
|
+
- "/details"
|
|
290
|
+
path: ~~.itemDetails
|
|
291
|
+
blocking: true
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
#### 2. Segment objects — `{ segment, required? }`
|
|
295
|
+
|
|
296
|
+
A segment object resolves a dynamic value and inserts it as a path part.
|
|
297
|
+
|
|
298
|
+
```yaml
|
|
299
|
+
additionalDataSource:
|
|
300
|
+
- src:
|
|
301
|
+
- "/api/"
|
|
302
|
+
- segment: ~~.category # resolved as a path segment
|
|
303
|
+
- "/items"
|
|
304
|
+
path: ~~.items
|
|
305
|
+
blocking: true
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
When `required: true` is set and the segment resolves to `null` or empty, the entire URL is aborted (returns `null`) instead of producing a broken URL. This triggers the `fallbackDataSource` if one is defined, otherwise the source is skipped with a warning.
|
|
309
|
+
|
|
310
|
+
```yaml
|
|
311
|
+
additionalDataSource:
|
|
312
|
+
- src:
|
|
313
|
+
- "/api/items/"
|
|
314
|
+
- segment: ~~.requiredId
|
|
315
|
+
required: true # abort URL if null
|
|
316
|
+
fallbackDataSource:
|
|
317
|
+
src: "/api/items/default"
|
|
318
|
+
path: ~~.item
|
|
319
|
+
path: ~~.item
|
|
320
|
+
blocking: true
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
#### 3. Query param objects — `{ param, value, required? }`
|
|
324
|
+
|
|
325
|
+
A param object adds a key-value pair to the URL query string. Both the key and the value accept store references.
|
|
326
|
+
|
|
327
|
+
```yaml
|
|
328
|
+
additionalDataSource:
|
|
329
|
+
- src:
|
|
330
|
+
- "/api/items"
|
|
331
|
+
- param: id
|
|
332
|
+
value: ~~.itemId # ?id=<itemId>
|
|
333
|
+
- param: ~~.filterParamName
|
|
334
|
+
value: ~~.filterValue # dynamic key and value
|
|
335
|
+
path: ~~.items
|
|
336
|
+
blocking: true
|
|
337
|
+
```
|
|
338
|
+
|
|
339
|
+
**Null handling for params:**
|
|
340
|
+
- If either the key or the value resolves to `null`/empty and `required` is absent or `false`, the param is **silently omitted** from the URL.
|
|
341
|
+
- If either resolves to `null`/empty and `required: true` is set, the entire URL is **aborted**, triggering `fallbackDataSource` if defined.
|
|
342
|
+
|
|
343
|
+
```yaml
|
|
344
|
+
additionalDataSource:
|
|
345
|
+
- src:
|
|
346
|
+
- "/api/search"
|
|
347
|
+
- param: q
|
|
348
|
+
value: ~~.searchQuery # omitted if null
|
|
349
|
+
- param: type
|
|
350
|
+
value: ~~.filterType
|
|
351
|
+
required: true # abort if null
|
|
352
|
+
path: ~~.results
|
|
353
|
+
blocking: true
|
|
354
|
+
```
|
|
355
|
+
|
|
356
|
+
#### Mixing all three types
|
|
357
|
+
|
|
358
|
+
Path parts (plain strings, `~~.`/`~.` strings, and `segment` objects) are concatenated in order. All `param` objects are collected and appended as a query string after the path.
|
|
359
|
+
|
|
360
|
+
```yaml
|
|
361
|
+
additionalDataSource:
|
|
362
|
+
- src:
|
|
363
|
+
- "/api/"
|
|
364
|
+
- segment: ~~.category # path: /api/electronics
|
|
365
|
+
required: true # abort URL if category is null
|
|
366
|
+
- "/items" # path: /api/electronics/items
|
|
367
|
+
- param: id
|
|
368
|
+
value: ~~.itemId # ?id=42
|
|
369
|
+
- param: ~~.extraKey
|
|
370
|
+
value: ~~.extraValue # &q=hello
|
|
371
|
+
path: ~~.result
|
|
372
|
+
blocking: true
|
|
373
|
+
# Resolved URL: /api/electronics/items?id=42&q=hello
|
|
374
|
+
```
|
|
278
375
|
|
|
279
376
|
This is particularly useful when an RjBuild is loaded inside a `ReactiveJsonSubroot` with `dataOverride`, where dynamic values (like entity IDs) are injected by the parent.
|
|
280
377
|
|
|
@@ -288,25 +385,54 @@ This is particularly useful when an RjBuild is loaded inside a `ReactiveJsonSubr
|
|
|
288
385
|
```
|
|
289
386
|
|
|
290
387
|
```yaml
|
|
291
|
-
# TimeLogManager.yaml — uses taskId
|
|
388
|
+
# TimeLogManager.yaml — uses taskId as a query param
|
|
292
389
|
additionalDataSource:
|
|
293
390
|
- src:
|
|
294
|
-
- "/api/time-logs
|
|
295
|
-
-
|
|
391
|
+
- "/api/time-logs"
|
|
392
|
+
- param: "filter[task]"
|
|
393
|
+
value: ~~.taskId
|
|
394
|
+
required: true
|
|
296
395
|
path: ~~.timeLogs
|
|
297
396
|
blocking: true
|
|
298
397
|
|
|
299
398
|
data:
|
|
300
|
-
taskId: ""
|
|
399
|
+
taskId: "" # Will be overridden by dataOverride
|
|
301
400
|
timeLogs: []
|
|
302
401
|
```
|
|
303
402
|
|
|
304
|
-
|
|
403
|
+
### Fallback Sources
|
|
404
|
+
|
|
405
|
+
The `fallbackDataSource` property defines an alternate source that is tried automatically when the primary source cannot be used. It accepts the same structure as a regular `additionalDataSource` item, including its own `fallbackDataSource` for chaining.
|
|
406
|
+
|
|
407
|
+
A fallback is triggered in two situations:
|
|
408
|
+
|
|
409
|
+
1. **The URL cannot be resolved** — a segment or param marked `required: true` resolved to `null` or empty.
|
|
410
|
+
2. **The HTTP request fails** — the server returns an error (4xx, 5xx, network failure, etc.).
|
|
305
411
|
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
-
|
|
412
|
+
```yaml
|
|
413
|
+
additionalDataSource:
|
|
414
|
+
# Fallback on missing required param
|
|
415
|
+
- src:
|
|
416
|
+
- "/api/items"
|
|
417
|
+
- param: id
|
|
418
|
+
value: ~~.selectedId
|
|
419
|
+
required: true
|
|
420
|
+
path: ~~.item
|
|
421
|
+
fallbackDataSource:
|
|
422
|
+
src: "/api/items/default"
|
|
423
|
+
path: ~~.item
|
|
424
|
+
blocking: true
|
|
425
|
+
|
|
426
|
+
# Fallback on HTTP error
|
|
427
|
+
- src: "/api/live-config"
|
|
428
|
+
path: ~~.config
|
|
429
|
+
fallbackDataSource:
|
|
430
|
+
src: "/api/config-cache"
|
|
431
|
+
path: ~~.config
|
|
432
|
+
blocking: true
|
|
433
|
+
```
|
|
434
|
+
|
|
435
|
+
When a fallback is triggered, a warning is logged in the console explaining the reason. If no fallback is defined and the primary fails, the source is skipped with a warning.
|
|
310
436
|
|
|
311
437
|
### Loading Modes
|
|
312
438
|
|
|
@@ -368,30 +494,47 @@ additionalDataSource:
|
|
|
368
494
|
### Complete Example
|
|
369
495
|
|
|
370
496
|
```yaml
|
|
371
|
-
renderView:
|
|
372
|
-
- type: div
|
|
373
|
-
content:
|
|
374
|
-
- type: h1
|
|
375
|
-
content: ["Hello ", ~~.currentUser.name]
|
|
376
|
-
- type: p
|
|
377
|
-
content: ["Version: ", ~~.systemConfig.version]
|
|
378
|
-
|
|
379
497
|
data:
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
498
|
+
userId: "42"
|
|
499
|
+
section: "reports"
|
|
500
|
+
formatParam: "json"
|
|
501
|
+
optionalFilter: null # null → param silently omitted
|
|
502
|
+
fallbackSection: "home"
|
|
384
503
|
|
|
385
504
|
additionalDataSource:
|
|
386
|
-
#
|
|
505
|
+
# Static URL — simple string form
|
|
387
506
|
- src: "/api/user-profile.json"
|
|
388
507
|
path: ~~.currentUser
|
|
389
508
|
blocking: true
|
|
390
|
-
|
|
391
|
-
#
|
|
392
|
-
- src:
|
|
509
|
+
|
|
510
|
+
# Dynamic path segment + query params
|
|
511
|
+
- src:
|
|
512
|
+
- "/api/"
|
|
513
|
+
- segment: ~~.section # e.g. /api/reports
|
|
514
|
+
- param: userId
|
|
515
|
+
value: ~~.userId # ?userId=42
|
|
516
|
+
- param: format
|
|
517
|
+
value: ~~.formatParam # &format=json
|
|
518
|
+
- param: filter
|
|
519
|
+
value: ~~.optionalFilter # null → omitted
|
|
520
|
+
path: ~~.sectionData
|
|
521
|
+
blocking: true
|
|
522
|
+
|
|
523
|
+
# Required param with fallback on failure or HTTP error
|
|
524
|
+
- src: "/api/system-config"
|
|
393
525
|
path: ~~.systemConfig
|
|
526
|
+
fallbackDataSource:
|
|
527
|
+
src: "/api/system-config-cache"
|
|
528
|
+
path: ~~.systemConfig
|
|
394
529
|
blocking: false
|
|
530
|
+
|
|
531
|
+
renderView:
|
|
532
|
+
- type: div
|
|
533
|
+
content:
|
|
534
|
+
- type: h1
|
|
535
|
+
content: ["Hello ", ~~.currentUser.name]
|
|
536
|
+
- type: p
|
|
537
|
+
content: ["Version: ", ~~.systemConfig.version]
|
|
395
538
|
```
|
|
396
539
|
|
|
397
540
|
## Best Practices
|
|
@@ -305,15 +305,100 @@ renderView:
|
|
|
305
305
|
content: |
|
|
306
306
|
|
|
307
307
|
### Properties
|
|
308
|
-
- **`src`** (required): URL of the data source. Can be a **string** (used as-is) or an **array of segments**
|
|
309
|
-
- **`path`** (optional): Path where to place the data (template syntax)
|
|
310
|
-
- **`method`** (optional): HTTP method (GET, POST, etc.)
|
|
311
|
-
- **`dataMapping`** (optional): Configure selective data dispatch using mapping processors
|
|
312
|
-
- **`blocking`** (optional): If `true`, waits for loading before displaying
|
|
308
|
+
- **`src`** (required): URL of the data source. Can be a **string** (used as-is) or an **array of segments** — see [Dynamic URLs](#dynamic-urls-with-src-as-array) below.
|
|
309
|
+
- **`path`** (optional): Path where to place the data (template syntax).
|
|
310
|
+
- **`method`** (optional): HTTP method (GET, POST, etc.).
|
|
311
|
+
- **`dataMapping`** (optional): Configure selective data dispatch using mapping processors.
|
|
312
|
+
- **`blocking`** (optional): If `true`, waits for loading before displaying.
|
|
313
|
+
- **`fallbackDataSource`** (optional): An alternate source tried when the primary fails — see [Fallback Sources](#fallback-sources) below.
|
|
313
314
|
|
|
314
315
|
### Dynamic URLs with `src` as Array
|
|
315
316
|
|
|
316
|
-
When `src` is an array, each
|
|
317
|
+
When `src` is an array, each element is processed individually and the results are assembled into the final URL. The array can mix three kinds of elements:
|
|
318
|
+
|
|
319
|
+
#### 1. Plain strings and store references
|
|
320
|
+
|
|
321
|
+
A plain string is used as a literal. A string starting with `~~.` or `~.` is resolved from the root store data (both notations are equivalent in this context).
|
|
322
|
+
|
|
323
|
+
- type: TabbedSerializer
|
|
324
|
+
yamlSerializedContent: |
|
|
325
|
+
additionalDataSource:
|
|
326
|
+
- src:
|
|
327
|
+
- "/api/items/"
|
|
328
|
+
- ~~.itemId # resolved from root data
|
|
329
|
+
- "/details"
|
|
330
|
+
path: ~~.itemDetails
|
|
331
|
+
blocking: true
|
|
332
|
+
|
|
333
|
+
- type: Markdown
|
|
334
|
+
content: |
|
|
335
|
+
|
|
336
|
+
#### 2. Segment objects — `{ segment, required? }`
|
|
337
|
+
|
|
338
|
+
A segment object resolves a dynamic value and inserts it as a path part. When `required: true` is set and the segment resolves to `null` or empty, the entire URL is aborted instead of producing a broken URL. This triggers `fallbackDataSource` if one is defined.
|
|
339
|
+
|
|
340
|
+
- type: TabbedSerializer
|
|
341
|
+
yamlSerializedContent: |
|
|
342
|
+
additionalDataSource:
|
|
343
|
+
- src:
|
|
344
|
+
- "/api/"
|
|
345
|
+
- segment: ~~.category # resolved as a path segment
|
|
346
|
+
required: true # abort URL if null
|
|
347
|
+
- "/items"
|
|
348
|
+
path: ~~.items
|
|
349
|
+
blocking: true
|
|
350
|
+
|
|
351
|
+
- type: Markdown
|
|
352
|
+
content: |
|
|
353
|
+
|
|
354
|
+
#### 3. Query param objects — `{ param, value, required? }`
|
|
355
|
+
|
|
356
|
+
A param object adds a key-value pair to the URL query string. Both the key and the value accept store references.
|
|
357
|
+
|
|
358
|
+
**Null handling:**
|
|
359
|
+
- If either the key or the value resolves to `null`/empty and `required` is absent or `false`, the param is **silently omitted** from the URL.
|
|
360
|
+
- If either resolves to `null`/empty and `required: true` is set, the entire URL is **aborted**, triggering `fallbackDataSource` if defined.
|
|
361
|
+
|
|
362
|
+
- type: TabbedSerializer
|
|
363
|
+
yamlSerializedContent: |
|
|
364
|
+
additionalDataSource:
|
|
365
|
+
- src:
|
|
366
|
+
- "/api/search"
|
|
367
|
+
- param: q
|
|
368
|
+
value: ~~.searchQuery # omitted if null
|
|
369
|
+
- param: ~~.filterParamName # dynamic key
|
|
370
|
+
value: ~~.filterValue # dynamic value
|
|
371
|
+
- param: type
|
|
372
|
+
value: ~~.filterType
|
|
373
|
+
required: true # abort if null
|
|
374
|
+
path: ~~.results
|
|
375
|
+
blocking: true
|
|
376
|
+
|
|
377
|
+
- type: Markdown
|
|
378
|
+
content: |
|
|
379
|
+
|
|
380
|
+
#### Mixing all three types
|
|
381
|
+
|
|
382
|
+
Path parts (plain strings, `~~.`/`~.` strings, and `segment` objects) are concatenated in order. All `param` objects are collected and appended as a query string after the path.
|
|
383
|
+
|
|
384
|
+
- type: TabbedSerializer
|
|
385
|
+
yamlSerializedContent: |
|
|
386
|
+
additionalDataSource:
|
|
387
|
+
- src:
|
|
388
|
+
- "/api/"
|
|
389
|
+
- segment: ~~.category # path: /api/electronics
|
|
390
|
+
required: true # abort URL if category is null
|
|
391
|
+
- "/items" # path: /api/electronics/items
|
|
392
|
+
- param: id
|
|
393
|
+
value: ~~.itemId # ?id=42
|
|
394
|
+
- param: ~~.extraKey
|
|
395
|
+
value: ~~.extraValue # &q=hello
|
|
396
|
+
path: ~~.result
|
|
397
|
+
blocking: true
|
|
398
|
+
# Resolved URL: /api/electronics/items?id=42&q=hello
|
|
399
|
+
|
|
400
|
+
- type: Markdown
|
|
401
|
+
content: |
|
|
317
402
|
|
|
318
403
|
This is particularly useful when an RjBuild is loaded inside a `ReactiveJsonSubroot` with `dataOverride`, where dynamic values (like entity IDs) are injected by the parent.
|
|
319
404
|
|
|
@@ -327,32 +412,58 @@ renderView:
|
|
|
327
412
|
dataOverride:
|
|
328
413
|
taskId: ~.task.id
|
|
329
414
|
|
|
330
|
-
- type: Markdown
|
|
331
|
-
content: |
|
|
332
|
-
|
|
333
415
|
- type: TabbedSerializer
|
|
334
416
|
yamlSerializedContent: |
|
|
335
|
-
# TimeLogManager.yaml — uses taskId
|
|
417
|
+
# TimeLogManager.yaml — uses taskId as a query param
|
|
336
418
|
additionalDataSource:
|
|
337
419
|
- src:
|
|
338
|
-
- "/api/time-logs
|
|
339
|
-
-
|
|
420
|
+
- "/api/time-logs"
|
|
421
|
+
- param: "filter[task]"
|
|
422
|
+
value: ~~.taskId
|
|
423
|
+
required: true
|
|
340
424
|
path: ~~.timeLogs
|
|
341
425
|
blocking: true
|
|
342
426
|
|
|
343
427
|
data:
|
|
344
|
-
taskId: ""
|
|
428
|
+
taskId: "" # Will be overridden by dataOverride
|
|
345
429
|
timeLogs: []
|
|
346
430
|
|
|
347
431
|
- type: Markdown
|
|
348
432
|
content: |
|
|
349
433
|
|
|
350
|
-
|
|
434
|
+
### Fallback Sources
|
|
435
|
+
|
|
436
|
+
The `fallbackDataSource` property defines an alternate source that is tried automatically when the primary source cannot be used. It accepts the same structure as a regular `additionalDataSource` item, including its own `fallbackDataSource` for chaining.
|
|
437
|
+
|
|
438
|
+
A fallback is triggered in two situations:
|
|
351
439
|
|
|
352
|
-
**
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
440
|
+
1. **The URL cannot be resolved** — a segment or param marked `required: true` resolved to `null` or empty.
|
|
441
|
+
2. **The HTTP request fails** — the server returns an error (4xx, 5xx, network failure, etc.).
|
|
442
|
+
|
|
443
|
+
When a fallback is triggered, a warning is logged in the console explaining the reason.
|
|
444
|
+
|
|
445
|
+
- type: TabbedSerializer
|
|
446
|
+
yamlSerializedContent: |
|
|
447
|
+
additionalDataSource:
|
|
448
|
+
# Fallback on missing required param
|
|
449
|
+
- src:
|
|
450
|
+
- "/api/items"
|
|
451
|
+
- param: id
|
|
452
|
+
value: ~~.selectedId
|
|
453
|
+
required: true
|
|
454
|
+
path: ~~.item
|
|
455
|
+
fallbackDataSource:
|
|
456
|
+
src: "/api/items/default"
|
|
457
|
+
path: ~~.item
|
|
458
|
+
blocking: true
|
|
459
|
+
|
|
460
|
+
# Fallback on HTTP error
|
|
461
|
+
- src: "/api/live-config"
|
|
462
|
+
path: ~~.config
|
|
463
|
+
fallbackDataSource:
|
|
464
|
+
src: "/api/config-cache"
|
|
465
|
+
path: ~~.config
|
|
466
|
+
blocking: true
|
|
356
467
|
|
|
357
468
|
### Loading Modes
|
|
358
469
|
|
|
@@ -434,31 +545,48 @@ renderView:
|
|
|
434
545
|
|
|
435
546
|
- type: TabbedSerializer
|
|
436
547
|
yamlSerializedContent: |
|
|
437
|
-
renderView:
|
|
438
|
-
- type: div
|
|
439
|
-
content:
|
|
440
|
-
- type: h1
|
|
441
|
-
content: ["Hello ", ~~.currentUser.name]
|
|
442
|
-
- type: p
|
|
443
|
-
content: ["Version: ", ~~.systemConfig.version]
|
|
444
|
-
|
|
445
548
|
data:
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
549
|
+
userId: "42"
|
|
550
|
+
section: "reports"
|
|
551
|
+
formatParam: "json"
|
|
552
|
+
optionalFilter: null # null → param silently omitted
|
|
450
553
|
|
|
451
554
|
additionalDataSource:
|
|
452
|
-
#
|
|
555
|
+
# Static URL — simple string form
|
|
453
556
|
- src: "/api/user-profile.json"
|
|
454
557
|
path: ~~.currentUser
|
|
455
558
|
blocking: true
|
|
456
|
-
|
|
457
|
-
#
|
|
458
|
-
- src:
|
|
559
|
+
|
|
560
|
+
# Dynamic path segment + query params
|
|
561
|
+
- src:
|
|
562
|
+
- "/api/"
|
|
563
|
+
- segment: ~~.section # e.g. /api/reports
|
|
564
|
+
required: true
|
|
565
|
+
- param: userId
|
|
566
|
+
value: ~~.userId # ?userId=42
|
|
567
|
+
- param: format
|
|
568
|
+
value: ~~.formatParam # &format=json
|
|
569
|
+
- param: filter
|
|
570
|
+
value: ~~.optionalFilter # null → omitted
|
|
571
|
+
path: ~~.sectionData
|
|
572
|
+
blocking: true
|
|
573
|
+
|
|
574
|
+
# Fallback on HTTP error or unavailable source
|
|
575
|
+
- src: "/api/system-config"
|
|
459
576
|
path: ~~.systemConfig
|
|
577
|
+
fallbackDataSource:
|
|
578
|
+
src: "/api/system-config-cache"
|
|
579
|
+
path: ~~.systemConfig
|
|
460
580
|
blocking: false
|
|
461
581
|
|
|
582
|
+
renderView:
|
|
583
|
+
- type: div
|
|
584
|
+
content:
|
|
585
|
+
- type: h1
|
|
586
|
+
content: ["Hello ", ~~.currentUser.name]
|
|
587
|
+
- type: p
|
|
588
|
+
content: ["Version: ", ~~.systemConfig.version]
|
|
589
|
+
|
|
462
590
|
- type: Markdown
|
|
463
591
|
content: |
|
|
464
592
|
|