@quanthea/plugin-kit 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +63 -0
- package/checks.d.ts +13 -0
- package/chunk-3ayrhcmg.js +5 -0
- package/connector-kind.d.ts +128 -0
- package/errors.d.ts +34 -0
- package/frame-builder.d.ts +38 -0
- package/frames.d.ts +62 -0
- package/host.d.ts +12 -0
- package/http-address.d.ts +18 -0
- package/http.d.ts +91 -0
- package/index.d.ts +21 -0
- package/index.js +7 -0
- package/kit.d.ts +37 -0
- package/languages.d.ts +16 -0
- package/package.json +41 -0
- package/queries.d.ts +151 -0
- package/schema.d.ts +58 -0
- package/series-frames.d.ts +55 -0
- package/testing.d.ts +36 -0
- package/testing.js +642 -0
package/queries.d.ts
ADDED
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
/** The queries a connector executes: already bound, never a template with raw variables. */
|
|
2
|
+
import type { QueryLanguage } from './languages.js';
|
|
3
|
+
export type { QueryLanguage };
|
|
4
|
+
/**
|
|
5
|
+
* The SQL dialects the core knows how to bind: how each writes its literals and its placeholders.
|
|
6
|
+
* A SQL connector kind declares one of them. `ansi` is standard SQL, for the sources the others do
|
|
7
|
+
* not fit; its kind also picks a placeholder style and a row-limit style.
|
|
8
|
+
*/
|
|
9
|
+
export declare const sqlDialects: readonly ["postgres", "mysql", "clickhouse", "trino", "influxdb", "ansi"];
|
|
10
|
+
/** A SQL dialect name. */
|
|
11
|
+
export type SqlDialect = (typeof sqlDialects)[number];
|
|
12
|
+
/**
|
|
13
|
+
* How an `ansi` source writes a placeholder: `?` (JDBC, ODBC, SQLite, Snowflake), `$1` (numbered,
|
|
14
|
+
* as PostgreSQL), `:1` (numbered, as Oracle) or `@p1` (named, as SQL Server drivers).
|
|
15
|
+
*/
|
|
16
|
+
export declare const sqlPlaceholderStyles: readonly ["?", "$1", ":1", "@p1"];
|
|
17
|
+
/** A placeholder style. */
|
|
18
|
+
export type SqlPlaceholderStyle = (typeof sqlPlaceholderStyles)[number];
|
|
19
|
+
/** How an `ansi` source limits rows: `FETCH FIRST n ROWS ONLY`, the standard, or `LIMIT n`. */
|
|
20
|
+
export declare const sqlRowLimits: readonly ["fetch", "limit"];
|
|
21
|
+
/** A row-limit style. */
|
|
22
|
+
export type SqlRowLimit = (typeof sqlRowLimits)[number];
|
|
23
|
+
/** A value bound to a SQL placeholder. */
|
|
24
|
+
export type SqlParameter = string | number | boolean | Date | null;
|
|
25
|
+
/**
|
|
26
|
+
* A SQL query with positional placeholders and their values. The placeholders are the dialect's:
|
|
27
|
+
* `$1`, `$2`… for `postgres`, `?` for `mysql` and `trino`, `{p1:Type}`, `{p2:Type}`… for
|
|
28
|
+
* `clickhouse`, `$p1`, `$p2`… for `influxdb`, and the kind's placeholder style for `ansi`, where `pN`
|
|
29
|
+
* is the Nth value. The core checked that it is a single read statement.
|
|
30
|
+
*/
|
|
31
|
+
export interface SqlQuery {
|
|
32
|
+
/** The query language. */
|
|
33
|
+
readonly language: 'sql';
|
|
34
|
+
/** The statement, with the dialect's placeholders. */
|
|
35
|
+
readonly text: string;
|
|
36
|
+
/** The placeholder values, in order. */
|
|
37
|
+
readonly parameters: readonly SqlParameter[];
|
|
38
|
+
}
|
|
39
|
+
/** A PromQL expression with every variable already substituted and escaped. */
|
|
40
|
+
export interface PromqlQuery {
|
|
41
|
+
/** The query language. */
|
|
42
|
+
readonly language: 'promql';
|
|
43
|
+
/** The expression. */
|
|
44
|
+
readonly expr: string;
|
|
45
|
+
/** `true` evaluates once at the end of the time range; `false` over the range. */
|
|
46
|
+
readonly instant: boolean;
|
|
47
|
+
/** Seconds between points of a range query. */
|
|
48
|
+
readonly stepSeconds: number;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* A search request in the Elasticsearch and OpenSearch query DSL, every variable already put in
|
|
52
|
+
* as a JSON value. The core checked that the body holds no script.
|
|
53
|
+
*/
|
|
54
|
+
export interface SearchQuery {
|
|
55
|
+
/** The query language. */
|
|
56
|
+
readonly language: 'search';
|
|
57
|
+
/** The index, index pattern or comma-separated list of them. */
|
|
58
|
+
readonly index: string;
|
|
59
|
+
/** The search body: `query`, `aggs`, `sort`, `size` and the like. */
|
|
60
|
+
readonly body: Readonly<Record<string, unknown>>;
|
|
61
|
+
}
|
|
62
|
+
/** A LogQL expression with every variable already substituted and escaped. */
|
|
63
|
+
export interface LogqlQuery {
|
|
64
|
+
/** The query language. */
|
|
65
|
+
readonly language: 'logql';
|
|
66
|
+
/** The expression: a log query or a metric query. */
|
|
67
|
+
readonly expr: string;
|
|
68
|
+
/** `true` evaluates once at the end of the time range; `false` over the range. */
|
|
69
|
+
readonly instant: boolean;
|
|
70
|
+
/** Seconds between points of a metric range query. */
|
|
71
|
+
readonly stepSeconds: number;
|
|
72
|
+
}
|
|
73
|
+
/** How a column of an HTTP response is read. */
|
|
74
|
+
export interface HttpField {
|
|
75
|
+
/** The column name. */
|
|
76
|
+
readonly name: string;
|
|
77
|
+
/** Where the value is in each row, as a JSON pointer from the row. */
|
|
78
|
+
readonly pointer: string;
|
|
79
|
+
/** The column type; inferred from the values when absent. */
|
|
80
|
+
readonly type?: 'time' | 'number' | 'string' | 'boolean' | undefined;
|
|
81
|
+
/** For a time given as a number: seconds or milliseconds since the epoch. */
|
|
82
|
+
readonly unit?: 's' | 'ms' | undefined;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* An HTTP request with every variable already put in: the path encoded, each query parameter a
|
|
86
|
+
* separate value, the body's variables JSON values. The connector checks the method and the path
|
|
87
|
+
* against what its settings allow.
|
|
88
|
+
*/
|
|
89
|
+
export interface HttpQuery {
|
|
90
|
+
/** The query language. */
|
|
91
|
+
readonly language: 'http';
|
|
92
|
+
/** `GET` or `POST`. */
|
|
93
|
+
readonly method: 'GET' | 'POST';
|
|
94
|
+
/** The path under the connector's base URL, encoded, without a query string. */
|
|
95
|
+
readonly path: string;
|
|
96
|
+
/** The query parameters, in order; a name may repeat. */
|
|
97
|
+
readonly query: readonly (readonly [string, string])[];
|
|
98
|
+
/** The JSON body of a POST. */
|
|
99
|
+
readonly body?: Readonly<Record<string, unknown>> | undefined;
|
|
100
|
+
/** How the response becomes a table. */
|
|
101
|
+
readonly extract: {
|
|
102
|
+
/** The array of rows, as a JSON pointer. */
|
|
103
|
+
readonly rows: string;
|
|
104
|
+
/** The columns, or none to take every value of the rows. */
|
|
105
|
+
readonly fields?: readonly HttpField[] | undefined;
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
/** A Redis or Valkey read command, with every variable put in. The core checked it only reads. */
|
|
109
|
+
export interface RedisQuery {
|
|
110
|
+
/** The query language. */
|
|
111
|
+
readonly language: 'redis';
|
|
112
|
+
/** The command, upper case. */
|
|
113
|
+
readonly command: string;
|
|
114
|
+
/** Its arguments, each sent as one argument whatever it holds. */
|
|
115
|
+
readonly args: readonly string[];
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* A MongoDB aggregation over one collection, every variable already put in as a JSON value. The
|
|
119
|
+
* stages are Extended JSON (`{"$date": …}` for a date), and the core checked that none writes or
|
|
120
|
+
* runs JavaScript.
|
|
121
|
+
*/
|
|
122
|
+
export interface MongodbQuery {
|
|
123
|
+
/** The query language. */
|
|
124
|
+
readonly language: 'mongodb';
|
|
125
|
+
/** The collection the pipeline starts from. */
|
|
126
|
+
readonly collection: string;
|
|
127
|
+
/** The stages, each an object with one `$` key. */
|
|
128
|
+
readonly pipeline: readonly Readonly<Record<string, unknown>>[];
|
|
129
|
+
}
|
|
130
|
+
/** A query ready to execute. Connectors receive nothing else. */
|
|
131
|
+
export type BoundQuery = SqlQuery | PromqlQuery | SearchQuery | LogqlQuery | HttpQuery | RedisQuery | MongodbQuery;
|
|
132
|
+
/** The time range a query covers. */
|
|
133
|
+
export interface TimeRange {
|
|
134
|
+
/** The start, inclusive. */
|
|
135
|
+
readonly from: Date;
|
|
136
|
+
/** The end, inclusive. */
|
|
137
|
+
readonly to: Date;
|
|
138
|
+
}
|
|
139
|
+
/** What the core asks of one execution. Connectors must respect every field. */
|
|
140
|
+
export interface ExecutionContext {
|
|
141
|
+
/** The name of the frames this query returns. */
|
|
142
|
+
readonly refId: string;
|
|
143
|
+
/** Aborted on timeout or when the caller gives up. Stop work and reject when it fires. */
|
|
144
|
+
readonly signal: AbortSignal;
|
|
145
|
+
/** The query timeout, for sources that enforce one themselves (a statement timeout). */
|
|
146
|
+
readonly timeoutMs: number;
|
|
147
|
+
/** Rows (or points) to return at most, per frame. Mark the frame truncated when there are more. */
|
|
148
|
+
readonly maxRows: number;
|
|
149
|
+
/** The time range of the query. */
|
|
150
|
+
readonly timeRange: TimeRange;
|
|
151
|
+
}
|
package/schema.d.ts
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/** What a connector reports about its source: health, schema and sample values. */
|
|
2
|
+
import type { FieldType } from './frames.js';
|
|
3
|
+
/** The result of a connection test. */
|
|
4
|
+
export interface HealthReport {
|
|
5
|
+
/** Whether the source answered and the credentials worked. */
|
|
6
|
+
readonly ok: boolean;
|
|
7
|
+
/** Round-trip time of the test, in milliseconds. */
|
|
8
|
+
readonly latencyMs: number;
|
|
9
|
+
/** A short sentence for the admin, such as the server version or why the test failed. */
|
|
10
|
+
readonly message: string;
|
|
11
|
+
/** Whether the credentials can only read: `true`, `false`, or `null` when the source cannot tell. */
|
|
12
|
+
readonly readOnly: boolean | null;
|
|
13
|
+
}
|
|
14
|
+
/** One field of a schema entity: a column, a label, a document field. */
|
|
15
|
+
export interface SchemaField {
|
|
16
|
+
/** The field name as queries write it. */
|
|
17
|
+
readonly name: string;
|
|
18
|
+
/** The source's own type name, such as `timestamptz` or `keyword`. */
|
|
19
|
+
readonly nativeType: string;
|
|
20
|
+
/** The frame type the field maps to, when known. */
|
|
21
|
+
readonly type?: FieldType;
|
|
22
|
+
/** A description from the source, such as a column comment. */
|
|
23
|
+
readonly description?: string;
|
|
24
|
+
/** An estimate of the number of distinct values, when the source knows it cheaply. */
|
|
25
|
+
readonly distinctEstimate?: number;
|
|
26
|
+
}
|
|
27
|
+
/** A queryable thing in the source: a table, a view, a metric, an index. */
|
|
28
|
+
export interface SchemaEntity {
|
|
29
|
+
/** The name as queries write it, qualified when the source needs it (`public.orders`). */
|
|
30
|
+
readonly name: string;
|
|
31
|
+
/** What kind of thing it is, for display. */
|
|
32
|
+
readonly kind: 'table' | 'view' | 'metric' | 'index' | 'endpoint' | 'collection';
|
|
33
|
+
/** A description from the source, such as a table comment or a metric's help text. */
|
|
34
|
+
readonly description?: string;
|
|
35
|
+
/** An estimate of the number of rows or series, when the source knows it cheaply. */
|
|
36
|
+
readonly rowEstimate?: number;
|
|
37
|
+
/** The fields. */
|
|
38
|
+
readonly fields: readonly SchemaField[];
|
|
39
|
+
}
|
|
40
|
+
/** The shape of a source: every entity and its fields, never their values. */
|
|
41
|
+
export interface SchemaSnapshot {
|
|
42
|
+
/** The entities, in the order to show them. */
|
|
43
|
+
readonly entities: readonly SchemaEntity[];
|
|
44
|
+
}
|
|
45
|
+
/** Names one field of one entity. */
|
|
46
|
+
export interface FieldReference {
|
|
47
|
+
/** The entity name, as in {@link SchemaEntity.name}. */
|
|
48
|
+
readonly entity: string;
|
|
49
|
+
/** The field name, as in {@link SchemaField.name}. */
|
|
50
|
+
readonly field: string;
|
|
51
|
+
}
|
|
52
|
+
/** Distinct values of a field. */
|
|
53
|
+
export interface SampleResult {
|
|
54
|
+
/** At most the requested number of distinct values. */
|
|
55
|
+
readonly values: readonly string[];
|
|
56
|
+
/** `false` when the field has more distinct values than were requested. */
|
|
57
|
+
readonly complete: boolean;
|
|
58
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Turns labelled series, in the result format of the Prometheus HTTP API, into frames. Prometheus
|
|
3
|
+
* answers in it, and so do Loki's metric queries.
|
|
4
|
+
*/
|
|
5
|
+
import type { Frame } from './frames.js';
|
|
6
|
+
import type { ExecutionContext } from './queries.js';
|
|
7
|
+
/** One series of a range query. */
|
|
8
|
+
interface MatrixSeries {
|
|
9
|
+
/** The series labels, including `__name__` when the query keeps it. */
|
|
10
|
+
readonly metric: Readonly<Record<string, string>>;
|
|
11
|
+
/** The points: Unix seconds and the value as a string. */
|
|
12
|
+
readonly values: readonly (readonly [number, string])[];
|
|
13
|
+
}
|
|
14
|
+
/** One sample of an instant query. */
|
|
15
|
+
interface VectorSample {
|
|
16
|
+
/** The series labels. */
|
|
17
|
+
readonly metric: Readonly<Record<string, string>>;
|
|
18
|
+
/** The point: Unix seconds and the value as a string. */
|
|
19
|
+
readonly value: readonly [number, string];
|
|
20
|
+
}
|
|
21
|
+
/** The `data` of a query response: series over time, samples at one time, or one value. */
|
|
22
|
+
export type SeriesData = {
|
|
23
|
+
readonly resultType: 'matrix';
|
|
24
|
+
readonly result: readonly MatrixSeries[];
|
|
25
|
+
} | {
|
|
26
|
+
readonly resultType: 'vector';
|
|
27
|
+
readonly result: readonly VectorSample[];
|
|
28
|
+
} | {
|
|
29
|
+
readonly resultType: 'scalar' | 'string';
|
|
30
|
+
readonly result: readonly [number, string];
|
|
31
|
+
};
|
|
32
|
+
/**
|
|
33
|
+
* Converts a sample value.
|
|
34
|
+
*
|
|
35
|
+
* @param value - The value as the API writes it, such as `"0.084"`, `"NaN"` or `"+Inf"`.
|
|
36
|
+
* @returns The number, or `null` when it is not finite.
|
|
37
|
+
*/
|
|
38
|
+
export declare function sampleValue(value: string): number | null;
|
|
39
|
+
/**
|
|
40
|
+
* The display name of a series: its metric name and labels.
|
|
41
|
+
*
|
|
42
|
+
* @param metric - The series labels.
|
|
43
|
+
* @returns Such as `http_requests_total{service="checkout-svc"}`, or `{}` for no labels.
|
|
44
|
+
*/
|
|
45
|
+
export declare function seriesName(metric: Readonly<Record<string, string>>): string;
|
|
46
|
+
/**
|
|
47
|
+
* Turns a series result into frames: one per series over time, one table of samples at one time.
|
|
48
|
+
*
|
|
49
|
+
* @param data - The `data` of the response.
|
|
50
|
+
* @param context - The execution context.
|
|
51
|
+
* @param durationMs - How long the query took.
|
|
52
|
+
* @returns The frames.
|
|
53
|
+
*/
|
|
54
|
+
export declare function seriesFrames(data: SeriesData, context: ExecutionContext, durationMs: number): Frame[];
|
|
55
|
+
export {};
|
package/testing.d.ts
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { ConnectorKind } from './connector-kind.js';
|
|
2
|
+
import type { ConnectorKit } from './kit.js';
|
|
3
|
+
import type { BoundQuery, TimeRange } from './queries.js';
|
|
4
|
+
import type { FieldReference } from './schema.js';
|
|
5
|
+
/** What the suite needs to exercise a kind. */
|
|
6
|
+
export interface ConformanceFixture {
|
|
7
|
+
/** A valid configuration, before parsing. */
|
|
8
|
+
readonly config: unknown;
|
|
9
|
+
/** Valid credentials, before parsing. */
|
|
10
|
+
readonly secret: unknown;
|
|
11
|
+
/** A query that returns at least two rows over {@link ConformanceFixture.timeRange}. */
|
|
12
|
+
readonly query: BoundQuery;
|
|
13
|
+
/** A query the source rejects, to check the error. */
|
|
14
|
+
readonly invalidQuery: BoundQuery;
|
|
15
|
+
/** A field with at least two distinct values. */
|
|
16
|
+
readonly sampleField: FieldReference;
|
|
17
|
+
/** The time range to query. */
|
|
18
|
+
readonly timeRange: TimeRange;
|
|
19
|
+
/** Whether a source is available. Without one, only the static checks run. */
|
|
20
|
+
readonly live: boolean;
|
|
21
|
+
/** Names the source in the test titles, when a kind runs against several. */
|
|
22
|
+
readonly label?: string;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* The kit to call a plugin with in its tests: the same functions quanthea passes at load.
|
|
26
|
+
*
|
|
27
|
+
* @returns The kit.
|
|
28
|
+
*/
|
|
29
|
+
export declare function createTestKit(): ConnectorKit;
|
|
30
|
+
/**
|
|
31
|
+
* Registers the conformance tests of a connector kind.
|
|
32
|
+
*
|
|
33
|
+
* @param kind - The connector kind under test.
|
|
34
|
+
* @param fixture - How to exercise it.
|
|
35
|
+
*/
|
|
36
|
+
export declare function testConnectorConformance(kind: ConnectorKind, fixture: ConformanceFixture): void;
|