[{"data":1,"prerenderedAt":2748},["ShallowReactive",2],{"navigation":3,"page:\u002Fguides\u002Ftesting":1019,"surround:\u002Fguides\u002Ftesting":2740},[4,21,82,133,154,1010],[5,6,7,8],"Getting started","\u002Fgetting-started","i-lucide-rocket",[9,12,15,18],[10,6,11,11],"What Kaabist is",0,[13,14,11,11],"API keys and modes","keys-and-modes",[16,17,11,11],"Your first job","first-job",[19,20,11,11],"Understanding the envelope","envelope",[22,23,24,25],"Concepts","\u002Fconcepts","i-lucide-book-open",[26,28,31,34,37,40,43,46,49,52,55,58,61,64,67,70,73,76,79],[27,23,11,11],"Overview",[29,30,11,11],"Programs and versions","programs-and-versions",[32,33,11,11],"Tiers and escalation","tiers-and-escalation",[35,36,11,11],"The http tier","http-tier",[38,39,11,11],"The browser tier","browser-tier",[41,42,11,11],"Sessions","sessions",[44,45,11,11],"Human in the loop","human-in-the-loop",[47,48,11,11],"How healing works","self-healing",[50,51,11,11],"Obstacles and captchas","obstacles",[53,54,11,11],"Validation and warnings","validation-and-warnings",[56,57,11,11],"Credits and billing","credits-and-billing",[59,60,11,11],"Limits","limits",[62,63,11,11],"Locators","locators",[65,66,11,11],"Snapshots","snapshots",[68,69,11,11],"Egress and proxies","egress-and-proxies",[71,72,11,11],"Queueing and fairness","queueing",[74,75,11,11],"Accounts, organisations and roles","accounts-and-roles",[77,78,11,11],"Data retention","data-retention",[80,81,11,11],"Documents and PDFs","documents-and-pdfs",[83,84,85,86],"Guides","\u002Fguides","i-lucide-map",[87,88,91,94,97,100,103,106,109,112,115,118,121,124,127,130],[27,84,11,11],[89,90,11,11],"Authenticated flows","authenticated-flows",[92,93,11,11],"Hosted resolve pages","hosted-resolve",[95,96,11,11],"Lists and pagination","lists-and-pagination",[98,99,11,11],"JSON and network locators","json-and-network-locators",[101,102,11,11],"Schedules and change detection","schedules-and-change-detection",[104,105,11,11],"Webhooks","webhooks",[107,108,11,11],"Bring your own providers","byo-providers",[110,111,11,11],"Testing: replay, dry-run and test mode","testing",[113,114,11,11],"Side-effect safety","side-effect-safety",[116,117,11,11],"Security & data handling","security-and-data",[119,120,11,11],"Migrating from other tools","migrating",[122,123,11,11],"Drift detection and canaries","drift-and-canaries",[125,126,11,11],"SDKs","sdks",[128,129,11,11],"The kaab CLI","cli",[131,132,11,11],"Coding agents (MCP)","mcp",[134,135,136,137],"Panel","\u002Fpanel","i-lucide-layout-dashboard",[138,139,142,145,148,151],[27,135,11,11],[140,141,11,11],"The program builder","builder",[143,144,11,11],"Picking and recording","picking-and-recording",[146,147,11,11],"Taking over a job","takeover",[149,150,11,11],"Getting help","support",[152,153,11,11],"Referrals","referrals",[155,156,157,158],"Reference","\u002Freference","i-lucide-library",[159,160,520,834,837,840,1006,1007],[27,156,11,11],[161,162,11,163],"API","api",[164,166,169,212,222,274,297,316,347,376,389,396,403,410,426,447,454],[27,165,11,11],"\u002Freference\u002Fapi",[167,168,11,11],"Interactive reference","explorer",[170,171,11,172],"Jobs","jobs",[173,176,179,182,185,188,191,194,197,200,203,206,209],[174,175,11,11],"Create job","create-job",[177,178,11,11],"List jobs","list-jobs",[180,181,11,11],"Create job batch","create-job-batch",[183,184,11,11],"Estimate job","estimate-job",[186,187,11,11],"Get job","get-job",[189,190,11,11],"List job steps","list-job-steps",[192,193,11,11],"Get job snapshot","get-job-snapshot",[195,196,11,11],"Get job screenshot","get-job-screenshot",[198,199,11,11],"Get job artifact","get-job-artifact",[201,202,11,11],"Submit job input","submit-job-input",[204,205,11,11],"Resume job","resume-job",[207,208,11,11],"Takeover job","takeover-job",[210,211,11,11],"Cancel job","cancel-job",[213,214,11,215],"Batches","batches",[216,219],[217,218,11,11],"Get batch","get-batch",[220,221,11,11],"List batch jobs","list-batch-jobs",[223,224,11,225],"Programs","programs",[226,229,232,235,238,241,244,247,250,253,256,259,262,265,268,271],[227,228,11,11],"Create program","create-program",[230,231,11,11],"List programs","list-programs",[233,234,11,11],"Lint program","lint-program",[236,237,11,11],"Get program","get-program",[239,240,11,11],"Update program","update-program",[242,243,11,11],"Delete program","delete-program",[245,246,11,11],"List program versions","list-program-versions",[248,249,11,11],"Create program version","create-program-version",[251,252,11,11],"Update program version","update-program-version",[254,255,11,11],"Delete program version","delete-program-version",[257,258,11,11],"Get program version","get-program-version",[260,261,11,11],"Publish program version","publish-program-version",[263,264,11,11],"Diff program version","diff-program-version",[266,267,11,11],"Replay program version","replay-program-version",[269,270,11,11],"Set program alias","set-program-alias",[272,273,11,11],"Delete program alias","delete-program-alias",[41,42,11,275],[276,279,282,285,288,291,294],[277,278,11,11],"Create session","create-session",[280,281,11,11],"List sessions","list-sessions",[283,284,11,11],"Get session","get-session",[286,287,11,11],"Update session","update-session",[289,290,11,11],"Delete session","delete-session",[292,293,11,11],"Verify session","verify-session",[295,296,11,11],"Refresh session","refresh-session",[298,299,11,300],"Proposals","proposals",[301,304,307,310,313],[302,303,11,11],"List proposals","list-proposals",[305,306,11,11],"Get proposal","get-proposal",[308,309,11,11],"Approve proposal","approve-proposal",[311,312,11,11],"Reject proposal","reject-proposal",[314,315,11,11],"Edit proposal","edit-proposal",[317,318,11,319],"Schedules","schedules",[320,323,326,329,332,335,338,341,344],[321,322,11,11],"Create schedule","create-schedule",[324,325,11,11],"List schedules","list-schedules",[327,328,11,11],"Get schedule","get-schedule",[330,331,11,11],"Update schedule","update-schedule",[333,334,11,11],"Delete schedule","delete-schedule",[336,337,11,11],"Pause schedule","pause-schedule",[339,340,11,11],"Resume schedule","resume-schedule",[342,343,11,11],"List schedule runs","list-schedule-runs",[345,346,11,11],"Get schedule run diff","get-schedule-run-diff",[104,105,11,348],[349,352,355,358,361,364,367,370,373],[350,351,11,11],"Create webhook","create-webhook",[353,354,11,11],"List webhooks","list-webhooks",[356,357,11,11],"Get webhook","get-webhook",[359,360,11,11],"Update webhook","update-webhook",[362,363,11,11],"Delete webhook","delete-webhook",[365,366,11,11],"Rotate webhook secret","rotate-webhook-secret",[368,369,11,11],"List webhook deliveries","list-webhook-deliveries",[371,372,11,11],"Resend webhook delivery","resend-webhook-delivery",[374,375,11,11],"Test webhook","test-webhook",[377,378,11,379],"Organisation","org",[380,383,386],[381,382,11,11],"Rotate org webhook secret","rotate-org-webhook-secret",[384,385,11,11],"Get org","get-org",[387,388,11,11],"Request data deletion","request-data-deletion",[390,391,11,392],"Credits","credits",[393],[394,395,11,11],"Get credits","get-credits",[397,398,11,399],"Ledger","ledger",[400],[401,402,11,11],"List ledger entries","list-ledger-entries",[404,405,11,406],"Usage","usage",[407],[408,409,11,11],"Get usage","get-usage",[411,412,11,413],"API keys","api-keys",[414,417,420,423],[415,416,11,11],"List API keys","list-api-keys",[418,419,11,11],"Create API key","create-api-key",[421,422,11,11],"Rotate API key","rotate-api-key",[424,425,11,11],"Revoke API key","revoke-api-key",[427,108,11,428],"BYO providers",[429,432,435,438,441,444],[430,431,11,11],"List BYO providers","list-byo-providers",[433,434,11,11],"Create BYO provider","create-byo-provider",[436,437,11,11],"Get BYO provider","get-byo-provider",[439,440,11,11],"Update BYO provider","update-byo-provider",[442,443,11,11],"Delete BYO provider","delete-byo-provider",[445,446,11,11],"Test BYO provider","test-byo-provider",[448,449,11,450],"Sites","sites",[451],[452,453,11,11],"Get site profile","get-site-profile",[455,456,11,457],"Objects","schemas",[458,460,463,466,469,470,473,476,479,482,485,487,490,493,496,499,502,505,508,511,514,517],[27,459,11,11],"\u002Freference\u002Fapi\u002Fschemas",[461,462,11,11],"ApiKey","api-key",[464,465,11,11],"Batch","batch",[467,468,11,11],"ByoProvider","byo-provider",[390,391,11,11],[471,472,11,11],"ErrorResponse","error-response",[474,475,11,11],"ExtractSchema","extract-schema",[477,478,11,11],"Job","job",[480,481,11,11],"LedgerEntry","ledger-entry",[483,484,11,11],"LintResult","lint-result",[486,378,11,11],"Org",[488,489,11,11],"Program","program",[491,492,11,11],"ProgramDefinition","program-definition",[494,495,11,11],"ProgramVersion","program-version",[497,498,11,11],"Proposal","proposal",[500,501,11,11],"Schedule","schedule",[503,504,11,11],"Session","session",[506,507,11,11],"SiteProfile","site-profile",[509,510,11,11],"StepRecord","step-record",[512,513,11,11],"Webhook","webhook",[515,516,11,11],"WebhookDelivery","webhook-delivery",[518,519,11,11],"WebhookEvent","webhook-event",[521,522,11,523],"DSL","dsl",[524,526,529,532,535,538,603,637,640,751,754,756],[27,525,11,11],"\u002Freference\u002Fdsl",[527,528,11,11],"Inputs","inputs",[530,531,11,11],"Options","options",[533,534,11,11],"Policies","policies",[536,537,11,11],"Steps","steps",[539,540,11,541],"Actions","actions",[542,544,546,548,550,552,554,556,558,560,562,565,568,570,573,575,577,579,582,585,587,589,591,594,596,598,600],[27,543,11,11],"\u002Freference\u002Fdsl\u002Factions",[545,545,11,11],"fill",[547,547,11,11],"click",[549,549,11,11],"press",[551,551,11,11],"hover",[553,553,11,11],"select",[555,555,11,11],"check",[557,557,11,11],"uncheck",[559,559,11,11],"upload",[561,561,11,11],"scroll",[563,564,11,11],"wait_for","wait-for",[566,567,11,11],"set_session","set-session",[569,569,11,11],"extract",[571,572,11,11],"capture_response","capture-response",[574,574,11,11],"evaluate",[576,576,11,11],"paginate",[578,578,11,11],"download",[580,581,11,11],"read_pdf","read-pdf",[583,584,11,11],"set_vars","set-vars",[586,586,11,11],"sleep",[588,588,11,11],"reload",[590,590,11,11],"goto",[592,593,11,11],"await_input","await-input",[595,595,11,11],"checkpoint",[597,597,11,11],"screenshot",[599,599,11,11],"when",[601,602,11,11],"loop_until","loop-until",[604,605,11,606],"Conditions","conditions",[607,609,611,613,615,617,619,621,623,625,627,629,631,633,635],[27,608,11,11],"\u002Freference\u002Fdsl\u002Fconditions",[610,610,11,11],"url",[612,612,11,11],"title",[614,614,11,11],"visible",[616,616,11,11],"hidden",[618,618,11,11],"exists",[620,620,11,11],"absent",[622,622,11,11],"text",[624,624,11,11],"count",[626,626,11,11],"network",[628,628,11,11],"var",[630,630,11,11],"landmark",[632,632,11,11],"all",[634,634,11,11],"any",[636,636,11,11],"not",[638,639,11,11],"Targets","targets",[641,642,11,643],"Fields","fields",[644,646,649,651,654,656,659,661,663,666,668,671,672,674,677,679,681,683,685,687,690,693,695,698,701,703,704,707,709,712,714,716,718,720,722,724,726,728,731,733,734,736,738,740,743,745,747,749],[27,645,11,11],"\u002Freference\u002Fdsl\u002Ffields",[647,648,11,11],"allow_suffix","allow-suffix",[650,650,11,11],"attr",[652,653,11,11],"base_url","base-url",[655,655,11,11],"candidates",[657,658,11,11],"case_insensitive","case-insensitive",[660,660,11,11],"country",[662,662,11,11],"currency",[664,665,11,11],"date_format","date-format",[667,667,11,11],"decimal",[669,670,11,11],"false_values","false-values",[642,642,11,11],[673,673,11,11],"flags",[675,676,11,11],"follow_redirects","follow-redirects",[678,678,11,11],"frame",[680,680,11,11],"json",[682,682,11,11],"locale",[684,684,11,11],"many",[686,686,11,11],"max",[688,689,11,11],"max_items","max-items",[691,692,11,11],"max_length","max-length",[694,694,11,11],"min",[696,697,11,11],"min_items","min-items",[699,700,11,11],"min_length","min-length",[702,702,11,11],"name",[626,626,11,11],[705,706,11,11],"null_markers","null-markers",[708,708,11,11],"nullable",[710,711,11,11],"on_invalid","on-invalid",[713,713,11,11],"pattern",[715,715,11,11],"presence",[717,717,11,11],"regex",[719,719,11,11],"required",[721,721,11,11],"role",[723,723,11,11],"sample",[725,725,11,11],"selector",[727,727,11,11],"source",[729,730,11,11],"strip_fragment","strip-fragment",[732,732,11,11],"testid",[622,622,11,11],[735,735,11,11],"thousands",[737,737,11,11],"timezone",[739,739,11,11],"transforms",[741,742,11,11],"true_values","true-values",[744,744,11,11],"type",[746,746,11,11],"unique",[748,748,11,11],"values",[750,750,11,11],"xpath",[752,753,11,11],"Assertions","assertions",[755,739,11,11],"Transforms",[757,758,11,759],"Lint codes","lint",[760,762,765,768,771,774,777,780,783,786,789,792,795,798,801,804,807,810,813,816,819,822,825,828,831],[27,761,11,11],"\u002Freference\u002Fdsl\u002Flint",[763,764,11,11],"E001","e001",[766,767,11,11],"E002","e002",[769,770,11,11],"E003","e003",[772,773,11,11],"E004","e004",[775,776,11,11],"E005","e005",[778,779,11,11],"E006","e006",[781,782,11,11],"E007","e007",[784,785,11,11],"E008","e008",[787,788,11,11],"E009","e009",[790,791,11,11],"E010","e010",[793,794,11,11],"E011","e011",[796,797,11,11],"E012","e012",[799,800,11,11],"E013","e013",[802,803,11,11],"E014","e014",[805,806,11,11],"W001","w001",[808,809,11,11],"W002","w002",[811,812,11,11],"W003","w003",[814,815,11,11],"W004","w004",[817,818,11,11],"W005","w005",[820,821,11,11],"W006","w006",[823,824,11,11],"W007","w007",[826,827,11,11],"W008","w008",[829,830,11,11],"W009","w009",[832,833,11,11],"W010","w010",[835,836,11,11],"Codes","codes",[838,839,11,11],"Events","events",[841,129,11,842],"CLI",[843,845,847,850,853,856,859,862,865,868,871,874,877,880,883,886,889,892,895,898,901,904,907,910,913,916,919,922,925,928,931,934,937,940,943,946,949,952,955,958,961,964,967,970,973,976,979,982,985,988,991,994,997,1000,1003],[27,844,11,11],"\u002Freference\u002Fcli",[846,846,11,11],"kaab",[848,849,11,11],"kaab auth","kaab-auth",[851,852,11,11],"kaab auth login","kaab-auth-login",[854,855,11,11],"kaab auth logout","kaab-auth-logout",[857,858,11,11],"kaab auth status","kaab-auth-status",[860,861,11,11],"kaab completion","kaab-completion",[863,864,11,11],"kaab completion bash","kaab-completion-bash",[866,867,11,11],"kaab completion fish","kaab-completion-fish",[869,870,11,11],"kaab completion powershell","kaab-completion-powershell",[872,873,11,11],"kaab completion zsh","kaab-completion-zsh",[875,876,11,11],"kaab credits","kaab-credits",[878,879,11,11],"kaab jobs","kaab-jobs",[881,882,11,11],"kaab jobs cancel","kaab-jobs-cancel",[884,885,11,11],"kaab jobs get","kaab-jobs-get",[887,888,11,11],"kaab jobs input","kaab-jobs-input",[890,891,11,11],"kaab jobs list","kaab-jobs-list",[893,894,11,11],"kaab jobs resume","kaab-jobs-resume",[896,897,11,11],"kaab jobs run","kaab-jobs-run",[899,900,11,11],"kaab jobs snapshot","kaab-jobs-snapshot",[902,903,11,11],"kaab jobs steps","kaab-jobs-steps",[905,906,11,11],"kaab jobs takeover","kaab-jobs-takeover",[908,909,11,11],"kaab open","kaab-open",[911,912,11,11],"kaab open job","kaab-open-job",[914,915,11,11],"kaab programs","kaab-programs",[917,918,11,11],"kaab programs alias","kaab-programs-alias",[920,921,11,11],"kaab programs alias set","kaab-programs-alias-set",[923,924,11,11],"kaab programs diff","kaab-programs-diff",[926,927,11,11],"kaab programs lint","kaab-programs-lint",[929,930,11,11],"kaab programs list","kaab-programs-list",[932,933,11,11],"kaab programs pull","kaab-programs-pull",[935,936,11,11],"kaab programs push","kaab-programs-push",[938,939,11,11],"kaab proposals","kaab-proposals",[941,942,11,11],"kaab proposals approve","kaab-proposals-approve",[944,945,11,11],"kaab proposals list","kaab-proposals-list",[947,948,11,11],"kaab proposals reject","kaab-proposals-reject",[950,951,11,11],"kaab proposals show","kaab-proposals-show",[953,954,11,11],"kaab schedules","kaab-schedules",[956,957,11,11],"kaab schedules create","kaab-schedules-create",[959,960,11,11],"kaab schedules list","kaab-schedules-list",[962,963,11,11],"kaab schedules pause","kaab-schedules-pause",[965,966,11,11],"kaab schedules resume","kaab-schedules-resume",[968,969,11,11],"kaab schedules runs","kaab-schedules-runs",[971,972,11,11],"kaab sessions","kaab-sessions",[974,975,11,11],"kaab sessions create","kaab-sessions-create",[977,978,11,11],"kaab sessions delete","kaab-sessions-delete",[980,981,11,11],"kaab sessions list","kaab-sessions-list",[983,984,11,11],"kaab sessions verify","kaab-sessions-verify",[986,987,11,11],"kaab usage","kaab-usage",[989,990,11,11],"kaab version","kaab-version",[992,993,11,11],"kaab webhooks","kaab-webhooks",[995,996,11,11],"kaab webhooks create","kaab-webhooks-create",[998,999,11,11],"kaab webhooks deliveries","kaab-webhooks-deliveries",[1001,1002,11,11],"kaab webhooks list","kaab-webhooks-list",[1004,1005,11,11],"kaab webhooks test","kaab-webhooks-test",[125,126,11,11],[1008,1009,11,11],"Plans and pricing","plans",[1011,1012,1013,1014],"Changelog","\u002Fchangelog","i-lucide-history",[1015,1016],[27,1012,11,11],[1017,1018,11,11],"Deprecations","deprecations",{"id":1020,"title":110,"body":1021,"breaking":2731,"date":2732,"description":2733,"extension":2734,"generated":2731,"meta":2735,"navigation":2151,"path":2736,"seo":2737,"stem":2738,"version":2732,"__hash__":2739},"docs\u002F3.guides\u002F08.testing.md",{"type":1022,"value":1023,"toc":2720},"minimark",[1024,1028,1033,1118,1127,1131,1142,1150,1196,1362,1371,1375,1383,1435,1449,1599,1603,1635,2004,2013,2017,2044,2048,2058,2077,2088,2519,2527,2531,2537,2604,2608,2653,2656,2716],[1025,1026,1027],"p",{},"Kaabist gives you several ways to try a program before it matters: test-mode keys run real jobs for\nfree, an estimate tells you what a job will cost without running it, lint checks a definition\nbefore you publish it, and a replay runs a new version against a page Kaabist already stored. Used\ntogether, they let you change a program with confidence and without surprises on your bill.",[1029,1030,1032],"h2",{"id":1031},"when-to-use-it","When to use it",[1034,1035,1036,1049],"table",{},[1037,1038,1039],"thead",{},[1040,1041,1042,1046],"tr",{},[1043,1044,1045],"th",{},"You want to know",[1043,1047,1048],{},"Use",[1050,1051,1052,1064,1074,1086,1100,1110],"tbody",{},[1040,1053,1054,1058],{},[1055,1056,1057],"td",{},"Is this definition valid?",[1055,1059,1060],{},[1061,1062,1063],"code",{},"POST \u002Fv1\u002Fprograms\u002Flint",[1040,1065,1066,1069],{},[1055,1067,1068],{},"What will this job cost, and at which tier?",[1055,1070,1071],{},[1061,1072,1073],{},"POST \u002Fv1\u002Fjobs\u002Festimate",[1040,1075,1076,1079],{},[1055,1077,1078],{},"Does it work end to end, without spending credits?",[1055,1080,1081,1082,1085],{},"a job created with an ",[1061,1083,1084],{},"sk_test_"," key",[1040,1087,1088,1091],{},[1055,1089,1090],{},"Does it work without performing its side effects?",[1055,1092,1093,1096,1097],{},[1061,1094,1095],{},"dry_run: true"," on ",[1061,1098,1099],{},"POST \u002Fv1\u002Fjobs",[1040,1101,1102,1105],{},[1055,1103,1104],{},"Does my new version read the page the old one read?",[1055,1106,1107],{},[1061,1108,1109],{},"POST \u002Fv1\u002Fprograms\u002F{id}\u002Fversions\u002F{n}\u002Freplay",[1040,1111,1112,1115],{},[1055,1113,1114],{},"Does it keep working after I stop watching?",[1055,1116,1117],{},"canaries and drift detection",[1025,1119,1120,1121,1126],{},"Testing does not replace watching production: a site can change the day after your tests pass.\nThat is what canaries and drift detection are for; see ",[1122,1123,1125],"a",{"href":1124},"\u002Fguides\u002Fdrift-and-canaries","drift and canaries",".",[1029,1128,1130],{"id":1129},"test-mode-keys","Test-mode keys",[1025,1132,1133,1134,1137,1138,1141],{},"An API key's prefix decides the mode of every job it creates: ",[1061,1135,1136],{},"sk_live_…"," runs live jobs,\n",[1061,1139,1140],{},"sk_test_…"," runs test jobs, and a request cannot override it.",[1025,1143,1144,1145,1149],{},"A test job is a ",[1146,1147,1148],"strong",{},"real"," job: it fetches the site, runs the browser and returns the same envelope.\nWhat differs:",[1151,1152,1153,1161,1176,1182,1193],"ul",{},[1154,1155,1156,1157,1160],"li",{},"it reserves and spends ",[1146,1158,1159],{},"no credits",";",[1154,1162,1163,1164,1167,1168,1171,1172,1175],{},"instead, each plan allows a number of test jobs per UTC day (50 on Free, 200 on Developer, 1 000\non Team; see ",[1122,1165,1009],{"href":1166},"\u002Freference\u002Fplans","). Past it, creating one answers ",[1061,1169,1170],{},"429 test_quota_exceeded","\nwith ",[1061,1173,1174],{},"Retry-After"," until midnight UTC;",[1154,1177,1178,1179,1160],{},"its result is kept for ",[1146,1180,1181],{},"24 hours",[1154,1183,1184,1185,1188,1189,1192],{},"its events carry ",[1061,1186,1187],{},"mode: \"test\"",", and a webhook endpoint registered with ",[1061,1190,1191],{},"mode: \"live\""," does not\nreceive them;",[1154,1194,1195],{},"a schedule created with a test key runs test jobs.",[1197,1198,1199],"code-group",{},[1200,1201,1207],"pre",{"className":1202,"code":1203,"filename":1204,"language":1205,"meta":1206,"style":1206},"language-ts shiki shiki-themes kaab-code kaab-code kaab-code","\u002F\u002F The key decides the mode: an sk_test_ key runs test jobs, which the live data never sees.\nconst probe = await kaab.jobs.create({\n  url: `${site}\u002Fp\u002F1`,\n  extract: { title: \"h1\" },\n  wait: 30,\n});\nconsole.log(`${probe.id} ran in ${probe.mode} mode`);\n","TypeScript","ts","",[1061,1208,1209,1218,1250,1279,1300,1313,1319],{"__ignoreMap":1206},[1210,1211,1214],"span",{"class":1212,"line":1213},"line",1,[1210,1215,1217],{"class":1216},"sKog0","\u002F\u002F The key decides the mode: an sk_test_ key runs test jobs, which the live data never sees.\n",[1210,1219,1221,1225,1229,1232,1235,1238,1241,1243,1246],{"class":1212,"line":1220},2,[1210,1222,1224],{"class":1223},"sqn18","const",[1210,1226,1228],{"class":1227},"sHJIl"," probe",[1210,1230,1231],{"class":1223}," =",[1210,1233,1234],{"class":1223}," await",[1210,1236,1237],{"class":1227}," kaab",[1210,1239,1126],{"class":1240},"sJugJ",[1210,1242,171],{"class":1227},[1210,1244,1245],{"class":1240},".create",[1210,1247,1249],{"class":1248},"sTzsn","({\n",[1210,1251,1253,1256,1259,1263,1266,1269,1272,1275],{"class":1212,"line":1252},3,[1210,1254,1255],{"class":1248},"  url",[1210,1257,1258],{"class":1223},":",[1210,1260,1262],{"class":1261},"suOy4"," `",[1210,1264,1265],{"class":1223},"${",[1210,1267,1268],{"class":1248},"site",[1210,1270,1271],{"class":1223},"}",[1210,1273,1274],{"class":1261},"\u002Fp\u002F1`",[1210,1276,1278],{"class":1277},"sQm2-",",\n",[1210,1280,1282,1285,1287,1290,1292,1295,1298],{"class":1212,"line":1281},4,[1210,1283,1284],{"class":1248},"  extract",[1210,1286,1258],{"class":1223},[1210,1288,1289],{"class":1248}," { title",[1210,1291,1258],{"class":1223},[1210,1293,1294],{"class":1261}," \"h1\"",[1210,1296,1297],{"class":1248}," }",[1210,1299,1278],{"class":1277},[1210,1301,1303,1306,1308,1311],{"class":1212,"line":1302},5,[1210,1304,1305],{"class":1248},"  wait",[1210,1307,1258],{"class":1223},[1210,1309,1310],{"class":1227}," 30",[1210,1312,1278],{"class":1277},[1210,1314,1316],{"class":1212,"line":1315},6,[1210,1317,1318],{"class":1248},"});\n",[1210,1320,1322,1325,1328,1331,1334,1336,1339,1342,1344,1347,1349,1351,1354,1356,1359],{"class":1212,"line":1321},7,[1210,1323,1324],{"class":1227},"console",[1210,1326,1327],{"class":1240},".log",[1210,1329,1330],{"class":1248},"(",[1210,1332,1333],{"class":1261},"`",[1210,1335,1265],{"class":1223},[1210,1337,1338],{"class":1227},"probe",[1210,1340,1341],{"class":1248},".id",[1210,1343,1271],{"class":1223},[1210,1345,1346],{"class":1261}," ran in ",[1210,1348,1265],{"class":1223},[1210,1350,1338],{"class":1227},[1210,1352,1353],{"class":1248},".mode",[1210,1355,1271],{"class":1223},[1210,1357,1358],{"class":1261}," mode`",[1210,1360,1361],{"class":1248},");\n",[1025,1363,1364,1365,1367,1368,1126],{},"Keep test and live keys apart in your configuration, the same way you keep separate databases:\nCI and staging get ",[1061,1366,1084],{},", production gets ",[1061,1369,1370],{},"sk_live_",[1029,1372,1374],{"id":1373},"estimating-a-jobs-cost","Estimating a job's cost",[1025,1376,1377,1379,1380,1382],{},[1061,1378,1073],{}," takes exactly the body of ",[1061,1381,1099],{}," and answers without running\nanything:",[1200,1384,1387],{"className":1385,"code":1386,"language":680,"meta":1206,"style":1206},"language-json shiki shiki-themes kaab-code kaab-code kaab-code","{ \"credits_min\": …, \"credits_max\": …, \"tier_expected\": \"http\", \"cache\": \"possible\" }\n",[1061,1388,1389],{"__ignoreMap":1206},[1210,1390,1391,1394,1397,1399,1402,1405,1408,1410,1412,1414,1417,1419,1422,1424,1427,1429,1432],{"class":1212,"line":1213},[1210,1392,1393],{"class":1248},"{ ",[1210,1395,1396],{"class":1223},"\"credits_min\"",[1210,1398,1258],{"class":1277},[1210,1400,1401],{"class":1248}," …",[1210,1403,1404],{"class":1277},",",[1210,1406,1407],{"class":1223}," \"credits_max\"",[1210,1409,1258],{"class":1277},[1210,1411,1401],{"class":1248},[1210,1413,1404],{"class":1277},[1210,1415,1416],{"class":1223}," \"tier_expected\"",[1210,1418,1258],{"class":1277},[1210,1420,1421],{"class":1261}," \"http\"",[1210,1423,1404],{"class":1277},[1210,1425,1426],{"class":1223}," \"cache\"",[1210,1428,1258],{"class":1277},[1210,1430,1431],{"class":1261}," \"possible\"",[1210,1433,1434],{"class":1248}," }\n",[1025,1436,1437,1440,1441,1444,1445,1448],{},[1061,1438,1439],{},"tier_expected"," is where the job is expected to run, and ",[1061,1442,1443],{},"cache"," says whether a cached result could\nserve it. The range is deliberately generous: the upper end includes a safety margin.\nAn estimate needs only a ",[1061,1446,1447],{},"read"," key, is free and never\nenqueues a job.",[1197,1450,1451],{},[1200,1452,1454],{"className":1202,"code":1453,"filename":1204,"language":1205,"meta":1206,"style":1206},"const estimate = await kaab.jobs.estimate({\n  url: `${site}\u002Fp\u002F1`,\n  extract: { title: \"h1\", price: { selector: \".price\", type: \"money\" } },\n});\nconsole.log(\n  `between ${estimate.credits_min} and ${estimate.credits_max} credits at ${estimate.tier_expected} tier`,\n);\n",[1061,1455,1456,1478,1496,1538,1542,1551,1595],{"__ignoreMap":1206},[1210,1457,1458,1460,1463,1465,1467,1469,1471,1473,1476],{"class":1212,"line":1213},[1210,1459,1224],{"class":1223},[1210,1461,1462],{"class":1227}," estimate",[1210,1464,1231],{"class":1223},[1210,1466,1234],{"class":1223},[1210,1468,1237],{"class":1227},[1210,1470,1126],{"class":1240},[1210,1472,171],{"class":1227},[1210,1474,1475],{"class":1240},".estimate",[1210,1477,1249],{"class":1248},[1210,1479,1480,1482,1484,1486,1488,1490,1492,1494],{"class":1212,"line":1220},[1210,1481,1255],{"class":1248},[1210,1483,1258],{"class":1223},[1210,1485,1262],{"class":1261},[1210,1487,1265],{"class":1223},[1210,1489,1268],{"class":1248},[1210,1491,1271],{"class":1223},[1210,1493,1274],{"class":1261},[1210,1495,1278],{"class":1277},[1210,1497,1498,1500,1502,1504,1506,1508,1510,1513,1515,1518,1520,1523,1525,1528,1530,1533,1536],{"class":1212,"line":1252},[1210,1499,1284],{"class":1248},[1210,1501,1258],{"class":1223},[1210,1503,1289],{"class":1248},[1210,1505,1258],{"class":1223},[1210,1507,1294],{"class":1261},[1210,1509,1404],{"class":1277},[1210,1511,1512],{"class":1248}," price",[1210,1514,1258],{"class":1223},[1210,1516,1517],{"class":1248}," { selector",[1210,1519,1258],{"class":1223},[1210,1521,1522],{"class":1261}," \".price\"",[1210,1524,1404],{"class":1277},[1210,1526,1527],{"class":1248}," type",[1210,1529,1258],{"class":1223},[1210,1531,1532],{"class":1261}," \"money\"",[1210,1534,1535],{"class":1248}," } }",[1210,1537,1278],{"class":1277},[1210,1539,1540],{"class":1212,"line":1281},[1210,1541,1318],{"class":1248},[1210,1543,1544,1546,1548],{"class":1212,"line":1302},[1210,1545,1324],{"class":1227},[1210,1547,1327],{"class":1240},[1210,1549,1550],{"class":1248},"(\n",[1210,1552,1553,1556,1558,1561,1564,1566,1569,1571,1573,1576,1578,1581,1583,1585,1588,1590,1593],{"class":1212,"line":1315},[1210,1554,1555],{"class":1261},"  `between ",[1210,1557,1265],{"class":1223},[1210,1559,1560],{"class":1227},"estimate",[1210,1562,1563],{"class":1248},".credits_min",[1210,1565,1271],{"class":1223},[1210,1567,1568],{"class":1261}," and ",[1210,1570,1265],{"class":1223},[1210,1572,1560],{"class":1227},[1210,1574,1575],{"class":1248},".credits_max",[1210,1577,1271],{"class":1223},[1210,1579,1580],{"class":1261}," credits at ",[1210,1582,1265],{"class":1223},[1210,1584,1560],{"class":1227},[1210,1586,1587],{"class":1248},".tier_expected",[1210,1589,1271],{"class":1223},[1210,1591,1592],{"class":1261}," tier`",[1210,1594,1278],{"class":1277},[1210,1596,1597],{"class":1212,"line":1321},[1210,1598,1361],{"class":1248},[1029,1600,1602],{"id":1601},"linting-a-definition","Linting a definition",[1025,1604,1605,1607,1608,1611,1612,1615,1616,1619,1620,1623,1624,1626,1627,1630,1631,1634],{},[1061,1606,1063],{}," checks a definition without saving it, and answers ",[1061,1609,1610],{},"errors",", ",[1061,1613,1614],{},"warnings"," and\nthe ",[1061,1617,1618],{},"normalized"," program. Errors block publishing: creating or publishing a version with lint\nerrors is refused with ",[1061,1621,1622],{},"422 program_invalid",", carrying the same findings. Warnings are advice, such\nas a ",[1061,1625,590],{}," without an ",[1061,1628,1629],{},"expect"," (",[1122,1632,805],{"href":1633},"\u002Freference\u002Fdsl\u002Flint\u002Fw001",").",[1197,1636,1637],{},[1200,1638,1640],{"className":1202,"code":1639,"filename":1204,"language":1205,"meta":1206,"style":1206},"const definition = {\n  kaab: \"1\",\n  name,\n  inputs: { product_id: { type: \"string\", default: \"1\", pattern: \"^[0-9]+$\" } },\n  options: { max_tier: \"http\" },\n  steps: [\n    { goto: `${site}\u002Fp\u002F{{inputs.product_id}}` },\n    {\n      extract: {\n        title: \"h1\",\n        price: { selector: \".price\", type: \"money\", locale: \"tr\", currency: \"TRY\" },\n      },\n    },\n  ],\n};\nconst lint = await kaab.programs.lint({ definition });\nif (lint.errors.length > 0) throw new Error(`lint: ${JSON.stringify(lint.errors)}`);\nconsole.log(`lint: ${lint.errors.length} errors, ${lint.warnings.length} warnings`);\n",[1061,1641,1642,1654,1666,1673,1716,1734,1744,1766,1772,1782,1794,1840,1848,1856,1864,1870,1894,1957],{"__ignoreMap":1206},[1210,1643,1644,1646,1649,1651],{"class":1212,"line":1213},[1210,1645,1224],{"class":1223},[1210,1647,1648],{"class":1227}," definition",[1210,1650,1231],{"class":1223},[1210,1652,1653],{"class":1248}," {\n",[1210,1655,1656,1659,1661,1664],{"class":1212,"line":1220},[1210,1657,1658],{"class":1248},"  kaab",[1210,1660,1258],{"class":1223},[1210,1662,1663],{"class":1261}," \"1\"",[1210,1665,1278],{"class":1277},[1210,1667,1668,1671],{"class":1212,"line":1252},[1210,1669,1670],{"class":1248},"  name",[1210,1672,1278],{"class":1277},[1210,1674,1675,1678,1680,1683,1685,1688,1690,1693,1695,1698,1700,1702,1704,1707,1709,1712,1714],{"class":1212,"line":1281},[1210,1676,1677],{"class":1248},"  inputs",[1210,1679,1258],{"class":1223},[1210,1681,1682],{"class":1248}," { product_id",[1210,1684,1258],{"class":1223},[1210,1686,1687],{"class":1248}," { type",[1210,1689,1258],{"class":1223},[1210,1691,1692],{"class":1261}," \"string\"",[1210,1694,1404],{"class":1277},[1210,1696,1697],{"class":1248}," default",[1210,1699,1258],{"class":1223},[1210,1701,1663],{"class":1261},[1210,1703,1404],{"class":1277},[1210,1705,1706],{"class":1248}," pattern",[1210,1708,1258],{"class":1223},[1210,1710,1711],{"class":1261}," \"^[0-9]+$\"",[1210,1713,1535],{"class":1248},[1210,1715,1278],{"class":1277},[1210,1717,1718,1721,1723,1726,1728,1730,1732],{"class":1212,"line":1302},[1210,1719,1720],{"class":1248},"  options",[1210,1722,1258],{"class":1223},[1210,1724,1725],{"class":1248}," { max_tier",[1210,1727,1258],{"class":1223},[1210,1729,1421],{"class":1261},[1210,1731,1297],{"class":1248},[1210,1733,1278],{"class":1277},[1210,1735,1736,1739,1741],{"class":1212,"line":1315},[1210,1737,1738],{"class":1248},"  steps",[1210,1740,1258],{"class":1223},[1210,1742,1743],{"class":1248}," [\n",[1210,1745,1746,1749,1751,1753,1755,1757,1759,1762,1764],{"class":1212,"line":1321},[1210,1747,1748],{"class":1248},"    { goto",[1210,1750,1258],{"class":1223},[1210,1752,1262],{"class":1261},[1210,1754,1265],{"class":1223},[1210,1756,1268],{"class":1248},[1210,1758,1271],{"class":1223},[1210,1760,1761],{"class":1261},"\u002Fp\u002F{{inputs.product_id}}`",[1210,1763,1297],{"class":1248},[1210,1765,1278],{"class":1277},[1210,1767,1769],{"class":1212,"line":1768},8,[1210,1770,1771],{"class":1248},"    {\n",[1210,1773,1775,1778,1780],{"class":1212,"line":1774},9,[1210,1776,1777],{"class":1248},"      extract",[1210,1779,1258],{"class":1223},[1210,1781,1653],{"class":1248},[1210,1783,1785,1788,1790,1792],{"class":1212,"line":1784},10,[1210,1786,1787],{"class":1248},"        title",[1210,1789,1258],{"class":1223},[1210,1791,1294],{"class":1261},[1210,1793,1278],{"class":1277},[1210,1795,1797,1800,1802,1804,1806,1808,1810,1812,1814,1816,1818,1821,1823,1826,1828,1831,1833,1836,1838],{"class":1212,"line":1796},11,[1210,1798,1799],{"class":1248},"        price",[1210,1801,1258],{"class":1223},[1210,1803,1517],{"class":1248},[1210,1805,1258],{"class":1223},[1210,1807,1522],{"class":1261},[1210,1809,1404],{"class":1277},[1210,1811,1527],{"class":1248},[1210,1813,1258],{"class":1223},[1210,1815,1532],{"class":1261},[1210,1817,1404],{"class":1277},[1210,1819,1820],{"class":1248}," locale",[1210,1822,1258],{"class":1223},[1210,1824,1825],{"class":1261}," \"tr\"",[1210,1827,1404],{"class":1277},[1210,1829,1830],{"class":1248}," currency",[1210,1832,1258],{"class":1223},[1210,1834,1835],{"class":1261}," \"TRY\"",[1210,1837,1297],{"class":1248},[1210,1839,1278],{"class":1277},[1210,1841,1843,1846],{"class":1212,"line":1842},12,[1210,1844,1845],{"class":1248},"      }",[1210,1847,1278],{"class":1277},[1210,1849,1851,1854],{"class":1212,"line":1850},13,[1210,1852,1853],{"class":1248},"    }",[1210,1855,1278],{"class":1277},[1210,1857,1859,1862],{"class":1212,"line":1858},14,[1210,1860,1861],{"class":1248},"  ]",[1210,1863,1278],{"class":1277},[1210,1865,1867],{"class":1212,"line":1866},15,[1210,1868,1869],{"class":1248},"};\n",[1210,1871,1873,1875,1878,1880,1882,1884,1886,1888,1891],{"class":1212,"line":1872},16,[1210,1874,1224],{"class":1223},[1210,1876,1877],{"class":1227}," lint",[1210,1879,1231],{"class":1223},[1210,1881,1234],{"class":1223},[1210,1883,1237],{"class":1227},[1210,1885,1126],{"class":1240},[1210,1887,224],{"class":1227},[1210,1889,1890],{"class":1240},".lint",[1210,1892,1893],{"class":1248},"({ definition });\n",[1210,1895,1897,1900,1902,1904,1906,1908,1910,1913,1916,1919,1922,1925,1928,1931,1933,1936,1938,1941,1944,1946,1948,1951,1953,1955],{"class":1212,"line":1896},17,[1210,1898,1899],{"class":1223},"if",[1210,1901,1630],{"class":1248},[1210,1903,758],{"class":1227},[1210,1905,1126],{"class":1248},[1210,1907,1610],{"class":1227},[1210,1909,1126],{"class":1248},[1210,1911,1912],{"class":1227},"length",[1210,1914,1915],{"class":1223}," >",[1210,1917,1918],{"class":1227}," 0",[1210,1920,1921],{"class":1248},") ",[1210,1923,1924],{"class":1223},"throw",[1210,1926,1927],{"class":1223}," new",[1210,1929,1930],{"class":1240}," Error",[1210,1932,1330],{"class":1248},[1210,1934,1935],{"class":1261},"`lint: ",[1210,1937,1265],{"class":1223},[1210,1939,1940],{"class":1227},"JSON",[1210,1942,1943],{"class":1240},".stringify",[1210,1945,1330],{"class":1248},[1210,1947,758],{"class":1227},[1210,1949,1950],{"class":1248},".errors)",[1210,1952,1271],{"class":1223},[1210,1954,1333],{"class":1261},[1210,1956,1361],{"class":1248},[1210,1958,1960,1962,1964,1966,1968,1970,1972,1974,1976,1978,1980,1982,1985,1987,1989,1991,1993,1995,1997,1999,2002],{"class":1212,"line":1959},18,[1210,1961,1324],{"class":1227},[1210,1963,1327],{"class":1240},[1210,1965,1330],{"class":1248},[1210,1967,1935],{"class":1261},[1210,1969,1265],{"class":1223},[1210,1971,758],{"class":1227},[1210,1973,1126],{"class":1248},[1210,1975,1610],{"class":1227},[1210,1977,1126],{"class":1248},[1210,1979,1912],{"class":1227},[1210,1981,1271],{"class":1223},[1210,1983,1984],{"class":1261}," errors, ",[1210,1986,1265],{"class":1223},[1210,1988,758],{"class":1227},[1210,1990,1126],{"class":1248},[1210,1992,1614],{"class":1227},[1210,1994,1126],{"class":1248},[1210,1996,1912],{"class":1227},[1210,1998,1271],{"class":1223},[1210,2000,2001],{"class":1261}," warnings`",[1210,2003,1361],{"class":1248},[1025,2005,2006,2007,2012],{},"Run lint in CI on every program you keep in your repository; the ",[1122,2008,2010],{"href":2009},"\u002Freference\u002Fcli\u002Fkaab-programs-lint",[1061,2011,926],{},"\ncommand does the same from a shell.",[1029,2014,2016],{"id":2015},"dry-runs","Dry runs",[1025,2018,2019,1096,2022,2024,2025,2028,2029,1611,2032,2035,2036,2039,2040,1126],{},[1061,2020,2021],{},"\"dry_run\": true",[1061,2023,1099],{}," runs the program until its first ",[1061,2026,2027],{},"side_effect"," step and stops\nthere: the job is ",[1061,2030,2031],{},"succeeded",[1061,2033,2034],{},"meta.dry_run"," is ",[1061,2037,2038],{},"true",", and nothing marked as a side effect\nhappened. Use it to test a program that submits an order or sends a message. See\n",[1122,2041,2043],{"href":2042},"\u002Fguides\u002Fside-effect-safety","side-effect safety",[1029,2045,2047],{"id":2046},"replaying-a-version-against-a-stored-page","Replaying a version against a stored page",[1025,2049,2050,2051,2054,2055,1258],{},"Every step of a job stores a snapshot of its page. A replay runs a program version's extraction\nagainst one of those snapshots, ",[1146,2052,2053],{},"without fetching anything",", and returns ",[1061,2056,2057],{},"{ data, raw, warnings }",[1200,2059,2061],{"className":1385,"code":2060,"language":680,"meta":1206,"style":1206},"{ \"snapshot\": \"job_…:steps[1]\" }\n",[1061,2062,2063],{"__ignoreMap":1206},[1210,2064,2065,2067,2070,2072,2075],{"class":1212,"line":1213},[1210,2066,1393],{"class":1248},[1210,2068,2069],{"class":1223},"\"snapshot\"",[1210,2071,1258],{"class":1277},[1210,2073,2074],{"class":1261}," \"job_…:steps[1]\"",[1210,2076,1434],{"class":1248},[1025,2078,2079,2080,2083,2084,2087],{},"The snapshot is named by the job and the step path; ",[1061,2081,2082],{},"\"ground_truth\""," instead uses the page the\nversion's fields were confirmed against in the builder. A replay costs 0 credits, needs a ",[1061,2085,2086],{},"run"," key,\nand works on a draft, so you can check a change before you publish it:",[1197,2089,2090],{},[1200,2091,2093],{"className":1202,"code":2092,"filename":1204,"language":1205,"meta":1206,"style":1206},"\u002F\u002F Run version 1 once: every step stores a snapshot of the page.\nconst run = await kaab.jobs.create({ program: `${name}@1`, wait: 30 });\n\n\u002F\u002F Version 2 reads one more field. Try it on the stored page: no fetch, no credits.\nconst v2 = await kaab.programs.versions.create(program.id, {\n  publish: false, \u002F\u002F a draft until the replay looks right\n  definition: {\n    kaab: \"1\",\n    name,\n    options: { max_tier: \"http\" },\n    steps: [\n      { goto: `${site}\u002Fp\u002F3` },\n      {\n        extract: {\n          title: \"h1\",\n          price: { selector: \".price\", type: \"money\" },\n          sku: { selector: \".sku\", regex: \"SKU:\\\\s*([A-Z0-9-]+)\" },\n        },\n      },\n    ],\n  },\n});\nconst replay = await kaab.programs.versions.replay(program.id, v2.version, {\n  snapshot: `${run.id}:steps[1]`,\n});\nconsole.log(`v${v2.version} on ${run.id}'s page: ${JSON.stringify(replay.data)}`);\n",[1061,2094,2095,2100,2147,2153,2158,2192,2207,2216,2227,2234,2251,2260,2282,2287,2296,2307,2332,2360,2367,2374,2382,2390,2395,2437,2460,2465],{"__ignoreMap":1206},[1210,2096,2097],{"class":1212,"line":1213},[1210,2098,2099],{"class":1216},"\u002F\u002F Run version 1 once: every step stores a snapshot of the page.\n",[1210,2101,2102,2104,2107,2109,2111,2113,2115,2117,2119,2122,2124,2126,2128,2130,2132,2135,2137,2140,2142,2144],{"class":1212,"line":1220},[1210,2103,1224],{"class":1223},[1210,2105,2106],{"class":1227}," run",[1210,2108,1231],{"class":1223},[1210,2110,1234],{"class":1223},[1210,2112,1237],{"class":1227},[1210,2114,1126],{"class":1240},[1210,2116,171],{"class":1227},[1210,2118,1245],{"class":1240},[1210,2120,2121],{"class":1248},"({ program",[1210,2123,1258],{"class":1223},[1210,2125,1262],{"class":1261},[1210,2127,1265],{"class":1223},[1210,2129,702],{"class":1248},[1210,2131,1271],{"class":1223},[1210,2133,2134],{"class":1261},"@1`",[1210,2136,1404],{"class":1277},[1210,2138,2139],{"class":1248}," wait",[1210,2141,1258],{"class":1223},[1210,2143,1310],{"class":1227},[1210,2145,2146],{"class":1248}," });\n",[1210,2148,2149],{"class":1212,"line":1252},[1210,2150,2152],{"emptyLinePlaceholder":2151},true,"\n",[1210,2154,2155],{"class":1212,"line":1281},[1210,2156,2157],{"class":1216},"\u002F\u002F Version 2 reads one more field. Try it on the stored page: no fetch, no credits.\n",[1210,2159,2160,2162,2165,2167,2169,2171,2173,2175,2177,2180,2182,2184,2186,2188,2190],{"class":1212,"line":1302},[1210,2161,1224],{"class":1223},[1210,2163,2164],{"class":1227}," v2",[1210,2166,1231],{"class":1223},[1210,2168,1234],{"class":1223},[1210,2170,1237],{"class":1227},[1210,2172,1126],{"class":1240},[1210,2174,224],{"class":1227},[1210,2176,1126],{"class":1240},[1210,2178,2179],{"class":1227},"versions",[1210,2181,1245],{"class":1240},[1210,2183,1330],{"class":1248},[1210,2185,489],{"class":1227},[1210,2187,1341],{"class":1248},[1210,2189,1404],{"class":1277},[1210,2191,1653],{"class":1248},[1210,2193,2194,2197,2199,2202,2204],{"class":1212,"line":1315},[1210,2195,2196],{"class":1248},"  publish",[1210,2198,1258],{"class":1223},[1210,2200,2201],{"class":1227}," false",[1210,2203,1404],{"class":1277},[1210,2205,2206],{"class":1216}," \u002F\u002F a draft until the replay looks right\n",[1210,2208,2209,2212,2214],{"class":1212,"line":1321},[1210,2210,2211],{"class":1248},"  definition",[1210,2213,1258],{"class":1223},[1210,2215,1653],{"class":1248},[1210,2217,2218,2221,2223,2225],{"class":1212,"line":1768},[1210,2219,2220],{"class":1248},"    kaab",[1210,2222,1258],{"class":1223},[1210,2224,1663],{"class":1261},[1210,2226,1278],{"class":1277},[1210,2228,2229,2232],{"class":1212,"line":1774},[1210,2230,2231],{"class":1248},"    name",[1210,2233,1278],{"class":1277},[1210,2235,2236,2239,2241,2243,2245,2247,2249],{"class":1212,"line":1784},[1210,2237,2238],{"class":1248},"    options",[1210,2240,1258],{"class":1223},[1210,2242,1725],{"class":1248},[1210,2244,1258],{"class":1223},[1210,2246,1421],{"class":1261},[1210,2248,1297],{"class":1248},[1210,2250,1278],{"class":1277},[1210,2252,2253,2256,2258],{"class":1212,"line":1796},[1210,2254,2255],{"class":1248},"    steps",[1210,2257,1258],{"class":1223},[1210,2259,1743],{"class":1248},[1210,2261,2262,2265,2267,2269,2271,2273,2275,2278,2280],{"class":1212,"line":1842},[1210,2263,2264],{"class":1248},"      { goto",[1210,2266,1258],{"class":1223},[1210,2268,1262],{"class":1261},[1210,2270,1265],{"class":1223},[1210,2272,1268],{"class":1248},[1210,2274,1271],{"class":1223},[1210,2276,2277],{"class":1261},"\u002Fp\u002F3`",[1210,2279,1297],{"class":1248},[1210,2281,1278],{"class":1277},[1210,2283,2284],{"class":1212,"line":1850},[1210,2285,2286],{"class":1248},"      {\n",[1210,2288,2289,2292,2294],{"class":1212,"line":1858},[1210,2290,2291],{"class":1248},"        extract",[1210,2293,1258],{"class":1223},[1210,2295,1653],{"class":1248},[1210,2297,2298,2301,2303,2305],{"class":1212,"line":1866},[1210,2299,2300],{"class":1248},"          title",[1210,2302,1258],{"class":1223},[1210,2304,1294],{"class":1261},[1210,2306,1278],{"class":1277},[1210,2308,2309,2312,2314,2316,2318,2320,2322,2324,2326,2328,2330],{"class":1212,"line":1872},[1210,2310,2311],{"class":1248},"          price",[1210,2313,1258],{"class":1223},[1210,2315,1517],{"class":1248},[1210,2317,1258],{"class":1223},[1210,2319,1522],{"class":1261},[1210,2321,1404],{"class":1277},[1210,2323,1527],{"class":1248},[1210,2325,1258],{"class":1223},[1210,2327,1532],{"class":1261},[1210,2329,1297],{"class":1248},[1210,2331,1278],{"class":1277},[1210,2333,2334,2337,2339,2341,2343,2346,2348,2351,2353,2356,2358],{"class":1212,"line":1896},[1210,2335,2336],{"class":1248},"          sku",[1210,2338,1258],{"class":1223},[1210,2340,1517],{"class":1248},[1210,2342,1258],{"class":1223},[1210,2344,2345],{"class":1261}," \".sku\"",[1210,2347,1404],{"class":1277},[1210,2349,2350],{"class":1248}," regex",[1210,2352,1258],{"class":1223},[1210,2354,2355],{"class":1261}," \"SKU:\\\\s*([A-Z0-9-]+)\"",[1210,2357,1297],{"class":1248},[1210,2359,1278],{"class":1277},[1210,2361,2362,2365],{"class":1212,"line":1959},[1210,2363,2364],{"class":1248},"        }",[1210,2366,1278],{"class":1277},[1210,2368,2370,2372],{"class":1212,"line":2369},19,[1210,2371,1845],{"class":1248},[1210,2373,1278],{"class":1277},[1210,2375,2377,2380],{"class":1212,"line":2376},20,[1210,2378,2379],{"class":1248},"    ]",[1210,2381,1278],{"class":1277},[1210,2383,2385,2388],{"class":1212,"line":2384},21,[1210,2386,2387],{"class":1248},"  }",[1210,2389,1278],{"class":1277},[1210,2391,2393],{"class":1212,"line":2392},22,[1210,2394,1318],{"class":1248},[1210,2396,2398,2400,2403,2405,2407,2409,2411,2413,2415,2417,2420,2422,2424,2426,2428,2430,2433,2435],{"class":1212,"line":2397},23,[1210,2399,1224],{"class":1223},[1210,2401,2402],{"class":1227}," replay",[1210,2404,1231],{"class":1223},[1210,2406,1234],{"class":1223},[1210,2408,1237],{"class":1227},[1210,2410,1126],{"class":1240},[1210,2412,224],{"class":1227},[1210,2414,1126],{"class":1240},[1210,2416,2179],{"class":1227},[1210,2418,2419],{"class":1240},".replay",[1210,2421,1330],{"class":1248},[1210,2423,489],{"class":1227},[1210,2425,1341],{"class":1248},[1210,2427,1404],{"class":1277},[1210,2429,2164],{"class":1227},[1210,2431,2432],{"class":1248},".version",[1210,2434,1404],{"class":1277},[1210,2436,1653],{"class":1248},[1210,2438,2440,2443,2445,2447,2449,2451,2453,2455,2458],{"class":1212,"line":2439},24,[1210,2441,2442],{"class":1248},"  snapshot",[1210,2444,1258],{"class":1223},[1210,2446,1262],{"class":1261},[1210,2448,1265],{"class":1223},[1210,2450,2086],{"class":1227},[1210,2452,1341],{"class":1248},[1210,2454,1271],{"class":1223},[1210,2456,2457],{"class":1261},":steps[1]`",[1210,2459,1278],{"class":1277},[1210,2461,2463],{"class":1212,"line":2462},25,[1210,2464,1318],{"class":1248},[1210,2466,2468,2470,2472,2474,2477,2479,2482,2484,2486,2488,2490,2492,2494,2496,2499,2501,2503,2505,2507,2510,2513,2515,2517],{"class":1212,"line":2467},26,[1210,2469,1324],{"class":1227},[1210,2471,1327],{"class":1240},[1210,2473,1330],{"class":1248},[1210,2475,2476],{"class":1261},"`v",[1210,2478,1265],{"class":1223},[1210,2480,2481],{"class":1227},"v2",[1210,2483,2432],{"class":1248},[1210,2485,1271],{"class":1223},[1210,2487,1096],{"class":1261},[1210,2489,1265],{"class":1223},[1210,2491,2086],{"class":1227},[1210,2493,1341],{"class":1248},[1210,2495,1271],{"class":1223},[1210,2497,2498],{"class":1261},"'s page: ",[1210,2500,1265],{"class":1223},[1210,2502,1940],{"class":1227},[1210,2504,1943],{"class":1240},[1210,2506,1330],{"class":1248},[1210,2508,2509],{"class":1227},"replay",[1210,2511,2512],{"class":1248},".data)",[1210,2514,1271],{"class":1223},[1210,2516,1333],{"class":1261},[1210,2518,1361],{"class":1248},[1025,2520,2521,2522,2526],{},"A replay answers \"does the new version read ",[2523,2524,2525],"em",{},"that"," page correctly\". It does not navigate, log in\nor click, so it tests the extract, not the steps before it.",[1029,2528,2530],{"id":2529},"worked-example","Worked example",[1025,2532,2533,2534,1258],{},"Change a program safely, from definition to replay, on a shop's product pages at\n",[1061,2535,2536],{},"https:\u002F\u002Fshop.example.com",[2538,2539,2540,2546,2559,2572,2582,2591],"ol",{},[1154,2541,2542,2545],{},[1146,2543,2544],{},"Lint"," the definition: no errors, so it may be published.",[1154,2547,2548,2551,2552,2555,2556,1126],{},[1146,2549,2550],{},"Estimate"," a job on ",[1061,2553,2554],{},"\u002Fp\u002F1",": a credit range and ",[1061,2557,2558],{},"tier_expected: \"http\"",[1154,2560,2561,2564,2565,2035,2568,2571],{},[1146,2562,2563],{},"Run it with a test key",": the job's ",[1061,2566,2567],{},"mode",[1061,2569,2570],{},"test",", and your credit balance does not move.",[1154,2573,2574,2577,2578,2581],{},[1146,2575,2576],{},"Publish version 1"," and run it once on ",[1061,2579,2580],{},"\u002Fp\u002F3",". Every step stores a snapshot.",[1154,2583,2584,2587,2588,1126],{},[1146,2585,2586],{},"Create version 2 as a draft"," that also reads the SKU, with\n",[1061,2589,2590],{},"\"sku\": { \"selector\": \".sku\", \"regex\": \"SKU:\\\\s*([A-Z0-9-]+)\" }",[1154,2592,2593,1096,2596,2599,2600,2603],{},[1146,2594,2595],{},"Replay version 2",[1061,2597,2598],{},"job_…:steps[1]",", the extract step of step 4's job. ",[1061,2601,2602],{},"data.sku"," holds the\nSKU, read from the stored page with no fetch and no credits. Publish version 2 when it looks\nright.",[1029,2605,2607],{"id":2606},"pitfalls","Pitfalls",[1151,2609,2610,2619,2629,2635,2644],{},[1154,2611,2612,2615,2616,2618],{},[1146,2613,2614],{},"A test key in production."," Test results disappear after 24 hours and are capped per day.\nCheck ",[1061,2617,2567],{}," on a job if numbers look wrong.",[1154,2620,2621,2624,2625,2628],{},[1146,2622,2623],{},"Treating an estimate as a price."," The job's settled cost is what it used, usually below\n",[1061,2626,2627],{},"credits_max","; an escalation to a higher tier is why the range exists.",[1154,2630,2631,2634],{},[1146,2632,2633],{},"Replays only see one page."," A version whose change is in the steps (a new click, another\npage) cannot be checked by a replay; run it as a test job.",[1154,2636,2637,2640,2641,1126],{},[1146,2638,2639],{},"Snapshots expire."," Snapshots follow your plan's retention. Replay against a recent job, or\nagainst ",[1061,2642,2643],{},"ground_truth",[1154,2645,2646,2649,2650,2652],{},[1146,2647,2648],{},"Dry runs stop at the first side effect."," Everything after it is untested; test that part with\nan ",[1061,2651,1084],{}," key against an account where the side effect is harmless.",[1029,2654,155],{"id":2655},"reference",[1151,2657,2658,2682,2691,2703],{},[1154,2659,2660,2661,1611,2665,2669,2670,2673,2674,1278,2678],{},"API: ",[1122,2662,2664],{"href":2663},"\u002Freference\u002Fapi\u002Fjobs\u002Festimate-job","estimate a job",[1122,2666,2668],{"href":2667},"\u002Freference\u002Fapi\u002Fjobs\u002Fcreate-job","create a job","\n(",[1061,2671,2672],{},"dry_run","), ",[1122,2675,2677],{"href":2676},"\u002Freference\u002Fapi\u002Fprograms\u002Flint-program","lint a program",[1122,2679,2681],{"href":2680},"\u002Freference\u002Fapi\u002Fprograms\u002Freplay-program-version","replay a version",[1154,2683,2684,1611,2688],{},[1122,2685,2687],{"href":2686},"\u002Freference\u002Fapi\u002Fschemas\u002Flint-result","Lint result schema",[1122,2689,2690],{"href":761},"lint rules",[1154,2692,2693,1611,2697,1630,2699,2702],{},[1122,2694,2696],{"href":2695},"\u002Freference\u002Fcodes#modes","Modes",[1122,2698,1009],{"href":1166},[1061,2700,2701],{},"test_mode_daily_jobs",")",[1154,2704,2705,2706,1611,2710,1278,2713],{},"Concepts: ",[1122,2707,2709],{"href":2708},"\u002Fgetting-started\u002Fkeys-and-modes","keys and modes",[1122,2711,66],{"href":2712},"\u002Fconcepts\u002Fsnapshots",[1122,2714,60],{"href":2715},"\u002Fconcepts\u002Flimits",[2717,2718,2719],"style",{},"html pre.shiki code .sKog0, html code.shiki .sKog0{--shiki-light:var(--kaab-code-token-comment);--shiki-default:var(--kaab-code-token-comment);--shiki-dark:var(--kaab-code-token-comment)}html pre.shiki code .sqn18, html code.shiki .sqn18{--shiki-light:var(--kaab-code-token-keyword);--shiki-default:var(--kaab-code-token-keyword);--shiki-dark:var(--kaab-code-token-keyword)}html pre.shiki code .sHJIl, html code.shiki .sHJIl{--shiki-light:var(--kaab-code-token-constant);--shiki-default:var(--kaab-code-token-constant);--shiki-dark:var(--kaab-code-token-constant)}html pre.shiki code .sJugJ, html code.shiki .sJugJ{--shiki-light:var(--kaab-code-token-function);--shiki-default:var(--kaab-code-token-function);--shiki-dark:var(--kaab-code-token-function)}html pre.shiki code .sTzsn, html code.shiki .sTzsn{--shiki-light:var(--kaab-code-foreground);--shiki-default:var(--kaab-code-foreground);--shiki-dark:var(--kaab-code-foreground)}html pre.shiki code .suOy4, html code.shiki .suOy4{--shiki-light:var(--kaab-code-token-string-expression);--shiki-default:var(--kaab-code-token-string-expression);--shiki-dark:var(--kaab-code-token-string-expression)}html pre.shiki code .sQm2-, html code.shiki .sQm2-{--shiki-light:var(--kaab-code-token-punctuation);--shiki-default:var(--kaab-code-token-punctuation);--shiki-dark:var(--kaab-code-token-punctuation)}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":1206,"searchDepth":1252,"depth":1252,"links":2721},[2722,2723,2724,2725,2726,2727,2728,2729,2730],{"id":1031,"depth":1220,"text":1032},{"id":1129,"depth":1220,"text":1130},{"id":1373,"depth":1220,"text":1374},{"id":1601,"depth":1220,"text":1602},{"id":2015,"depth":1220,"text":2016},{"id":2046,"depth":1220,"text":2047},{"id":2529,"depth":1220,"text":2530},{"id":2606,"depth":1220,"text":2607},{"id":2655,"depth":1220,"text":155},false,null,"Test programs without spending credits or touching live data: test keys, cost estimates, lint, dry runs and replays against stored pages.","md",{},"\u002Fguides\u002Ftesting",{"title":110,"description":2733},"3.guides\u002F08.testing","QLdmg1rfJYCtgX_FSztotOA9KATov-kCFtyC05TSCsg",[2741,2745],{"title":107,"path":2742,"stem":2743,"description":2744,"children":-1},"\u002Fguides\u002Fbyo-providers","3.guides\u002F07.byo-providers","Use your own proxies, language models and captcha solvers: configuration, fallback, billing, health checks and how credentials are kept.",{"title":113,"path":2042,"stem":2746,"description":2747,"children":-1},"3.guides\u002F09.side-effect-safety","Mark the steps that change something, checkpoint after them, and send an idempotency key, so an order is never placed twice.",1793593417332]