Compare commits

..

4 Commits

Author SHA1 Message Date
Naman Verma
47fd48951c Merge branch 'main' into nv/perses-spec 2026-06-04 18:37:21 +05:30
Naman Verma
9abf8a6188 chore: generate frontend api spec 2026-06-03 12:56:52 +05:30
Naman Verma
259ad8a760 fix: enforce query kind values 2026-06-03 12:56:01 +05:30
Naman Verma
95b98a50af chore: replace perses/perses with perses/spec and introduce kind enum values 2026-06-03 12:28:59 +05:30
23 changed files with 255 additions and 1465 deletions

View File

@@ -2380,6 +2380,26 @@ components:
repeatVariable:
type: string
type: object
DashboardLink:
properties:
name:
type: string
renderVariables:
type: boolean
targetBlank:
type: boolean
tooltip:
type: string
url:
type: string
type: object
DashboardPanelDisplay:
properties:
description:
type: string
name:
type: string
type: object
DashboardTextVariableSpec:
properties:
constant:
@@ -2510,7 +2530,7 @@ components:
type: array
links:
items:
$ref: '#/components/schemas/V1Link'
$ref: '#/components/schemas/DashboardLink'
type: array
panels:
additionalProperties:
@@ -2671,8 +2691,8 @@ components:
type: object
DashboardtypesLayout:
oneOf:
- $ref: '#/components/schemas/DashboardtypesLayoutEnvelopeGithubComPersesPersesPkgModelApiV1DashboardGridLayoutSpec'
DashboardtypesLayoutEnvelopeGithubComPersesPersesPkgModelApiV1DashboardGridLayoutSpec:
- $ref: '#/components/schemas/DashboardtypesLayoutEnvelopeGithubComPersesSpecGoDashboardGridLayoutSpec'
DashboardtypesLayoutEnvelopeGithubComPersesSpecGoDashboardGridLayoutSpec:
properties:
kind:
enum:
@@ -2755,7 +2775,7 @@ components:
DashboardtypesPanel:
properties:
kind:
type: string
$ref: '#/components/schemas/DashboardtypesPanelKind'
spec:
$ref: '#/components/schemas/DashboardtypesPanelSpec'
type: object
@@ -2766,6 +2786,10 @@ components:
unit:
type: string
type: object
DashboardtypesPanelKind:
enum:
- Panel
type: string
DashboardtypesPanelPlugin:
oneOf:
- $ref: '#/components/schemas/DashboardtypesPanelPluginVariantGithubComSigNozSignozPkgTypesDashboardtypesTimeSeriesPanelSpec'
@@ -2872,10 +2896,10 @@ components:
DashboardtypesPanelSpec:
properties:
display:
$ref: '#/components/schemas/V1PanelDisplay'
$ref: '#/components/schemas/DashboardPanelDisplay'
links:
items:
$ref: '#/components/schemas/V1Link'
$ref: '#/components/schemas/DashboardLink'
type: array
plugin:
$ref: '#/components/schemas/DashboardtypesPanelPlugin'
@@ -2948,10 +2972,18 @@ components:
DashboardtypesQuery:
properties:
kind:
type: string
$ref: '#/components/schemas/DashboardtypesQueryKind'
spec:
$ref: '#/components/schemas/DashboardtypesQuerySpec'
type: object
DashboardtypesQueryKind:
enum:
- scalar
- time_series
- raw
- raw_stream
- trace
type: string
DashboardtypesQueryPlugin:
oneOf:
- $ref: '#/components/schemas/DashboardtypesQueryPluginVariantGithubComSigNozSignozPkgTypesDashboardtypesBuilderQuerySpec'
@@ -3216,8 +3248,8 @@ components:
DashboardtypesVariable:
oneOf:
- $ref: '#/components/schemas/DashboardtypesVariableEnvelopeGithubComSigNozSignozPkgTypesDashboardtypesListVariableSpec'
- $ref: '#/components/schemas/DashboardtypesVariableEnvelopeGithubComPersesPersesPkgModelApiV1DashboardTextVariableSpec'
DashboardtypesVariableEnvelopeGithubComPersesPersesPkgModelApiV1DashboardTextVariableSpec:
- $ref: '#/components/schemas/DashboardtypesVariableEnvelopeGithubComPersesSpecGoDashboardTextVariableSpec'
DashboardtypesVariableEnvelopeGithubComPersesSpecGoDashboardTextVariableSpec:
properties:
kind:
enum:
@@ -7235,26 +7267,6 @@ components:
required:
- id
type: object
V1Link:
properties:
name:
type: string
renderVariables:
type: boolean
targetBlank:
type: boolean
tooltip:
type: string
url:
type: string
type: object
V1PanelDisplay:
properties:
description:
type: string
name:
type: string
type: object
VariableDefaultValue:
type: object
VariableDisplay:

View File

@@ -291,8 +291,6 @@
// Prevents the usage of specific antd components in favor of our lib
"signoz/no-signozhq-ui-barrel": "error",
// Forces subpath imports (@signozhq/ui/<component>) instead of the eagerly-loaded barrel
"signoz/no-css-module-bracket-access": "warn",
// Prevents bracket access on CSS modules (styles['kebab-case']) which fails with camelCaseOnly config
"no-restricted-globals": [
"error",
{

View File

@@ -2,33 +2,9 @@
const path = require('path');
module.exports = {
plugins: [
path.join(__dirname, 'stylelint-rules/no-unsupported-asset-url.js'),
path.join(__dirname, 'stylelint-rules/css-modules/no-deep-nesting.js'),
path.join(__dirname, 'stylelint-rules/css-modules/no-id-selectors.js'),
path.join(
__dirname,
'stylelint-rules/css-modules/no-bare-element-selectors.js',
),
path.join(__dirname, 'stylelint-rules/css-modules/prefer-css-variables.js'),
path.join(__dirname, 'stylelint-rules/css-modules/class-name-pattern.js'),
],
plugins: [path.join(__dirname, 'stylelint-rules/no-unsupported-asset-url.js')],
customSyntax: 'postcss-scss',
rules: {
// Applies to all SCSS files
'local/no-unsupported-asset-url': true,
},
overrides: [
{
// CSS module-specific rules
files: ['**/*.module.scss'],
rules: {
'local/no-deep-nesting': [true, { severity: 'warning' }],
'local/no-id-selectors': true,
'local/no-bare-element-selectors': true,
'local/prefer-css-variables': [true, { severity: 'warning' }],
'local/class-name-pattern': [true, { severity: 'warning' }],
},
},
],
};

View File

@@ -23,8 +23,6 @@ You are operating within a constrained context window and strict system prompts.
- Always add data-testid or testId (if supported) to critical/behavioral components like inputs, buttons, etc...
- When creating test, these IDs should be used instead of finding by role.
- Never create barrel files.
- When writing new css, prefer CSS Modules
- Use ./docs/css-modules-guide.md as reference on how to write good CSS Modules.
3. FORCED VERIFICATION: Your internal tools mark file writes as successful even if the code does not compile. You are FORBIDDEN from reporting a task as complete until you have:
- Run `pnpm tsgo --noEmit`

View File

@@ -1,471 +0,0 @@
# CSS Modules Guide for AI Agents
## Config (vite.config.ts)
```ts
css: {
modules: {
localsConvention: 'camelCaseOnly',
},
}
```
**Critical:** `camelCaseOnly` exports ONLY camelCase keys. Original kebab-case NOT accessible.
## Quick Reference
| CSS Class | JS Access | Works? |
|-----------|-----------|--------|
| `.alertHistory` | `styles.alertHistory` | Yes |
| `.alert-history` | `styles.alertHistory` | Yes |
| `.alert-history` | `styles['alert-history']` | NO - undefined |
## Bad Patterns
### Class Naming
```scss
// BAD: Bracket access won't work
.my-class { }
// Then in JS: styles['my-class'] -> undefined
// BAD: Collision - both become same key
.alertHistory { }
.alert-history { } // -> styles.alertHistory (conflicts)
// BAD: Underscore inconsistency
.my_class { } // -> styles.myClass (confusing)
// GOOD: Direct camelCase
.alertHistory { }
.statsCard { }
```
### Nesting
```scss
// BAD: Deep nesting - specificity wars, hard to override
.container {
.wrapper {
.inner {
.content { }
}
}
}
// BAD: Nesting creates separate classes you might not expect
.button {
.icon { } // -> styles.icon (separate class, not scoped under .button)
}
// GOOD: Flat structure
.container { }
.containerWrapper { }
.containerContent { }
// GOOD: Nesting only for pseudo/states
.button {
&:hover { }
&:disabled { }
&::before { }
}
```
### Global Escapes
```scss
// BAD: Overusing global
:global {
.everything { }
.in-here { }
.is-global { }
}
// BAD: Global without necessity
:global(.myComponent) { } // defeats purpose of modules
// GOOD: Targeted global for third-party overrides
.container {
:global(.ant-modal-content) {
padding: 0;
}
}
```
### Selectors
```scss
// BAD: ID selectors - not reusable
#myComponent { }
// BAD: Element selectors without scope
div { } // affects ALL divs in component
// BAD: Complex selectors
.container > div + span ~ p { }
// GOOD: Class-only selectors
.container { }
.title { }
```
### Variables & Values
```scss
// BAD: Hardcoded colors
.button {
background: #1890ff;
color: white;
}
// BAD: Magic numbers
.container {
padding: 17px;
margin-left: 43px;
}
// GOOD: Semantic tokens (theme-aware)
.button {
background: var(--primary-background);
color: var(--primary-foreground);
}
.card {
background: var(--l2-background);
color: var(--l2-foreground);
}
// GOOD: Spacing system
.container {
padding: var(--spacing-4);
margin-left: var(--spacing-5);
}
```
## Design Tokens (@signozhq/design-tokens)
Prefer semantic tokens over hardcoded values.
You can read the ./node_modules/@signozhq/design-tokens/dist/style.css to find complete list of available tokens.
### Spacing
```scss
// Spacing scale (index -> px):
// --spacing-0=0 --spacing-1=2 --spacing-2=4 --spacing-3=6 --spacing-4=8
// --spacing-5=10 --spacing-6=12 --spacing-7=14 --spacing-8=16 --spacing-10=20
// --spacing-12=24 --spacing-16=32 --spacing-20=40 --spacing-24=48 --spacing-32=64
// --spacing-40=80 --spacing-48=96 --spacing-56=112 --spacing-64=128
// (index != px; --spacing-2 is 4px, not 2px)
.container {
padding: var(--spacing-4); // 8px
gap: var(--spacing-6); // 12px
margin-bottom: var(--spacing-8); // 16px
}
// Also available: --padding-* and --margin-* (rem-based)
// --padding-1 = 0.25rem, --padding-4 = 1rem, etc.
```
### Typography
```scss
// Font sizes (preferred)
.title {
font-size: var(--periscope-font-size-large); // 18px
font-size: var(--periscope-font-size-medium); // 16px
font-size: var(--periscope-font-size-base); // 13px
font-size: var(--periscope-font-size-small); // 11px
}
// Alternative scale (rem-based)
.heading {
font-size: var(--font-size-xl); // 1.25rem
font-size: var(--font-size-lg); // 1.125rem
font-size: var(--font-size-base); // 1rem
font-size: var(--font-size-sm); // 0.875rem
}
// Font weights
.bold {
font-weight: var(--font-weight-semibold); // 600
font-weight: var(--font-weight-medium); // 500
font-weight: var(--font-weight-normal); // 400
}
// Line heights
.text {
line-height: var(--line-height-20); // 20px
line-height: var(--line-height-24); // 24px
}
```
### Colors (Prefer Semantic Tokens)
Use L1/L2/L3 semantic tokens - they handle light/dark theme automatically.
```scss
// BAD: Primitive tokens (fixed value across themes, won't swap on theme change)
.card {
background: var(--bg-ink-400);
color: var(--text-vanilla-100);
}
// GOOD: L1/L2/L3 tokens (theme-aware - swap automatically light/dark)
.card {
background: var(--l1-background); // base layer
color: var(--l1-foreground); // primary text
}
.panel {
background: var(--l2-background); // elevated surface
color: var(--l2-foreground); // secondary text
border-color: var(--l2-border);
}
.nested {
background: var(--l3-background); // nested/inset
color: var(--l3-foreground); // tertiary text
}
// Hover states
.card:hover {
background: var(--l1-background-hover);
color: var(--l1-foreground-hover);
}
// Semantic action colors (also theme-aware)
.primary {
background: var(--primary-background);
color: var(--primary-foreground);
}
.danger {
background: var(--danger-background);
color: var(--danger-foreground);
}
.success {
background: var(--success-background);
color: var(--success-foreground);
}
.warning {
background: var(--warning-background);
color: var(--warning-foreground);
}
// Accent colors (for highlights, badges, etc.)
.accent {
background: var(--accent-primary); // robin blue
background: var(--accent-forest); // green
background: var(--accent-cherry); // red
background: var(--accent-amber); // yellow
}
```
**Token hierarchy:**
- Primitive tokens (`--bg-*`, `--text-*`, etc.) have fixed values across themes.
- Semantic tokens (L1/L2/L3, `--primary-*`, `--danger-*`, etc.) automatically swap based on theme.
- L1 = base/root layer
- L2 = elevated surfaces (cards, panels)
- L3 = nested/inset elements
## Overriding @signozhq/ui Components
Components expose CSS variables for customization.
You can ensure they exist by looking at ./node_modules/@signozhq/ui/dist.
Never write a override without confirm it exists.
Override via:
### Method 1: CSS Variables (Preferred)
Each component exposes `--<component>-<property>` variables:
```scss
// Override Button
.customButton {
--button-background: var(--success-background);
--button-border-radius: var(--radius-2);
--button-padding: var(--spacing-4) var(--spacing-8);
--button-font-size: var(--periscope-font-size-base);
}
// Override Input
.customInput {
--input-height: 2.5rem;
--input-border-color: var(--l2-border);
--input-padding: var(--spacing-2) var(--spacing-6);
--input-placeholder-color: var(--l3-foreground);
}
// Override nested parts
.customInput {
--input-prefix-padding: 0 var(--spacing-4) 0 var(--spacing-6);
--input-suffix-color: var(--accent-primary);
}
```
### Method 2: Data Attributes
Components use data attributes for variants/states. Target them for state-specific overrides:
```scss
// Target variant
.wrapper :global([data-variant="outlined"]) {
--button-border-color: var(--accent-primary);
}
// Target size
.wrapper :global([data-size="sm"]) {
--button-font-size: var(--periscope-font-size-small);
}
// Target color
.wrapper :global([data-color="destructive"]) {
--button-background: var(--danger-background);
}
// Target state (Radix patterns)
.popover :global([data-state="open"]) {
opacity: 1;
}
.tooltip :global([data-side="top"]) {
margin-bottom: var(--spacing-2);
}
```
### Common Component CSS Variables
**Button:**
- `--button-background`, `--button-border-radius`, `--button-padding`
- `--button-font-size`, `--button-height`, `--button-gap`
- `--button-hover-background`, `--button-disabled-opacity`
**Input:**
- `--input-height`, `--input-border-color`, `--input-background`
- `--input-padding`, `--input-font-size`, `--input-placeholder-color`
- `--input-focus-outline-color`, `--input-hover-border-color`
- `--input-prefix-*`, `--input-suffix-*` for adornments
**General pattern:** `--<component>-<property>` or `--<component>-<state>-<property>`
## Good Patterns
### Structure
```scss
// Flat, descriptive, component-scoped
.alertHistory { }
.alertHistoryHeader { }
.alertHistoryContent { }
.alertHistoryFooter { }
// State modifiers as separate classes
.alertHistory { }
.alertHistoryLoading { }
.alertHistoryEmpty { }
.alertHistoryError { }
```
### Composition
```scss
// GOOD: Composing styles
.baseButton {
padding: var(--spacing-2) var(--spacing-4);
border-radius: var(--radius-2);
}
.primaryButton {
composes: baseButton;
background: var(--primary-background);
}
```
### Pseudo Elements
```scss
.button {
// States
&:hover { opacity: 0.9; }
&:focus { outline: 2px solid var(--ring); outline-offset: 2px; }
&:disabled { opacity: 0.5; cursor: not-allowed; }
// Pseudo elements
&::before { content: ''; }
&::after { content: ''; }
}
```
### Media Queries
```scss
.container {
display: flex;
flex-direction: column;
@media (min-width: 768px) {
flex-direction: row;
}
}
```
## JS Import Patterns
```tsx
// GOOD
import styles from './Component.module.scss';
<div className={styles.container}>
<span className={styles.title}>Title</span>
</div>
// GOOD: Conditional classes
<div className={`${styles.button} ${isActive ? styles.buttonActive : ''}`}>
// GOOD: With clsx/classnames
<div className={clsx(styles.button, { [styles.buttonActive]: isActive })}>
// BAD: Bracket access (may be undefined)
<div className={styles['button-active']}> // undefined if CSS has .button-active
// BAD: String interpolation for class names
<div className={`${styles.button}-active`}> // won't work
```
## Checklist Before Committing
- [ ] All class names use camelCase in CSS
- [ ] No bracket access (`styles['...']`) in JS unless verified
- [ ] No deep class nesting (max 3 class levels; pseudo-classes/elements and parent-reference selectors like `&.active`, `&#bar` are not counted)
- [ ] No hardcoded colors - use `--l1/l2/l3-*` semantic tokens (not `--bg-*` primitives)
- [ ] No magic numbers - use `--spacing-*` tokens
- [ ] Typography uses `--periscope-font-size-*` or `--font-size-*` tokens
- [ ] @signozhq/ui overrides use CSS variables, not direct class overrides
- [ ] Global escapes only for third-party overrides
- [ ] No ID selectors
- [ ] No bare element selectors
## Lint Rules
### JS/TS (oxlint)
| Rule | Severity | Catches |
|------|----------|---------|
| `signoz/no-css-module-bracket-access` | warn | `styles['kebab-case']`, dynamic access |
### CSS/SCSS (stylelint)
| Rule | Severity | Catches |
|------|----------|---------|
| `local/no-deep-nesting` | warning | class nesting >3 levels (pseudo-classes/elements and parent-reference selectors `&.foo`, `&#bar` not counted; configurable via `maxDepth` secondary option) |
| `local/no-id-selectors` | error | `#id` selectors |
| `local/no-bare-element-selectors` | error | root-level `div`, `span` etc |
| `local/prefer-css-variables` | warning | hardcoded colors |
| `local/class-name-pattern` | warning | kebab-case, snake_case, PascalCase |
Run: `pnpm lint:styles` to check CSS modules.

View File

@@ -1,144 +0,0 @@
/**
* Rule: no-css-module-bracket-access
*
* Prevents bracket access on CSS module imports that may fail with camelCaseOnly config.
*
* With Vite's `localsConvention: 'camelCaseOnly'`, kebab-case class names are
* converted to camelCase and the original key is NOT exported.
*
* This rule catches patterns like:
* styles['my-class'] // BAD - undefined if CSS has .my-class
* styles['myClass'] // OK but prefer dot notation
* styles.myClass // GOOD
*
* Catches:
* - Bracket access with kebab-case strings (always fails)
* - Bracket access with any string literal (warn - prefer dot notation)
* - Dynamic bracket access (warn - risky)
*/
const CSS_MODULE_IMPORT_NAMES = new Set([
'styles',
'classes',
'css',
'classNames',
]);
function looksLikeCssModuleImport(name) {
// Common patterns: styles, componentStyles, alertHistoryStyles
return (
CSS_MODULE_IMPORT_NAMES.has(name) ||
name.endsWith('Styles') ||
name.endsWith('Classes') ||
name.endsWith('Css')
);
}
function isKebabCase(str) {
return str.includes('-');
}
function isSnakeCase(str) {
return str.includes('_');
}
export default {
create(context) {
return {
MemberExpression(node) {
// Only check bracket notation: styles['...']
if (!node.computed) {
return;
}
const object = node.object;
if (object.type !== 'Identifier') {
return;
}
// Check if this looks like a CSS module import
if (!looksLikeCssModuleImport(object.name)) {
return;
}
const property = node.property;
// Dynamic access: styles[variable]
if (property.type === 'Identifier') {
context.report({
node,
message: `Dynamic CSS module access '${object.name}[${property.name}]' is risky. With 'camelCaseOnly' config, kebab-case keys don't exist. Use dot notation or verify the key exists.`,
});
return;
}
// Template literal: styles[\`...\`]
if (property.type === 'TemplateLiteral') {
context.report({
node,
message: `Template literal CSS module access is risky. With 'camelCaseOnly' config, kebab-case keys don't exist. Prefer dot notation.`,
});
return;
}
// Numeric / boolean / null literal: styles[0]. Not a class lookup; ignore.
if (property.type === 'Literal' && typeof property.value !== 'string') {
return;
}
// String literal: styles['...']
if (property.type === 'Literal' && typeof property.value === 'string') {
const className = property.value;
// Kebab-case will definitely fail
if (isKebabCase(className)) {
context.report({
node,
message: `CSS module class '${className}' uses kebab-case which won't work with 'camelCaseOnly' config. Use '${object.name}.${toCamelCase(className)}' instead.`,
});
return;
}
// Snake_case is suspicious
if (isSnakeCase(className)) {
context.report({
node,
message: `CSS module class '${className}' uses snake_case which may not work as expected. Prefer camelCase: '${object.name}.${toCamelCase(className)}'.`,
});
return;
}
// Valid camelCase but using bracket notation - prefer dot
if (/^[a-z][a-zA-Z0-9]*$/.test(className)) {
context.report({
node,
message: `Prefer dot notation: '${object.name}.${className}' instead of '${object.name}['${className}']'.`,
});
}
return;
}
// Catch-all for other dynamic expressions:
// styles['prefix' + suffix] (BinaryExpression)
// styles[isActive && 'foo'] (LogicalExpression)
// styles[isActive ? 'a' : 'b'] (ConditionalExpression)
// styles[fn()] (CallExpression)
context.report({
node,
message: `Dynamic CSS module access on '${object.name}' is risky. With 'camelCaseOnly' config, kebab-case keys don't exist. Use dot notation or verify each key resolves to an exported camelCase class.`,
});
},
};
},
};
function toCamelCase(str) {
return str
.split(/[-_]/)
.map((part, i) =>
i === 0
? part.toLowerCase()
: part.charAt(0).toUpperCase() + part.slice(1).toLowerCase(),
)
.join('');
}

View File

@@ -11,7 +11,6 @@ import noUnsupportedAssetPattern from './rules/no-unsupported-asset-pattern.mjs'
import noRawAbsolutePath from './rules/no-raw-absolute-path.mjs';
import noAntdComponents from './rules/no-antd-components.mjs';
import noSignozhqUiBarrel from './rules/no-signozhq-ui-barrel.mjs';
import noCssModuleBracketAccess from './rules/no-css-module-bracket-access.mjs';
export default {
meta: {
@@ -24,6 +23,5 @@ export default {
'no-raw-absolute-path': noRawAbsolutePath,
'no-antd-components': noAntdComponents,
'no-signozhq-ui-barrel': noSignozhqUiBarrel,
'no-css-module-bracket-access': noCssModuleBracketAccess,
},
};

View File

@@ -3089,6 +3089,40 @@ export interface DashboardGridLayoutSpecDTO {
repeatVariable?: string;
}
export interface DashboardLinkDTO {
/**
* @type string
*/
name?: string;
/**
* @type boolean
*/
renderVariables?: boolean;
/**
* @type boolean
*/
targetBlank?: boolean;
/**
* @type string
*/
tooltip?: string;
/**
* @type string
*/
url?: string;
}
export interface DashboardPanelDisplayDTO {
/**
* @type string
*/
description?: string;
/**
* @type string
*/
name?: string;
}
export interface VariableDisplayDTO {
/**
* @type string
@@ -3786,40 +3820,9 @@ export type DashboardtypesDashboardSpecDTODatasources = {
[key: string]: DashboardtypesDatasourceSpecDTO;
};
export interface V1PanelDisplayDTO {
/**
* @type string
*/
description?: string;
/**
* @type string
*/
name?: string;
export enum DashboardtypesPanelKindDTO {
Panel = 'Panel',
}
export interface V1LinkDTO {
/**
* @type string
*/
name?: string;
/**
* @type boolean
*/
renderVariables?: boolean;
/**
* @type boolean
*/
targetBlank?: boolean;
/**
* @type string
*/
tooltip?: string;
/**
* @type string
*/
url?: string;
}
export enum DashboardtypesPanelPluginVariantGithubComSigNozSignozPkgTypesDashboardtypesTimeSeriesPanelSpecDTOKind {
'signoz/TimeSeriesPanel' = 'signoz/TimeSeriesPanel',
}
@@ -4061,6 +4064,13 @@ export type DashboardtypesPanelPluginDTO =
| DashboardtypesPanelPluginVariantGithubComSigNozSignozPkgTypesDashboardtypesHistogramPanelSpecDTO
| DashboardtypesPanelPluginVariantGithubComSigNozSignozPkgTypesDashboardtypesListPanelSpecDTO;
export enum DashboardtypesQueryKindDTO {
scalar = 'scalar',
time_series = 'time_series',
raw = 'raw',
raw_stream = 'raw_stream',
trace = 'trace',
}
export enum DashboardtypesQueryPluginVariantGithubComSigNozSignozPkgTypesDashboardtypesBuilderQuerySpecDTOKind {
'signoz/BuilderQuery' = 'signoz/BuilderQuery',
}
@@ -4365,19 +4375,16 @@ export interface DashboardtypesQuerySpecDTO {
}
export interface DashboardtypesQueryDTO {
/**
* @type string
*/
kind?: string;
kind?: DashboardtypesQueryKindDTO;
spec?: DashboardtypesQuerySpecDTO;
}
export interface DashboardtypesPanelSpecDTO {
display?: V1PanelDisplayDTO;
display?: DashboardPanelDisplayDTO;
/**
* @type array
*/
links?: V1LinkDTO[];
links?: DashboardLinkDTO[];
plugin?: DashboardtypesPanelPluginDTO;
/**
* @type array
@@ -4386,10 +4393,7 @@ export interface DashboardtypesPanelSpecDTO {
}
export interface DashboardtypesPanelDTO {
/**
* @type string
*/
kind?: string;
kind?: DashboardtypesPanelKindDTO;
spec?: DashboardtypesPanelSpecDTO;
}
@@ -4403,20 +4407,20 @@ export type DashboardtypesDashboardSpecDTOPanelsAnyOf = {
export type DashboardtypesDashboardSpecDTOPanels =
DashboardtypesDashboardSpecDTOPanelsAnyOf | null;
export enum DashboardtypesLayoutEnvelopeGithubComPersesPersesPkgModelApiV1DashboardGridLayoutSpecDTOKind {
export enum DashboardtypesLayoutEnvelopeGithubComPersesSpecGoDashboardGridLayoutSpecDTOKind {
Grid = 'Grid',
}
export interface DashboardtypesLayoutEnvelopeGithubComPersesPersesPkgModelApiV1DashboardGridLayoutSpecDTO {
export interface DashboardtypesLayoutEnvelopeGithubComPersesSpecGoDashboardGridLayoutSpecDTO {
/**
* @enum Grid
* @type string
*/
kind: DashboardtypesLayoutEnvelopeGithubComPersesPersesPkgModelApiV1DashboardGridLayoutSpecDTOKind;
kind: DashboardtypesLayoutEnvelopeGithubComPersesSpecGoDashboardGridLayoutSpecDTOKind;
spec: DashboardGridLayoutSpecDTO;
}
export type DashboardtypesLayoutDTO =
DashboardtypesLayoutEnvelopeGithubComPersesPersesPkgModelApiV1DashboardGridLayoutSpecDTO;
DashboardtypesLayoutEnvelopeGithubComPersesSpecGoDashboardGridLayoutSpecDTO;
export enum DashboardtypesVariableEnvelopeGithubComSigNozSignozPkgTypesDashboardtypesListVariableSpecDTOKind {
ListVariable = 'ListVariable',
@@ -4520,21 +4524,21 @@ export interface DashboardtypesVariableEnvelopeGithubComSigNozSignozPkgTypesDash
spec: DashboardtypesListVariableSpecDTO;
}
export enum DashboardtypesVariableEnvelopeGithubComPersesPersesPkgModelApiV1DashboardTextVariableSpecDTOKind {
export enum DashboardtypesVariableEnvelopeGithubComPersesSpecGoDashboardTextVariableSpecDTOKind {
TextVariable = 'TextVariable',
}
export interface DashboardtypesVariableEnvelopeGithubComPersesPersesPkgModelApiV1DashboardTextVariableSpecDTO {
export interface DashboardtypesVariableEnvelopeGithubComPersesSpecGoDashboardTextVariableSpecDTO {
/**
* @enum TextVariable
* @type string
*/
kind: DashboardtypesVariableEnvelopeGithubComPersesPersesPkgModelApiV1DashboardTextVariableSpecDTOKind;
kind: DashboardtypesVariableEnvelopeGithubComPersesSpecGoDashboardTextVariableSpecDTOKind;
spec: DashboardTextVariableSpecDTO;
}
export type DashboardtypesVariableDTO =
| DashboardtypesVariableEnvelopeGithubComSigNozSignozPkgTypesDashboardtypesListVariableSpecDTO
| DashboardtypesVariableEnvelopeGithubComPersesPersesPkgModelApiV1DashboardTextVariableSpecDTO;
| DashboardtypesVariableEnvelopeGithubComPersesSpecGoDashboardTextVariableSpecDTO;
export interface DashboardtypesDashboardSpecDTO {
/**
@@ -4553,7 +4557,7 @@ export interface DashboardtypesDashboardSpecDTO {
/**
* @type array
*/
links?: V1LinkDTO[];
links?: DashboardLinkDTO[];
/**
* @type object,null
*/

View File

@@ -1,186 +0,0 @@
/**
* Stylelint rule: local/class-name-pattern
*
* Enforces camelCase class names in CSS modules.
* With Vite's `localsConvention: 'camelCaseOnly'`, kebab-case is converted but
* using camelCase directly avoids confusion.
*
* BAD:
* .my-class { } // converted to myClass, but confusing
* .my_class { } // converted to myClass, but confusing
* .MyClass { } // PascalCase not conventional
*
* GOOD:
* .myClass { }
* .alertHistory { }
* .statsCard { }
*/
import stylelint from 'stylelint';
const ruleName = 'local/class-name-pattern';
const messages = stylelint.utils.ruleMessages(ruleName, {
kebabCase: (className) =>
`Class "${className}" uses kebab-case. Use camelCase instead: "${toCamelCase(className)}".`,
snakeCase: (className) =>
`Class "${className}" uses snake_case. Use camelCase instead: "${toCamelCase(className)}".`,
pascalCase: (className) =>
`Class "${className}" uses PascalCase. Use camelCase instead: "${className.charAt(0).toLowerCase() + className.slice(1)}".`,
});
function toCamelCase(str) {
return str
.split(/[-_]/)
.map((part, i) =>
i === 0
? part.toLowerCase()
: part.charAt(0).toUpperCase() + part.slice(1).toLowerCase(),
)
.join('');
}
function isKebabCase(str) {
return str.includes('-');
}
function isSnakeCase(str) {
return str.includes('_');
}
function isPascalCase(str) {
return /^[A-Z][a-zA-Z0-9]*$/.test(str);
}
const CLASS_PATTERN = /\.([a-zA-Z_][a-zA-Z0-9_-]*)/g;
const DEFAULT_THIRD_PARTY_PREFIXES = [
'ant-', // Ant Design
'rc-', // rc-components (Ant Design internals)
'recharts-', // Recharts
'uplot-', // uPlot
'u-', // uPlot legacy
'leaflet-', // Leaflet
'monaco-', // Monaco editor
'react-resizable', // react-resizable
'cm-', // CodeMirror
];
// Bare `:global { ... }` block (no parens) makes all descendants global.
// `:global(.foo)` is a per-selector escape — handled separately by globalRanges().
function hasBareGlobalAncestor(node) {
let current = node.parent;
while (current) {
const selector = current.selector;
if (selector && /:global(?!\s*\()/.test(selector)) {
return true;
}
current = current.parent;
}
return false;
}
// Return list of [start, end) index ranges inside `selector` that fall within a
// balanced `:global(...)` argument list. Class matches inside these ranges are
// third-party and should be skipped.
function globalRanges(selector) {
const ranges = [];
const re = /:global\s*\(/g;
let match;
while ((match = re.exec(selector)) !== null) {
const argStart = match.index + match[0].length;
let depth = 1;
let i = argStart;
while (i < selector.length && depth > 0) {
const ch = selector[i];
if (ch === '(') {
depth++;
} else if (ch === ')') {
depth--;
}
if (depth > 0) {
i++;
}
}
ranges.push([argStart, i]);
re.lastIndex = i;
}
return ranges;
}
function indexInRanges(index, ranges) {
for (const [start, end] of ranges) {
if (index >= start && index < end) {
return true;
}
}
return false;
}
const rule = (primaryOption, secondaryOptions) => {
return (root, result) => {
if (!primaryOption) {
return;
}
const userPrefixes =
(secondaryOptions && secondaryOptions.ignoreThirdPartyPrefixes) || [];
const allPrefixes = [...DEFAULT_THIRD_PARTY_PREFIXES, ...userPrefixes];
root.walkRules((ruleNode) => {
const selector = ruleNode.selector;
// Bare `:global { }` block makes all descendants global — skip entirely.
if (hasBareGlobalAncestor(ruleNode)) {
return;
}
const ranges = globalRanges(selector);
let match;
CLASS_PATTERN.lastIndex = 0;
while ((match = CLASS_PATTERN.exec(selector)) !== null) {
const className = match[1];
// Skip classes inside `:global(...)` ranges of this selector.
if (indexInRanges(match.index, ranges)) {
continue;
}
// Skip third-party library classes
if (allPrefixes.some((prefix) => className.startsWith(prefix))) {
continue;
}
if (isKebabCase(className)) {
stylelint.utils.report({
message: messages.kebabCase(className),
node: ruleNode,
result,
ruleName,
});
} else if (isSnakeCase(className)) {
stylelint.utils.report({
message: messages.snakeCase(className),
node: ruleNode,
result,
ruleName,
});
} else if (isPascalCase(className)) {
stylelint.utils.report({
message: messages.pascalCase(className),
node: ruleNode,
result,
ruleName,
});
}
}
});
};
};
rule.ruleName = ruleName;
rule.messages = messages;
rule.meta = {};
export { ruleName, rule };
export default { ruleName, rule };

View File

@@ -1,164 +0,0 @@
/**
* Stylelint rule: local/no-bare-element-selectors
*
* Prevents bare element selectors at root level in CSS modules.
* Bare elements affect ALL instances of that element within component scope,
* often unintentionally.
*
* BAD:
* div { }
* span { padding: 4px; }
* p { margin: 0; }
*
* GOOD:
* .container { }
* .title { }
*
* ALLOWED (nested under class):
* .container {
* p { margin: 0; } // Scoped to .container
* }
*/
import stylelint from 'stylelint';
const ruleName = 'local/no-bare-element-selectors';
const messages = stylelint.utils.ruleMessages(ruleName, {
unexpected: (element) =>
`Bare element selector "${element}" at root level affects all instances. Use class selector or nest under a class.`,
unexpectedInCompound: (element, full) =>
`Bare element "${element}" in unscoped selector "${full}" at root level matches every "<${element}>" in the module. Anchor the selector with a class (e.g., ".container ${element}") or use :global() if intentional.`,
});
const ELEMENT_PATTERN = /^[a-z][a-z0-9]*$/i;
const EXCLUDED_ELEMENTS = new Set(['html', 'body', 'root']);
function isPartBareElement(part) {
const trimmed = part.trim();
if (!trimmed) {
return false;
}
// Strip pseudo suffixes (`:hover`, `::before`) — element part is what's before
const elementOnly = trimmed.split(':')[0];
if (!elementOnly) {
return false;
}
// Skip class/id/attribute parts
if (/[.#[]/.test(elementOnly)) {
return false;
}
return (
ELEMENT_PATTERN.test(elementOnly) &&
!EXCLUDED_ELEMENTS.has(elementOnly.toLowerCase())
);
}
function isBareElement(selector) {
const trimmed = selector.trim();
// Skip combined selectors
if (/[,\s>+~]/.test(trimmed)) {
return false;
}
// Skip pseudo-selectors
if (trimmed.includes(':')) {
return false;
}
// Skip class/id/attribute selectors
if (/[.#[]/.test(trimmed)) {
return false;
}
return (
ELEMENT_PATTERN.test(trimmed) && !EXCLUDED_ELEMENTS.has(trimmed.toLowerCase())
);
}
// Split a compound selector on combinators (`>`, `+`, `~`, descendant space)
// while keeping each part intact. Returns an array of selector parts.
function splitOnCombinators(selector) {
return selector
.split(/\s*[>+~]\s*|\s+/)
.map((p) => p.trim())
.filter(Boolean);
}
function isInsideGlobal(selector) {
return /:global\s*\(/.test(selector);
}
const KEYFRAME_ATRULES = new Set([
'keyframes',
'-webkit-keyframes',
'-moz-keyframes',
'-o-keyframes',
]);
// Rule's effective parent is root if all ancestors above it are atrules
// (e.g. `@media`, `@supports`). A bare `div` inside `@media { }` at top level
// still matches every `<div>` in the module. Exclude `@keyframes` — its
// `from`/`to`/`0%` children are not element selectors.
function isEffectivelyTopLevel(node) {
let parent = node.parent;
while (parent && parent.type === 'atrule') {
if (KEYFRAME_ATRULES.has(parent.name.toLowerCase())) {
return false;
}
parent = parent.parent;
}
return Boolean(parent) && parent.type === 'root';
}
const rule = (primaryOption) => {
return (root, result) => {
if (!primaryOption) {
return;
}
root.walkRules((ruleNode) => {
if (!isEffectivelyTopLevel(ruleNode)) {
return;
}
const selectors = ruleNode.selector.split(',').map((s) => s.trim());
for (const selector of selectors) {
if (isInsideGlobal(selector)) {
continue;
}
if (isBareElement(selector)) {
stylelint.utils.report({
message: messages.unexpected(selector),
node: ruleNode,
result,
ruleName,
});
continue;
}
// Check combined selectors part-by-part. Only flag when the compound
// has NO class/id anchor — `.container > div` is already scoped, but
// `div + span` at root affects every matching descendant in the module.
if (/[\s>+~]/.test(selector) && !/[.#]/.test(selector)) {
const parts = splitOnCombinators(selector);
for (const part of parts) {
if (isPartBareElement(part)) {
stylelint.utils.report({
message: messages.unexpectedInCompound(part.split(':')[0], selector),
node: ruleNode,
result,
ruleName,
});
}
}
}
}
});
};
};
rule.ruleName = ruleName;
rule.messages = messages;
rule.meta = {};
export { ruleName, rule };
export default { ruleName, rule };

View File

@@ -1,97 +0,0 @@
/**
* Stylelint rule: local/no-deep-nesting
*
* Prevents deep nesting in CSS modules (max 3 levels).
* Deep nesting creates specificity wars and hard-to-override styles.
*
* BAD:
* .container { .wrapper { .inner { .content { } } } } // 4 levels
*
* GOOD:
* .container { }
* .containerWrapper { }
* .containerContent { }
*
* Allowed nesting (pseudo-classes/elements):
* .button { &:hover { } &::before { } }
*/
import stylelint from 'stylelint';
const ruleName = 'local/no-deep-nesting';
const DEFAULT_MAX_DEPTH = 3;
const messages = stylelint.utils.ruleMessages(ruleName, {
tooDeep: (depth, max) =>
`Nesting depth ${depth} exceeds maximum of ${max}. Flatten selectors for CSS modules.`,
});
function isPseudoSelector(selector) {
return /^&?:/.test(selector.trim());
}
function isParentReference(selector) {
return /^&[.#[]/.test(selector.trim());
}
function countNestingDepth(rule, depth = 0) {
const selector = rule.selector || '';
// Don't count pseudo-selectors or parent references toward depth
if (isPseudoSelector(selector) || isParentReference(selector)) {
// Still check children
let maxChildDepth = depth;
rule.walkRules?.((child) => {
const childDepth = countNestingDepth(child, depth);
maxChildDepth = Math.max(maxChildDepth, childDepth);
});
return maxChildDepth;
}
const currentDepth = depth + 1;
let maxDepth = currentDepth;
rule.walkRules?.((child) => {
const childDepth = countNestingDepth(child, currentDepth);
maxDepth = Math.max(maxDepth, childDepth);
});
return maxDepth;
}
const rule = (primaryOption, secondaryOptions) => {
return (root, result) => {
if (!primaryOption) {
return;
}
const maxDepth =
secondaryOptions && Number.isInteger(secondaryOptions.maxDepth)
? secondaryOptions.maxDepth
: DEFAULT_MAX_DEPTH;
root.walkRules((ruleNode) => {
// Only check top-level rules
if (ruleNode.parent.type !== 'root' && ruleNode.parent.type !== 'atrule') {
return;
}
const depth = countNestingDepth(ruleNode);
if (depth > maxDepth) {
stylelint.utils.report({
message: messages.tooDeep(depth, maxDepth),
node: ruleNode,
result,
ruleName,
});
}
});
};
};
rule.ruleName = ruleName;
rule.messages = messages;
rule.meta = {};
export { ruleName, rule };
export default { ruleName, rule };

View File

@@ -1,55 +0,0 @@
/**
* Stylelint rule: local/no-id-selectors
*
* Prevents ID selectors in CSS modules.
* IDs create high specificity, can't be reused, and defeat CSS modules scoping purpose.
*
* BAD:
* #myComponent { }
* .container #header { }
*
* GOOD:
* .myComponent { }
* .containerHeader { }
*/
import stylelint from 'stylelint';
const ruleName = 'local/no-id-selectors';
const messages = stylelint.utils.ruleMessages(ruleName, {
unexpected: (selector) =>
`ID selector "${selector}" not allowed in CSS modules. Use class selector instead.`,
});
const ID_PATTERN = /#[a-zA-Z_][a-zA-Z0-9_-]*/g;
const rule = (primaryOption) => {
return (root, result) => {
if (!primaryOption) {
return;
}
root.walkRules((ruleNode) => {
const selector = ruleNode.selector;
const matches = selector.match(ID_PATTERN);
if (matches) {
for (const match of matches) {
stylelint.utils.report({
message: messages.unexpected(match),
node: ruleNode,
result,
ruleName,
});
}
}
});
};
};
rule.ruleName = ruleName;
rule.messages = messages;
rule.meta = {};
export { ruleName, rule };
export default { ruleName, rule };

View File

@@ -1,168 +0,0 @@
/**
* Stylelint rule: local/prefer-css-variables
*
* Warns on hardcoded colors in CSS modules.
* Use CSS variables for consistent theming.
*
* BAD:
* color: #ff0000;
* background: rgb(255, 0, 0);
* border: 1px solid blue;
*
* GOOD:
* color: var(--l1-foreground);
* background: var(--primary-background);
* border: 1px solid var(--l2-border);
*
* ALLOWED:
* transparent, inherit, currentColor, none
* Colors inside var() fallbacks
*/
import stylelint from 'stylelint';
const ruleName = 'local/prefer-css-variables';
const messages = stylelint.utils.ruleMessages(ruleName, {
hardcodedColor: (value, property) =>
`Hardcoded color "${value}" in "${property}". Use a semantic CSS variable instead (e.g., var(--l1-foreground), var(--primary-background)). See docs/css-modules-guide.md.`,
});
const COLOR_PROPERTIES = new Set([
'color',
'background',
'background-color',
'background-image',
'border',
'border-color',
'border-top',
'border-right',
'border-bottom',
'border-left',
'border-top-color',
'border-right-color',
'border-bottom-color',
'border-left-color',
'border-image',
'border-image-source',
'outline',
'outline-color',
'box-shadow',
'text-shadow',
'fill',
'stroke',
'caret-color',
'text-decoration-color',
'column-rule-color',
'mask',
'mask-image',
]);
const ALLOWED_VALUES = new Set([
'transparent',
'inherit',
'initial',
'unset',
'currentcolor',
'none',
'auto',
]);
const HEX_PATTERN = /#[0-9a-fA-F]{3,8}\b/;
const RGB_PATTERN = /rgba?\s*\([^)]+\)/i;
const HSL_PATTERN = /hsla?\s*\([^)]+\)/i;
const NAMED_COLOR_PATTERN =
/\b(red|blue|green|yellow|orange|purple|pink|black|white|gray|grey|cyan|magenta|brown|navy|teal|olive|maroon|lime|aqua|fuchsia|silver)\b/i;
// Strip balanced `fn(...)` sections (e.g. `var(...)`, `url(...)`) from value.
// Handles nested parens by counting depth.
function stripBalancedFn(value, fnName) {
const needle = `${fnName}(`;
let out = '';
let i = 0;
while (i < value.length) {
if (value.startsWith(needle, i)) {
let depth = 1;
i += needle.length;
while (i < value.length && depth > 0) {
const ch = value[i];
if (ch === '(') {
depth++;
} else if (ch === ')') {
depth--;
}
i++;
}
} else {
out += value[i];
i++;
}
}
return out;
}
function containsHardcodedColor(value) {
// Strip url(...) first — paths/fragments may contain hex-like or color-named substrings
let scanned = value.includes('url(') ? stripBalancedFn(value, 'url') : value;
// Strip var(...) — color tokens inside var fallbacks are allowed
if (scanned.includes('var(')) {
scanned = stripBalancedFn(scanned, 'var');
if (!scanned.trim()) {
return null;
}
}
const lower = scanned.toLowerCase();
if (ALLOWED_VALUES.has(lower)) {
return null;
}
if (HEX_PATTERN.test(scanned)) {
return scanned.match(HEX_PATTERN)[0];
}
if (RGB_PATTERN.test(scanned)) {
return scanned.match(RGB_PATTERN)[0];
}
if (HSL_PATTERN.test(scanned)) {
return scanned.match(HSL_PATTERN)[0];
}
if (NAMED_COLOR_PATTERN.test(scanned)) {
return scanned.match(NAMED_COLOR_PATTERN)[0];
}
return null;
}
const rule = (primaryOption) => {
return (root, result) => {
if (!primaryOption) {
return;
}
root.walkDecls((decl) => {
const prop = decl.prop.toLowerCase();
if (!COLOR_PROPERTIES.has(prop)) {
return;
}
const hardcodedColor = containsHardcodedColor(decl.value);
if (hardcodedColor) {
stylelint.utils.report({
message: messages.hardcodedColor(hardcodedColor, decl.prop),
node: decl,
result,
ruleName,
});
}
});
};
};
rule.ruleName = ruleName;
rule.messages = messages;
rule.meta = {};
export { ruleName, rule };
export default { ruleName, rule };

7
go.mod
View File

@@ -43,7 +43,7 @@ require (
github.com/openfga/api/proto v0.0.0-20260319214821-f153694bfc20
github.com/openfga/language/pkg/go v0.2.1
github.com/opentracing/opentracing-go v1.2.0
github.com/perses/perses v0.53.1
github.com/perses/spec v0.1.2
github.com/pkg/errors v0.9.1
github.com/prometheus/alertmanager v0.31.1
github.com/prometheus/client_golang v1.23.2
@@ -138,9 +138,8 @@ require (
github.com/huandu/go-clone v1.7.3 // indirect
github.com/leodido/go-urn v1.4.0 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/muhlemmer/gu v0.3.1 // indirect
github.com/ncruces/go-strftime v1.0.0 // indirect
github.com/perses/common v0.30.2 // indirect
github.com/nxadm/tail v1.4.11 // indirect
github.com/prometheus/client_golang/exp v0.0.0-20260325093428-d8591d0db856 // indirect
github.com/puzpuzpuz/xsync/v4 v4.4.0 // indirect
github.com/redis/go-redis/extra/rediscmd/v9 v9.15.1 // indirect
@@ -151,8 +150,6 @@ require (
github.com/ugorji/go/codec v1.3.0 // indirect
github.com/uptrace/opentelemetry-go-extra/otelsql v0.3.2 // indirect
github.com/x448/float16 v0.8.4 // indirect
github.com/zitadel/oidc/v3 v3.45.4 // indirect
github.com/zitadel/schema v1.3.2 // indirect
go.opentelemetry.io/collector/client v1.54.0 // indirect
go.opentelemetry.io/collector/config/configoptional v1.50.0 // indirect
go.opentelemetry.io/collector/config/configretry v1.50.0 // indirect

16
go.sum
View File

@@ -332,6 +332,7 @@ github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHk
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU=
github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=
github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
github.com/fxamacker/cbor/v2 v2.9.0 h1:NpKPmjDBgUfBms6tr6JZkTHtfFGcMKsw3eGcmD/sapM=
@@ -832,8 +833,6 @@ github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjY
github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee h1:W5t00kpgFdJifH4BDsTlE89Zl93FEloxaWZfGcifgq8=
github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc=
github.com/muhlemmer/gu v0.3.1 h1:7EAqmFrW7n3hETvuAdmFmn4hS8W+z3LgKtrnow+YzNM=
github.com/muhlemmer/gu v0.3.1/go.mod h1:YHtHR+gxM+bKEIIs7Hmi9sPT3ZDUvTN/i88wQpZkrdM=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
@@ -843,8 +842,6 @@ github.com/natefinch/wrap v0.2.0 h1:IXzc/pw5KqxJv55gV0lSOcKHYuEZPGbQrOOXr/bamRk=
github.com/natefinch/wrap v0.2.0/go.mod h1:6gMHlAl12DwYEfKP3TkuykYUfLSEAvHw67itm4/KAS8=
github.com/ncruces/go-strftime v1.0.0 h1:HMFp8mLCTPp341M/ZnA4qaf7ZlsbTc+miZjCLOFAw7w=
github.com/ncruces/go-strftime v1.0.0/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls=
github.com/nexucis/lamenv v0.5.2 h1:tK/u3XGhCq9qIoVNcXsK9LZb8fKopm0A5weqSRvHd7M=
github.com/nexucis/lamenv v0.5.2/go.mod h1:HusJm6ltmmT7FMG8A750mOLuME6SHCsr2iFYxp5fFi0=
github.com/npillmayer/nestext v0.1.3/go.mod h1:h2lrijH8jpicr25dFY+oAJLyzlya6jhnuG+zWp9L0Uk=
github.com/nxadm/tail v1.4.11 h1:8feyoE3OzPrcshW5/MJ4sGESc5cqmGkGCWlco4l0bqY=
github.com/nxadm/tail v1.4.11/go.mod h1:OTaG3NK980DZzxbRq6lEuzgU+mug70nY11sMd4JXXHc=
@@ -907,10 +904,8 @@ github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCko
github.com/pelletier/go-toml/v2 v2.0.5/go.mod h1:OMHamSCAODeSsVrwwvcJOaoN0LIUIaFVNZzmWyNfXas=
github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4=
github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
github.com/perses/common v0.30.2 h1:RAiVxUpX76lTCb4X7pfcXSvYdXQmZwKi4oDKAEO//u0=
github.com/perses/common v0.30.2/go.mod h1:DFtur1QPah2/ChXbKKhw7djYdwNgz27s5fPKpiK0Xao=
github.com/perses/perses v0.53.1 h1:9VY/6p9QWrZwPSV7qiwTMSOsgcB37Lb1AXKT0ORXc6I=
github.com/perses/perses v0.53.1/go.mod h1:ro8fsgBkHYOdrL/MV+fdP9mflKzYCy/+gcbxiaReI/A=
github.com/perses/spec v0.1.2 h1:yGoygcR3ZusuGDCmRMwsVXCMvMwi1qZndKV6NYNreEw=
github.com/perses/spec v0.1.2/go.mod h1:NoGI5jmGwRdkdPgyYSZJTBL4/Py+dqIPKS2QV8NOvGE=
github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
github.com/pierrec/lz4/v4 v4.1.25 h1:kocOqRffaIbU5djlIBr7Wh+cx82C0vtFb0fOurZHqD0=
github.com/pierrec/lz4/v4 v4.1.25/go.mod h1:EoQMVJgeeEOMsCqCzqFm2O0cJvljX2nGZjcRIPL34O4=
@@ -1171,10 +1166,6 @@ github.com/zeebo/assert v1.3.1 h1:vukIABvugfNMZMQO1ABsyQDJDTVQbn+LWSMy1ol1h6A=
github.com/zeebo/assert v1.3.1/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0=
github.com/zeebo/xxh3 v1.0.2 h1:xZmwmqxHZA8AI603jOQ0tMqmBr9lPeFwGg6d+xy9DC0=
github.com/zeebo/xxh3 v1.0.2/go.mod h1:5NWz9Sef7zIDm2JHfFlcQvNekmcEl9ekUZQQKCYaDcA=
github.com/zitadel/oidc/v3 v3.45.4 h1:GKyWaPRVQ8sCu9XgJ3NgNGtG52FzwVJpzXjIUG2+YrI=
github.com/zitadel/oidc/v3 v3.45.4/go.mod h1:XALmFXS9/kSom9B6uWin1yJ2WTI/E4Ti5aXJdewAVEs=
github.com/zitadel/schema v1.3.2 h1:gfJvt7dOMfTmxzhscZ9KkapKo3Nei3B6cAxjav+lyjI=
github.com/zitadel/schema v1.3.2/go.mod h1:IZmdfF9Wu62Zu6tJJTH3UsArevs3Y4smfJIj3L8fzxw=
go.etcd.io/etcd/api/v3 v3.5.4/go.mod h1:5GB2vv4A4AOn3yk7MftYGHkUfGtDHnEraIjym4dYz5A=
go.etcd.io/etcd/client/pkg/v3 v3.5.4/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g=
go.etcd.io/etcd/client/v2 v2.305.4/go.mod h1:Ud+VUwIi9/uQHOMA+4ekToJ12lTxlv0zB/+DHwTGEbU=
@@ -1619,6 +1610,7 @@ golang.org/x/sys v0.0.0-20220328115105-d36c6a25d886/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220502124256-b6088ccd6cba/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo=

View File

@@ -12,7 +12,7 @@ import (
"github.com/SigNoz/signoz/pkg/types/coretypes"
"github.com/SigNoz/signoz/pkg/types/tagtypes"
"github.com/SigNoz/signoz/pkg/valuer"
"github.com/perses/perses/pkg/model/api/v1/common"
"github.com/perses/spec/go/common"
"k8s.io/apimachinery/pkg/util/validation"
)

View File

@@ -9,8 +9,9 @@ import (
"github.com/SigNoz/signoz/pkg/types"
"github.com/SigNoz/signoz/pkg/types/coretypes"
"github.com/SigNoz/signoz/pkg/types/tagtypes"
qb "github.com/SigNoz/signoz/pkg/types/querybuildertypes/querybuildertypesv5"
"github.com/SigNoz/signoz/pkg/valuer"
"github.com/perses/perses/pkg/model/api/v1/common"
"github.com/perses/spec/go/common"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
@@ -43,7 +44,7 @@ func newTestDashboardV2(t *testing.T, orgID valuer.UUID, source Source) *Dashboa
},
Queries: []Query{
{
Kind: "TimeSeriesQuery",
Kind: QueryKind(qb.RequestTypeTimeSeries),
Spec: QuerySpec{
Plugin: QueryPlugin{
Kind: QueryKindPromQL,

View File

@@ -8,12 +8,12 @@ import (
"github.com/SigNoz/signoz/pkg/errors"
qb "github.com/SigNoz/signoz/pkg/types/querybuildertypes/querybuildertypesv5"
v1 "github.com/perses/perses/pkg/model/api/v1"
"github.com/perses/perses/pkg/model/api/v1/common"
"github.com/perses/spec/go/common"
"github.com/perses/spec/go/dashboard"
)
// DashboardSpec is the SigNoz dashboard v2 spec shape. It mirrors
// v1.DashboardSpec (Perses) field-for-field, except every common.Plugin
// dashboard.Spec (Perses) field-for-field, except every common.Plugin
// occurrence is replaced with a typed SigNoz plugin whose OpenAPI schema is a
// per-site discriminated oneOf.
type DashboardSpec struct {
@@ -24,7 +24,7 @@ type DashboardSpec struct {
Layouts []Layout `json:"layouts"`
Duration common.DurationString `json:"duration"`
RefreshInterval common.DurationString `json:"refreshInterval,omitempty"`
Links []v1.Link `json:"links,omitempty"`
Links []dashboard.Link `json:"links,omitempty"`
}
// ══════════════════════════════════════════════

View File

@@ -133,6 +133,21 @@ func TestInvalidateUnknownPluginKind(t *testing.T) {
}`,
wantContain: "NonExistentPanel",
},
{
name: "unknown panel envelope kind",
data: `{
"panels": {
"p1": {
"kind": "Row",
"spec": {
"plugin": {"kind": "signoz/TimeSeriesPanel", "spec": {}}
}
}
},
"layouts": []
}`,
wantContain: "unknown panel kind",
},
{
name: "unknown query plugin",
data: `{
@@ -142,7 +157,7 @@ func TestInvalidateUnknownPluginKind(t *testing.T) {
"spec": {
"plugin": {"kind": "signoz/TimeSeriesPanel", "spec": {}},
"queries": [{
"kind": "TimeSeriesQuery",
"kind": "time_series",
"spec": {
"plugin": {"kind": "FakeQueryPlugin", "spec": {}}
}
@@ -154,6 +169,48 @@ func TestInvalidateUnknownPluginKind(t *testing.T) {
}`,
wantContain: "FakeQueryPlugin",
},
{
name: "unknown query envelope kind",
data: `{
"panels": {
"p1": {
"kind": "Panel",
"spec": {
"plugin": {"kind": "signoz/TimeSeriesPanel", "spec": {}},
"queries": [{
"kind": "TimeSeriesQuery",
"spec": {
"plugin": {"kind": "signoz/BuilderQuery", "spec": {"name": "A", "signal": "metrics"}}
}
}]
}
}
},
"layouts": []
}`,
wantContain: "unknown query kind",
},
{
name: "empty query envelope kind",
data: `{
"panels": {
"p1": {
"kind": "Panel",
"spec": {
"plugin": {"kind": "signoz/TimeSeriesPanel", "spec": {}},
"queries": [{
"kind": "",
"spec": {
"plugin": {"kind": "signoz/BuilderQuery", "spec": {"name": "A", "signal": "metrics"}}
}
}]
}
}
},
"layouts": []
}`,
wantContain: "unknown query kind",
},
{
name: "unknown variable plugin",
data: `{
@@ -246,7 +303,7 @@ func TestRejectUnknownFieldsInPluginSpec(t *testing.T) {
"spec": {
"plugin": {"kind": "signoz/TimeSeriesPanel", "spec": {}},
"queries": [{
"kind": "TimeSeriesQuery",
"kind": "time_series",
"spec": {
"plugin": {
"kind": "signoz/PromQLQuery",
@@ -324,7 +381,7 @@ func TestInvalidateWrongFieldTypeInPluginSpec(t *testing.T) {
"spec": {
"plugin": {"kind": "signoz/TimeSeriesPanel", "spec": {}},
"queries": [{
"kind": "TimeSeriesQuery",
"kind": "time_series",
"spec": {
"plugin": {
"kind": "signoz/PromQLQuery",
@@ -389,7 +446,7 @@ func TestInvalidateBadPanelSpecValues(t *testing.T) {
"spec": {}
},
"queries": [{
"kind": "TimeSeriesQuery",
"kind": "time_series",
"spec": {
"plugin": {
"kind": "signoz/BuilderQuery",
@@ -620,8 +677,8 @@ func TestInvalidatePanelWithMultipleDirectQueries(t *testing.T) {
"spec": {
"plugin": {"kind": "signoz/TimeSeriesPanel", "spec": {}},
"queries": [
{"kind": "TimeSeriesQuery", "spec": {"plugin": {"kind": "signoz/BuilderQuery", "spec": {"name": "A", "signal": "metrics"}}}},
{"kind": "TimeSeriesQuery", "spec": {"plugin": {"kind": "signoz/BuilderQuery", "spec": {"name": "B", "signal": "metrics"}}}}
{"kind": "time_series", "spec": {"plugin": {"kind": "signoz/BuilderQuery", "spec": {"name": "A", "signal": "metrics"}}}},
{"kind": "time_series", "spec": {"plugin": {"kind": "signoz/BuilderQuery", "spec": {"name": "B", "signal": "metrics"}}}}
]
}
}
@@ -738,7 +795,7 @@ func TestTimeSeriesPanelDefaults(t *testing.T) {
"kind": "signoz/TimeSeriesPanel",
"spec": {}
},
"queries": [{"kind": "TimeSeriesQuery", "spec": {"plugin": {"kind": "signoz/PromQLQuery", "spec": {"name": "A", "query": "up"}}}}]
"queries": [{"kind": "time_series", "spec": {"plugin": {"kind": "signoz/PromQLQuery", "spec": {"name": "A", "query": "up"}}}}]
}
}
},
@@ -786,7 +843,7 @@ func TestNumberPanelDefaults(t *testing.T) {
"kind": "signoz/NumberPanel",
"spec": {"thresholds": [{"value": 100, "color": "Red"}]}
},
"queries": [{"kind": "TimeSeriesQuery", "spec": {"plugin": {"kind": "signoz/PromQLQuery", "spec": {"name": "A", "query": "up"}}}}]
"queries": [{"kind": "time_series", "spec": {"plugin": {"kind": "signoz/PromQLQuery", "spec": {"name": "A", "query": "up"}}}}]
}
}
},
@@ -847,7 +904,7 @@ func TestStorageRoundTrip(t *testing.T) {
"kind": "signoz/TimeSeriesPanel",
"spec": {}
},
"queries": [{"kind": "TimeSeriesQuery", "spec": {"plugin": {"kind": "signoz/PromQLQuery", "spec": {"name": "A", "query": "up"}}}}]
"queries": [{"kind": "time_series", "spec": {"plugin": {"kind": "signoz/PromQLQuery", "spec": {"name": "A", "query": "up"}}}}]
}
},
"p2": {
@@ -857,7 +914,7 @@ func TestStorageRoundTrip(t *testing.T) {
"kind": "signoz/NumberPanel",
"spec": {"thresholds": [{"value": 100, "color": "Red"}]}
},
"queries": [{"kind": "TimeSeriesQuery", "spec": {"plugin": {"kind": "signoz/PromQLQuery", "spec": {"name": "A", "query": "up"}}}}]
"queries": [{"kind": "time_series", "spec": {"plugin": {"kind": "signoz/PromQLQuery", "spec": {"name": "A", "query": "up"}}}}]
}
}
},
@@ -1062,7 +1119,7 @@ func TestPanelTypeQueryTypeCompatibility(t *testing.T) {
return []byte(`{
"panels": {"p1": {"kind": "Panel", "spec": {
"plugin": {"kind": "` + panelKind + `", "spec": {}},
"queries": [{"kind": "TimeSeriesQuery", "spec": {"plugin": {"kind": "` + queryKind + `", "spec": ` + querySpec + `}}}]
"queries": [{"kind": "time_series", "spec": {"plugin": {"kind": "` + queryKind + `", "spec": ` + querySpec + `}}}]
}}},
"layouts": []
}`)
@@ -1071,7 +1128,7 @@ func TestPanelTypeQueryTypeCompatibility(t *testing.T) {
return []byte(`{
"panels": {"p1": {"kind": "Panel", "spec": {
"plugin": {"kind": "` + panelKind + `", "spec": {}},
"queries": [{"kind": "TimeSeriesQuery", "spec": {"plugin": {"kind": "signoz/CompositeQuery", "spec": {
"queries": [{"kind": "time_series", "spec": {"plugin": {"kind": "signoz/CompositeQuery", "spec": {
"queries": [{"type": "` + subType + `", "spec": ` + subSpec + `}]
}}}}]
}}},

View File

@@ -10,8 +10,8 @@ import (
"strings"
"testing"
v1 "github.com/perses/perses/pkg/model/api/v1"
"github.com/perses/perses/pkg/model/api/v1/dashboard"
"github.com/perses/spec/go/dashboard"
"github.com/perses/spec/go/datasource"
"github.com/stretchr/testify/assert"
)
@@ -22,12 +22,12 @@ func TestDashboardSpecMatchesPerses(t *testing.T) {
ours reflect.Type
perses reflect.Type
}{
{"DashboardSpec", typeOf[DashboardSpec](), typeOf[v1.DashboardSpec]()},
{"Panel", typeOf[Panel](), typeOf[v1.Panel]()},
{"PanelSpec", typeOf[PanelSpec](), typeOf[v1.PanelSpec]()},
{"Query", typeOf[Query](), typeOf[v1.Query]()},
{"QuerySpec", typeOf[QuerySpec](), typeOf[v1.QuerySpec]()},
{"DatasourceSpec", typeOf[DatasourceSpec](), typeOf[v1.DatasourceSpec]()},
{"DashboardSpec", typeOf[DashboardSpec](), typeOf[dashboard.Spec]()},
{"Panel", typeOf[Panel](), typeOf[dashboard.Panel]()},
{"PanelSpec", typeOf[PanelSpec](), typeOf[dashboard.PanelSpec]()},
{"Query", typeOf[Query](), typeOf[dashboard.Query]()},
{"QuerySpec", typeOf[QuerySpec](), typeOf[dashboard.QuerySpec]()},
{"DatasourceSpec", typeOf[DatasourceSpec](), typeOf[datasource.Spec]()},
{"Variable", typeOf[Variable](), typeOf[dashboard.Variable]()},
{"ListVariableSpec", typeOf[ListVariableSpec](), typeOf[dashboard.ListVariableSpec]()},
{"Layout", typeOf[Layout](), typeOf[dashboard.Layout]()},

View File

@@ -1,14 +1,16 @@
package dashboardtypes
import (
"encoding/json"
"maps"
"slices"
"github.com/SigNoz/signoz/pkg/errors"
v1 "github.com/perses/perses/pkg/model/api/v1"
"github.com/perses/perses/pkg/model/api/v1/common"
"github.com/perses/perses/pkg/model/api/v1/dashboard"
"github.com/perses/perses/pkg/model/api/v1/variable"
qb "github.com/SigNoz/signoz/pkg/types/querybuildertypes/querybuildertypesv5"
"github.com/SigNoz/signoz/pkg/valuer"
"github.com/perses/spec/go/common"
"github.com/perses/spec/go/dashboard"
"github.com/perses/spec/go/dashboard/variable"
"github.com/swaggest/jsonschema-go"
)
@@ -27,15 +29,36 @@ type DatasourceSpec struct {
// ══════════════════════════════════════════════
type Panel struct {
Kind string `json:"kind"`
Kind PanelKind `json:"kind"`
Spec PanelSpec `json:"spec"`
}
// PanelKind is the panel envelope discriminator. Perses leaves it a free
// string; SigNoz locks it to the single valid value.
type PanelKind string
const PanelKindPanel PanelKind = "Panel"
// Enum surfaces the allowed value in the generated OpenAPI schema.
func (PanelKind) Enum() []any { return []any{PanelKindPanel} }
func (k *PanelKind) UnmarshalJSON(data []byte) error {
var s string
if err := json.Unmarshal(data, &s); err != nil {
return errors.WrapInvalidInputf(err, ErrCodeDashboardInvalidInput, "invalid panel kind")
}
if PanelKind(s) != PanelKindPanel {
return errors.NewInvalidInputf(ErrCodeDashboardInvalidInput, "unknown panel kind %q; allowed values: %s", s, allowedValuesForKind([]PanelKind{PanelKindPanel}))
}
*k = PanelKind(s)
return nil
}
type PanelSpec struct {
Display *v1.PanelDisplay `json:"display,omitempty"`
Plugin PanelPlugin `json:"plugin"`
Queries []Query `json:"queries,omitempty"`
Links []v1.Link `json:"links,omitempty"`
Display *dashboard.PanelDisplay `json:"display,omitempty"`
Plugin PanelPlugin `json:"plugin"`
Queries []Query `json:"queries,omitempty"`
Links []dashboard.Link `json:"links,omitempty"`
}
// ══════════════════════════════════════════════
@@ -43,10 +66,29 @@ type PanelSpec struct {
// ══════════════════════════════════════════════
type Query struct {
Kind string `json:"kind"`
Kind QueryKind `json:"kind"`
Spec QuerySpec `json:"spec"`
}
type QueryKind qb.RequestType
func (QueryKind) Enum() []any { return (qb.RequestType{}).Enum() }
func (k *QueryKind) UnmarshalJSON(data []byte) error {
var s string
if err := json.Unmarshal(data, &s); err != nil {
return errors.WrapInvalidInputf(err, ErrCodeDashboardInvalidInput, "invalid query kind")
}
rt := qb.RequestType{String: valuer.NewString(s)}
for _, allowed := range (qb.RequestType{}).Enum() {
if allowed == rt {
*k = QueryKind(rt)
return nil
}
}
return errors.NewInvalidInputf(ErrCodeDashboardInvalidInput, "unknown query kind %q; allowed values: %s", s, "`scalar`, `time_series`, `raw`, `raw_stream`, `trace`")
}
type QuerySpec struct {
Name string `json:"name,omitempty"`
Plugin QueryPlugin `json:"plugin"`

View File

@@ -132,7 +132,7 @@
],
"queries": [
{
"kind": "TimeSeriesQuery",
"kind": "time_series",
"spec": {
"plugin": {
"kind": "signoz/BuilderQuery",
@@ -191,7 +191,7 @@
},
"queries": [
{
"kind": "TimeSeriesQuery",
"kind": "time_series",
"spec": {
"plugin": {
"kind": "signoz/CompositeQuery",
@@ -269,7 +269,7 @@
},
"queries": [
{
"kind": "TimeSeriesQuery",
"kind": "time_series",
"spec": {
"plugin": {
"kind": "signoz/BuilderQuery",
@@ -334,7 +334,7 @@
},
"queries": [
{
"kind": "TimeSeriesQuery",
"kind": "time_series",
"spec": {
"plugin": {
"kind": "signoz/BuilderQuery",
@@ -373,7 +373,7 @@
},
"queries": [
{
"kind": "TimeSeriesQuery",
"kind": "time_series",
"spec": {
"plugin": {
"kind": "signoz/BuilderQuery",
@@ -418,7 +418,7 @@
},
"queries": [
{
"kind": "TimeSeriesQuery",
"kind": "time_series",
"spec": {
"plugin": {
"kind": "signoz/BuilderQuery",
@@ -476,7 +476,7 @@
},
"queries": [
{
"kind": "TimeSeriesQuery",
"kind": "time_series",
"spec": {
"plugin": {
"kind": "signoz/ClickHouseSQL",
@@ -517,7 +517,7 @@
},
"queries": [
{
"kind": "LogQuery",
"kind": "raw",
"spec": {
"plugin": {
"kind": "signoz/BuilderQuery",
@@ -571,7 +571,7 @@
},
"queries": [
{
"kind": "TimeSeriesQuery",
"kind": "time_series",
"spec": {
"plugin": {
"kind": "signoz/BuilderQuery",
@@ -610,7 +610,7 @@
},
"queries": [
{
"kind": "TimeSeriesQuery",
"kind": "time_series",
"spec": {
"plugin": {
"kind": "signoz/CompositeQuery",
@@ -681,7 +681,7 @@
},
"queries": [
{
"kind": "TimeSeriesQuery",
"kind": "time_series",
"spec": {
"plugin": {
"kind": "signoz/CompositeQuery",
@@ -722,7 +722,7 @@
},
"queries": [
{
"kind": "TimeSeriesQuery",
"kind": "time_series",
"spec": {
"plugin": {
"kind": "signoz/PromQLQuery",

View File

@@ -30,7 +30,7 @@
},
"queries": [
{
"kind": "TimeSeriesQuery",
"kind": "time_series",
"spec": {
"plugin": {
"kind": "signoz/BuilderQuery",
@@ -85,7 +85,7 @@
},
"queries": [
{
"kind": "TimeSeriesQuery",
"kind": "time_series",
"spec": {
"plugin": {
"kind": "signoz/BuilderQuery",