@duckcodeailabs/dql-cli 1.4.3 → 1.4.4
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/apps-api.d.ts +77 -0
- package/apps-api.d.ts.map +1 -0
- package/apps-api.js +612 -0
- package/apps-api.js.map +1 -0
- package/apps-api.test.d.ts +2 -0
- package/apps-api.test.d.ts.map +1 -0
- package/apps-api.test.js +111 -0
- package/apps-api.test.js.map +1 -0
- package/args.d.ts +30 -0
- package/args.d.ts.map +1 -0
- package/args.js +105 -0
- package/args.js.map +1 -0
- package/args.test.d.ts +2 -0
- package/args.test.d.ts.map +1 -0
- package/args.test.js +33 -0
- package/args.test.js.map +1 -0
- package/assets/dql-notebook/assets/codemirror-DJYUkPr1.js +11 -0
- package/assets/dql-notebook/assets/index-DUTeFz5j.js +858 -0
- package/assets/dql-notebook/assets/index-DrhoZmtv.css +1 -0
- package/assets/dql-notebook/assets/react-CRB3T2We.js +32 -0
- package/assets/dql-notebook/index.html +18 -0
- package/assets/notebook-browser/app.js +548 -0
- package/assets/notebook-browser/index.html +83 -0
- package/assets/notebook-browser/styles.css +336 -0
- package/block-templates.d.ts +8 -0
- package/block-templates.d.ts.map +1 -0
- package/block-templates.js +60 -0
- package/block-templates.js.map +1 -0
- package/commands/agent.d.ts +19 -0
- package/commands/agent.d.ts.map +1 -0
- package/commands/agent.js +165 -0
- package/commands/agent.js.map +1 -0
- package/commands/app.d.ts +32 -0
- package/commands/app.d.ts.map +1 -0
- package/commands/app.js +307 -0
- package/commands/app.js.map +1 -0
- package/commands/build.d.ts +3 -0
- package/commands/build.d.ts.map +1 -0
- package/commands/build.js +69 -0
- package/commands/build.js.map +1 -0
- package/commands/build.test.d.ts +2 -0
- package/commands/build.test.d.ts.map +1 -0
- package/commands/build.test.js +44 -0
- package/commands/build.test.js.map +1 -0
- package/commands/certify.d.ts +3 -0
- package/commands/certify.d.ts.map +1 -0
- package/commands/certify.js +228 -0
- package/commands/certify.js.map +1 -0
- package/commands/compile.d.ts +21 -0
- package/commands/compile.d.ts.map +1 -0
- package/commands/compile.js +198 -0
- package/commands/compile.js.map +1 -0
- package/commands/compile.test.d.ts +2 -0
- package/commands/compile.test.d.ts.map +1 -0
- package/commands/compile.test.js +115 -0
- package/commands/compile.test.js.map +1 -0
- package/commands/diff.d.ts +3 -0
- package/commands/diff.d.ts.map +1 -0
- package/commands/diff.js +52 -0
- package/commands/diff.js.map +1 -0
- package/commands/doctor.d.ts +3 -0
- package/commands/doctor.d.ts.map +1 -0
- package/commands/doctor.js +191 -0
- package/commands/doctor.js.map +1 -0
- package/commands/doctor.test.d.ts +2 -0
- package/commands/doctor.test.d.ts.map +1 -0
- package/commands/doctor.test.js +43 -0
- package/commands/doctor.test.js.map +1 -0
- package/commands/fmt.d.ts +3 -0
- package/commands/fmt.d.ts.map +1 -0
- package/commands/fmt.js +53 -0
- package/commands/fmt.js.map +1 -0
- package/commands/info.d.ts +3 -0
- package/commands/info.d.ts.map +1 -0
- package/commands/info.js +56 -0
- package/commands/info.js.map +1 -0
- package/commands/init.d.ts +3 -0
- package/commands/init.d.ts.map +1 -0
- package/commands/init.js +250 -0
- package/commands/init.js.map +1 -0
- package/commands/init.test.d.ts +2 -0
- package/commands/init.test.d.ts.map +1 -0
- package/commands/init.test.js +118 -0
- package/commands/init.test.js.map +1 -0
- package/commands/lineage.d.ts +24 -0
- package/commands/lineage.d.ts.map +1 -0
- package/commands/lineage.js +634 -0
- package/commands/lineage.js.map +1 -0
- package/commands/mcp.d.ts +7 -0
- package/commands/mcp.d.ts.map +1 -0
- package/commands/mcp.js +16 -0
- package/commands/mcp.js.map +1 -0
- package/commands/migrate.d.ts +12 -0
- package/commands/migrate.d.ts.map +1 -0
- package/commands/migrate.js +192 -0
- package/commands/migrate.js.map +1 -0
- package/commands/new.d.ts +3 -0
- package/commands/new.d.ts.map +1 -0
- package/commands/new.js +490 -0
- package/commands/new.js.map +1 -0
- package/commands/new.test.d.ts +2 -0
- package/commands/new.test.d.ts.map +1 -0
- package/commands/new.test.js +191 -0
- package/commands/new.test.js.map +1 -0
- package/commands/notebook.d.ts +3 -0
- package/commands/notebook.d.ts.map +1 -0
- package/commands/notebook.js +46 -0
- package/commands/notebook.js.map +1 -0
- package/commands/parse.d.ts +3 -0
- package/commands/parse.d.ts.map +1 -0
- package/commands/parse.js +63 -0
- package/commands/parse.js.map +1 -0
- package/commands/preview.d.ts +3 -0
- package/commands/preview.d.ts.map +1 -0
- package/commands/preview.js +42 -0
- package/commands/preview.js.map +1 -0
- package/commands/schedule.d.ts +3 -0
- package/commands/schedule.d.ts.map +1 -0
- package/commands/schedule.js +215 -0
- package/commands/schedule.js.map +1 -0
- package/commands/semantic.d.ts +12 -0
- package/commands/semantic.d.ts.map +1 -0
- package/commands/semantic.js +356 -0
- package/commands/semantic.js.map +1 -0
- package/commands/serve.d.ts +3 -0
- package/commands/serve.d.ts.map +1 -0
- package/commands/serve.js +30 -0
- package/commands/serve.js.map +1 -0
- package/commands/slack.d.ts +13 -0
- package/commands/slack.d.ts.map +1 -0
- package/commands/slack.js +53 -0
- package/commands/slack.js.map +1 -0
- package/commands/sync.d.ts +3 -0
- package/commands/sync.d.ts.map +1 -0
- package/commands/sync.js +192 -0
- package/commands/sync.js.map +1 -0
- package/commands/sync.test.d.ts +2 -0
- package/commands/sync.test.d.ts.map +1 -0
- package/commands/sync.test.js +147 -0
- package/commands/sync.test.js.map +1 -0
- package/commands/test.d.ts +3 -0
- package/commands/test.d.ts.map +1 -0
- package/commands/test.js +167 -0
- package/commands/test.js.map +1 -0
- package/commands/validate.d.ts +3 -0
- package/commands/validate.d.ts.map +1 -0
- package/commands/validate.js +116 -0
- package/commands/validate.js.map +1 -0
- package/commands/verify.d.ts +11 -0
- package/commands/verify.d.ts.map +1 -0
- package/commands/verify.js +74 -0
- package/commands/verify.js.map +1 -0
- package/digest.d.ts +10 -0
- package/digest.d.ts.map +1 -0
- package/digest.js +83 -0
- package/digest.js.map +1 -0
- package/git-service.d.ts +17 -0
- package/git-service.d.ts.map +1 -0
- package/git-service.js +54 -0
- package/git-service.js.map +1 -0
- package/governance-runtime.d.ts +15 -0
- package/governance-runtime.d.ts.map +1 -0
- package/governance-runtime.js +50 -0
- package/governance-runtime.js.map +1 -0
- package/index.d.ts +3 -0
- package/index.d.ts.map +1 -0
- package/index.js.map +1 -0
- package/llm/index.d.ts +4 -0
- package/llm/index.d.ts.map +1 -0
- package/llm/index.js +19 -0
- package/llm/index.js.map +1 -0
- package/llm/providers/claude-agent-sdk.d.ts +3 -0
- package/llm/providers/claude-agent-sdk.d.ts.map +1 -0
- package/llm/providers/claude-agent-sdk.js +174 -0
- package/llm/providers/claude-agent-sdk.js.map +1 -0
- package/llm/providers/claude-code.d.ts +8 -0
- package/llm/providers/claude-code.d.ts.map +1 -0
- package/llm/providers/claude-code.js +171 -0
- package/llm/providers/claude-code.js.map +1 -0
- package/llm/providers/dql-agent-provider.d.ts +5 -0
- package/llm/providers/dql-agent-provider.d.ts.map +1 -0
- package/llm/providers/dql-agent-provider.js +99 -0
- package/llm/providers/dql-agent-provider.js.map +1 -0
- package/llm/tools.d.ts +9 -0
- package/llm/tools.d.ts.map +1 -0
- package/llm/tools.js +112 -0
- package/llm/tools.js.map +1 -0
- package/llm/types.d.ts +70 -0
- package/llm/types.d.ts.map +1 -0
- package/llm/types.js +2 -0
- package/llm/types.js.map +1 -0
- package/local-runtime.d.ts +142 -0
- package/local-runtime.d.ts.map +1 -0
- package/local-runtime.js +4357 -0
- package/local-runtime.js.map +1 -0
- package/local-runtime.test.d.ts +2 -0
- package/local-runtime.test.d.ts.map +1 -0
- package/local-runtime.test.js +241 -0
- package/local-runtime.test.js.map +1 -0
- package/metricflow.d.ts +35 -0
- package/metricflow.d.ts.map +1 -0
- package/metricflow.js +122 -0
- package/metricflow.js.map +1 -0
- package/metricflow.test.d.ts +2 -0
- package/metricflow.test.d.ts.map +1 -0
- package/metricflow.test.js +54 -0
- package/metricflow.test.js.map +1 -0
- package/open-browser.d.ts +2 -0
- package/open-browser.d.ts.map +1 -0
- package/open-browser.js +29 -0
- package/open-browser.js.map +1 -0
- package/package.json +10 -13
- package/schedule/alerts.d.ts +5 -0
- package/schedule/alerts.d.ts.map +1 -0
- package/schedule/alerts.js +54 -0
- package/schedule/alerts.js.map +1 -0
- package/schedule/discovery.d.ts +4 -0
- package/schedule/discovery.d.ts.map +1 -0
- package/schedule/discovery.js +36 -0
- package/schedule/discovery.js.map +1 -0
- package/schedule/notifiers/email.d.ts +3 -0
- package/schedule/notifiers/email.d.ts.map +1 -0
- package/schedule/notifiers/email.js +76 -0
- package/schedule/notifiers/email.js.map +1 -0
- package/schedule/notifiers/file.d.ts +3 -0
- package/schedule/notifiers/file.d.ts.map +1 -0
- package/schedule/notifiers/file.js +50 -0
- package/schedule/notifiers/file.js.map +1 -0
- package/schedule/notifiers/index.d.ts +10 -0
- package/schedule/notifiers/index.d.ts.map +1 -0
- package/schedule/notifiers/index.js +33 -0
- package/schedule/notifiers/index.js.map +1 -0
- package/schedule/notifiers/slack.d.ts +3 -0
- package/schedule/notifiers/slack.d.ts.map +1 -0
- package/schedule/notifiers/slack.js +58 -0
- package/schedule/notifiers/slack.js.map +1 -0
- package/schedule/runner.d.ts +14 -0
- package/schedule/runner.d.ts.map +1 -0
- package/schedule/runner.js +221 -0
- package/schedule/runner.js.map +1 -0
- package/schedule/runs.d.ts +5 -0
- package/schedule/runs.d.ts.map +1 -0
- package/schedule/runs.js +41 -0
- package/schedule/runs.js.map +1 -0
- package/schedule/service.d.ts +13 -0
- package/schedule/service.d.ts.map +1 -0
- package/schedule/service.js +87 -0
- package/schedule/service.js.map +1 -0
- package/schedule/types.d.ts +70 -0
- package/schedule/types.d.ts.map +1 -0
- package/schedule/types.js +2 -0
- package/schedule/types.js.map +1 -0
- package/semantic-import.d.ts +135 -0
- package/semantic-import.d.ts.map +1 -0
- package/semantic-import.js +979 -0
- package/semantic-import.js.map +1 -0
- package/semantic-import.test.d.ts +2 -0
- package/semantic-import.test.d.ts.map +1 -0
- package/semantic-import.test.js +95 -0
- package/semantic-import.test.js.map +1 -0
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { createBlockArtifacts, createSemanticBuilderBlock, formatLocalQueryRuntimeError, normalizeProjectConnection, prepareLocalExecution, resolveProjectRelativeSqlPaths, serializeJSON, validateBlockStudioSource, } from './local-runtime.js';
|
|
3
|
+
import { afterEach } from 'vitest';
|
|
4
|
+
import { mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
|
|
5
|
+
import { join } from 'node:path';
|
|
6
|
+
import { tmpdir } from 'node:os';
|
|
7
|
+
import { SemanticLayer } from '@duckcodeailabs/dql-core';
|
|
8
|
+
const tempDirs = [];
|
|
9
|
+
afterEach(() => {
|
|
10
|
+
while (tempDirs.length > 0) {
|
|
11
|
+
const dir = tempDirs.pop();
|
|
12
|
+
if (dir)
|
|
13
|
+
rmSync(dir, { recursive: true, force: true });
|
|
14
|
+
}
|
|
15
|
+
});
|
|
16
|
+
describe('formatLocalQueryRuntimeError', () => {
|
|
17
|
+
it('explains missing DuckDB native bindings with actionable guidance', () => {
|
|
18
|
+
const message = formatLocalQueryRuntimeError({ driver: 'file', filepath: ':memory:' }, new Error("Cannot find module '/tmp/duckdb/lib/binding/duckdb.node'"));
|
|
19
|
+
expect(message).toContain('DuckDB native bindings could not be loaded');
|
|
20
|
+
expect(message).toContain(`Current Node.js runtime: ${process.versions.node}`);
|
|
21
|
+
expect(message).toContain('Node 18, 20, or 22');
|
|
22
|
+
expect(message).toContain('pnpm install');
|
|
23
|
+
});
|
|
24
|
+
});
|
|
25
|
+
describe('serializeJSON', () => {
|
|
26
|
+
it('serializes safe bigint values as numbers', () => {
|
|
27
|
+
expect(serializeJSON({ revenue: 42n })).toBe('{"revenue":42}');
|
|
28
|
+
});
|
|
29
|
+
it('serializes unsafe bigint values as strings', () => {
|
|
30
|
+
const value = BigInt(Number.MAX_SAFE_INTEGER) + 1n;
|
|
31
|
+
expect(serializeJSON({ revenue: value })).toBe(`{"revenue":"${value.toString()}"}`);
|
|
32
|
+
});
|
|
33
|
+
});
|
|
34
|
+
describe('resolveProjectRelativeSqlPaths', () => {
|
|
35
|
+
it('rewrites notebook sample file paths relative to the selected project', () => {
|
|
36
|
+
const sql = "SELECT * FROM read_csv_auto('./data/revenue.csv')";
|
|
37
|
+
const resolved = resolveProjectRelativeSqlPaths(sql, '/tmp/demo-project');
|
|
38
|
+
expect(resolved).toBe("SELECT * FROM read_csv_auto('/tmp/demo-project/data/revenue.csv')");
|
|
39
|
+
});
|
|
40
|
+
it('leaves unrelated string literals untouched', () => {
|
|
41
|
+
const sql = "SELECT './data/revenue.csv' AS label";
|
|
42
|
+
expect(resolveProjectRelativeSqlPaths(sql, '/tmp/demo-project')).toBe(sql);
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
describe('normalizeProjectConnection', () => {
|
|
46
|
+
it('resolves relative local database paths against the project root', () => {
|
|
47
|
+
expect(normalizeProjectConnection({ driver: 'duckdb', filepath: './local/dev.duckdb' }, '/tmp/demo-project')).toEqual({ driver: 'duckdb', filepath: '/tmp/demo-project/local/dev.duckdb' });
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
describe('prepareLocalExecution', () => {
|
|
51
|
+
it('rewrites SQL paths for file-backed notebook queries', () => {
|
|
52
|
+
const prepared = prepareLocalExecution("SELECT * FROM read_csv_auto('./data/revenue.csv')", { driver: 'file', filepath: ':memory:' }, '/tmp/demo-project', { dataDir: './data' });
|
|
53
|
+
expect(prepared.connection).toEqual({ driver: 'file', filepath: ':memory:' });
|
|
54
|
+
expect(prepared.sql).toBe("SELECT * FROM read_csv_auto('/tmp/demo-project/data/revenue.csv')");
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
describe('semantic block save artifacts', () => {
|
|
58
|
+
it('writes both the block file and semantic companion metadata for save-from-cell flows', () => {
|
|
59
|
+
const projectRoot = mkdtempSync(join(tmpdir(), 'dql-block-artifacts-'));
|
|
60
|
+
tempDirs.push(projectRoot);
|
|
61
|
+
writeFileSync(join(projectRoot, 'dql.config.json'), '{}\n');
|
|
62
|
+
const created = createBlockArtifacts(projectRoot, {
|
|
63
|
+
name: 'Revenue Summary',
|
|
64
|
+
domain: 'finance',
|
|
65
|
+
content: 'SELECT @metric(total_revenue), @dim(order_date);',
|
|
66
|
+
description: 'Finance summary block',
|
|
67
|
+
tags: ['finance', 'exec'],
|
|
68
|
+
});
|
|
69
|
+
expect(created.path).toBe('blocks/finance/revenue-summary.dql');
|
|
70
|
+
expect(created.companionPath).toBe('semantic-layer/blocks/finance/revenue-summary.yaml');
|
|
71
|
+
expect(readFileSync(join(projectRoot, created.path), 'utf-8')).toContain('@metric(total_revenue)');
|
|
72
|
+
const companion = readFileSync(join(projectRoot, created.companionPath), 'utf-8');
|
|
73
|
+
expect(companion).toContain('provider: dql');
|
|
74
|
+
expect(companion).toContain('semanticMetrics:');
|
|
75
|
+
expect(companion).toContain(' - total_revenue');
|
|
76
|
+
expect(companion).toContain('semanticDimensions:');
|
|
77
|
+
expect(companion).toContain(' - order_date');
|
|
78
|
+
expect(companion).toContain('reviewStatus: draft');
|
|
79
|
+
});
|
|
80
|
+
it('writes semantic builder blocks with lineage companion metadata', () => {
|
|
81
|
+
const projectRoot = mkdtempSync(join(tmpdir(), 'dql-builder-artifacts-'));
|
|
82
|
+
tempDirs.push(projectRoot);
|
|
83
|
+
writeFileSync(join(projectRoot, 'dql.config.json'), '{}\n');
|
|
84
|
+
const created = createSemanticBuilderBlock(projectRoot, {
|
|
85
|
+
name: 'Executive Revenue',
|
|
86
|
+
domain: 'finance',
|
|
87
|
+
description: 'Executive revenue cut',
|
|
88
|
+
owner: 'finance-analytics',
|
|
89
|
+
tags: ['finance'],
|
|
90
|
+
metrics: ['total_revenue'],
|
|
91
|
+
dimensions: ['sales_channel'],
|
|
92
|
+
timeDimension: { name: 'order_date', granularity: 'month' },
|
|
93
|
+
chart: 'line',
|
|
94
|
+
blockType: 'semantic',
|
|
95
|
+
sql: 'SELECT 1',
|
|
96
|
+
tables: ['analytics.orders'],
|
|
97
|
+
provider: 'dbt',
|
|
98
|
+
});
|
|
99
|
+
expect(created.path).toBe('blocks/finance/executive-revenue.dql');
|
|
100
|
+
expect(created.content).toContain('type = "semantic"');
|
|
101
|
+
expect(created.content).toContain('metric = "total_revenue"');
|
|
102
|
+
const companion = readFileSync(join(projectRoot, created.companionPath), 'utf-8');
|
|
103
|
+
expect(companion).toContain('provider: dbt');
|
|
104
|
+
expect(companion).toContain('lineage:');
|
|
105
|
+
expect(companion).toContain('analytics.orders');
|
|
106
|
+
expect(companion).toContain('semanticMetrics:');
|
|
107
|
+
expect(companion).toContain(' - total_revenue');
|
|
108
|
+
expect(companion).toContain('semanticDimensions:');
|
|
109
|
+
expect(companion).toContain(' - sales_channel');
|
|
110
|
+
expect(companion).toContain(' - order_date');
|
|
111
|
+
});
|
|
112
|
+
});
|
|
113
|
+
describe('validateBlockStudioSource', () => {
|
|
114
|
+
const semanticLayer = new SemanticLayer({
|
|
115
|
+
metrics: [
|
|
116
|
+
{
|
|
117
|
+
name: 'total_revenue',
|
|
118
|
+
label: 'Total Revenue',
|
|
119
|
+
description: 'Revenue metric',
|
|
120
|
+
domain: 'finance',
|
|
121
|
+
sql: 'SUM(revenue)',
|
|
122
|
+
type: 'sum',
|
|
123
|
+
table: 'orders',
|
|
124
|
+
tags: [],
|
|
125
|
+
},
|
|
126
|
+
],
|
|
127
|
+
dimensions: [
|
|
128
|
+
{
|
|
129
|
+
name: 'customer_type',
|
|
130
|
+
label: 'Customer Type',
|
|
131
|
+
description: 'Customer type dimension',
|
|
132
|
+
domain: 'finance',
|
|
133
|
+
sql: 'customer_type',
|
|
134
|
+
type: 'string',
|
|
135
|
+
table: 'orders',
|
|
136
|
+
tags: [],
|
|
137
|
+
},
|
|
138
|
+
],
|
|
139
|
+
hierarchies: [],
|
|
140
|
+
});
|
|
141
|
+
it('composes executable SQL for semantic blocks with metric and dimensions', () => {
|
|
142
|
+
const source = `block "Revenue by Type" {
|
|
143
|
+
domain = "finance"
|
|
144
|
+
type = "semantic"
|
|
145
|
+
description = ""
|
|
146
|
+
owner = ""
|
|
147
|
+
tags = []
|
|
148
|
+
metric = "total_revenue"
|
|
149
|
+
dimensions = ["customer_type"]
|
|
150
|
+
}`;
|
|
151
|
+
const validation = validateBlockStudioSource(source, semanticLayer);
|
|
152
|
+
expect(validation.valid).toBe(true);
|
|
153
|
+
expect(validation.executableSql).toContain('SUM(revenue) AS total_revenue');
|
|
154
|
+
expect(validation.executableSql).toContain('customer_type AS customer_type');
|
|
155
|
+
expect(validation.executableSql).toContain('GROUP BY customer_type');
|
|
156
|
+
});
|
|
157
|
+
it('returns an actionable diagnostic when a semantic block is missing a metric', () => {
|
|
158
|
+
const source = `block "Revenue by Type" {
|
|
159
|
+
domain = "finance"
|
|
160
|
+
type = "semantic"
|
|
161
|
+
description = ""
|
|
162
|
+
owner = ""
|
|
163
|
+
tags = []
|
|
164
|
+
dimensions = ["customer_type"]
|
|
165
|
+
}`;
|
|
166
|
+
const validation = validateBlockStudioSource(source, semanticLayer);
|
|
167
|
+
expect(validation.valid).toBe(false);
|
|
168
|
+
expect(validation.executableSql).toBeNull();
|
|
169
|
+
expect(validation.diagnostics.some((item) => item.code === 'semantic_metric_missing')).toBe(true);
|
|
170
|
+
});
|
|
171
|
+
it('returns a semantic validation error for unknown dimensions', () => {
|
|
172
|
+
const source = `block "Revenue by Type" {
|
|
173
|
+
domain = "finance"
|
|
174
|
+
type = "semantic"
|
|
175
|
+
description = ""
|
|
176
|
+
owner = ""
|
|
177
|
+
tags = []
|
|
178
|
+
metric = "total_revenue"
|
|
179
|
+
dimensions = ["missing_dimension"]
|
|
180
|
+
}`;
|
|
181
|
+
const validation = validateBlockStudioSource(source, semanticLayer);
|
|
182
|
+
expect(validation.valid).toBe(false);
|
|
183
|
+
expect(validation.diagnostics.some((item) => item.code === 'semantic_ref' && item.message.includes('missing_dimension'))).toBe(true);
|
|
184
|
+
});
|
|
185
|
+
it('keeps custom block validation behavior unchanged', () => {
|
|
186
|
+
const source = `block "Custom Revenue" {
|
|
187
|
+
domain = "finance"
|
|
188
|
+
type = "custom"
|
|
189
|
+
description = ""
|
|
190
|
+
owner = ""
|
|
191
|
+
tags = []
|
|
192
|
+
|
|
193
|
+
query = """
|
|
194
|
+
SELECT revenue
|
|
195
|
+
FROM orders
|
|
196
|
+
"""
|
|
197
|
+
}`;
|
|
198
|
+
const validation = validateBlockStudioSource(source, semanticLayer);
|
|
199
|
+
expect(validation.valid).toBe(true);
|
|
200
|
+
expect(validation.executableSql).toContain('SELECT revenue');
|
|
201
|
+
});
|
|
202
|
+
it('resolves semantic refs inside custom block SQL before execution', () => {
|
|
203
|
+
const source = `block "Revenue Query" {
|
|
204
|
+
domain = "finance"
|
|
205
|
+
type = "custom"
|
|
206
|
+
description = ""
|
|
207
|
+
owner = ""
|
|
208
|
+
tags = []
|
|
209
|
+
|
|
210
|
+
query = """
|
|
211
|
+
SELECT
|
|
212
|
+
@metric(total_revenue),
|
|
213
|
+
@dim(customer_type)
|
|
214
|
+
FROM orders
|
|
215
|
+
GROUP BY @dim(customer_type)
|
|
216
|
+
"""
|
|
217
|
+
}`;
|
|
218
|
+
const validation = validateBlockStudioSource(source, semanticLayer);
|
|
219
|
+
expect(validation.valid).toBe(true);
|
|
220
|
+
expect(validation.executableSql).toContain('SUM(revenue) AS total_revenue');
|
|
221
|
+
expect(validation.executableSql).toContain('customer_type AS customer_type');
|
|
222
|
+
expect(validation.executableSql).toContain('GROUP BY customer_type');
|
|
223
|
+
});
|
|
224
|
+
it('returns a semantic validation error for unresolved refs in custom SQL', () => {
|
|
225
|
+
const source = `block "Broken Revenue Query" {
|
|
226
|
+
domain = "finance"
|
|
227
|
+
type = "custom"
|
|
228
|
+
description = ""
|
|
229
|
+
owner = ""
|
|
230
|
+
tags = []
|
|
231
|
+
|
|
232
|
+
query = """
|
|
233
|
+
SELECT @metric(missing_metric)
|
|
234
|
+
"""
|
|
235
|
+
}`;
|
|
236
|
+
const validation = validateBlockStudioSource(source, semanticLayer);
|
|
237
|
+
expect(validation.valid).toBe(false);
|
|
238
|
+
expect(validation.diagnostics.some((item) => item.code === 'semantic_ref' && item.message.includes('missing_metric'))).toBe(true);
|
|
239
|
+
});
|
|
240
|
+
});
|
|
241
|
+
//# sourceMappingURL=local-runtime.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"local-runtime.test.js","sourceRoot":"","sources":["../src/local-runtime.test.ts"],"names":[],"mappings":"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"}
|
package/metricflow.d.ts
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
export declare class MetricFlowUnavailableError extends Error {
|
|
2
|
+
constructor(message: string);
|
|
3
|
+
}
|
|
4
|
+
export interface MetricFlowQueryRequest {
|
|
5
|
+
projectRoot: string;
|
|
6
|
+
dbtProjectPath?: string;
|
|
7
|
+
metrics: string[];
|
|
8
|
+
dimensions: string[];
|
|
9
|
+
timeDimension?: {
|
|
10
|
+
name: string;
|
|
11
|
+
granularity: string;
|
|
12
|
+
};
|
|
13
|
+
filters?: Array<{
|
|
14
|
+
dimension?: string;
|
|
15
|
+
operator?: string;
|
|
16
|
+
values?: string[];
|
|
17
|
+
expression?: string;
|
|
18
|
+
}>;
|
|
19
|
+
orderBy?: Array<{
|
|
20
|
+
name: string;
|
|
21
|
+
direction?: 'asc' | 'desc';
|
|
22
|
+
}>;
|
|
23
|
+
limit?: number;
|
|
24
|
+
savedQuery?: string;
|
|
25
|
+
}
|
|
26
|
+
export interface MetricFlowCompileResult {
|
|
27
|
+
sql: string;
|
|
28
|
+
command: string[];
|
|
29
|
+
stdout: string;
|
|
30
|
+
stderr: string;
|
|
31
|
+
}
|
|
32
|
+
export declare function resolveDbtProjectRoot(projectRoot: string, configuredPath?: string): string;
|
|
33
|
+
export declare function hasDbtSemanticManifest(projectRoot: string, configuredPath?: string): boolean;
|
|
34
|
+
export declare function compileMetricFlowQuery(request: MetricFlowQueryRequest): MetricFlowCompileResult;
|
|
35
|
+
//# sourceMappingURL=metricflow.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"metricflow.d.ts","sourceRoot":"","sources":["../src/metricflow.ts"],"names":[],"mappings":"AAIA,qBAAa,0BAA2B,SAAQ,KAAK;gBACvC,OAAO,EAAE,MAAM;CAI5B;AAED,MAAM,WAAW,sBAAsB;IACrC,WAAW,EAAE,MAAM,CAAC;IACpB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,aAAa,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAA;KAAE,CAAC;IACtD,OAAO,CAAC,EAAE,KAAK,CAAC;QAAE,SAAS,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;QAAC,UAAU,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACnG,OAAO,CAAC,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,SAAS,CAAC,EAAE,KAAK,GAAG,MAAM,CAAA;KAAE,CAAC,CAAC;IAC9D,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,uBAAuB;IACtC,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,wBAAgB,qBAAqB,CAAC,WAAW,EAAE,MAAM,EAAE,cAAc,CAAC,EAAE,MAAM,GAAG,MAAM,CAE1F;AAED,wBAAgB,sBAAsB,CAAC,WAAW,EAAE,MAAM,EAAE,cAAc,CAAC,EAAE,MAAM,GAAG,OAAO,CAE5F;AAED,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,sBAAsB,GAAG,uBAAuB,CA0C/F"}
|
package/metricflow.js
ADDED
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import { existsSync } from 'node:fs';
|
|
2
|
+
import { join, resolve } from 'node:path';
|
|
3
|
+
import { spawnSync } from 'node:child_process';
|
|
4
|
+
export class MetricFlowUnavailableError extends Error {
|
|
5
|
+
constructor(message) {
|
|
6
|
+
super(message);
|
|
7
|
+
this.name = 'MetricFlowUnavailableError';
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
export function resolveDbtProjectRoot(projectRoot, configuredPath) {
|
|
11
|
+
return configuredPath ? resolve(projectRoot, configuredPath) : projectRoot;
|
|
12
|
+
}
|
|
13
|
+
export function hasDbtSemanticManifest(projectRoot, configuredPath) {
|
|
14
|
+
return existsSync(join(resolveDbtProjectRoot(projectRoot, configuredPath), 'target', 'semantic_manifest.json'));
|
|
15
|
+
}
|
|
16
|
+
export function compileMetricFlowQuery(request) {
|
|
17
|
+
const dbtRoot = resolveDbtProjectRoot(request.projectRoot, request.dbtProjectPath);
|
|
18
|
+
if (!existsSync(join(dbtRoot, 'target', 'semantic_manifest.json'))) {
|
|
19
|
+
throw new MetricFlowUnavailableError('dbt semantic execution requires target/semantic_manifest.json. Run `dbt parse` or `dbt build` in the dbt project, then retry.');
|
|
20
|
+
}
|
|
21
|
+
const bin = process.env.DQL_METRICFLOW_BIN || process.env.METRICFLOW_BIN || 'mf';
|
|
22
|
+
const args = buildMetricFlowArgs(request);
|
|
23
|
+
const result = spawnSync(bin, args, {
|
|
24
|
+
cwd: dbtRoot,
|
|
25
|
+
encoding: 'utf-8',
|
|
26
|
+
env: process.env,
|
|
27
|
+
});
|
|
28
|
+
if (result.error) {
|
|
29
|
+
if (result.error.code === 'ENOENT') {
|
|
30
|
+
throw new MetricFlowUnavailableError('MetricFlow CLI was not found. Install dbt Semantic Layer dependencies so `mf` is on PATH, or set DQL_METRICFLOW_BIN to the MetricFlow executable.');
|
|
31
|
+
}
|
|
32
|
+
throw result.error;
|
|
33
|
+
}
|
|
34
|
+
const stdout = result.stdout ?? '';
|
|
35
|
+
const stderr = result.stderr ?? '';
|
|
36
|
+
if (result.status !== 0) {
|
|
37
|
+
throw new Error(`MetricFlow compile failed (${result.status}): ${stderr || stdout || 'no output'}`);
|
|
38
|
+
}
|
|
39
|
+
const sql = extractCompiledSql(stdout);
|
|
40
|
+
if (!sql) {
|
|
41
|
+
throw new Error('MetricFlow compile completed but no SQL statement was found in stdout.');
|
|
42
|
+
}
|
|
43
|
+
return {
|
|
44
|
+
sql,
|
|
45
|
+
command: [bin, ...args],
|
|
46
|
+
stdout,
|
|
47
|
+
stderr,
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
function buildMetricFlowArgs(request) {
|
|
51
|
+
const args = ['query', '--compile'];
|
|
52
|
+
if (request.savedQuery) {
|
|
53
|
+
args.push('--saved-query', request.savedQuery);
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
if (request.metrics.length === 0) {
|
|
57
|
+
throw new Error('MetricFlow semantic query requires at least one metric.');
|
|
58
|
+
}
|
|
59
|
+
args.push('--metrics', request.metrics.join(','));
|
|
60
|
+
const groupBy = [...request.dimensions];
|
|
61
|
+
if (request.timeDimension) {
|
|
62
|
+
groupBy.push(`${request.timeDimension.name}__${request.timeDimension.granularity}`);
|
|
63
|
+
}
|
|
64
|
+
if (groupBy.length > 0)
|
|
65
|
+
args.push('--group-by', groupBy.join(','));
|
|
66
|
+
}
|
|
67
|
+
for (const where of buildWhereClauses(request.filters ?? [])) {
|
|
68
|
+
args.push('--where', where);
|
|
69
|
+
}
|
|
70
|
+
for (const order of request.orderBy ?? []) {
|
|
71
|
+
if (!order.name)
|
|
72
|
+
continue;
|
|
73
|
+
args.push('--order', `${order.name} ${order.direction ?? 'asc'}`);
|
|
74
|
+
}
|
|
75
|
+
if (request.limit && Number.isFinite(request.limit)) {
|
|
76
|
+
args.push('--limit', String(request.limit));
|
|
77
|
+
}
|
|
78
|
+
return args;
|
|
79
|
+
}
|
|
80
|
+
function buildWhereClauses(filters) {
|
|
81
|
+
return filters.flatMap((filter) => {
|
|
82
|
+
if (filter.expression?.trim())
|
|
83
|
+
return [filter.expression.trim()];
|
|
84
|
+
if (!filter.dimension || !filter.operator)
|
|
85
|
+
return [];
|
|
86
|
+
const values = filter.values ?? [];
|
|
87
|
+
const quote = (value) => /^-?\d+(\.\d+)?$/.test(value.trim())
|
|
88
|
+
? value
|
|
89
|
+
: `'${value.replace(/'/g, "''")}'`;
|
|
90
|
+
const first = values[0] ?? '';
|
|
91
|
+
switch (filter.operator) {
|
|
92
|
+
case 'equals':
|
|
93
|
+
return values.length <= 1
|
|
94
|
+
? [`{{ Dimension('${filter.dimension}') }} = ${quote(first)}`]
|
|
95
|
+
: [`{{ Dimension('${filter.dimension}') }} IN (${values.map(quote).join(', ')})`];
|
|
96
|
+
case 'not_equals':
|
|
97
|
+
return [`{{ Dimension('${filter.dimension}') }} != ${quote(first)}`];
|
|
98
|
+
case 'in':
|
|
99
|
+
return values.length > 0 ? [`{{ Dimension('${filter.dimension}') }} IN (${values.map(quote).join(', ')})`] : [];
|
|
100
|
+
case 'not_in':
|
|
101
|
+
return values.length > 0 ? [`{{ Dimension('${filter.dimension}') }} NOT IN (${values.map(quote).join(', ')})`] : [];
|
|
102
|
+
case 'gt':
|
|
103
|
+
return [`{{ Dimension('${filter.dimension}') }} > ${quote(first)}`];
|
|
104
|
+
case 'gte':
|
|
105
|
+
return [`{{ Dimension('${filter.dimension}') }} >= ${quote(first)}`];
|
|
106
|
+
case 'lt':
|
|
107
|
+
return [`{{ Dimension('${filter.dimension}') }} < ${quote(first)}`];
|
|
108
|
+
case 'lte':
|
|
109
|
+
return [`{{ Dimension('${filter.dimension}') }} <= ${quote(first)}`];
|
|
110
|
+
default:
|
|
111
|
+
return [];
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
function extractCompiledSql(output) {
|
|
116
|
+
const normalized = output.trim();
|
|
117
|
+
const index = normalized.search(/\b(with|select)\b/i);
|
|
118
|
+
if (index < 0)
|
|
119
|
+
return '';
|
|
120
|
+
return normalized.slice(index).trim().replace(/;?\s*$/, '');
|
|
121
|
+
}
|
|
122
|
+
//# sourceMappingURL=metricflow.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"metricflow.js","sourceRoot":"","sources":["../src/metricflow.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"metricflow.test.d.ts","sourceRoot":"","sources":["../src/metricflow.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { afterEach, beforeEach, describe, expect, it } from 'vitest';
|
|
2
|
+
import { chmodSync, mkdirSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
import { tmpdir } from 'node:os';
|
|
5
|
+
import { compileMetricFlowQuery, MetricFlowUnavailableError } from './metricflow.js';
|
|
6
|
+
describe('MetricFlow compile wrapper', () => {
|
|
7
|
+
let tmpDir;
|
|
8
|
+
let previousBin;
|
|
9
|
+
beforeEach(() => {
|
|
10
|
+
tmpDir = mkdtempSync(join(tmpdir(), 'dql-mf-'));
|
|
11
|
+
previousBin = process.env.DQL_METRICFLOW_BIN;
|
|
12
|
+
});
|
|
13
|
+
afterEach(() => {
|
|
14
|
+
if (previousBin === undefined) {
|
|
15
|
+
delete process.env.DQL_METRICFLOW_BIN;
|
|
16
|
+
}
|
|
17
|
+
else {
|
|
18
|
+
process.env.DQL_METRICFLOW_BIN = previousBin;
|
|
19
|
+
}
|
|
20
|
+
rmSync(tmpDir, { recursive: true, force: true });
|
|
21
|
+
});
|
|
22
|
+
it('compiles through mf and extracts SQL from mixed stdout', () => {
|
|
23
|
+
mkdirSync(join(tmpDir, 'target'), { recursive: true });
|
|
24
|
+
writeFileSync(join(tmpDir, 'target', 'semantic_manifest.json'), '{}', 'utf-8');
|
|
25
|
+
const bin = join(tmpDir, 'mf');
|
|
26
|
+
writeFileSync(bin, [
|
|
27
|
+
'#!/bin/sh',
|
|
28
|
+
'printf "%s\\n" "$*" > args.txt',
|
|
29
|
+
'printf "%s\\n" "info: compiling"',
|
|
30
|
+
'printf "%s\\n" "SELECT metric_time, revenue FROM compiled_metric_sql"',
|
|
31
|
+
].join('\n'), 'utf-8');
|
|
32
|
+
chmodSync(bin, 0o755);
|
|
33
|
+
process.env.DQL_METRICFLOW_BIN = bin;
|
|
34
|
+
const result = compileMetricFlowQuery({
|
|
35
|
+
projectRoot: tmpDir,
|
|
36
|
+
metrics: ['revenue'],
|
|
37
|
+
dimensions: ['region'],
|
|
38
|
+
timeDimension: { name: 'metric_time', granularity: 'month' },
|
|
39
|
+
filters: [{ expression: "{{ Dimension('region') }} = 'NA'" }],
|
|
40
|
+
limit: 25,
|
|
41
|
+
});
|
|
42
|
+
expect(result.sql).toBe('SELECT metric_time, revenue FROM compiled_metric_sql');
|
|
43
|
+
expect(result.command).toContain('--compile');
|
|
44
|
+
expect(result.command).toContain('--metrics');
|
|
45
|
+
});
|
|
46
|
+
it('returns a setup error when semantic_manifest.json is missing', () => {
|
|
47
|
+
expect(() => compileMetricFlowQuery({
|
|
48
|
+
projectRoot: tmpDir,
|
|
49
|
+
metrics: ['revenue'],
|
|
50
|
+
dimensions: [],
|
|
51
|
+
})).toThrow(MetricFlowUnavailableError);
|
|
52
|
+
});
|
|
53
|
+
});
|
|
54
|
+
//# sourceMappingURL=metricflow.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"metricflow.test.js","sourceRoot":"","sources":["../src/metricflow.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"open-browser.d.ts","sourceRoot":"","sources":["../src/open-browser.ts"],"names":[],"mappings":"AAEA,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,EAAE,UAAU,EAAE,OAAO,GAAG,IAAI,CAevE"}
|
package/open-browser.js
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { spawn } from 'node:child_process';
|
|
2
|
+
export function maybeOpenBrowser(url, shouldOpen) {
|
|
3
|
+
if (!shouldOpen)
|
|
4
|
+
return;
|
|
5
|
+
const command = browserCommand();
|
|
6
|
+
if (!command)
|
|
7
|
+
return;
|
|
8
|
+
try {
|
|
9
|
+
const child = spawn(command.bin, [...command.args, url], {
|
|
10
|
+
detached: true,
|
|
11
|
+
stdio: 'ignore',
|
|
12
|
+
});
|
|
13
|
+
child.unref();
|
|
14
|
+
}
|
|
15
|
+
catch {
|
|
16
|
+
// best-effort only; keep CLI flow quiet if no browser opener exists
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
function browserCommand() {
|
|
20
|
+
switch (process.platform) {
|
|
21
|
+
case 'darwin':
|
|
22
|
+
return { bin: 'open', args: [] };
|
|
23
|
+
case 'win32':
|
|
24
|
+
return { bin: 'cmd', args: ['/c', 'start', ''] };
|
|
25
|
+
default:
|
|
26
|
+
return { bin: 'xdg-open', args: [] };
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=open-browser.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"open-browser.js","sourceRoot":"","sources":["../src/open-browser.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAE3C,MAAM,UAAU,gBAAgB,CAAC,GAAW,EAAE,UAAmB;IAC/D,IAAI,CAAC,UAAU;QAAE,OAAO;IAExB,MAAM,OAAO,GAAG,cAAc,EAAE,CAAC;IACjC,IAAI,CAAC,OAAO;QAAE,OAAO;IAErB,IAAI,CAAC;QACH,MAAM,KAAK,GAAG,KAAK,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,GAAG,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,EAAE;YACvD,QAAQ,EAAE,IAAI;YACd,KAAK,EAAE,QAAQ;SAChB,CAAC,CAAC;QACH,KAAK,CAAC,KAAK,EAAE,CAAC;IAChB,CAAC;IAAC,MAAM,CAAC;QACP,oEAAoE;IACtE,CAAC;AACH,CAAC;AAED,SAAS,cAAc;IACrB,QAAQ,OAAO,CAAC,QAAQ,EAAE,CAAC;QACzB,KAAK,QAAQ;YACX,OAAO,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC;QACnC,KAAK,OAAO;YACV,OAAO,EAAE,GAAG,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,IAAI,EAAE,OAAO,EAAE,EAAE,CAAC,EAAE,CAAC;QACnD;YACE,OAAO,EAAE,GAAG,EAAE,UAAU,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC;IACzC,CAAC;AACH,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@duckcodeailabs/dql-cli",
|
|
3
|
-
"version": "1.4.
|
|
3
|
+
"version": "1.4.4",
|
|
4
4
|
"description": "Public CLI for parsing, formatting, testing, and certifying DQL blocks",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"type": "module",
|
|
@@ -18,24 +18,21 @@
|
|
|
18
18
|
"publishConfig": {
|
|
19
19
|
"access": "public"
|
|
20
20
|
},
|
|
21
|
-
"files": [
|
|
22
|
-
"."
|
|
23
|
-
],
|
|
24
21
|
"repository": {
|
|
25
22
|
"type": "git",
|
|
26
23
|
"url": "https://github.com/duckcode-ai/dql.git",
|
|
27
24
|
"directory": "apps/cli"
|
|
28
25
|
},
|
|
29
26
|
"dependencies": {
|
|
30
|
-
"@duckcodeailabs/dql-agent": "^1.4.
|
|
31
|
-
"@duckcodeailabs/dql-slack": "^1.4.
|
|
32
|
-
"@duckcodeailabs/dql-compiler": "^1.4.
|
|
33
|
-
"@duckcodeailabs/dql-connectors": "^1.4.
|
|
34
|
-
"@duckcodeailabs/dql-core": "^1.4.
|
|
35
|
-
"@duckcodeailabs/dql-governance": "^1.4.
|
|
36
|
-
"@duckcodeailabs/dql-mcp": "^1.4.
|
|
37
|
-
"@duckcodeailabs/dql-notebook": "^1.4.
|
|
38
|
-
"@duckcodeailabs/dql-project": "^1.4.
|
|
27
|
+
"@duckcodeailabs/dql-agent": "^1.4.4",
|
|
28
|
+
"@duckcodeailabs/dql-slack": "^1.4.4",
|
|
29
|
+
"@duckcodeailabs/dql-compiler": "^1.4.4",
|
|
30
|
+
"@duckcodeailabs/dql-connectors": "^1.4.4",
|
|
31
|
+
"@duckcodeailabs/dql-core": "^1.4.4",
|
|
32
|
+
"@duckcodeailabs/dql-governance": "^1.4.4",
|
|
33
|
+
"@duckcodeailabs/dql-mcp": "^1.4.4",
|
|
34
|
+
"@duckcodeailabs/dql-notebook": "^1.4.4",
|
|
35
|
+
"@duckcodeailabs/dql-project": "^1.4.4",
|
|
39
36
|
"isomorphic-git": "^1.27.0",
|
|
40
37
|
"js-yaml": "^4.1.0",
|
|
41
38
|
"node-cron": "^3.0.3",
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { AlertIR } from '@duckcodeailabs/dql-compiler';
|
|
2
|
+
import type { QueryExecutor, ConnectionConfig } from '@duckcodeailabs/dql-connectors';
|
|
3
|
+
import type { AlertEvaluation } from './types.js';
|
|
4
|
+
export declare function evaluateAlerts(alerts: AlertIR[], executor: QueryExecutor, connection: ConnectionConfig): Promise<AlertEvaluation[]>;
|
|
5
|
+
//# sourceMappingURL=alerts.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"alerts.d.ts","sourceRoot":"","sources":["../../src/schedule/alerts.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,8BAA8B,CAAC;AAC5D,OAAO,KAAK,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,gCAAgC,CAAC;AACtF,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAElD,wBAAsB,cAAc,CAClC,MAAM,EAAE,OAAO,EAAE,EACjB,QAAQ,EAAE,aAAa,EACvB,UAAU,EAAE,gBAAgB,GAC3B,OAAO,CAAC,eAAe,EAAE,CAAC,CA+B5B"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
export async function evaluateAlerts(alerts, executor, connection) {
|
|
2
|
+
const results = [];
|
|
3
|
+
for (const alert of alerts) {
|
|
4
|
+
try {
|
|
5
|
+
const result = await executor.executeQuery(alert.conditionSQL, [], {}, connection);
|
|
6
|
+
const firstRow = result.rows[0] ?? {};
|
|
7
|
+
const firstCol = Object.values(firstRow)[0];
|
|
8
|
+
const observedValue = toNumber(firstCol);
|
|
9
|
+
if (observedValue === null) {
|
|
10
|
+
results.push({
|
|
11
|
+
alert,
|
|
12
|
+
breached: false,
|
|
13
|
+
reason: 'condition SQL did not return a numeric first column',
|
|
14
|
+
});
|
|
15
|
+
continue;
|
|
16
|
+
}
|
|
17
|
+
const breached = compareThreshold(observedValue, alert.operator ?? '>', alert.threshold ?? 0);
|
|
18
|
+
results.push({ alert, breached, observedValue });
|
|
19
|
+
}
|
|
20
|
+
catch (err) {
|
|
21
|
+
results.push({
|
|
22
|
+
alert,
|
|
23
|
+
breached: false,
|
|
24
|
+
error: err instanceof Error ? err.message : String(err),
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
return results;
|
|
29
|
+
}
|
|
30
|
+
function compareThreshold(value, op, threshold) {
|
|
31
|
+
switch (op) {
|
|
32
|
+
case '>': return value > threshold;
|
|
33
|
+
case '<': return value < threshold;
|
|
34
|
+
case '>=': return value >= threshold;
|
|
35
|
+
case '<=': return value <= threshold;
|
|
36
|
+
case '==': return value === threshold;
|
|
37
|
+
case '!=': return value !== threshold;
|
|
38
|
+
default: return value > threshold;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
function toNumber(v) {
|
|
42
|
+
if (typeof v === 'number' && Number.isFinite(v))
|
|
43
|
+
return v;
|
|
44
|
+
if (typeof v === 'bigint')
|
|
45
|
+
return Number(v);
|
|
46
|
+
if (typeof v === 'string') {
|
|
47
|
+
const n = Number(v);
|
|
48
|
+
return Number.isFinite(n) ? n : null;
|
|
49
|
+
}
|
|
50
|
+
if (typeof v === 'boolean')
|
|
51
|
+
return v ? 1 : 0;
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
//# sourceMappingURL=alerts.js.map
|