@mastra/playground-ui 23.0.2 → 24.0.0-alpha.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/CHANGELOG.md +19 -0
- package/dist/index.cjs.js +4254 -13
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.css +354 -84
- package/dist/index.es.js +4137 -16
- package/dist/index.es.js.map +1 -1
- package/dist/src/domains/logs/components/index.d.ts +6 -0
- package/dist/src/domains/logs/components/log-details-view.d.ts +12 -0
- package/dist/src/domains/logs/components/logs-error-content.d.ts +14 -0
- package/dist/src/domains/logs/components/logs-layout.d.ts +19 -0
- package/dist/src/domains/logs/components/logs-list-view.d.ts +20 -0
- package/dist/src/domains/logs/components/logs-toolbar.d.ts +13 -0
- package/dist/src/domains/logs/components/no-logs-info.d.ts +1 -0
- package/dist/src/domains/logs/hooks/index.d.ts +4 -0
- package/dist/src/domains/logs/hooks/use-logs-filter-persistence.d.ts +22 -0
- package/dist/src/domains/logs/hooks/use-logs-list-navigation.d.ts +22 -0
- package/dist/src/domains/logs/hooks/use-logs-url-state.d.ts +38 -0
- package/dist/src/domains/logs/hooks/use-logs.d.ts +1271 -0
- package/dist/src/domains/logs/index.d.ts +4 -0
- package/dist/src/domains/logs/log-filters.d.ts +109 -0
- package/dist/src/domains/logs/types.d.ts +35 -0
- package/dist/src/domains/metrics/components/bar-list.d.ts +21 -0
- package/dist/src/domains/metrics/components/date-range-selector.d.ts +1 -0
- package/dist/src/domains/metrics/components/index.d.ts +9 -0
- package/dist/src/domains/metrics/components/kpi-card-view.d.ts +9 -0
- package/dist/src/domains/metrics/components/latency-card-view.d.ts +11 -0
- package/dist/src/domains/metrics/components/metrics-utils.d.ts +15 -0
- package/dist/src/domains/metrics/components/model-usage-cost-card-view.d.ts +7 -0
- package/dist/src/domains/metrics/components/scores-card-view.d.ts +12 -0
- package/dist/src/domains/metrics/components/token-usage-by-agent-card-view.d.ts +7 -0
- package/dist/src/domains/metrics/components/traces-volume-card-view.d.ts +11 -0
- package/dist/src/domains/metrics/hooks/index.d.ts +12 -0
- package/dist/src/domains/metrics/hooks/use-agent-runs-kpi-metrics.d.ts +10 -0
- package/dist/src/domains/metrics/hooks/use-avg-score-kpi-metrics.d.ts +10 -0
- package/dist/src/domains/metrics/hooks/use-latency-metrics.d.ts +11 -0
- package/dist/src/domains/metrics/hooks/use-metrics-filters.d.ts +9 -0
- package/dist/src/domains/metrics/hooks/use-metrics.d.ts +43 -0
- package/dist/src/domains/metrics/hooks/use-model-cost-kpi-metrics.d.ts +7 -0
- package/dist/src/domains/metrics/hooks/use-model-usage-cost-metrics.d.ts +10 -0
- package/dist/src/domains/metrics/hooks/use-scores-metrics.d.ts +22 -0
- package/dist/src/domains/metrics/hooks/use-token-usage-by-agent-metrics.d.ts +9 -0
- package/dist/src/domains/metrics/hooks/use-total-tokens-kpi-metrics.d.ts +6 -0
- package/dist/src/domains/metrics/hooks/use-trace-volume-metrics.d.ts +10 -0
- package/dist/src/domains/metrics/index.d.ts +2 -0
- package/dist/src/domains/traces/components/format-hierarchical-spans.d.ts +12 -0
- package/dist/src/domains/traces/components/index.d.ts +18 -0
- package/dist/src/domains/traces/components/shared.d.ts +3 -0
- package/dist/src/domains/traces/components/span-data-panel-view.d.ts +26 -0
- package/dist/src/domains/traces/components/span-details-view.d.ts +14 -0
- package/dist/src/domains/traces/components/span-token-usage.d.ts +22 -0
- package/dist/src/domains/traces/components/timeline-expand-col.d.ts +12 -0
- package/dist/src/domains/traces/components/timeline-name-col.d.ts +15 -0
- package/dist/src/domains/traces/components/timeline-structure-sign.d.ts +5 -0
- package/dist/src/domains/traces/components/timeline-timing-col.d.ts +12 -0
- package/dist/src/domains/traces/components/trace-data-panel-view.d.ts +28 -0
- package/dist/src/domains/traces/components/trace-details-view.d.ts +18 -0
- package/dist/src/domains/traces/components/trace-keys-and-values.d.ts +16 -0
- package/dist/src/domains/traces/components/trace-timeline-span.d.ts +18 -0
- package/dist/src/domains/traces/components/trace-timeline.d.ts +15 -0
- package/dist/src/domains/traces/components/traces-error-content.d.ts +16 -0
- package/dist/src/domains/traces/components/traces-layout.d.ts +18 -0
- package/dist/src/domains/traces/components/traces-list-view.d.ts +39 -0
- package/dist/src/domains/traces/components/traces-toolbar.d.ts +19 -0
- package/dist/src/domains/traces/hooks/get-all-span-ids.d.ts +3 -0
- package/dist/src/domains/traces/hooks/index.d.ts +13 -0
- package/dist/src/domains/traces/hooks/use-entity-names.d.ts +7 -0
- package/dist/src/domains/traces/hooks/use-environments.d.ts +1 -0
- package/dist/src/domains/traces/hooks/use-service-names.d.ts +1 -0
- package/dist/src/domains/traces/hooks/use-span-detail.d.ts +46 -0
- package/dist/src/domains/traces/hooks/use-tags.d.ts +1 -0
- package/dist/src/domains/traces/hooks/use-trace-filter-persistence.d.ts +31 -0
- package/dist/src/domains/traces/hooks/use-trace-light-spans.d.ts +19 -0
- package/dist/src/domains/traces/hooks/use-trace-list-navigation.d.ts +12 -0
- package/dist/src/domains/traces/hooks/use-trace-span-navigation.d.ts +10 -0
- package/dist/src/domains/traces/hooks/use-trace-spans.d.ts +47 -0
- package/dist/src/domains/traces/hooks/use-trace-url-state.d.ts +53 -0
- package/dist/src/domains/traces/hooks/use-traces.d.ts +1549 -0
- package/dist/src/domains/traces/index.d.ts +8 -0
- package/dist/src/domains/traces/trace-filters.d.ts +121 -0
- package/dist/src/domains/traces/types.d.ts +35 -0
- package/dist/src/domains/traces/utils/group-traces-by-thread.d.ts +20 -0
- package/dist/src/domains/traces/utils/index.d.ts +2 -0
- package/dist/src/domains/traces/utils/span-utils.d.ts +15 -0
- package/dist/src/index.d.ts +3 -0
- package/package.json +12 -9
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export * from './components';
|
|
2
|
+
export * from './hooks';
|
|
3
|
+
export * from './utils';
|
|
4
|
+
export * from './trace-filters';
|
|
5
|
+
export type { UISpan, UISpanStyle, TraceDatePreset, EntityOptions } from './types';
|
|
6
|
+
export { CONTEXT_FIELD_IDS } from './types';
|
|
7
|
+
/** Tab identifier for SpanDataPanelView. */
|
|
8
|
+
export type SpanTab = 'details' | 'scoring';
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { EntityType } from '@mastra/core/observability';
|
|
2
|
+
import { ListTracesArgs } from '@mastra/core/storage';
|
|
3
|
+
import { TraceDatePreset } from './types';
|
|
4
|
+
import { PropertyFilterField, PropertyFilterToken } from '../../ds/components/PropertyFilter/types';
|
|
5
|
+
export type EntityOptions = {
|
|
6
|
+
label: string;
|
|
7
|
+
entityType: EntityType;
|
|
8
|
+
};
|
|
9
|
+
export declare const ROOT_ENTITY_TYPES: {
|
|
10
|
+
readonly AGENT: EntityType.AGENT;
|
|
11
|
+
readonly WORKFLOW: EntityType.WORKFLOW_RUN;
|
|
12
|
+
readonly SCORER: EntityType.SCORER;
|
|
13
|
+
readonly INGEST: EntityType.RAG_INGESTION;
|
|
14
|
+
};
|
|
15
|
+
export declare const ROOT_ENTITY_TYPE_OPTIONS: readonly [{
|
|
16
|
+
readonly label: "Agent";
|
|
17
|
+
readonly entityType: EntityType.AGENT;
|
|
18
|
+
}, {
|
|
19
|
+
readonly label: "Workflow";
|
|
20
|
+
readonly entityType: EntityType.WORKFLOW_RUN;
|
|
21
|
+
}, {
|
|
22
|
+
readonly label: "Scorer";
|
|
23
|
+
readonly entityType: EntityType.SCORER;
|
|
24
|
+
}, {
|
|
25
|
+
readonly label: "Ingest";
|
|
26
|
+
readonly entityType: EntityType.RAG_INGESTION;
|
|
27
|
+
}];
|
|
28
|
+
export type TraceStatusFilter = 'running' | 'success' | 'error';
|
|
29
|
+
export declare const TRACE_STATUS_OPTIONS: readonly [{
|
|
30
|
+
readonly label: "Running";
|
|
31
|
+
readonly value: "running";
|
|
32
|
+
}, {
|
|
33
|
+
readonly label: "Success";
|
|
34
|
+
readonly value: "success";
|
|
35
|
+
}, {
|
|
36
|
+
readonly label: "Error";
|
|
37
|
+
readonly value: "error";
|
|
38
|
+
}];
|
|
39
|
+
/** Field ids for "synthetic" filter entries — they live in dedicated URL params
|
|
40
|
+
* rather than the generic `filter*` set, but appear as rows in the Filter
|
|
41
|
+
* popover so users can manage all filters from one place. */
|
|
42
|
+
export declare const TRACE_SYNTHETIC_FILTER_FIELD_IDS: readonly ["rootEntityType", "status"];
|
|
43
|
+
export declare const TRACE_ROOT_ENTITY_TYPE_PARAM = "rootEntityType";
|
|
44
|
+
export declare const TRACE_STATUS_PARAM = "status";
|
|
45
|
+
export declare const TRACE_DATE_PRESET_PARAM = "datePreset";
|
|
46
|
+
export declare const TRACE_DATE_FROM_PARAM = "dateFrom";
|
|
47
|
+
export declare const TRACE_DATE_TO_PARAM = "dateTo";
|
|
48
|
+
export declare const TRACE_DATE_PRESET_VALUES: Set<TraceDatePreset>;
|
|
49
|
+
export declare const TRACE_PROPERTY_FILTER_PARAM_BY_FIELD: {
|
|
50
|
+
readonly tags: "filterTags";
|
|
51
|
+
readonly entityId: "filterEntityId";
|
|
52
|
+
readonly entityName: "filterEntityName";
|
|
53
|
+
readonly traceId: "filterTraceId";
|
|
54
|
+
readonly runId: "filterRunId";
|
|
55
|
+
readonly threadId: "filterThreadId";
|
|
56
|
+
readonly sessionId: "filterSessionId";
|
|
57
|
+
readonly requestId: "filterRequestId";
|
|
58
|
+
readonly resourceId: "filterResourceId";
|
|
59
|
+
readonly userId: "filterUserId";
|
|
60
|
+
readonly organizationId: "filterOrganizationId";
|
|
61
|
+
readonly serviceName: "filterServiceName";
|
|
62
|
+
readonly environment: "filterEnvironment";
|
|
63
|
+
readonly experimentId: "filterExperimentId";
|
|
64
|
+
};
|
|
65
|
+
export declare const TRACE_PROPERTY_FILTER_FIELD_IDS: Array<keyof typeof TRACE_PROPERTY_FILTER_PARAM_BY_FIELD>;
|
|
66
|
+
export declare const TRACE_STATUS_VALUES: Set<TraceStatusFilter>;
|
|
67
|
+
export declare const DEFAULT_TRACE_FILTERS_STORAGE_KEY = "mastra:traces:saved-filters";
|
|
68
|
+
/** Serialize the filter-related URL params (date + rootEntityType + status +
|
|
69
|
+
* generic filterX set) to localStorage so the user can restore them on next
|
|
70
|
+
* visit. Throws no errors — storage being unavailable is fine. */
|
|
71
|
+
export declare function saveTraceFiltersToStorage(params: URLSearchParams, storageKey?: string): void;
|
|
72
|
+
/** Forget any previously saved filter set. Called from the "Remove filters"
|
|
73
|
+
* action so the next plain sidebar nav lands on an empty page. */
|
|
74
|
+
export declare function clearSavedTraceFilters(storageKey?: string): void;
|
|
75
|
+
/** Read a previously saved filter set and return it as URLSearchParams, or
|
|
76
|
+
* null if nothing is saved or storage is unavailable. */
|
|
77
|
+
export declare function loadTraceFiltersFromStorage(storageKey?: string): URLSearchParams | null;
|
|
78
|
+
/** True when `params` carries any filter-related key — used to decide whether
|
|
79
|
+
* to hydrate from localStorage on page mount (we only hydrate when the URL is
|
|
80
|
+
* filter-clean, i.e. the user landed here via a plain sidebar nav). */
|
|
81
|
+
export declare function hasAnyTraceFilterParams(params: URLSearchParams): boolean;
|
|
82
|
+
export declare function createTracePropertyFilterFields({ availableTags, availableRootEntityNames, availableServiceNames, availableEnvironments, loading, }: {
|
|
83
|
+
availableTags: string[];
|
|
84
|
+
availableRootEntityNames: string[];
|
|
85
|
+
availableServiceNames: string[];
|
|
86
|
+
availableEnvironments: string[];
|
|
87
|
+
loading?: {
|
|
88
|
+
tags?: boolean;
|
|
89
|
+
entityNames?: boolean;
|
|
90
|
+
serviceNames?: boolean;
|
|
91
|
+
environments?: boolean;
|
|
92
|
+
};
|
|
93
|
+
}): PropertyFilterField[];
|
|
94
|
+
/**
|
|
95
|
+
* Read filter tokens from URL search params preserving the order in which each
|
|
96
|
+
* filter was first added (URLSearchParams iterates in insertion order). This
|
|
97
|
+
* is used by the Filter popover + PropertyFilterApplied pills so the UI reflects the
|
|
98
|
+
* order the user created the filters in.
|
|
99
|
+
*/
|
|
100
|
+
export declare function getTracePropertyFilterTokens(searchParams: URLSearchParams): PropertyFilterToken[];
|
|
101
|
+
export declare function getPreservedTraceFilterParams(searchParams: URLSearchParams): URLSearchParams;
|
|
102
|
+
/**
|
|
103
|
+
* Clear all filter params from `params` and re-add them in the given `tokens`
|
|
104
|
+
* order so the URL (and therefore the PropertyFilterApplied pills) reflects the
|
|
105
|
+
* creation order of filters. Handles the generic `filterX` params plus the
|
|
106
|
+
* dedicated synthetic params (rootEntityType, status).
|
|
107
|
+
*/
|
|
108
|
+
export declare function applyTracePropertyFilterTokens(params: URLSearchParams, tokens: PropertyFilterToken[]): void;
|
|
109
|
+
export declare function buildTraceListFilters({ rootEntityType, status, dateFrom, dateTo, tokens, }: {
|
|
110
|
+
rootEntityType?: string;
|
|
111
|
+
status?: TraceStatusFilter;
|
|
112
|
+
dateFrom?: Date;
|
|
113
|
+
dateTo?: Date;
|
|
114
|
+
tokens: PropertyFilterToken[];
|
|
115
|
+
}): ListTracesArgs['filters'];
|
|
116
|
+
/**
|
|
117
|
+
* "Clear" semantics: keep all filter pills but neutralize each value.
|
|
118
|
+
* '' for text fields, 'Any' for single-select pick-multi, [] for multi-select pick-multi.
|
|
119
|
+
* Date range is intentionally NOT touched here — that's a separate concern.
|
|
120
|
+
*/
|
|
121
|
+
export declare function neutralizeFilterTokens(filterFields: PropertyFilterField[], filterTokens: PropertyFilterToken[]): PropertyFilterToken[];
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { EntityType } from '@mastra/core/observability';
|
|
2
|
+
import { ReactNode } from '../../../node_modules/@types/react';
|
|
3
|
+
export type UISpan = {
|
|
4
|
+
id: string;
|
|
5
|
+
name: string;
|
|
6
|
+
type: string;
|
|
7
|
+
latency: number;
|
|
8
|
+
startTime: string;
|
|
9
|
+
endTime?: string;
|
|
10
|
+
spans?: UISpan[];
|
|
11
|
+
parentSpanId?: string | null;
|
|
12
|
+
};
|
|
13
|
+
export type UISpanStyle = {
|
|
14
|
+
icon?: ReactNode;
|
|
15
|
+
color?: string;
|
|
16
|
+
label?: string;
|
|
17
|
+
bgColor?: string;
|
|
18
|
+
typePrefix: string;
|
|
19
|
+
};
|
|
20
|
+
export type EntityOptions = {
|
|
21
|
+
value: string;
|
|
22
|
+
label: string;
|
|
23
|
+
type: EntityType.AGENT;
|
|
24
|
+
} | {
|
|
25
|
+
value: string;
|
|
26
|
+
label: string;
|
|
27
|
+
type: EntityType.WORKFLOW_RUN;
|
|
28
|
+
} | {
|
|
29
|
+
value: string;
|
|
30
|
+
label: string;
|
|
31
|
+
type: 'all';
|
|
32
|
+
};
|
|
33
|
+
export type TraceDatePreset = 'all' | 'last-24h' | 'last-3d' | 'last-7d' | 'last-14d' | 'last-30d' | 'custom';
|
|
34
|
+
/** Canonical list of context field IDs used for trace filtering and value extraction */
|
|
35
|
+
export declare const CONTEXT_FIELD_IDS: readonly ["environment", "serviceName", "source", "scope", "userId", "organizationId", "resourceId", "runId", "sessionId", "threadId", "requestId", "experimentId", "spanType", "entityName", "parentEntityType", "parentEntityId", "parentEntityName", "rootEntityType", "rootEntityId", "rootEntityName"];
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/** Minimal trace shape required by groupTracesByThread */
|
|
2
|
+
export type GroupableTrace = {
|
|
3
|
+
threadId?: string | null;
|
|
4
|
+
createdAt: Date | string;
|
|
5
|
+
};
|
|
6
|
+
export type ThreadGroup<T extends GroupableTrace = GroupableTrace> = {
|
|
7
|
+
threadId: string;
|
|
8
|
+
traces: T[];
|
|
9
|
+
};
|
|
10
|
+
export type GroupedTraces<T extends GroupableTrace = GroupableTrace> = {
|
|
11
|
+
groups: ThreadGroup<T>[];
|
|
12
|
+
ungrouped: T[];
|
|
13
|
+
};
|
|
14
|
+
/**
|
|
15
|
+
* Groups traces by their threadId field.
|
|
16
|
+
* Traces without a threadId are placed in the `ungrouped` bucket.
|
|
17
|
+
* Groups are ordered by the most recent trace's createdAt (descending).
|
|
18
|
+
* Within each group, traces maintain their original order.
|
|
19
|
+
*/
|
|
20
|
+
export declare function groupTracesByThread<T extends GroupableTrace>(traces: T[]): GroupedTraces<T>;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { SpanRecord } from '@mastra/core/storage';
|
|
2
|
+
/**
|
|
3
|
+
* Extract a truncated text preview from a span's input field.
|
|
4
|
+
* Agent traces store `input` as an array of message objects.
|
|
5
|
+
* Returns the text content of all user messages joined, truncated to maxLength.
|
|
6
|
+
*/
|
|
7
|
+
export declare function getInputPreview(input: unknown, maxLength?: number): string;
|
|
8
|
+
/**
|
|
9
|
+
* Check if a span indicates that the token limit was exceeded
|
|
10
|
+
*/
|
|
11
|
+
export declare function isTokenLimitExceeded(span?: SpanRecord): boolean;
|
|
12
|
+
/**
|
|
13
|
+
* Get a human-readable message for token limit exceeded
|
|
14
|
+
*/
|
|
15
|
+
export declare function getTokenLimitMessage(span?: SpanRecord): string;
|
package/dist/src/index.d.ts
CHANGED
|
@@ -117,4 +117,7 @@ export * from './lib/resize';
|
|
|
117
117
|
export * from './lib/file';
|
|
118
118
|
export * from './lib/template';
|
|
119
119
|
export { usePlaygroundStore, useIsDarkMode, type PlaygroundTheme } from './store/playground-store';
|
|
120
|
+
export * from './domains/metrics';
|
|
121
|
+
export * from './domains/traces';
|
|
122
|
+
export * from './domains/logs';
|
|
120
123
|
export type { LinkComponent, LinkComponentProps } from './ds/types/link-component';
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mastra/playground-ui",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "
|
|
4
|
+
"version": "24.0.0-alpha.0",
|
|
5
5
|
"description": "Mastra Playground components",
|
|
6
6
|
"main": "dist/index.umd.js",
|
|
7
7
|
"module": "dist/index.es.js",
|
|
@@ -94,15 +94,17 @@
|
|
|
94
94
|
},
|
|
95
95
|
"peerDependencies": {
|
|
96
96
|
"@mastra/core": ">=1.7.1-0 <2.0.0-0",
|
|
97
|
+
"@tanstack/react-query": "^5.90.21",
|
|
97
98
|
"lucide-react": "^0.474.0",
|
|
98
99
|
"react": ">=19.0.0",
|
|
99
100
|
"tailwindcss": "^4.0.0",
|
|
100
|
-
"@mastra/client-js": "^1.
|
|
101
|
-
"@mastra/react": "0.2.
|
|
101
|
+
"@mastra/client-js": "^1.15.0-alpha.0",
|
|
102
|
+
"@mastra/react": "0.2.30-alpha.0"
|
|
102
103
|
},
|
|
103
104
|
"devDependencies": {
|
|
104
|
-
"@storybook/addon-docs": "^
|
|
105
|
-
"@storybook/react-vite": "^
|
|
105
|
+
"@storybook/addon-docs": "^10.3.5",
|
|
106
|
+
"@storybook/react-vite": "^10.3.5",
|
|
107
|
+
"@tanstack/react-query": "^5.90.21",
|
|
106
108
|
"@tailwindcss/postcss": "^4.2.2",
|
|
107
109
|
"@tailwindcss/typography": "^0.5.19",
|
|
108
110
|
"@tailwindcss/vite": "4.2.2",
|
|
@@ -119,7 +121,7 @@
|
|
|
119
121
|
"eslint-plugin-react-refresh": "^0.5.2",
|
|
120
122
|
"jsdom": "^26.1.0",
|
|
121
123
|
"rollup-plugin-node-externals": "^8.1.2",
|
|
122
|
-
"storybook": "^
|
|
124
|
+
"storybook": "^10.3.5",
|
|
123
125
|
"tailwind-merge": "^3.5.0",
|
|
124
126
|
"tailwindcss": "4.2.2",
|
|
125
127
|
"tw-animate-css": "^1.4.0",
|
|
@@ -128,10 +130,11 @@
|
|
|
128
130
|
"vite-plugin-dts": "^4.5.4",
|
|
129
131
|
"vite-plugin-lib-inject-css": "^2.2.2",
|
|
130
132
|
"vitest": "4.1.5",
|
|
133
|
+
"eslint-plugin-storybook": "^10.3.5",
|
|
131
134
|
"@internal/lint": "0.0.86",
|
|
132
|
-
"@mastra/client-js": "^1.
|
|
133
|
-
"@mastra/
|
|
134
|
-
"@mastra/
|
|
135
|
+
"@mastra/client-js": "^1.15.0-alpha.0",
|
|
136
|
+
"@mastra/react": "0.2.30-alpha.0",
|
|
137
|
+
"@mastra/core": "1.29.0-alpha.0"
|
|
135
138
|
},
|
|
136
139
|
"homepage": "https://mastra.ai",
|
|
137
140
|
"repository": {
|