[{"data":1,"prerenderedAt":1500},["ShallowReactive",2],{"page-\u002Fworkflows\u002Ftransforms":3,"all-pages":1166},{"id":4,"title":5,"body":6,"description":1157,"extension":1158,"meta":1159,"navigation":426,"path":1160,"robots":1161,"seo":1162,"sitemap":1163,"stem":1164,"__hash__":1165},"content\u002F01.workflows\u002F06.transforms.md","Transforms",{"type":7,"value":8,"toc":1144},"minimark",[9,23,28,35,76,116,119,146,149,153,249,267,301,308,312,315,320,323,390,462,466,469,536,601,614,621,685,699,703,706,812,824,828,831,860,863,892,898,902,909,930,963,966,1009,1024,1111,1117,1140],[10,11,12,13,17,18,22],"p",{},"Recon and scanning produce messy text: duplicate subdomains, mixed case,\nblank lines, noise you did not ask for. BountyHub workflows treat data\ntransformation as a first-class, ",[14,15,16],"strong",{},"typed"," pipeline primitive instead of a pile\nof ",[19,20,21],"code",{},"awk | sort | uniq"," shell glue.",[24,25,27],"h2",{"id":26},"the-shape-of-a-transform","The Shape of a Transform",[10,29,30,31,34],{},"A transform is a ",[14,32,33],{},"source"," plus an ordered chain of operations:",[36,37,42],"pre",{"className":38,"code":39,"language":40,"meta":41,"style":41},"language-bhw shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","step.transform(\n    source = path.file(\"subs.txt\").lines(),\n    pipeline = transform.trim().lower().unique().sort(),\n    destination = path.file(\"subs-clean.txt\"),\n)\n","bhw","",[19,43,44,52,58,64,70],{"__ignoreMap":41},[45,46,49],"span",{"class":47,"line":48},"line",1,[45,50,51],{},"step.transform(\n",[45,53,55],{"class":47,"line":54},2,[45,56,57],{},"    source = path.file(\"subs.txt\").lines(),\n",[45,59,61],{"class":47,"line":60},3,[45,62,63],{},"    pipeline = transform.trim().lower().unique().sort(),\n",[45,65,67],{"class":47,"line":66},4,[45,68,69],{},"    destination = path.file(\"subs-clean.txt\"),\n",[45,71,73],{"class":47,"line":72},5,[45,74,75],{},")\n",[77,78,79,101,107],"ul",{},[80,81,82,84,85,88,89,92,93,96,97,100],"li",{},[19,83,33],{}," — how to read the input: ",[19,86,87],{},"path.file(\"subs.txt\").lines()"," parses the\nfile as newline-delimited lines. Other sources: ",[19,90,91],{},".text()",", ",[19,94,95],{},".json()",",\n",[19,98,99],{},".jsonl()",".",[80,102,103,106],{},[19,104,105],{},"pipeline"," — the operations applied to each element (or the collection).",[80,108,109,112,113,100],{},[19,110,111],{},"destination"," — where the result is written; must be ",[19,114,115],{},"path.file(...)",[10,117,118],{},"The equivalent explicit form passes the source separately:",[36,120,122],{"className":38,"code":121,"language":40,"meta":41,"style":41},"step.transform(\n    source = path.file(\"subs.txt\"),\n    pipeline = transform.lines().trim().lower().unique().sort(),\n    destination = path.file(\"subs-clean.txt\"),\n)\n",[19,123,124,128,133,138,142],{"__ignoreMap":41},[45,125,126],{"class":47,"line":48},[45,127,51],{},[45,129,130],{"class":47,"line":54},[45,131,132],{},"    source = path.file(\"subs.txt\"),\n",[45,134,135],{"class":47,"line":60},[45,136,137],{},"    pipeline = transform.lines().trim().lower().unique().sort(),\n",[45,139,140],{"class":47,"line":66},[45,141,69],{},[45,143,144],{"class":47,"line":72},[45,145,75],{},[10,147,148],{},"Both forms compile to the same expression. Use whichever reads better to you.",[24,150,152],{"id":151},"sources","Sources",[154,155,156,172],"table",{},[157,158,159],"thead",{},[160,161,162,166,169],"tr",{},[163,164,165],"th",{},"Source",[163,167,168],{},"Parses input as",[163,170,171],{},"Produces",[173,174,175,195,213,231],"tbody",{},[160,176,177,187,190],{},[178,179,180,183,184],"td",{},[19,181,182],{},"path.file(f).lines()"," \u002F ",[19,185,186],{},"transform.lines()",[178,188,189],{},"Newline-delimited lines",[178,191,192],{},[19,193,194],{},"Items\u003Cstring>",[160,196,197,205,208],{},[178,198,199,183,202],{},[19,200,201],{},"path.file(f).text()",[19,203,204],{},"transform.text()",[178,206,207],{},"Whole file as one string",[178,209,210],{},[19,211,212],{},"string",[160,214,215,223,226],{},[178,216,217,183,220],{},[19,218,219],{},"path.file(f).json()",[19,221,222],{},"transform.json()",[178,224,225],{},"One JSON document",[178,227,228],{},[19,229,230],{},"json",[160,232,233,241,244],{},[178,234,235,183,238],{},[19,236,237],{},"path.file(f).jsonl()",[19,239,240],{},"transform.jsonl()",[178,242,243],{},"Newline-delimited JSON records",[178,245,246],{},[19,247,248],{},"Items\u003Crecord>",[10,250,251,254,255,258,259,262,263,266],{},[19,252,253],{},"record"," values support field access via ",[19,256,257],{},".field",", which is how JSONL output\nfrom tools like ",[19,260,261],{},"httpx -jsonl"," becomes structured data. Generic collection ops\nlike ",[19,264,265],{},".unique()"," work on any item type:",[36,268,270],{"className":38,"code":269,"language":40,"meta":41,"style":41},"# Deduplicate identical httpx JSONL observations.\nstep.transform(\n    source = path.file(\"live.jsonl\").jsonl(),\n    pipeline = transform.unique(),\n    destination = path.file(\"live-dedup.jsonl\"),\n)\n",[19,271,272,277,281,286,291,296],{"__ignoreMap":41},[45,273,274],{"class":47,"line":48},[45,275,276],{},"# Deduplicate identical httpx JSONL observations.\n",[45,278,279],{"class":47,"line":54},[45,280,51],{},[45,282,283],{"class":47,"line":60},[45,284,285],{},"    source = path.file(\"live.jsonl\").jsonl(),\n",[45,287,288],{"class":47,"line":66},[45,289,290],{},"    pipeline = transform.unique(),\n",[45,292,293],{"class":47,"line":72},[45,294,295],{},"    destination = path.file(\"live-dedup.jsonl\"),\n",[45,297,299],{"class":47,"line":298},6,[45,300,75],{},[10,302,303,304,307],{},"A bare ",[19,305,306],{},"path.file(\"x\")"," without a parse is just a file reference — transforms\nreject it (\"raw file\"). Always choose a source first.",[24,309,311],{"id":310},"operations","Operations",[10,313,314],{},"Operations are chained fluently and are statically type-checked.",[316,317,319],"h3",{"id":318},"element-wise-string-ops","Element-wise string ops",[10,321,322],{},"Applied to every item:",[154,324,325,335],{},[157,326,327],{},[160,328,329,332],{},[163,330,331],{},"Op",[163,333,334],{},"Effect",[173,336,337,347,360,370,380],{},[160,338,339,344],{},[178,340,341],{},[19,342,343],{},".trim()",[178,345,346],{},"Strip surrounding whitespace",[160,348,349,357],{},[178,350,351,183,354],{},[19,352,353],{},".lower()",[19,355,356],{},".upper()",[178,358,359],{},"Change case",[160,361,362,367],{},[178,363,364],{},[19,365,366],{},".replace(from, to)",[178,368,369],{},"Replace substring",[160,371,372,377],{},[178,373,374],{},[19,375,376],{},".regex(pattern)",[178,378,379],{},"Extract regex match (optionally a capture group)",[160,381,382,387],{},[178,383,384],{},[19,385,386],{},".regex_replace(pattern, replacement)",[178,388,389],{},"Substitute regex matches",[36,391,393],{"className":38,"code":392,"language":40,"meta":41,"style":41},"# Normalize subdomains and keep only the root domain part.\nstep.transform(\n    source = path.file(\"subs.txt\").lines(),\n    pipeline = transform.trim().lower(),\n    destination = path.file(\"subs-clean.txt\"),\n)\n\n# Extract hosts from raw httpx output lines like \"https:\u002F\u002Ftarget.com [200]\".\nstep.transform(\n    source = path.file(\"raw-httpx.txt\").lines(),\n    pipeline = transform.regex(r\"https?:\u002F\u002F([^ \\]]+)\").unique(),\n    destination = path.file(\"hosts.txt\"),\n)\n",[19,394,395,400,404,408,413,417,421,428,434,439,445,451,457],{"__ignoreMap":41},[45,396,397],{"class":47,"line":48},[45,398,399],{},"# Normalize subdomains and keep only the root domain part.\n",[45,401,402],{"class":47,"line":54},[45,403,51],{},[45,405,406],{"class":47,"line":60},[45,407,57],{},[45,409,410],{"class":47,"line":66},[45,411,412],{},"    pipeline = transform.trim().lower(),\n",[45,414,415],{"class":47,"line":72},[45,416,69],{},[45,418,419],{"class":47,"line":298},[45,420,75],{},[45,422,424],{"class":47,"line":423},7,[45,425,427],{"emptyLinePlaceholder":426},true,"\n",[45,429,431],{"class":47,"line":430},8,[45,432,433],{},"# Extract hosts from raw httpx output lines like \"https:\u002F\u002Ftarget.com [200]\".\n",[45,435,437],{"class":47,"line":436},9,[45,438,51],{},[45,440,442],{"class":47,"line":441},10,[45,443,444],{},"    source = path.file(\"raw-httpx.txt\").lines(),\n",[45,446,448],{"class":47,"line":447},11,[45,449,450],{},"    pipeline = transform.regex(r\"https?:\u002F\u002F([^ \\]]+)\").unique(),\n",[45,452,454],{"class":47,"line":453},12,[45,455,456],{},"    destination = path.file(\"hosts.txt\"),\n",[45,458,460],{"class":47,"line":459},13,[45,461,75],{},[316,463,465],{"id":464},"collection-ops","Collection ops",[10,467,468],{},"Applied to the whole stream:",[154,470,471,479],{},[157,472,473],{},[160,474,475,477],{},[163,476,331],{},[163,478,334],{},[173,480,481,490,503,516,526],{},[160,482,483,487],{},[178,484,485],{},[19,486,265],{},[178,488,489],{},"Remove duplicates",[160,491,492,500],{},[178,493,494,183,497],{},[19,495,496],{},".take(n)",[19,498,499],{},".drop(n)",[178,501,502],{},"Head\u002Ftail slicing",[160,504,505,513],{},[178,506,507,183,510],{},[19,508,509],{},".sort()",[19,511,512],{},".reverse()",[178,514,515],{},"Order the stream",[160,517,518,523],{},[178,519,520],{},[19,521,522],{},".split(sep)",[178,524,525],{},"Split each item into multiple items",[160,527,528,533],{},[178,529,530],{},[19,531,532],{},".join(sep)",[178,534,535],{},"Join all items into one string",[36,537,539],{"className":38,"code":538,"language":40,"meta":41,"style":41},"# Split comma-separated targets into individual lines.\nstep.transform(\n    source = path.file(\"targets.csv\").lines(),\n    pipeline = transform.trim().split(\",\").trim().lower().unique().sort(),\n    destination = path.file(\"targets-clean.txt\"),\n)\n\n# Build an nuclei-friendly single-line argument from unique hosts.\nstep.transform(\n    source = path.file(\"hosts.txt\").lines(),\n    pipeline = transform.trim().unique().sort().join(\",\"),\n    destination = path.file(\"hosts-arg.txt\"),\n)\n",[19,540,541,546,550,555,560,565,569,573,578,582,587,592,597],{"__ignoreMap":41},[45,542,543],{"class":47,"line":48},[45,544,545],{},"# Split comma-separated targets into individual lines.\n",[45,547,548],{"class":47,"line":54},[45,549,51],{},[45,551,552],{"class":47,"line":60},[45,553,554],{},"    source = path.file(\"targets.csv\").lines(),\n",[45,556,557],{"class":47,"line":66},[45,558,559],{},"    pipeline = transform.trim().split(\",\").trim().lower().unique().sort(),\n",[45,561,562],{"class":47,"line":72},[45,563,564],{},"    destination = path.file(\"targets-clean.txt\"),\n",[45,566,567],{"class":47,"line":298},[45,568,75],{},[45,570,571],{"class":47,"line":423},[45,572,427],{"emptyLinePlaceholder":426},[45,574,575],{"class":47,"line":430},[45,576,577],{},"# Build an nuclei-friendly single-line argument from unique hosts.\n",[45,579,580],{"class":47,"line":436},[45,581,51],{},[45,583,584],{"class":47,"line":441},[45,585,586],{},"    source = path.file(\"hosts.txt\").lines(),\n",[45,588,589],{"class":47,"line":447},[45,590,591],{},"    pipeline = transform.trim().unique().sort().join(\",\"),\n",[45,593,594],{"class":47,"line":453},[45,595,596],{},"    destination = path.file(\"hosts-arg.txt\"),\n",[45,598,599],{"class":47,"line":459},[45,600,75],{},[316,602,604,605,183,608,183,611],{"id":603},"lambda-ops-map-filter-flat_map","Lambda ops: ",[19,606,607],{},"map",[19,609,610],{},"filter",[19,612,613],{},"flat_map",[10,615,616,617,620],{},"The only way to compute per-item values or drop records by predicate. Each\ntakes an ordinary one-parameter function (lambda or named ",[19,618,619],{},"def","), captured\ndeferred and applied at runtime:",[36,622,624],{"className":38,"code":623,"language":40,"meta":41,"style":41},"# Append a port to every host.\nstep.transform(\n    source = path.file(\"hosts.txt\").lines(),\n    pipeline = transform.map(lambda host: \"{}:8080\".format(host)),\n    destination = path.file(\"hosts-8080.txt\"),\n)\n\n# Keep only high-severity JSONL records, then expand each into its URLs.\nstep.transform(\n    source = path.file(\"findings.jsonl\").jsonl(),\n    pipeline = transform.filter(lambda row: row.severity == \"high\").flat_map(lambda row: row.urls),\n    destination = path.file(\"high-urls.txt\"),\n)\n",[19,625,626,631,635,639,644,649,653,657,662,666,671,676,681],{"__ignoreMap":41},[45,627,628],{"class":47,"line":48},[45,629,630],{},"# Append a port to every host.\n",[45,632,633],{"class":47,"line":54},[45,634,51],{},[45,636,637],{"class":47,"line":60},[45,638,586],{},[45,640,641],{"class":47,"line":66},[45,642,643],{},"    pipeline = transform.map(lambda host: \"{}:8080\".format(host)),\n",[45,645,646],{"class":47,"line":72},[45,647,648],{},"    destination = path.file(\"hosts-8080.txt\"),\n",[45,650,651],{"class":47,"line":298},[45,652,75],{},[45,654,655],{"class":47,"line":423},[45,656,427],{"emptyLinePlaceholder":426},[45,658,659],{"class":47,"line":430},[45,660,661],{},"# Keep only high-severity JSONL records, then expand each into its URLs.\n",[45,663,664],{"class":47,"line":436},[45,665,51],{},[45,667,668],{"class":47,"line":441},[45,669,670],{},"    source = path.file(\"findings.jsonl\").jsonl(),\n",[45,672,673],{"class":47,"line":447},[45,674,675],{},"    pipeline = transform.filter(lambda row: row.severity == \"high\").flat_map(lambda row: row.urls),\n",[45,677,678],{"class":47,"line":453},[45,679,680],{},"    destination = path.file(\"high-urls.txt\"),\n",[45,682,683],{"class":47,"line":459},[45,684,75],{},[10,686,687,688,691,692,695,696,698],{},"Named functions work too (",[19,689,690],{},"transform.map(add_port)"," where\n",[19,693,694],{},"def add_port(host): ...","). Signatures are checked: ",[19,697,607],{}," takes exactly one\nparameter.",[316,700,702],{"id":701},"execution-model","Execution model",[10,704,705],{},"Ops fall into four execution kinds; knowing them predicts memory behavior:",[154,707,708,721],{},[157,709,710],{},[160,711,712,715,718],{},[163,713,714],{},"Kind",[163,716,717],{},"Ops",[163,719,720],{},"Behavior",[173,722,723,753,770,793],{},[160,724,725,728,750],{},[178,726,727],{},"Element-wise (streaming)",[178,729,730,92,733,92,736,92,739,92,742,92,745,92,748],{},[19,731,732],{},"trim",[19,734,735],{},"lower",[19,737,738],{},"upper",[19,740,741],{},"replace",[19,743,744],{},"regex",[19,746,747],{},"regex_replace",[19,749,607],{},[178,751,752],{},"One item in, zero\u002Fone out; constant memory",[160,754,755,758,767],{},[178,756,757],{},"Streaming selection\u002Fexpansion",[178,759,760,92,762,92,764],{},[19,761,610],{},[19,763,613],{},[19,765,766],{},"split",[178,768,769],{},"Zero or more out per item; constant memory",[160,771,772,775,786],{},[178,773,774],{},"Stateful (streaming)",[178,776,777,92,780,92,783],{},[19,778,779],{},"take(n)",[19,781,782],{},"drop(n)",[19,784,785],{},"unique()",[178,787,788,789,792],{},"Small buffered state; ",[19,790,791],{},"unique"," memory grows with the unique set",[160,794,795,798,809],{},[178,796,797],{},"Barrier (buffering)",[178,799,800,92,803,92,806],{},[19,801,802],{},"sort",[19,804,805],{},"reverse",[19,807,808],{},"join(sep)",[178,810,811],{},"Buffers the whole sequence before emitting",[10,813,814,815,817,818,817,820,823],{},"Prefer streaming ops for 50k-host lists; reserve ",[19,816,802],{},"\u002F",[19,819,805],{},[19,821,822],{},"join"," for\nbounded outputs.",[24,825,827],{"id":826},"types-are-checked-statically","Types Are Checked Statically",[10,829,830],{},"Every value in a transform has a semantic type:",[10,832,833,92,836,92,839,92,842,92,845,92,848,92,851,96,854,92,857],{},[19,834,835],{},"String",[19,837,838],{},"Number",[19,840,841],{},"Bool",[19,843,844],{},"File",[19,846,847],{},"Directory",[19,849,850],{},"Json",[19,852,853],{},"Record",[19,855,856],{},"Items\u003CT>",[19,858,859],{},"Batch\u003CT>",[10,861,862],{},"Each operation declares which types it accepts, so mistakes fail at save time,\nnot at 3 AM mid-scan:",[36,864,866],{"className":38,"code":865,"language":40,"meta":41,"style":41},"# ERROR: .lower() needs strings, but json() produces a Json document.\ntransform.json().lower()\n\n# OK: .unique() works on any collection, including JSONL records.\ntransform.jsonl().unique()\n",[19,867,868,873,878,882,887],{"__ignoreMap":41},[45,869,870],{"class":47,"line":48},[45,871,872],{},"# ERROR: .lower() needs strings, but json() produces a Json document.\n",[45,874,875],{"class":47,"line":54},[45,876,877],{},"transform.json().lower()\n",[45,879,880],{"class":47,"line":60},[45,881,427],{"emptyLinePlaceholder":426},[45,883,884],{"class":47,"line":66},[45,885,886],{},"# OK: .unique() works on any collection, including JSONL records.\n",[45,888,889],{"class":47,"line":72},[45,890,891],{},"transform.jsonl().unique()\n",[10,893,894,895,897],{},"Collections serialize newline-delimited on write, so ",[19,896,194],{}," writes one\nvalue per line.",[24,899,901],{"id":900},"partitioning","Partitioning",[10,903,904,905,908],{},"Large datasets can be split into partitions for bounded parallel execution.\nPartition strategies come from the ",[19,906,907],{},"partition"," namespace:",[77,910,911,921],{},[80,912,913,916,917,920],{},[19,914,915],{},"partition.count(n)"," — approximately ",[19,918,919],{},"n"," partitions; sizes differ by at most\none.",[80,922,923,926,927,929],{},[19,924,925],{},"partition.items(n)"," — at most ",[19,928,919],{}," items per partition.",[36,931,933],{"className":38,"code":932,"language":40,"meta":41,"style":41},"# Split 50k live hosts into batches of 500 for parallel nuclei runs.\nstep.transform(\n    source = path.file(\"live.txt\").lines(),\n    pipeline = transform.trim().unique().partition(partition.items(500)),\n    destination = path.file(\"batch-{partition_seq}.txt\"),\n)\n",[19,934,935,940,944,949,954,959],{"__ignoreMap":41},[45,936,937],{"class":47,"line":48},[45,938,939],{},"# Split 50k live hosts into batches of 500 for parallel nuclei runs.\n",[45,941,942],{"class":47,"line":54},[45,943,51],{},[45,945,946],{"class":47,"line":60},[45,947,948],{},"    source = path.file(\"live.txt\").lines(),\n",[45,950,951],{"class":47,"line":66},[45,952,953],{},"    pipeline = transform.trim().unique().partition(partition.items(500)),\n",[45,955,956],{"class":47,"line":72},[45,957,958],{},"    destination = path.file(\"batch-{partition_seq}.txt\"),\n",[45,960,961],{"class":47,"line":298},[45,962,75],{},[10,964,965],{},"Rules:",[77,967,968,982,989,994,1006],{},[80,969,970,971,974,975,96,978,981],{},"A partitioned pipeline must use the ",[19,972,973],{},"{partition_seq}"," placeholder in its\ndestination, so each partition gets its own file (",[19,976,977],{},"batch-0.txt",[19,979,980],{},"batch-1.txt",", ...).",[80,983,984,985,988],{},"Non-partitioned pipelines must ",[14,986,987],{},"not"," contain the placeholder.",[80,990,991,993],{},[19,992,907],{}," is terminal — nothing can be chained after it.",[80,995,996,999,1000,1002,1003,1005],{},[19,997,998],{},"partition()"," takes only a strategy: ",[19,1001,915],{}," or\n",[19,1004,925],{},". A bare integer is rejected.",[80,1007,1008],{},"Execution is deterministic and order-preserving; an empty dataset produces\nzero partitions.",[10,1010,1011,1012,1019,1020,1023],{},"Pair partitioning with ",[1013,1014,1016],"a",{"href":1015},"\u002Fworkflows\u002Ffor-each",[19,1017,1018],{},"step.for_each"," to process each\nbatch in parallel. The ",[19,1021,1022],{},"for_each"," items can be a typed transform expression\ndirectly:",[36,1025,1027],{"className":38,"code":1026,"language":40,"meta":41,"style":41},"activity(\n    name = \"distributed-nuclei\",\n    triggers = [trigger.on(probe.succeeded())],\n    steps = [\n        step.for_each(\n            # Unique live hosts, split into ~20 balanced batches.\n            items = path.file(\"live.txt\").lines().unique().partition(partition.count(20)),\n            do = lambda batch: [\n                # Materialize this batch as a file and scan it.\n                step.exec(\n                    command = [\"nuclei\", \"-l\", batch.as_file(), \"-severity\", \"critical\", \"-o\", \"findings.txt\"],\n                ),\n            ],\n        ),\n    ],\n)\n",[19,1028,1029,1034,1039,1044,1049,1054,1059,1064,1069,1074,1079,1084,1089,1094,1100,1106],{"__ignoreMap":41},[45,1030,1031],{"class":47,"line":48},[45,1032,1033],{},"activity(\n",[45,1035,1036],{"class":47,"line":54},[45,1037,1038],{},"    name = \"distributed-nuclei\",\n",[45,1040,1041],{"class":47,"line":60},[45,1042,1043],{},"    triggers = [trigger.on(probe.succeeded())],\n",[45,1045,1046],{"class":47,"line":66},[45,1047,1048],{},"    steps = [\n",[45,1050,1051],{"class":47,"line":72},[45,1052,1053],{},"        step.for_each(\n",[45,1055,1056],{"class":47,"line":298},[45,1057,1058],{},"            # Unique live hosts, split into ~20 balanced batches.\n",[45,1060,1061],{"class":47,"line":423},[45,1062,1063],{},"            items = path.file(\"live.txt\").lines().unique().partition(partition.count(20)),\n",[45,1065,1066],{"class":47,"line":430},[45,1067,1068],{},"            do = lambda batch: [\n",[45,1070,1071],{"class":47,"line":436},[45,1072,1073],{},"                # Materialize this batch as a file and scan it.\n",[45,1075,1076],{"class":47,"line":441},[45,1077,1078],{},"                step.exec(\n",[45,1080,1081],{"class":47,"line":447},[45,1082,1083],{},"                    command = [\"nuclei\", \"-l\", batch.as_file(), \"-severity\", \"critical\", \"-o\", \"findings.txt\"],\n",[45,1085,1086],{"class":47,"line":453},[45,1087,1088],{},"                ),\n",[45,1090,1091],{"class":47,"line":459},[45,1092,1093],{},"            ],\n",[45,1095,1097],{"class":47,"line":1096},14,[45,1098,1099],{},"        ),\n",[45,1101,1103],{"class":47,"line":1102},15,[45,1104,1105],{},"    ],\n",[45,1107,1109],{"class":47,"line":1108},16,[45,1110,75],{},[10,1112,1113,1114,100],{},"Alternatively, write partitions to files first and fan out over them with\n",[19,1115,1116],{},"path.glob(base = \"batches\", pattern = \"scan-*.txt\")",[10,1118,1119,1120,92,1123,1126,1127,1130,1131,1134,1135,817,1137,1139],{},"Records support field access (",[19,1121,1122],{},"row.url",[19,1124,1125],{},"row.severity","), method-style string\nops on fields (",[19,1128,1129],{},"row.host.lower()","), and index access (",[19,1132,1133],{},"row.urls[0]",") inside\n",[19,1136,607],{},[19,1138,610],{}," lambdas and dataset mappings.",[1141,1142,1143],"style",{},"html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":41,"searchDepth":72,"depth":66,"links":1145},[1146,1147,1148,1155,1156],{"id":26,"depth":54,"text":27},{"id":151,"depth":54,"text":152},{"id":310,"depth":54,"text":311,"children":1149},[1150,1151,1152,1154],{"id":318,"depth":60,"text":319},{"id":464,"depth":60,"text":465},{"id":603,"depth":60,"text":1153},"Lambda ops: map \u002F filter \u002F flat_map",{"id":701,"depth":60,"text":702},{"id":826,"depth":54,"text":827},{"id":900,"depth":54,"text":901},"Typed data pipelines for filtering, normalizing, and partitioning scan output with step.transform.","md",{},"\u002Fworkflows\u002Ftransforms",null,{"title":5,"description":1157},{"loc":1160},"01.workflows\u002F06.transforms","zoOZEnuxSxQJ9uyd9qiaO1y8dxgANB2LF0ijgpuvTi4",[1167,1226,1242,1261,1296,1312,1324,1335,1346,1357,1381,1400,1420,1431,1442],{"title":1168,"path":1169,"stem":1170,"children":1171},"Workflows","\u002Fworkflows","01.workflows",[1172,1174,1178,1182,1186,1190,1194,1198,1199,1202,1206,1210,1214,1218,1222],{"title":1168,"path":1169,"stem":1173},"01.workflows\u002Findex",{"title":1175,"path":1176,"stem":1177},"Workflow Quick Reference","\u002Fworkflows\u002Fquick-reference","01.workflows\u002F00.quick-reference",{"title":1179,"path":1180,"stem":1181},"Workflows Overview","\u002Fworkflows\u002Foverview","01.workflows\u002F01.overview",{"title":1183,"path":1184,"stem":1185},"Workflow Syntax Reference","\u002Fworkflows\u002Fsyntax-reference","01.workflows\u002F02.syntax-reference",{"title":1187,"path":1188,"stem":1189},"Steps","\u002Fworkflows\u002Fsteps","01.workflows\u002F03.steps",{"title":1191,"path":1192,"stem":1193},"Step Outputs","\u002Fworkflows\u002Fstep-outputs","01.workflows\u002F04.step-outputs",{"title":1195,"path":1196,"stem":1197},"Concurrency","\u002Fworkflows\u002Fconcurrency","01.workflows\u002F05.concurrency",{"title":5,"path":1160,"stem":1164},{"title":1200,"path":1015,"stem":1201},"For Each","01.workflows\u002F07.for-each",{"title":1203,"path":1204,"stem":1205},"Modules","\u002Fworkflows\u002Fmodules","01.workflows\u002F08.modules",{"title":1207,"path":1208,"stem":1209},"Agents","\u002Fworkflows\u002Fagents","01.workflows\u002F09.agents",{"title":1211,"path":1212,"stem":1213},"Decisions","\u002Fworkflows\u002Fdecisions","01.workflows\u002F10.decisions",{"title":1215,"path":1216,"stem":1217},"Configuration References","\u002Fworkflows\u002Fconfig","01.workflows\u002F11.config",{"title":1219,"path":1220,"stem":1221},"Comparison With Other Automation Systems","\u002Fworkflows\u002Fcomparison","01.workflows\u002F12.comparison",{"title":1223,"path":1224,"stem":1225},"Generate with AI","\u002Fworkflows\u002Fai-generation","01.workflows\u002F13.ai-generation",{"title":1227,"path":1228,"stem":1229,"children":1230},"Projects","\u002Fprojects","02.projects",[1231,1234,1238],{"title":1232,"path":1228,"stem":1233},"Projects & Runs","02.projects\u002Findex",{"title":1235,"path":1236,"stem":1237},"Projects Overview","\u002Fprojects\u002Foverview","02.projects\u002F1.overview",{"title":1239,"path":1240,"stem":1241},"Runs & Artifacts","\u002Fprojects\u002Fruns-artifacts","02.projects\u002F2.runs-artifacts",{"title":1243,"path":1244,"stem":1245,"children":1246},"Runners","\u002Frunners","03.runners",[1247,1249,1253,1257],{"title":1243,"path":1244,"stem":1248},"03.runners\u002Findex",{"title":1250,"path":1251,"stem":1252},"Runner Overview","\u002Frunners\u002Foverview","03.runners\u002F1.overview",{"title":1254,"path":1255,"stem":1256},"Getting Started with Runners","\u002Frunners\u002Fgetting-started","03.runners\u002F2.getting-started",{"title":1258,"path":1259,"stem":1260},"Custom Runner Images","\u002Frunners\u002Fcustom-image","03.runners\u002F3.custom-image",{"title":1262,"path":1263,"stem":1264,"children":1265},"Datasets","\u002Fdatasets","04.datasets",[1266,1268,1272,1276,1280,1284,1288,1292],{"title":1262,"path":1263,"stem":1267},"04.datasets\u002Findex",{"title":1269,"path":1270,"stem":1271},"Datasets Overview","\u002Fdatasets\u002Foverview","04.datasets\u002F1.overview",{"title":1273,"path":1274,"stem":1275},"Dataset Schemas and Identity","\u002Fdatasets\u002Fschema","04.datasets\u002F2.schema",{"title":1277,"path":1278,"stem":1279},"Pushing and Reading Dataset Rows","\u002Fdatasets\u002Fworkflows","04.datasets\u002F3.workflows",{"title":1281,"path":1282,"stem":1283},"Dataset SQL Reference","\u002Fdatasets\u002Fsql","04.datasets\u002F4.sql",{"title":1285,"path":1286,"stem":1287},"Dataset Structured Filters","\u002Fdatasets\u002Ffilters","04.datasets\u002F5.filters",{"title":1289,"path":1290,"stem":1291},"Dataset Saved Views","\u002Fdatasets\u002Fviews","04.datasets\u002F6.views",{"title":1293,"path":1294,"stem":1295},"Datasets API and CLI","\u002Fdatasets\u002Fapi-cli","04.datasets\u002F7.api-cli",{"title":1297,"path":1298,"stem":1299,"children":1300},"Agent","\u002Fagent","05.agent",[1301,1304,1308],{"title":1302,"path":1298,"stem":1303},"BH Agent","05.agent\u002Findex",{"title":1305,"path":1306,"stem":1307},"BH Agent Overview","\u002Fagent\u002Foverview","05.agent\u002F1.overview",{"title":1309,"path":1310,"stem":1311},"MCP servers","\u002Fagent\u002Fmcp-servers","05.agent\u002F2.mcp-servers",{"title":1313,"path":1314,"stem":1315,"children":1316},"Bhlast","\u002Fbhlast","06.bhlast",[1317,1320],{"title":1318,"path":1314,"stem":1319},"Bhlast Servers","06.bhlast\u002Findex",{"title":1321,"path":1322,"stem":1323},"Bhlast servers overview","\u002Fbhlast\u002Foverview","06.bhlast\u002F1.overview",{"title":1325,"path":1326,"stem":1327,"children":1328},"Notifications","\u002Fnotifications","07.notifications",[1329,1331],{"title":1325,"path":1326,"stem":1330},"07.notifications\u002Findex",{"title":1332,"path":1333,"stem":1334},"Notifications Overview","\u002Fnotifications\u002Foverview","07.notifications\u002F1.overview",{"title":1336,"path":1337,"stem":1338,"children":1339},"Blob Storage","\u002Fblob-storage","08.blob-storage",[1340,1342],{"title":1336,"path":1337,"stem":1341},"08.blob-storage\u002Findex",{"title":1343,"path":1344,"stem":1345},"Blob Storage Overview","\u002Fblob-storage\u002Foverview","08.blob-storage\u002F1.overview",{"title":1347,"path":1348,"stem":1349,"children":1350},"Payloads","\u002Fpayloads","09.payloads",[1351,1353],{"title":1347,"path":1348,"stem":1352},"09.payloads\u002Findex",{"title":1354,"path":1355,"stem":1356},"Payloads overview","\u002Fpayloads\u002Foverview","09.payloads\u002F1.overview",{"title":1358,"path":1359,"stem":1360,"children":1361},"Cve Watcher","\u002Fcve-watcher","10.cve-watcher",[1362,1365,1369,1373,1377],{"title":1363,"path":1359,"stem":1364},"CVE Watcher","10.cve-watcher\u002Findex",{"title":1366,"path":1367,"stem":1368},"CVE Watcher Overview","\u002Fcve-watcher\u002Foverview","10.cve-watcher\u002F1.overview",{"title":1370,"path":1371,"stem":1372},"CVE Watcher Search and Filters","\u002Fcve-watcher\u002Fsearch","10.cve-watcher\u002F2.search",{"title":1374,"path":1375,"stem":1376},"CVE Watcher Push Notifications","\u002Fcve-watcher\u002Fnotifications","10.cve-watcher\u002F3.notifications",{"title":1378,"path":1379,"stem":1380},"CVE Watcher API and Scopes","\u002Fcve-watcher\u002Fapi","10.cve-watcher\u002F4.api",{"title":1382,"path":1383,"stem":1384,"children":1385},"Developer","\u002Fdeveloper","11.developer",[1386,1388,1392,1396],{"title":1382,"path":1383,"stem":1387},"11.developer\u002Findex",{"title":1389,"path":1390,"stem":1391},"Developer Overview","\u002Fdeveloper\u002Foverview","11.developer\u002F1.overview",{"title":1393,"path":1394,"stem":1395},"Personal Access Token Scopes","\u002Fdeveloper\u002Fscopes","11.developer\u002F2.scopes",{"title":1397,"path":1398,"stem":1399},"Personal Access Tokens","\u002Fdeveloper\u002Fpersonal-access-tokens","11.developer\u002F3.personal-access-tokens",{"title":1401,"path":1402,"stem":1403,"children":1404},"Bh","\u002Fbh","12.bh",[1405,1408,1412,1416],{"title":1406,"path":1402,"stem":1407},"BH CLI","12.bh\u002Findex",{"title":1409,"path":1410,"stem":1411},"BH CLI Overview","\u002Fbh\u002Foverview","12.bh\u002F1.overview",{"title":1413,"path":1414,"stem":1415},"BH CLI Commands","\u002Fbh\u002Fcommands","12.bh\u002F2.commands",{"title":1417,"path":1418,"stem":1419},"BH CLI Examples","\u002Fbh\u002Fexamples","12.bh\u002F3.examples",{"title":1421,"path":1422,"stem":1423,"children":1424},"Billing","\u002Fbilling","13.billing",[1425,1427],{"title":1421,"path":1422,"stem":1426},"13.billing\u002Findex",{"title":1428,"path":1429,"stem":1430},"Billing Overview","\u002Fbilling\u002Foverview","13.billing\u002F1.overview",{"title":1432,"path":1433,"stem":1434,"children":1435},"Tools","\u002Ftools","14.tools",[1436,1438],{"title":1432,"path":1433,"stem":1437},"14.tools\u002Findex",{"title":1439,"path":1440,"stem":1441},"Cookie Lab","\u002Ftools\u002Fcookie-lab","14.tools\u002F1.cookie-lab",{"title":1443,"path":1444,"stem":1445,"children":1446},"Site Policies","\u002Fsite-policies","99.site-policies",[1447,1449,1462,1466,1470,1474,1478,1482,1486],{"title":1443,"path":1444,"stem":1448},"99.site-policies\u002Findex",{"title":1450,"path":1451,"stem":1452,"children":1453},"Privacy Policy","\u002Fsite-policies\u002Fprivacy-policy","99.site-policies\u002F1.privacy-policy",[1454,1458],{"title":1455,"path":1456,"stem":1457},"Company Processors","\u002Fsite-policies\u002Fprivacy-policy\u002Fcompany-processors","99.site-policies\u002Fprivacy-policy\u002F1.company-processors",{"title":1459,"path":1460,"stem":1461},"Data Export Instructions","\u002Fsite-policies\u002Fprivacy-policy\u002Fdata-export-instructions","99.site-policies\u002Fprivacy-policy\u002F2.data-export-instructions",{"title":1463,"path":1464,"stem":1465},"Terms of Service","\u002Fsite-policies\u002Fterms-of-service","99.site-policies\u002F2.terms-of-service",{"title":1467,"path":1468,"stem":1469},"California Resident Notice at Collection","\u002Fsite-policies\u002Fccpa","99.site-policies\u002F3.ccpa",{"title":1471,"path":1472,"stem":1473},"Cancellation Policy","\u002Fsite-policies\u002Fcancellation-policy","99.site-policies\u002F4.cancellation-policy",{"title":1475,"path":1476,"stem":1477},"Refund Policy","\u002Fsite-policies\u002Frefund-policy","99.site-policies\u002F5.refund-policy",{"title":1479,"path":1480,"stem":1481},"Taxes on Services","\u002Fsite-policies\u002Ftaxes-on-services","99.site-policies\u002F6.taxes-on-services",{"title":1483,"path":1484,"stem":1485},"Disclaimer","\u002Fsite-policies\u002Fdisclaimer","99.site-policies\u002F7.disclaimer",{"title":1487,"path":1488,"stem":1489,"children":1490},"Security","\u002Fsite-policies\u002Fsecurity","99.site-policies\u002Fsecurity\u002Findex",[1491,1492,1496],{"title":1487,"path":1488,"stem":1489},{"title":1493,"path":1494,"stem":1495},"Security Overview","\u002Fsite-policies\u002Fsecurity\u002Fsecurity-overview","99.site-policies\u002Fsecurity\u002F1.security-overview",{"title":1497,"path":1498,"stem":1499},"Security Response","\u002Fsite-policies\u002Fsecurity\u002Fsecurity-response","99.site-policies\u002Fsecurity\u002F2.security-response",1794410312375]