@duckcodeailabs/dql-cli 1.5.3 → 1.6.1

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.
Files changed (54) hide show
  1. package/README.md +9 -9
  2. package/dist/apps-api.js +11 -4
  3. package/dist/apps-api.js.map +1 -1
  4. package/dist/args.d.ts +6 -0
  5. package/dist/args.d.ts.map +1 -1
  6. package/dist/args.js +9 -0
  7. package/dist/args.js.map +1 -1
  8. package/dist/assets/dql-notebook/assets/index-C-s-OCLW.css +1 -0
  9. package/dist/assets/dql-notebook/assets/{index-B5jI3I8Q.js → index-DZ_5zsCw.js} +123 -123
  10. package/dist/assets/dql-notebook/index.html +2 -2
  11. package/dist/commands/certify.d.ts.map +1 -1
  12. package/dist/commands/certify.js +55 -5
  13. package/dist/commands/certify.js.map +1 -1
  14. package/dist/commands/compile.d.ts.map +1 -1
  15. package/dist/commands/compile.js +3 -0
  16. package/dist/commands/compile.js.map +1 -1
  17. package/dist/commands/doctor.d.ts.map +1 -1
  18. package/dist/commands/doctor.js +91 -4
  19. package/dist/commands/doctor.js.map +1 -1
  20. package/dist/commands/doctor.test.js +1 -0
  21. package/dist/commands/doctor.test.js.map +1 -1
  22. package/dist/commands/info.d.ts.map +1 -1
  23. package/dist/commands/info.js +21 -6
  24. package/dist/commands/info.js.map +1 -1
  25. package/dist/commands/migrate.d.ts.map +1 -1
  26. package/dist/commands/migrate.js +2 -1
  27. package/dist/commands/migrate.js.map +1 -1
  28. package/dist/commands/validate.d.ts.map +1 -1
  29. package/dist/commands/validate.js +6 -5
  30. package/dist/commands/validate.js.map +1 -1
  31. package/dist/commands/verify.d.ts +1 -1
  32. package/dist/commands/verify.d.ts.map +1 -1
  33. package/dist/commands/verify.js +36 -6
  34. package/dist/commands/verify.js.map +1 -1
  35. package/dist/index.js +72 -4
  36. package/dist/index.js.map +1 -1
  37. package/dist/local-runtime.d.ts +2 -0
  38. package/dist/local-runtime.d.ts.map +1 -1
  39. package/dist/local-runtime.js +51 -12
  40. package/dist/local-runtime.js.map +1 -1
  41. package/dist/promote-from-draft.d.ts +40 -0
  42. package/dist/promote-from-draft.d.ts.map +1 -0
  43. package/dist/promote-from-draft.js +164 -0
  44. package/dist/promote-from-draft.js.map +1 -0
  45. package/dist/promote-from-draft.test.d.ts +2 -0
  46. package/dist/promote-from-draft.test.d.ts.map +1 -0
  47. package/dist/promote-from-draft.test.js +149 -0
  48. package/dist/promote-from-draft.test.js.map +1 -0
  49. package/dist/template-adoption.test.d.ts +2 -0
  50. package/dist/template-adoption.test.d.ts.map +1 -0
  51. package/dist/template-adoption.test.js +102 -0
  52. package/dist/template-adoption.test.js.map +1 -0
  53. package/package.json +12 -12
  54. package/dist/assets/dql-notebook/assets/index-cv-O4BEj.css +0 -1
@@ -0,0 +1,40 @@
1
+ import type { CLIFlags } from './args.js';
2
+ export interface PromoteResult {
3
+ ok: boolean;
4
+ message: string;
5
+ /** The path the draft moved to (relative to projectRoot). */
6
+ certifiedPath?: string;
7
+ /** The patch the human still needs to apply to datalex-manifest.json. */
8
+ datalexManifestDiff?: string;
9
+ }
10
+ /**
11
+ * Promote a Tier-2 draft block from `blocks/_drafts/<slug>.dql` to the
12
+ * canonical `blocks/<domain>/<slug>.dql` location, flipping `status` to
13
+ * `certified` and setting the `datalex_contract` reference.
14
+ *
15
+ * Companion to the dql-mcp `query_via_metadata` tool. Together they close
16
+ * the graduated-trust promotion loop:
17
+ *
18
+ * ask -> no Tier-1 hit -> query_via_metadata writes a draft
19
+ * -> human reviews / refines
20
+ * -> dql certify --from-draft <path>
21
+ * -> next ask hits Tier-1 forever
22
+ *
23
+ * Behavior:
24
+ * 1. Validates the draft exists, has status=draft, and has a sane
25
+ * `_proposed { proposed_contract_id = ... }` block.
26
+ * 2. Moves the file to blocks/<domain>/<slug>.dql.
27
+ * 3. Flips `status = "draft"` to `status = "certified"`.
28
+ * 4. Sets `datalex_contract = "<contract>"` (provided via --contract or
29
+ * taken from `_proposed.proposed_contract_id` with `@1` appended).
30
+ * 5. Sets `owner = "<owner>"` from --owner if provided.
31
+ * 6. Drops the `_proposed { ... }` provenance block (its job is done).
32
+ * 7. Computes the patch the human still needs to apply to
33
+ * datalex-manifest.json (or surfaces it for --open-pr).
34
+ *
35
+ * Does NOT auto-modify datalex-manifest.json. That file is hand-curated
36
+ * until the DataLex compiler ships its v1 emitter; this command shows
37
+ * the user the exact diff to apply.
38
+ */
39
+ export declare function promoteFromDraft(projectRoot: string, flags: CLIFlags): PromoteResult;
40
+ //# sourceMappingURL=promote-from-draft.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"promote-from-draft.d.ts","sourceRoot":"","sources":["../src/promote-from-draft.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAE1C,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,OAAO,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC;IAChB,6DAA6D;IAC7D,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,yEAAyE;IACzE,mBAAmB,CAAC,EAAE,MAAM,CAAC;CAC9B;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,wBAAgB,gBAAgB,CAC9B,WAAW,EAAE,MAAM,EACnB,KAAK,EAAE,QAAQ,GACd,aAAa,CAyEf"}
@@ -0,0 +1,164 @@
1
+ import { existsSync, mkdirSync, readFileSync, unlinkSync, writeFileSync } from 'node:fs';
2
+ import { dirname, join, resolve } from 'node:path';
3
+ /**
4
+ * Promote a Tier-2 draft block from `blocks/_drafts/<slug>.dql` to the
5
+ * canonical `blocks/<domain>/<slug>.dql` location, flipping `status` to
6
+ * `certified` and setting the `datalex_contract` reference.
7
+ *
8
+ * Companion to the dql-mcp `query_via_metadata` tool. Together they close
9
+ * the graduated-trust promotion loop:
10
+ *
11
+ * ask -> no Tier-1 hit -> query_via_metadata writes a draft
12
+ * -> human reviews / refines
13
+ * -> dql certify --from-draft <path>
14
+ * -> next ask hits Tier-1 forever
15
+ *
16
+ * Behavior:
17
+ * 1. Validates the draft exists, has status=draft, and has a sane
18
+ * `_proposed { proposed_contract_id = ... }` block.
19
+ * 2. Moves the file to blocks/<domain>/<slug>.dql.
20
+ * 3. Flips `status = "draft"` to `status = "certified"`.
21
+ * 4. Sets `datalex_contract = "<contract>"` (provided via --contract or
22
+ * taken from `_proposed.proposed_contract_id` with `@1` appended).
23
+ * 5. Sets `owner = "<owner>"` from --owner if provided.
24
+ * 6. Drops the `_proposed { ... }` provenance block (its job is done).
25
+ * 7. Computes the patch the human still needs to apply to
26
+ * datalex-manifest.json (or surfaces it for --open-pr).
27
+ *
28
+ * Does NOT auto-modify datalex-manifest.json. That file is hand-curated
29
+ * until the DataLex compiler ships its v1 emitter; this command shows
30
+ * the user the exact diff to apply.
31
+ */
32
+ export function promoteFromDraft(projectRoot, flags) {
33
+ const draftPathInput = flags.fromDraft;
34
+ const draftAbs = resolve(projectRoot, draftPathInput);
35
+ if (!existsSync(draftAbs)) {
36
+ return { ok: false, message: `Draft not found at ${draftPathInput}.` };
37
+ }
38
+ const content = readFileSync(draftAbs, 'utf-8');
39
+ if (!/status\s*=\s*"draft"/.test(content)) {
40
+ return {
41
+ ok: false,
42
+ message: `${draftPathInput} does not have status="draft". Use dql certify <path> (without --from-draft) to evaluate governance rules.`,
43
+ };
44
+ }
45
+ const proposedContractId = pickField(content, 'proposed_contract_id') ?? '';
46
+ const contract = flags.contract || (proposedContractId ? `${proposedContractId}@1` : '');
47
+ if (!contract) {
48
+ return {
49
+ ok: false,
50
+ message: 'No --contract <id@version> provided and no proposed_contract_id in the draft. ' +
51
+ 'Either pass --contract commerce.Customer.foo@1 or restore the _proposed block.',
52
+ };
53
+ }
54
+ const domain = flags.domain || pickField(content, 'proposed_domain') || guessDomain(contract);
55
+ if (!domain) {
56
+ return {
57
+ ok: false,
58
+ message: 'No --domain provided and could not infer one from the contract id.',
59
+ };
60
+ }
61
+ const slug = inferSlugFromPath(draftAbs);
62
+ const destAbs = join(projectRoot, 'blocks', domain, `${slug}.dql`);
63
+ if (existsSync(destAbs) && !flags.force) {
64
+ return {
65
+ ok: false,
66
+ message: `${shortPath(projectRoot, destAbs)} already exists. ` +
67
+ 'Pass --force to overwrite, or rename the draft.',
68
+ };
69
+ }
70
+ const promoted = renderPromoted(content, {
71
+ contract,
72
+ domain,
73
+ owner: flags.owner || pickField(content, 'owner') || '',
74
+ });
75
+ mkdirSync(dirname(destAbs), { recursive: true });
76
+ writeFileSync(destAbs, promoted);
77
+ // The draft and the certified copy must not coexist. Drop the draft now
78
+ // that the certified file is on disk.
79
+ unlinkSync(draftAbs);
80
+ const datalexManifestDiff = renderDataLexManifestDiff({
81
+ contract,
82
+ domain,
83
+ slug,
84
+ description: pickField(content, 'description') ?? '',
85
+ });
86
+ return {
87
+ ok: true,
88
+ message: `Promoted ${shortPath(projectRoot, draftAbs)} -> ${shortPath(projectRoot, destAbs)}.`,
89
+ certifiedPath: shortPath(projectRoot, destAbs),
90
+ datalexManifestDiff,
91
+ };
92
+ }
93
+ // -- helpers ---------------------------------------------------------------
94
+ function pickField(content, key) {
95
+ const m = content.match(new RegExp(`${key}\\s*=\\s*"([^"]*)"`)) ||
96
+ content.match(new RegExp(`${key}\\s*=\\s*"""([\\s\\S]*?)"""`));
97
+ return m?.[1]?.trim();
98
+ }
99
+ function guessDomain(contractId) {
100
+ const head = contractId.split('.')[0];
101
+ return head && head !== contractId ? head : null;
102
+ }
103
+ function inferSlugFromPath(absPath) {
104
+ return absPath.replace(/.*\//, '').replace(/\.dql$/, '');
105
+ }
106
+ function shortPath(projectRoot, abs) {
107
+ const root = projectRoot.endsWith('/') ? projectRoot : projectRoot + '/';
108
+ return abs.startsWith(root) ? abs.slice(root.length) : abs;
109
+ }
110
+ function renderPromoted(source, fields) {
111
+ let out = source;
112
+ // Flip status.
113
+ out = out.replace(/status\s*=\s*"draft"/, 'status = "certified"');
114
+ // Set domain (overwrite the existing one).
115
+ out = out.replace(/(domain\s*=\s*)"[^"]*"/, `$1"${fields.domain}"`);
116
+ // Set datalex_contract. The draft template ships an empty value
117
+ // (`datalex_contract = ""`); replace with the provided id@version.
118
+ out = out.replace(/datalex_contract\s*=\s*"[^"]*"/, `datalex_contract = "${fields.contract}"`);
119
+ // Set owner if provided + the field exists in the file.
120
+ if (fields.owner) {
121
+ if (/owner\s*=\s*"[^"]*"/.test(out)) {
122
+ out = out.replace(/owner\s*=\s*"[^"]*"/, `owner = "${fields.owner}"`);
123
+ }
124
+ else {
125
+ // Insert owner right after the status line.
126
+ out = out.replace(/(status\s*=\s*"certified"\s*\n)/, `$1 owner = "${fields.owner}"\n`);
127
+ }
128
+ }
129
+ // Drop the _proposed { ... } provenance block. Its job is done; the
130
+ // certified block is the new source of truth and should not carry the
131
+ // capture metadata.
132
+ out = out.replace(/\n\s*_proposed\s*\{[^}]*\}\s*\n/, '\n');
133
+ return out;
134
+ }
135
+ function renderDataLexManifestDiff(fields) {
136
+ const [contractId, versionStr] = fields.contract.split('@');
137
+ const version = Number.parseInt(versionStr ?? '1', 10) || 1;
138
+ const parts = contractId.split('.');
139
+ const datalexDomain = parts[0] ?? fields.domain;
140
+ const entity = parts[1] ?? 'Unknown';
141
+ const contractName = parts[2] ?? fields.slug;
142
+ const safeDesc = fields.description.replace(/"/g, '\\"').replace(/\n/g, ' ');
143
+ return `Add the following entry under domains[name=${datalexDomain}].entities[name=${entity}].contracts in datalex-manifest.json:
144
+
145
+ {
146
+ "id": "${contractId}",
147
+ "name": "${contractName}",
148
+ "version": ${version},
149
+ "description": "${safeDesc}",
150
+ "signature": {
151
+ "inputs": [],
152
+ "outputs": []
153
+ },
154
+ "owner": "<owner>",
155
+ "tags": []
156
+ }
157
+
158
+ Then re-run \`dql compile\` to refresh dql-manifest.json. The DataLex compiler
159
+ will emit this entry automatically once the v1 manifest emitter ships;
160
+ until then, hand-edit \`datalex-manifest.json\` (or its source YAML) so the
161
+ DQL block's datalex_contract reference resolves.
162
+ `;
163
+ }
164
+ //# sourceMappingURL=promote-from-draft.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"promote-from-draft.js","sourceRoot":"","sources":["../src/promote-from-draft.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=promote-from-draft.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"promote-from-draft.test.d.ts","sourceRoot":"","sources":["../src/promote-from-draft.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,149 @@
1
+ import { afterEach, beforeEach, describe, expect, it } from 'vitest';
2
+ import { existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
3
+ import { tmpdir } from 'node:os';
4
+ import { join } from 'node:path';
5
+ import { promoteFromDraft } from './promote-from-draft.js';
6
+ function baseFlags(overrides = {}) {
7
+ return {
8
+ format: 'text',
9
+ verbose: false,
10
+ help: false,
11
+ version: false,
12
+ check: false,
13
+ open: null,
14
+ input: '',
15
+ outDir: '',
16
+ port: null,
17
+ host: null,
18
+ chart: '',
19
+ domain: '',
20
+ owner: '',
21
+ queryOnly: false,
22
+ template: '',
23
+ connection: '',
24
+ skipTests: false,
25
+ force: false,
26
+ http: false,
27
+ ...overrides,
28
+ };
29
+ }
30
+ function writeDraft(root, slug, body) {
31
+ const draftDir = join(root, 'blocks', '_drafts');
32
+ mkdirSync(draftDir, { recursive: true });
33
+ const domain = body?.domain ?? 'customer';
34
+ const id = body?.proposedContractId ?? 'commerce.Customer.monthly_active_customers';
35
+ const status = body?.status ?? 'draft';
36
+ writeFileSync(join(draftDir, `${slug}.dql`), `block "${slug}" {
37
+ domain = "${domain}"
38
+ type = "custom"
39
+ status = "${status}"
40
+ description = """How many active customers each month?"""
41
+ datalex_contract = ""
42
+
43
+ _proposed {
44
+ asked_times = 3
45
+ first_asked = "2026-04-01T00:00:00Z"
46
+ last_asked = "2026-05-01T12:00:00Z"
47
+ proposed_contract_id = "${id}"
48
+ proposed_domain = "${domain}"
49
+ proposed_entity = "Customer"
50
+ }
51
+
52
+ query = """SELECT 1"""
53
+ }
54
+ `);
55
+ }
56
+ describe('promoteFromDraft (dql certify --from-draft)', () => {
57
+ let tmp;
58
+ beforeEach(() => {
59
+ tmp = mkdtempSync(join(tmpdir(), 'promote-'));
60
+ });
61
+ afterEach(() => {
62
+ rmSync(tmp, { recursive: true, force: true });
63
+ });
64
+ it('returns ok=false when the draft path does not exist', () => {
65
+ const out = promoteFromDraft(tmp, baseFlags({ fromDraft: 'blocks/_drafts/nope.dql' }));
66
+ expect(out.ok).toBe(false);
67
+ expect(out.message).toMatch(/not found/i);
68
+ });
69
+ it('refuses to promote a file whose status is not draft', () => {
70
+ writeDraft(tmp, 'mau', { status: 'certified' });
71
+ const out = promoteFromDraft(tmp, baseFlags({ fromDraft: 'blocks/_drafts/mau.dql' }));
72
+ expect(out.ok).toBe(false);
73
+ expect(out.message).toMatch(/status="draft"/);
74
+ });
75
+ it('promotes a draft to blocks/<domain>/<slug>.dql, flips status, sets contract, drops _proposed', () => {
76
+ writeDraft(tmp, 'mau');
77
+ const out = promoteFromDraft(tmp, baseFlags({
78
+ fromDraft: 'blocks/_drafts/mau.dql',
79
+ domain: 'customer',
80
+ contract: 'commerce.Customer.monthly_active_customers@1',
81
+ owner: 'growth@example.com',
82
+ }));
83
+ expect(out.ok).toBe(true);
84
+ expect(out.certifiedPath).toBe('blocks/customer/mau.dql');
85
+ const certified = readFileSync(join(tmp, 'blocks/customer/mau.dql'), 'utf-8');
86
+ expect(certified).toContain('status = "certified"');
87
+ expect(certified).toContain('datalex_contract = "commerce.Customer.monthly_active_customers@1"');
88
+ expect(certified).toContain('owner = "growth@example.com"');
89
+ expect(certified).not.toContain('_proposed');
90
+ expect(certified).not.toContain('asked_times');
91
+ expect(existsSync(join(tmp, 'blocks/_drafts/mau.dql'))).toBe(false);
92
+ });
93
+ it('falls back to the proposed contract id with @1 when --contract is omitted', () => {
94
+ writeDraft(tmp, 'mau', {
95
+ proposedContractId: 'commerce.Customer.monthly_active_customers',
96
+ });
97
+ const out = promoteFromDraft(tmp, baseFlags({ fromDraft: 'blocks/_drafts/mau.dql', domain: 'customer' }));
98
+ expect(out.ok).toBe(true);
99
+ const certified = readFileSync(join(tmp, 'blocks/customer/mau.dql'), 'utf-8');
100
+ expect(certified).toContain('datalex_contract = "commerce.Customer.monthly_active_customers@1"');
101
+ });
102
+ it('errors when no --contract and no proposed_contract_id available', () => {
103
+ writeDraft(tmp, 'mau', { proposedContractId: '' });
104
+ const out = promoteFromDraft(tmp, baseFlags({ fromDraft: 'blocks/_drafts/mau.dql', domain: 'customer' }));
105
+ expect(out.ok).toBe(false);
106
+ expect(out.message).toMatch(/contract/i);
107
+ });
108
+ it('refuses to overwrite an existing certified file unless --force', () => {
109
+ writeDraft(tmp, 'mau');
110
+ mkdirSync(join(tmp, 'blocks', 'customer'), { recursive: true });
111
+ writeFileSync(join(tmp, 'blocks', 'customer', 'mau.dql'), 'already here');
112
+ const out = promoteFromDraft(tmp, baseFlags({
113
+ fromDraft: 'blocks/_drafts/mau.dql',
114
+ domain: 'customer',
115
+ contract: 'commerce.Customer.monthly_active_customers@1',
116
+ }));
117
+ expect(out.ok).toBe(false);
118
+ expect(out.message).toMatch(/already exists/);
119
+ });
120
+ it('overwrites with --force', () => {
121
+ writeDraft(tmp, 'mau');
122
+ mkdirSync(join(tmp, 'blocks', 'customer'), { recursive: true });
123
+ writeFileSync(join(tmp, 'blocks', 'customer', 'mau.dql'), 'old content');
124
+ const out = promoteFromDraft(tmp, baseFlags({
125
+ fromDraft: 'blocks/_drafts/mau.dql',
126
+ domain: 'customer',
127
+ contract: 'commerce.Customer.monthly_active_customers@1',
128
+ force: true,
129
+ }));
130
+ expect(out.ok).toBe(true);
131
+ const content = readFileSync(join(tmp, 'blocks/customer/mau.dql'), 'utf-8');
132
+ expect(content).not.toBe('old content');
133
+ expect(content).toContain('status = "certified"');
134
+ });
135
+ it('surfaces a datalex-manifest.json patch with the contract entry to add', () => {
136
+ writeDraft(tmp, 'mau');
137
+ const out = promoteFromDraft(tmp, baseFlags({
138
+ fromDraft: 'blocks/_drafts/mau.dql',
139
+ domain: 'customer',
140
+ contract: 'commerce.Customer.monthly_active_customers@1',
141
+ }));
142
+ expect(out.datalexManifestDiff).toBeDefined();
143
+ expect(out.datalexManifestDiff).toContain('commerce.Customer.monthly_active_customers');
144
+ expect(out.datalexManifestDiff).toContain('"version": 1');
145
+ expect(out.datalexManifestDiff).toContain('domains[name=commerce]');
146
+ expect(out.datalexManifestDiff).toContain('entities[name=Customer]');
147
+ });
148
+ });
149
+ //# sourceMappingURL=promote-from-draft.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"promote-from-draft.test.js","sourceRoot":"","sources":["../src/promote-from-draft.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=template-adoption.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"template-adoption.test.d.ts","sourceRoot":"","sources":["../src/template-adoption.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,102 @@
1
+ import { cpSync, mkdtempSync, readdirSync, readFileSync, rmSync, statSync } from 'node:fs';
2
+ import { tmpdir } from 'node:os';
3
+ import { join, resolve } from 'node:path';
4
+ import { describe, expect, it } from 'vitest';
5
+ import { analyze, buildManifest, parse } from '@duckcodeailabs/dql-core';
6
+ import { Certifier } from '@duckcodeailabs/dql-governance';
7
+ const repoRoot = resolve(process.cwd(), '../..');
8
+ const templatesRoot = join(repoRoot, 'packages/create-dql-app/templates');
9
+ const fixturesRoot = resolve(process.cwd(), 'test/fixtures');
10
+ const lineageFixture = join(fixturesRoot, 'lineage-app');
11
+ function collectFiles(dir, predicate, out = []) {
12
+ for (const entry of readdirSync(dir)) {
13
+ const abs = join(dir, entry);
14
+ const stat = statSync(abs);
15
+ if (stat.isDirectory())
16
+ collectFiles(abs, predicate, out);
17
+ else if (predicate(abs))
18
+ out.push(abs);
19
+ }
20
+ return out;
21
+ }
22
+ function copyProject(src, name) {
23
+ const base = mkdtempSync(join(tmpdir(), `dql-${name}-`));
24
+ const root = join(base, name);
25
+ cpSync(src, root, { recursive: true });
26
+ return { root, cleanup: () => rmSync(base, { recursive: true, force: true }) };
27
+ }
28
+ describe('OSS adoption templates and fixtures', () => {
29
+ it('all template and fixture .dql files parse and analyze with canonical block syntax', () => {
30
+ const dqlFiles = [
31
+ ...collectFiles(templatesRoot, (path) => path.endsWith('.dql')),
32
+ ...collectFiles(fixturesRoot, (path) => path.endsWith('.dql')),
33
+ ];
34
+ expect(dqlFiles.length).toBeGreaterThan(0);
35
+ for (const filePath of dqlFiles) {
36
+ const source = readFileSync(filePath, 'utf-8');
37
+ expect(source, `${filePath} should not use block-level lifecycle`).not.toMatch(/\blifecycle\s*=/);
38
+ const ast = parse(source, filePath);
39
+ const diagnostics = analyze(ast);
40
+ const errors = diagnostics.filter((diagnostic) => diagnostic.severity === 'error');
41
+ expect(errors, `${filePath} should parse/analyze cleanly`).toEqual([]);
42
+ }
43
+ });
44
+ it('fixture project compiles source-to-block-to-dashboard-to-App lineage', () => {
45
+ const { root, cleanup } = copyProject(lineageFixture, 'lineage-app');
46
+ try {
47
+ const manifest = buildManifest({ projectRoot: root, dqlVersion: '1.6.1' });
48
+ const nodeTypes = new Set(manifest.lineage.nodes.map((node) => node.type));
49
+ expect(Object.keys(manifest.blocks).length).toBeGreaterThan(0);
50
+ expect(Object.keys(manifest.apps ?? {}).length).toBeGreaterThan(0);
51
+ expect(Object.keys(manifest.dashboards ?? {}).length).toBeGreaterThan(0);
52
+ expect(nodeTypes.has('source_table')).toBe(true);
53
+ expect(nodeTypes.has('block')).toBe(true);
54
+ expect(nodeTypes.has('dashboard')).toBe(true);
55
+ expect(nodeTypes.has('app')).toBe(true);
56
+ expect(manifest.lineage.edges.some((edge) => edge.type === 'reads_from' && edge.target.startsWith('block:'))).toBe(true);
57
+ expect(manifest.lineage.edges.some((edge) => edge.type === 'contains' && edge.source.startsWith('block:') && edge.target.startsWith('dashboard:'))).toBe(true);
58
+ expect(manifest.lineage.edges.some((edge) => edge.type === 'contains' && edge.source.startsWith('dashboard:') && edge.target.startsWith('app:'))).toBe(true);
59
+ }
60
+ finally {
61
+ cleanup();
62
+ }
63
+ });
64
+ it('fixture card approval block is certifiable with passing local test results', () => {
65
+ const filePath = join(lineageFixture, 'blocks/card_approval_rate.dql');
66
+ const source = readFileSync(filePath, 'utf-8');
67
+ const ast = parse(source, filePath);
68
+ const block = ast.statements.find((statement) => statement.kind === 'BlockDecl');
69
+ expect(block).toBeTruthy();
70
+ const record = {
71
+ id: 'block:card_approval_rate',
72
+ name: block.name,
73
+ domain: block.domain,
74
+ type: block.blockType,
75
+ version: '1.0.0',
76
+ status: block.status,
77
+ gitRepo: '',
78
+ gitPath: 'blocks/card_approval_rate.dql',
79
+ gitCommitSha: '',
80
+ description: block.description,
81
+ owner: block.owner,
82
+ tags: block.tags ?? [],
83
+ dependencies: [],
84
+ usedInCount: 1,
85
+ llmContext: block.llmContext,
86
+ createdAt: new Date(),
87
+ updatedAt: new Date(),
88
+ };
89
+ const testResults = {
90
+ passed: 1,
91
+ failed: 0,
92
+ skipped: 0,
93
+ duration: 1,
94
+ assertions: [{ name: 'assert row_count == 1', passed: true, actual: 1 }],
95
+ runAt: new Date(),
96
+ };
97
+ const result = new Certifier().evaluate(record, testResults);
98
+ expect(result.certified).toBe(true);
99
+ expect(result.errors).toEqual([]);
100
+ });
101
+ });
102
+ //# sourceMappingURL=template-adoption.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"template-adoption.test.js","sourceRoot":"","sources":["../src/template-adoption.test.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@duckcodeailabs/dql-cli",
3
- "version": "1.5.3",
3
+ "version": "1.6.1",
4
4
  "description": "Public CLI for parsing, formatting, testing, and certifying DQL blocks",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",
@@ -19,15 +19,15 @@
19
19
  "directory": "apps/cli"
20
20
  },
21
21
  "dependencies": {
22
- "@duckcodeailabs/dql-agent": "^1.5.3",
23
- "@duckcodeailabs/dql-slack": "^1.5.3",
24
- "@duckcodeailabs/dql-compiler": "^1.5.3",
25
- "@duckcodeailabs/dql-connectors": "^1.5.3",
26
- "@duckcodeailabs/dql-core": "^1.5.3",
27
- "@duckcodeailabs/dql-governance": "^1.5.3",
28
- "@duckcodeailabs/dql-mcp": "^1.5.3",
29
- "@duckcodeailabs/dql-notebook": "^1.5.3",
30
- "@duckcodeailabs/dql-project": "^1.5.3",
22
+ "@duckcodeailabs/dql-agent": "^1.6.1",
23
+ "@duckcodeailabs/dql-slack": "^1.6.1",
24
+ "@duckcodeailabs/dql-compiler": "^1.6.1",
25
+ "@duckcodeailabs/dql-connectors": "^1.6.1",
26
+ "@duckcodeailabs/dql-core": "^1.6.1",
27
+ "@duckcodeailabs/dql-governance": "^1.6.1",
28
+ "@duckcodeailabs/dql-mcp": "^1.6.1",
29
+ "@duckcodeailabs/dql-notebook": "^1.6.1",
30
+ "@duckcodeailabs/dql-project": "^1.6.1",
31
31
  "isomorphic-git": "^1.27.0",
32
32
  "js-yaml": "^4.1.0",
33
33
  "node-cron": "^3.0.3",
@@ -40,10 +40,10 @@
40
40
  "tsx": "^4.19.0",
41
41
  "typescript": "^5.7.0",
42
42
  "vitest": "^3.0.0",
43
- "@duckcodeailabs/dql-notebook-app": "1.5.3"
43
+ "@duckcodeailabs/dql-notebook-app": "1.6.1"
44
44
  },
45
45
  "engines": {
46
- "node": ">=18.0.0"
46
+ "node": ">=20 <23"
47
47
  },
48
48
  "scripts": {
49
49
  "build": "rm -rf dist tsconfig.tsbuildinfo && tsc -b && node ./scripts/copy-runtime-assets.mjs",