windmill-cli 1.748.0 → 1.749.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/esm/main.js +479 -33
- package/package.json +1 -1
package/esm/main.js
CHANGED
|
@@ -16784,7 +16784,7 @@ var init_OpenAPI = __esm(() => {
|
|
|
16784
16784
|
PASSWORD: undefined,
|
|
16785
16785
|
TOKEN: getEnv3("WM_TOKEN"),
|
|
16786
16786
|
USERNAME: undefined,
|
|
16787
|
-
VERSION: "1.
|
|
16787
|
+
VERSION: "1.749.0",
|
|
16788
16788
|
WITH_CREDENTIALS: true,
|
|
16789
16789
|
interceptors: {
|
|
16790
16790
|
request: new Interceptors,
|
|
@@ -26422,7 +26422,7 @@ var init_auth = __esm(async () => {
|
|
|
26422
26422
|
});
|
|
26423
26423
|
|
|
26424
26424
|
// src/core/constants.ts
|
|
26425
|
-
var WM_FORK_PREFIX = "wm-fork", VERSION = "1.
|
|
26425
|
+
var WM_FORK_PREFIX = "wm-fork", VERSION = "1.749.0";
|
|
26426
26426
|
|
|
26427
26427
|
// src/utils/git.ts
|
|
26428
26428
|
var exports_git = {};
|
|
@@ -86779,8 +86779,8 @@ inspect asset-driven pipelines (scripts marked \`// pipeline\`, wired by \`// on
|
|
|
86779
86779
|
- \`pipeline show <folder:string>\` - render a pipeline folder's DAG (sources, lineage, subscriptions) in the terminal
|
|
86780
86780
|
- \`--json\` - Output the raw asset graph as JSON
|
|
86781
86781
|
- \`--local\` - Build the graph from local working-tree files (// pipeline scripts) instead of the deployed workspace — no deploy needed.
|
|
86782
|
-
- \`pipeline run <folder:string>\` - run a
|
|
86783
|
-
- \`--from <script:string>\` - Start script (short name or path). Defaults to the folder's sole schedule/manual root.
|
|
86782
|
+
- \`pipeline run <folder:string>\` - run a cascade: from --from (a root OR any mid-DAG model), fan downstream up to the --to end node(s)
|
|
86783
|
+
- \`--from <script:string>\` - Start script (short name or path). May be any node, including a mid-DAG model — that node plus its transitive downstream runs, upstream is NOT re-run (dbt \`--select model+\`). Defaults to the folder's sole schedule/manual root.
|
|
86784
86784
|
- \`--to <node:string>\` - End node(s) to stop at — script names/paths or asset URIs (e.g. datatable://main/staged). Repeatable or comma-separated. Omit to run the full downstream.
|
|
86785
86785
|
- \`--dry-run\` - Print the topological run plan without executing.
|
|
86786
86786
|
- \`--json\` - Output the plan as JSON (for piping to jq).
|
|
@@ -95799,7 +95799,8 @@ function assetUriToNodeId(uri) {
|
|
|
95799
95799
|
return;
|
|
95800
95800
|
const prefix = m3[1].toLowerCase();
|
|
95801
95801
|
const kind = prefix === "s3" ? "s3object" : prefix;
|
|
95802
|
-
|
|
95802
|
+
const path23 = kind === "s3object" ? m3[2].replace(/^\/+/, "") : m3[2];
|
|
95803
|
+
return `${kind}:${path23}`;
|
|
95803
95804
|
}
|
|
95804
95805
|
function addEdge(dag, a2, b2) {
|
|
95805
95806
|
if (a2 === b2)
|
|
@@ -95920,6 +95921,18 @@ function validStarts(g2) {
|
|
|
95920
95921
|
}
|
|
95921
95922
|
return out;
|
|
95922
95923
|
}
|
|
95924
|
+
function validFromStarts(g2) {
|
|
95925
|
+
const nonAutorun = nonAutorunTriggerScripts(g2);
|
|
95926
|
+
const out = new Set(validStarts(g2));
|
|
95927
|
+
for (const r2 of g2.runnables ?? []) {
|
|
95928
|
+
if (r2.usage_kind !== "script")
|
|
95929
|
+
continue;
|
|
95930
|
+
const id = scriptNodeId(r2.path);
|
|
95931
|
+
if (!nonAutorun.has(id))
|
|
95932
|
+
out.add(id);
|
|
95933
|
+
}
|
|
95934
|
+
return out;
|
|
95935
|
+
}
|
|
95923
95936
|
function nonAutorunTriggerScripts(g2) {
|
|
95924
95937
|
const out = new Set;
|
|
95925
95938
|
for (const t2 of g2.triggers ?? []) {
|
|
@@ -96001,6 +96014,235 @@ await __promiseAll([
|
|
|
96001
96014
|
import * as fs17 from "node:fs";
|
|
96002
96015
|
import * as path23 from "node:path";
|
|
96003
96016
|
import process25 from "node:process";
|
|
96017
|
+
|
|
96018
|
+
// src/commands/pipeline/duckdbMacros.ts
|
|
96019
|
+
function stripKw(s2, kw) {
|
|
96020
|
+
if (s2.length < kw.length)
|
|
96021
|
+
return null;
|
|
96022
|
+
if (s2.slice(0, kw.length).toLowerCase() !== kw)
|
|
96023
|
+
return null;
|
|
96024
|
+
const after = s2.slice(kw.length);
|
|
96025
|
+
if (after.length === 0 || /^\s/.test(after))
|
|
96026
|
+
return after.replace(/^\s+/, "");
|
|
96027
|
+
return null;
|
|
96028
|
+
}
|
|
96029
|
+
function isIdent(s2) {
|
|
96030
|
+
return /^[A-Za-z_][A-Za-z0-9_]*$/.test(s2);
|
|
96031
|
+
}
|
|
96032
|
+
function splitStatements(sql) {
|
|
96033
|
+
const out = [];
|
|
96034
|
+
let cur = "";
|
|
96035
|
+
const chars = [...sql];
|
|
96036
|
+
let i = 0;
|
|
96037
|
+
const n2 = chars.length;
|
|
96038
|
+
while (i < n2) {
|
|
96039
|
+
const c2 = chars[i];
|
|
96040
|
+
if (c2 === "-" && i + 1 < n2 && chars[i + 1] === "-" || c2 === "/" && i + 1 < n2 && chars[i + 1] === "/") {
|
|
96041
|
+
while (i < n2 && chars[i] !== `
|
|
96042
|
+
`)
|
|
96043
|
+
i += 1;
|
|
96044
|
+
continue;
|
|
96045
|
+
}
|
|
96046
|
+
if (c2 === "/" && i + 1 < n2 && chars[i + 1] === "*") {
|
|
96047
|
+
i += 2;
|
|
96048
|
+
while (i + 1 < n2 && !(chars[i] === "*" && chars[i + 1] === "/"))
|
|
96049
|
+
i += 1;
|
|
96050
|
+
i += 2;
|
|
96051
|
+
continue;
|
|
96052
|
+
}
|
|
96053
|
+
if (c2 === "'") {
|
|
96054
|
+
cur += c2;
|
|
96055
|
+
i += 1;
|
|
96056
|
+
while (i < n2) {
|
|
96057
|
+
cur += chars[i];
|
|
96058
|
+
if (chars[i] === "'") {
|
|
96059
|
+
if (i + 1 < n2 && chars[i + 1] === "'") {
|
|
96060
|
+
cur += "'";
|
|
96061
|
+
i += 2;
|
|
96062
|
+
continue;
|
|
96063
|
+
}
|
|
96064
|
+
i += 1;
|
|
96065
|
+
break;
|
|
96066
|
+
}
|
|
96067
|
+
i += 1;
|
|
96068
|
+
}
|
|
96069
|
+
continue;
|
|
96070
|
+
}
|
|
96071
|
+
if (c2 === '"') {
|
|
96072
|
+
cur += c2;
|
|
96073
|
+
i += 1;
|
|
96074
|
+
while (i < n2) {
|
|
96075
|
+
cur += chars[i];
|
|
96076
|
+
if (chars[i] === '"') {
|
|
96077
|
+
i += 1;
|
|
96078
|
+
break;
|
|
96079
|
+
}
|
|
96080
|
+
i += 1;
|
|
96081
|
+
}
|
|
96082
|
+
continue;
|
|
96083
|
+
}
|
|
96084
|
+
if (c2 === ";") {
|
|
96085
|
+
const t3 = cur.trim();
|
|
96086
|
+
if (t3 !== "")
|
|
96087
|
+
out.push(t3);
|
|
96088
|
+
cur = "";
|
|
96089
|
+
i += 1;
|
|
96090
|
+
continue;
|
|
96091
|
+
}
|
|
96092
|
+
cur += c2;
|
|
96093
|
+
i += 1;
|
|
96094
|
+
}
|
|
96095
|
+
const t2 = cur.trim();
|
|
96096
|
+
if (t2 !== "")
|
|
96097
|
+
out.push(t2);
|
|
96098
|
+
return out;
|
|
96099
|
+
}
|
|
96100
|
+
function parseCreateMacro(stmt) {
|
|
96101
|
+
let rest = stripKw(stmt.trim(), "create");
|
|
96102
|
+
if (rest === null)
|
|
96103
|
+
return null;
|
|
96104
|
+
const afterOr = stripKw(rest, "or");
|
|
96105
|
+
if (afterOr !== null) {
|
|
96106
|
+
const afterReplace = stripKw(afterOr, "replace");
|
|
96107
|
+
if (afterReplace === null)
|
|
96108
|
+
return null;
|
|
96109
|
+
rest = afterReplace;
|
|
96110
|
+
}
|
|
96111
|
+
const afterTemp = stripKw(rest, "temp") ?? stripKw(rest, "temporary");
|
|
96112
|
+
if (afterTemp !== null)
|
|
96113
|
+
rest = afterTemp;
|
|
96114
|
+
const afterMacro = stripKw(rest, "macro") ?? stripKw(rest, "function");
|
|
96115
|
+
if (afterMacro === null)
|
|
96116
|
+
return null;
|
|
96117
|
+
rest = afterMacro;
|
|
96118
|
+
const nameEndMatch = rest.match(/[\s(]/);
|
|
96119
|
+
const nameEnd = nameEndMatch ? nameEndMatch.index : rest.length;
|
|
96120
|
+
const rawName = rest.slice(0, nameEnd);
|
|
96121
|
+
if (rawName === "" || rawName.includes(".") || rawName.includes('"') || !isIdent(rawName)) {
|
|
96122
|
+
return null;
|
|
96123
|
+
}
|
|
96124
|
+
const name = rawName.toLowerCase();
|
|
96125
|
+
rest = rest.slice(nameEnd).replace(/^\s+/, "");
|
|
96126
|
+
if (!rest.startsWith("("))
|
|
96127
|
+
return null;
|
|
96128
|
+
const pchars = [...rest];
|
|
96129
|
+
let depth = 0;
|
|
96130
|
+
let j2 = 0;
|
|
96131
|
+
let close = -1;
|
|
96132
|
+
while (j2 < pchars.length) {
|
|
96133
|
+
const ch = pchars[j2];
|
|
96134
|
+
if (ch === "(")
|
|
96135
|
+
depth += 1;
|
|
96136
|
+
else if (ch === ")") {
|
|
96137
|
+
depth -= 1;
|
|
96138
|
+
if (depth === 0) {
|
|
96139
|
+
close = j2;
|
|
96140
|
+
break;
|
|
96141
|
+
}
|
|
96142
|
+
} else if (ch === "'" || ch === '"') {
|
|
96143
|
+
const q2 = ch;
|
|
96144
|
+
j2 += 1;
|
|
96145
|
+
while (j2 < pchars.length && pchars[j2] !== q2)
|
|
96146
|
+
j2 += 1;
|
|
96147
|
+
}
|
|
96148
|
+
j2 += 1;
|
|
96149
|
+
}
|
|
96150
|
+
if (close === -1)
|
|
96151
|
+
return null;
|
|
96152
|
+
const params = pchars.slice(1, close).join("").trim();
|
|
96153
|
+
const afterParams = pchars.slice(close + 1).join("").replace(/^\s+/, "");
|
|
96154
|
+
let body = stripKw(afterParams, "as");
|
|
96155
|
+
if (body === null)
|
|
96156
|
+
return null;
|
|
96157
|
+
let isTable = false;
|
|
96158
|
+
const afterTable = stripKw(body, "table");
|
|
96159
|
+
if (afterTable !== null) {
|
|
96160
|
+
body = afterTable;
|
|
96161
|
+
isTable = true;
|
|
96162
|
+
}
|
|
96163
|
+
const trimmedBody = body.replace(/;+\s*$/, "").trim();
|
|
96164
|
+
if (trimmedBody === "")
|
|
96165
|
+
return null;
|
|
96166
|
+
return { name, params, isTable };
|
|
96167
|
+
}
|
|
96168
|
+
function parseMacroLibrary(sql) {
|
|
96169
|
+
const out = [];
|
|
96170
|
+
for (const stmt of splitStatements(sql)) {
|
|
96171
|
+
const m3 = parseCreateMacro(stmt);
|
|
96172
|
+
if (m3)
|
|
96173
|
+
out.push(m3);
|
|
96174
|
+
}
|
|
96175
|
+
return out;
|
|
96176
|
+
}
|
|
96177
|
+
function detectMacroCalls(sql, names) {
|
|
96178
|
+
const found = new Set;
|
|
96179
|
+
if (names.size === 0)
|
|
96180
|
+
return found;
|
|
96181
|
+
for (const stmt of splitStatements(sql)) {
|
|
96182
|
+
const chars = [...stmt];
|
|
96183
|
+
const n2 = chars.length;
|
|
96184
|
+
let i = 0;
|
|
96185
|
+
let prev = undefined;
|
|
96186
|
+
while (i < n2) {
|
|
96187
|
+
const c2 = chars[i];
|
|
96188
|
+
if (c2 === "'" || c2 === '"') {
|
|
96189
|
+
const q2 = c2;
|
|
96190
|
+
i += 1;
|
|
96191
|
+
while (i < n2 && chars[i] !== q2)
|
|
96192
|
+
i += 1;
|
|
96193
|
+
i += 1;
|
|
96194
|
+
prev = q2;
|
|
96195
|
+
continue;
|
|
96196
|
+
}
|
|
96197
|
+
const isIdentStart = /[A-Za-z_]/.test(c2);
|
|
96198
|
+
const prevBlocks = prev !== undefined && /[.A-Za-z0-9_]/.test(prev);
|
|
96199
|
+
if (isIdentStart && !prevBlocks) {
|
|
96200
|
+
const start = i;
|
|
96201
|
+
while (i < n2 && /[A-Za-z0-9_]/.test(chars[i]))
|
|
96202
|
+
i += 1;
|
|
96203
|
+
const word = chars.slice(start, i).join("").toLowerCase();
|
|
96204
|
+
let k2 = i;
|
|
96205
|
+
while (k2 < n2 && /\s/.test(chars[k2]))
|
|
96206
|
+
k2 += 1;
|
|
96207
|
+
if (k2 < n2 && chars[k2] === "(" && names.has(word))
|
|
96208
|
+
found.add(word);
|
|
96209
|
+
prev = chars[i - 1];
|
|
96210
|
+
continue;
|
|
96211
|
+
}
|
|
96212
|
+
prev = c2;
|
|
96213
|
+
i += 1;
|
|
96214
|
+
}
|
|
96215
|
+
}
|
|
96216
|
+
return found;
|
|
96217
|
+
}
|
|
96218
|
+
function parseMacroAnnotations(content) {
|
|
96219
|
+
let macros = false;
|
|
96220
|
+
const useLibs = [];
|
|
96221
|
+
for (const raw of content.split(`
|
|
96222
|
+
`)) {
|
|
96223
|
+
const line = raw.replace(/^\s+/, "");
|
|
96224
|
+
if (line === "")
|
|
96225
|
+
continue;
|
|
96226
|
+
let rest;
|
|
96227
|
+
if (line.startsWith("//") || line.startsWith("--"))
|
|
96228
|
+
rest = line.slice(2);
|
|
96229
|
+
else if (line.startsWith("#"))
|
|
96230
|
+
rest = line.slice(1);
|
|
96231
|
+
else
|
|
96232
|
+
break;
|
|
96233
|
+
rest = rest.replace(/^\s+/, "");
|
|
96234
|
+
if (/^macros\s*$/.test(rest)) {
|
|
96235
|
+
macros = true;
|
|
96236
|
+
continue;
|
|
96237
|
+
}
|
|
96238
|
+
const u2 = rest.match(/^use\s+(\S+)\s*$/);
|
|
96239
|
+
if (u2 && u2[1].includes("/") && !useLibs.includes(u2[1]))
|
|
96240
|
+
useLibs.push(u2[1]);
|
|
96241
|
+
}
|
|
96242
|
+
return { macros, useLibs };
|
|
96243
|
+
}
|
|
96244
|
+
|
|
96245
|
+
// src/commands/pipeline/localGraph.ts
|
|
96004
96246
|
function workspaceRoot() {
|
|
96005
96247
|
const yaml = getWmillYamlPath();
|
|
96006
96248
|
return yaml ? path23.dirname(yaml) : process25.cwd();
|
|
@@ -96076,7 +96318,8 @@ function fallbackParse(content, language) {
|
|
|
96076
96318
|
if (uri) {
|
|
96077
96319
|
const prefix = uri[1].toLowerCase();
|
|
96078
96320
|
const kind = prefix === "s3" ? "s3object" : prefix;
|
|
96079
|
-
|
|
96321
|
+
const path24 = kind === "s3object" ? uri[2].replace(/^\/+/, "") : uri[2];
|
|
96322
|
+
out.triggers.push({ kind: "asset", asset_kind: kind, path: path24 });
|
|
96080
96323
|
} else if (NATIVE_KINDS.has(firstTok) && rest === firstTok) {
|
|
96081
96324
|
out.triggers.push({ kind: firstTok });
|
|
96082
96325
|
}
|
|
@@ -96224,13 +96467,30 @@ async function buildLocalPipelineGraph(args) {
|
|
|
96224
96467
|
const folderClean = args.folder.replace(/^f\//, "").replace(/\/$/, "");
|
|
96225
96468
|
const folderDir = path23.join(args.root, "f", folderClean);
|
|
96226
96469
|
const all = await collectScripts(folderDir, args.root, args.defaultTs);
|
|
96470
|
+
const libMacros = collectMacroLibraries(args.root);
|
|
96227
96471
|
const runnables = [];
|
|
96228
96472
|
const edges = [];
|
|
96229
96473
|
const triggers = [];
|
|
96230
96474
|
const assetSet = new Map;
|
|
96475
|
+
const derivedFromByKey = new Map;
|
|
96231
96476
|
const pipelineScripts = [];
|
|
96232
96477
|
for (const s2 of all) {
|
|
96233
96478
|
const out = await inferScriptAssets(s2.content, s2.language);
|
|
96479
|
+
const macroLibDefs = libMacros.get(s2.path);
|
|
96480
|
+
if (macroLibDefs) {
|
|
96481
|
+
runnables.push({
|
|
96482
|
+
path: s2.path,
|
|
96483
|
+
usage_kind: "script",
|
|
96484
|
+
in_pipeline: true,
|
|
96485
|
+
...out.tag ? { tag: out.tag } : {},
|
|
96486
|
+
macros: macroLibDefs.map((m3) => ({
|
|
96487
|
+
name: m3.name,
|
|
96488
|
+
params: m3.params,
|
|
96489
|
+
is_table: m3.isTable
|
|
96490
|
+
}))
|
|
96491
|
+
});
|
|
96492
|
+
continue;
|
|
96493
|
+
}
|
|
96234
96494
|
if (!out.in_pipeline)
|
|
96235
96495
|
continue;
|
|
96236
96496
|
const retry = normalizeRetry(out.retry);
|
|
@@ -96263,19 +96523,30 @@ async function buildLocalPipelineGraph(args) {
|
|
|
96263
96523
|
});
|
|
96264
96524
|
}
|
|
96265
96525
|
if (mat) {
|
|
96266
|
-
|
|
96267
|
-
|
|
96268
|
-
|
|
96269
|
-
|
|
96270
|
-
|
|
96271
|
-
|
|
96272
|
-
|
|
96273
|
-
|
|
96274
|
-
|
|
96275
|
-
|
|
96276
|
-
|
|
96277
|
-
|
|
96526
|
+
const matWrites = [
|
|
96527
|
+
{ path: mat.target_path }
|
|
96528
|
+
];
|
|
96529
|
+
if (mat.scd2 && !mat.manual) {
|
|
96530
|
+
const currentPath = `${mat.target_path}_current`;
|
|
96531
|
+
matWrites.push({ path: currentPath, derived_from: mat.target_path });
|
|
96532
|
+
derivedFromByKey.set(`${mat.target_kind}:${currentPath}`, mat.target_path);
|
|
96533
|
+
}
|
|
96534
|
+
for (const w2 of matWrites) {
|
|
96535
|
+
assetSet.set(`${mat.target_kind}:${w2.path}`, {
|
|
96536
|
+
kind: mat.target_kind,
|
|
96537
|
+
path: w2.path,
|
|
96538
|
+
...w2.derived_from ? { derived_from: w2.derived_from } : {}
|
|
96278
96539
|
});
|
|
96540
|
+
const hasWrite = edges.some((e2) => e2.runnable_path === s2.path && e2.asset_kind === mat.target_kind && e2.asset_path === w2.path && (e2.access_type === "w" || e2.access_type === "rw"));
|
|
96541
|
+
if (!hasWrite) {
|
|
96542
|
+
edges.push({
|
|
96543
|
+
runnable_kind: "script",
|
|
96544
|
+
runnable_path: s2.path,
|
|
96545
|
+
asset_kind: mat.target_kind,
|
|
96546
|
+
asset_path: w2.path,
|
|
96547
|
+
access_type: "w"
|
|
96548
|
+
});
|
|
96549
|
+
}
|
|
96279
96550
|
}
|
|
96280
96551
|
}
|
|
96281
96552
|
const existingNativeTriggers = new Set;
|
|
@@ -96312,16 +96583,134 @@ async function buildLocalPipelineGraph(args) {
|
|
|
96312
96583
|
});
|
|
96313
96584
|
}
|
|
96314
96585
|
}
|
|
96586
|
+
const macroEdges = buildMacroEdges(all, libMacros, runnables);
|
|
96587
|
+
const assets = [...assetSet.entries()].map(([key, a2]) => {
|
|
96588
|
+
const derived_from = a2.derived_from ?? derivedFromByKey.get(key);
|
|
96589
|
+
return derived_from ? { ...a2, derived_from } : a2;
|
|
96590
|
+
});
|
|
96315
96591
|
return {
|
|
96316
96592
|
graph: {
|
|
96317
96593
|
runnables,
|
|
96318
|
-
assets
|
|
96594
|
+
assets,
|
|
96319
96595
|
edges,
|
|
96320
|
-
triggers
|
|
96596
|
+
triggers,
|
|
96597
|
+
...macroEdges.length > 0 ? { macro_edges: macroEdges } : {}
|
|
96321
96598
|
},
|
|
96322
96599
|
scripts: pipelineScripts
|
|
96323
96600
|
};
|
|
96324
96601
|
}
|
|
96602
|
+
function collectMacroLibraries(root) {
|
|
96603
|
+
const out = new Map;
|
|
96604
|
+
const walk = (dir) => {
|
|
96605
|
+
let entries;
|
|
96606
|
+
try {
|
|
96607
|
+
entries = fs17.readdirSync(dir, { withFileTypes: true });
|
|
96608
|
+
} catch {
|
|
96609
|
+
return;
|
|
96610
|
+
}
|
|
96611
|
+
for (const e2 of entries) {
|
|
96612
|
+
if (e2.name.startsWith(".") || e2.name === "node_modules")
|
|
96613
|
+
continue;
|
|
96614
|
+
const abs = path23.join(dir, e2.name);
|
|
96615
|
+
if (e2.isDirectory()) {
|
|
96616
|
+
walk(abs);
|
|
96617
|
+
continue;
|
|
96618
|
+
}
|
|
96619
|
+
if (!e2.isFile() || !e2.name.endsWith(".duckdb.sql"))
|
|
96620
|
+
continue;
|
|
96621
|
+
let content;
|
|
96622
|
+
try {
|
|
96623
|
+
content = fs17.readFileSync(abs, "utf-8");
|
|
96624
|
+
} catch {
|
|
96625
|
+
continue;
|
|
96626
|
+
}
|
|
96627
|
+
if (!parseMacroAnnotations(content).macros)
|
|
96628
|
+
continue;
|
|
96629
|
+
const relFromRoot = path23.relative(root, abs).replaceAll("\\", "/");
|
|
96630
|
+
out.set(removeExtensionToPath(relFromRoot), parseMacroLibrary(content));
|
|
96631
|
+
}
|
|
96632
|
+
};
|
|
96633
|
+
walk(path23.join(root, "f"));
|
|
96634
|
+
return out;
|
|
96635
|
+
}
|
|
96636
|
+
function buildMacroEdges(all, libMacros, runnables) {
|
|
96637
|
+
if (libMacros.size === 0)
|
|
96638
|
+
return [];
|
|
96639
|
+
const useLibsByScript = new Map;
|
|
96640
|
+
for (const s2 of all) {
|
|
96641
|
+
if (s2.language !== "duckdb")
|
|
96642
|
+
continue;
|
|
96643
|
+
const { useLibs } = parseMacroAnnotations(s2.content);
|
|
96644
|
+
if (useLibs.length > 0)
|
|
96645
|
+
useLibsByScript.set(s2.path, useLibs);
|
|
96646
|
+
}
|
|
96647
|
+
const providerByName = new Map;
|
|
96648
|
+
for (const [lib, macros] of libMacros) {
|
|
96649
|
+
for (const m3 of macros)
|
|
96650
|
+
providerByName.set(m3.name, lib);
|
|
96651
|
+
}
|
|
96652
|
+
const allMacroNames = new Set(providerByName.keys());
|
|
96653
|
+
const pipelinePaths = new Set(runnables.map((r2) => r2.path));
|
|
96654
|
+
const edgeMap = new Map;
|
|
96655
|
+
const aggFor = (lib, consumer) => {
|
|
96656
|
+
let byConsumer = edgeMap.get(lib);
|
|
96657
|
+
if (!byConsumer) {
|
|
96658
|
+
byConsumer = new Map;
|
|
96659
|
+
edgeMap.set(lib, byConsumer);
|
|
96660
|
+
}
|
|
96661
|
+
let agg = byConsumer.get(consumer);
|
|
96662
|
+
if (!agg) {
|
|
96663
|
+
agg = { names: new Set, viaUse: false };
|
|
96664
|
+
byConsumer.set(consumer, agg);
|
|
96665
|
+
}
|
|
96666
|
+
return agg;
|
|
96667
|
+
};
|
|
96668
|
+
for (const s2 of all) {
|
|
96669
|
+
if (s2.language !== "duckdb")
|
|
96670
|
+
continue;
|
|
96671
|
+
for (const name of detectMacroCalls(s2.content, allMacroNames)) {
|
|
96672
|
+
const lib = providerByName.get(name);
|
|
96673
|
+
if (lib === s2.path)
|
|
96674
|
+
continue;
|
|
96675
|
+
aggFor(lib, s2.path).names.add(name);
|
|
96676
|
+
}
|
|
96677
|
+
if (!pipelinePaths.has(s2.path))
|
|
96678
|
+
continue;
|
|
96679
|
+
for (const lib of useLibsByScript.get(s2.path) ?? []) {
|
|
96680
|
+
const macros = libMacros.get(lib);
|
|
96681
|
+
if (!macros || lib === s2.path)
|
|
96682
|
+
continue;
|
|
96683
|
+
const agg = aggFor(lib, s2.path);
|
|
96684
|
+
agg.viaUse = true;
|
|
96685
|
+
for (const m3 of macros)
|
|
96686
|
+
agg.names.add(m3.name);
|
|
96687
|
+
}
|
|
96688
|
+
}
|
|
96689
|
+
const edges = [...edgeMap.entries()].flatMap(([lib_path, byConsumer]) => [...byConsumer.entries()].map(([consumer_path, agg]) => ({
|
|
96690
|
+
lib_path,
|
|
96691
|
+
consumer_path,
|
|
96692
|
+
macro_names: [...agg.names].sort(),
|
|
96693
|
+
via_use: agg.viaUse
|
|
96694
|
+
}))).sort((a2, b2) => a2.lib_path.localeCompare(b2.lib_path) || a2.consumer_path.localeCompare(b2.consumer_path));
|
|
96695
|
+
const libPaths = new Set(edges.map((e2) => e2.lib_path));
|
|
96696
|
+
for (const lib of libPaths) {
|
|
96697
|
+
if (runnables.some((r2) => r2.path === lib))
|
|
96698
|
+
continue;
|
|
96699
|
+
const macros = (libMacros.get(lib) ?? []).map((m3) => ({
|
|
96700
|
+
name: m3.name,
|
|
96701
|
+
params: m3.params,
|
|
96702
|
+
is_table: m3.isTable
|
|
96703
|
+
}));
|
|
96704
|
+
runnables.push({ path: lib, usage_kind: "script", macros });
|
|
96705
|
+
}
|
|
96706
|
+
for (const consumer of new Set(edges.map((e2) => e2.consumer_path))) {
|
|
96707
|
+
if (!runnables.some((r2) => r2.path === consumer)) {
|
|
96708
|
+
runnables.push({ path: consumer, usage_kind: "script" });
|
|
96709
|
+
}
|
|
96710
|
+
}
|
|
96711
|
+
runnables.sort((a2, b2) => a2.path.localeCompare(b2.path));
|
|
96712
|
+
return edges;
|
|
96713
|
+
}
|
|
96325
96714
|
|
|
96326
96715
|
// src/commands/pipeline/pipeline.ts
|
|
96327
96716
|
await __promiseAll([
|
|
@@ -96552,7 +96941,17 @@ function generatePipelineMarkdown(folder, graph, datatableSchemas, local) {
|
|
|
96552
96941
|
(nativeByScript.get(t2.runnable_path) ?? nativeByScript.set(t2.runnable_path, []).get(t2.runnable_path)).push(t2.trigger_kind);
|
|
96553
96942
|
}
|
|
96554
96943
|
}
|
|
96555
|
-
const
|
|
96944
|
+
const macroLibs = graph.runnables.filter((r2) => r2.usage_kind === "script" && (r2.macros?.length ?? 0) > 0).sort((a2, b2) => a2.path.localeCompare(b2.path));
|
|
96945
|
+
const macroLibPaths = new Set(macroLibs.map((r2) => r2.path));
|
|
96946
|
+
const macroConsumersByLib = new Map;
|
|
96947
|
+
for (const me of graph.macro_edges ?? []) {
|
|
96948
|
+
(macroConsumersByLib.get(me.lib_path) ?? macroConsumersByLib.set(me.lib_path, []).get(me.lib_path)).push({
|
|
96949
|
+
consumer: me.consumer_path,
|
|
96950
|
+
names: me.macro_names,
|
|
96951
|
+
viaUse: me.via_use
|
|
96952
|
+
});
|
|
96953
|
+
}
|
|
96954
|
+
const scripts = graph.runnables.filter((r2) => r2.usage_kind === "script" && !macroLibPaths.has(r2.path)).map((r2) => r2.path).sort();
|
|
96556
96955
|
let md = `# Pipeline \`f/${folder}\`
|
|
96557
96956
|
|
|
96558
96957
|
${local ? "_Built from local working-tree files (`// pipeline` scripts)._" : "_Built from the deployed workspace asset graph._"}
|
|
@@ -96563,7 +96962,7 @@ produces data by reading/writing assets in its body (\`datatable://\`,
|
|
|
96563
96962
|
\`ducklake://\`, \`s3://\`, \`volume://\`). The cascade runs a producer, then every
|
|
96564
96963
|
downstream subscriber, in topological order.
|
|
96565
96964
|
|
|
96566
|
-
- **${scripts.length}** script${scripts.length === 1 ? "" : "s"} · **${graph.assets.length}** asset${graph.assets.length === 1 ? "" : "s"}
|
|
96965
|
+
- **${scripts.length}** script${scripts.length === 1 ? "" : "s"} · **${graph.assets.length}** asset${graph.assets.length === 1 ? "" : "s"}${macroLibs.length > 0 ? ` · **${macroLibs.length}** macro librar${macroLibs.length === 1 ? "y" : "ies"}` : ""}
|
|
96567
96966
|
|
|
96568
96967
|
## Scripts
|
|
96569
96968
|
|
|
@@ -96595,6 +96994,36 @@ downstream subscriber, in topological order.
|
|
|
96595
96994
|
md += `
|
|
96596
96995
|
`;
|
|
96597
96996
|
}
|
|
96997
|
+
if (macroLibs.length > 0) {
|
|
96998
|
+
md += `## Macro libraries
|
|
96999
|
+
|
|
97000
|
+
`;
|
|
97001
|
+
md += `\`// macros\` DuckDB libraries. Their \`CREATE MACRO\` definitions are injected as
|
|
97002
|
+
TEMP macros into consuming scripts at run time — call a macro by name, or force the
|
|
97003
|
+
whole library in with \`// use <lib-path>\` (needed for macros only reached via dynamic SQL).
|
|
97004
|
+
|
|
97005
|
+
`;
|
|
97006
|
+
for (const lib of macroLibs) {
|
|
97007
|
+
md += `### \`${lib.path}\`
|
|
97008
|
+
|
|
97009
|
+
`;
|
|
97010
|
+
for (const m3 of lib.macros ?? []) {
|
|
97011
|
+
md += `- \`${m3.name}(${m3.params ?? ""})\`${m3.is_table ? " → TABLE" : ""}
|
|
97012
|
+
`;
|
|
97013
|
+
}
|
|
97014
|
+
const consumers = [...macroConsumersByLib.get(lib.path) ?? []].sort((a2, b2) => a2.consumer.localeCompare(b2.consumer));
|
|
97015
|
+
if (consumers.length > 0) {
|
|
97016
|
+
md += `- **Used by:**
|
|
97017
|
+
`;
|
|
97018
|
+
for (const c2 of consumers) {
|
|
97019
|
+
md += ` - \`${c2.consumer}\` (${c2.viaUse ? "via `// use`" : `calls ${c2.names.map((n2) => `\`${n2}\``).join(", ")}`})
|
|
97020
|
+
`;
|
|
97021
|
+
}
|
|
97022
|
+
}
|
|
97023
|
+
md += `
|
|
97024
|
+
`;
|
|
97025
|
+
}
|
|
97026
|
+
}
|
|
96598
97027
|
const referencedDatatables = new Set(graph.assets.filter((a2) => a2.kind === "datatable").map((a2) => a2.path.split("/")[0]));
|
|
96599
97028
|
const relevant = (datatableSchemas ?? []).filter((dt) => referencedDatatables.has(dt.datatable_name));
|
|
96600
97029
|
if (relevant.length > 0) {
|
|
@@ -97104,11 +97533,13 @@ async function run5(opts, folder) {
|
|
|
97104
97533
|
});
|
|
97105
97534
|
graph = built.graph;
|
|
97106
97535
|
localScripts = new Map(built.scripts.map((s2) => [s2.path, s2]));
|
|
97107
|
-
|
|
97108
|
-
|
|
97109
|
-
|
|
97110
|
-
|
|
97111
|
-
|
|
97536
|
+
if (!opts.dryRun) {
|
|
97537
|
+
try {
|
|
97538
|
+
const codebases = await listSyncCodebases(merged);
|
|
97539
|
+
const { buildPreviewTempScriptRefs: buildPreviewTempScriptRefs2 } = await init_generate_metadata().then(() => exports_generate_metadata);
|
|
97540
|
+
tempScriptRefs = await buildPreviewTempScriptRefs2(workspace, merged, codebases, { kind: "all" });
|
|
97541
|
+
} catch {}
|
|
97542
|
+
}
|
|
97112
97543
|
} else {
|
|
97113
97544
|
graph = await apiGet(`/w/${workspace.workspaceId}/assets/graph?folder=${encodeURIComponent(f3)}&asset_kinds=${ASSET_KINDS2}`);
|
|
97114
97545
|
await enrichDeployedNonAutorunTriggers(workspace.workspaceId, graph);
|
|
@@ -97144,7 +97575,16 @@ async function run5(opts, folder) {
|
|
|
97144
97575
|
merged[b2.param] = b2.value;
|
|
97145
97576
|
}
|
|
97146
97577
|
const macroLibPaths = new Set(graph.runnables.filter((r2) => r2.usage_kind === "script" && (r2.macros?.length ?? 0) > 0).map((r2) => r2.path));
|
|
97147
|
-
const
|
|
97578
|
+
const notRunnablePaths = new Set(macroLibPaths);
|
|
97579
|
+
if (opts.local && localScripts) {
|
|
97580
|
+
for (const r2 of graph.runnables) {
|
|
97581
|
+
if (r2.usage_kind === "script" && !localScripts.has(r2.path)) {
|
|
97582
|
+
notRunnablePaths.add(r2.path);
|
|
97583
|
+
}
|
|
97584
|
+
}
|
|
97585
|
+
}
|
|
97586
|
+
const starts = new Set([...validStarts(graph), ...boundNodeIds].filter((id) => !notRunnablePaths.has(scriptPathOf(id))));
|
|
97587
|
+
const fromEligible = new Set([...validFromStarts(graph), ...boundNodeIds].filter((id) => !notRunnablePaths.has(scriptPathOf(id))));
|
|
97148
97588
|
let runAll = false;
|
|
97149
97589
|
let start;
|
|
97150
97590
|
if (opts.from) {
|
|
@@ -97159,8 +97599,14 @@ async function run5(opts, folder) {
|
|
|
97159
97599
|
if (macroLibPaths.has(scriptPathOf(resolved))) {
|
|
97160
97600
|
throw new Error(`--from '${opts.from}' is a \`// macros\` library — definition-only, injected into consuming scripts at run time, never a runnable step.`);
|
|
97161
97601
|
}
|
|
97162
|
-
if (
|
|
97163
|
-
throw new Error(`--from '${opts.from}'
|
|
97602
|
+
if (notRunnablePaths.has(scriptPathOf(resolved))) {
|
|
97603
|
+
throw new Error(`--from '${opts.from}' isn't a \`// pipeline\` script — it appears in the local graph only as a macro consumer (lineage). Mark it \`// pipeline\` to run it.`);
|
|
97604
|
+
}
|
|
97605
|
+
if (!resolved.startsWith("script:")) {
|
|
97606
|
+
throw new Error(`--from '${opts.from}' is an asset, not a runnable — start a run from a script (assets are produced, not run).`);
|
|
97607
|
+
}
|
|
97608
|
+
if (!fromEligible.has(resolved)) {
|
|
97609
|
+
throw new Error(`--from '${opts.from}' can't start a run: row-backed event triggers (kafka/mqtt/nats/postgres/sqs/gcp/email) and input-only entrypoints (webhook/data_upload) fan out per-event or need caller-supplied input — bind it with --upload to run it.`);
|
|
97164
97610
|
}
|
|
97165
97611
|
start = resolved;
|
|
97166
97612
|
} else if (starts.size === 1) {
|
|
@@ -97191,7 +97637,7 @@ async function run5(opts, folder) {
|
|
|
97191
97637
|
}
|
|
97192
97638
|
const idLabel = (id) => id.startsWith("script:") ? scriptPathOf(id) : id;
|
|
97193
97639
|
const dag = buildLineageDag(graph);
|
|
97194
|
-
const barriers = new Set([...nonAutorunTriggerScripts(graph)].filter((id) => !starts.has(id)));
|
|
97640
|
+
const barriers = new Set([...nonAutorunTriggerScripts(graph)].filter((id) => !starts.has(id) && id !== start));
|
|
97195
97641
|
let selectedScripts;
|
|
97196
97642
|
let reachableEnds = [];
|
|
97197
97643
|
let droppedEnds = [];
|
|
@@ -97216,7 +97662,7 @@ async function run5(opts, folder) {
|
|
|
97216
97662
|
}
|
|
97217
97663
|
}
|
|
97218
97664
|
}
|
|
97219
|
-
for (const p3 of
|
|
97665
|
+
for (const p3 of notRunnablePaths)
|
|
97220
97666
|
selectedScripts.delete(p3);
|
|
97221
97667
|
const { order, cyclic } = topoOrder(graph, selectedScripts);
|
|
97222
97668
|
if (cyclic.length > 0) {
|
|
@@ -97333,7 +97779,7 @@ Full logs: ${runUrl}`);
|
|
|
97333
97779
|
info(colors.green.bold(`Bounded run complete — ${order.length} script(s) succeeded.`));
|
|
97334
97780
|
}
|
|
97335
97781
|
}
|
|
97336
|
-
var command42 = new Command().description("inspect asset-driven pipelines (scripts marked `// pipeline`, wired by `// on <spec>` annotations)").command("list", "list pipeline folders in the workspace").option("--json", "Output as JSON (for piping to jq)").action(list18).command("show", "render a pipeline folder's DAG (sources, lineage, subscriptions) in the terminal").arguments("<folder:string>").option("--json", "Output the raw asset graph as JSON").option("--local", "Build the graph from local working-tree files (// pipeline scripts) instead of the deployed workspace — no deploy needed.").action(show2).command("run", "run a
|
|
97782
|
+
var command42 = new Command().description("inspect asset-driven pipelines (scripts marked `// pipeline`, wired by `// on <spec>` annotations)").command("list", "list pipeline folders in the workspace").option("--json", "Output as JSON (for piping to jq)").action(list18).command("show", "render a pipeline folder's DAG (sources, lineage, subscriptions) in the terminal").arguments("<folder:string>").option("--json", "Output the raw asset graph as JSON").option("--local", "Build the graph from local working-tree files (// pipeline scripts) instead of the deployed workspace — no deploy needed.").action(show2).command("run", "run a cascade: from --from (a root OR any mid-DAG model), fan downstream up to the --to end node(s)").arguments("<folder:string>").option("--from <script:string>", "Start script (short name or path). May be any node, including a mid-DAG model — that node plus its transitive downstream runs, upstream is NOT re-run (dbt `--select model+`). Defaults to the folder's sole schedule/manual root.").option("--to <node:string>", "End node(s) to stop at — script names/paths or asset URIs (e.g. datatable://main/staged). Repeatable or comma-separated. Omit to run the full downstream.", { collect: true }).option("--dry-run", "Print the topological run plan without executing.").option("--json", "Output the plan as JSON (for piping to jq).").option("--local", "Run the local working-tree scripts via preview (no deploy) instead of the deployed versions; the graph is built from local files.").option("--upload <binding:string>", "Bind an object to a data_upload/webhook entry point so it runs in the cascade, as SCRIPT[:PARAM]=SOURCE (SOURCE is a local file or an s3://key). Local files are uploaded to the workspace store; the S3Object param is inferred when the script has exactly one. Repeatable.", { collect: true }).option("--arg <binding:string>", "Pass a plain run arg to a script in the cascade, as SCRIPT:PARAM=VALUE (VALUE is parsed as JSON when possible, else taken as a string — e.g. daily_report:partition=2026-07-02). Repeatable.", { collect: true }).option("--partition <value:string>", "Partition value for `// partitioned` scripts in the run (e.g. 2026-06-30) — use it to backfill a past slice. With --local, time kinds (daily/hourly/weekly/monthly) default to the current UTC period when omitted; `dynamic` always needs it. Deployed runs without it defer to backend run-start resolution.").action(run5).command("docs", "generate PIPELINE.md (+ AGENTS.md pointer) describing a folder's pipeline graph and datatable schemas, for an editor / agentic loop").arguments("<folder:string>").option("--local", "Build the graph from local working-tree files instead of the deployed workspace.").action(generatePipelineDocs).command("dev", dev_default3);
|
|
97337
97783
|
var pipeline_default = command42;
|
|
97338
97784
|
|
|
97339
97785
|
// src/commands/ducklake/ducklake.ts
|