Skip to content

Commit ee943a6

Browse files
author
lex
committed
fix(http): limit dependency resolution to 1 level, skip interactive prompts
- resolve_content_dependencies: replace recursive build_dep_order / execute_deps_sequential with a simple 1-level direct dependency loop - resolve_request_variables: same 1-level constraint for same-file path - Both functions now detect prompt directives (<<) in dependency blocks and skip auto-execution with a warning log instead - Dependencies with prompts must be run manually before the dependent request This prevents infinite recursion in deep dependency chains and avoids hanging on interactive prompts during silent auto-execution.
1 parent 6bec8c7 commit ee943a6

1 file changed

Lines changed: 119 additions & 56 deletions

File tree

lua/poste/http/request_vars.lua

Lines changed: 119 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -868,11 +868,34 @@ function M.resolve_request_variables(binary, file, env_name, buf, cursor_line, c
868868

869869
state.log("INFO", string.format("Found %d request variable reference(s)", #refs))
870870

871-
-- Build topological execution order for all dependencies
872-
local dep_order = build_dep_order(refs, requests, content)
871+
-- Build list of pending dependencies (1 level only, no transitive resolution).
872+
-- Skip deps that are already cached or contain interactive prompt directives (<<).
873+
local pending_deps = {}
874+
for _, ref in ipairs(refs) do
875+
if request_response_cache[ref.request_name] then
876+
-- Already cached, skip execution
877+
else
878+
for _, req in ipairs(requests) do
879+
if req.name == ref.request_name then
880+
-- Check if this dependency has prompt directives (<<variable)
881+
local dep_block_lines = {}
882+
for i = req.start_line, req.end_line do
883+
table.insert(dep_block_lines, all_lines[i] or "")
884+
end
885+
local dep_text = table.concat(dep_block_lines, "\n")
886+
if dep_text:match("<<%S") then
887+
state.log("WARN", string.format("Dependency '%s' has prompt directives (<<), cannot auto-execute. Run it manually first.", ref.request_name))
888+
else
889+
table.insert(pending_deps, req)
890+
end
891+
break
892+
end
893+
end
894+
end
895+
end
873896

874-
if #dep_order == 0 then
875-
-- All dependencies already cached, substitute immediately
897+
-- Substitution helper: replace all refs with cached values in the block
898+
local function substitute_and_finish()
876899
local resolved_block = block_text
877900
for _, ref in ipairs(refs) do
878901
local value = resolve_request_variable(ref.full:sub(3, -3), request_response_cache)
@@ -882,7 +905,6 @@ function M.resolve_request_variables(binary, file, env_name, buf, cursor_line, c
882905
state.log("WARN", string.format("Could not resolve variable: %s", ref.full))
883906
end
884907
end
885-
886908
local result_lines = {}
887909
local resolved_split = vim.split(resolved_block, "\n", { plain = true })
888910
for i, l in ipairs(all_lines) do
@@ -892,36 +914,47 @@ function M.resolve_request_variables(binary, file, env_name, buf, cursor_line, c
892914
table.insert(result_lines, l)
893915
end
894916
end
895-
on_complete(table.concat(result_lines, "\n"))
917+
return table.concat(result_lines, "\n")
918+
end
919+
920+
if #pending_deps == 0 then
921+
-- All deps already cached or skipped — substitute immediately
922+
on_complete(substitute_and_finish())
896923
return
897924
end
898925

899-
-- Execute dependencies sequentially via callback chain (fully non-blocking)
900-
execute_deps_sequential(binary, file, env_name, dep_order, content, requests, 1, function()
901-
-- All deps resolved: substitute variables in current block
902-
local resolved_block = block_text
903-
for _, ref in ipairs(refs) do
904-
local value = resolve_request_variable(ref.full:sub(3, -3), request_response_cache)
905-
if value then
906-
resolved_block = resolved_block:gsub(vim.pesc(ref.full), tostring(value))
907-
else
908-
state.log("WARN", string.format("Could not resolve variable: %s", ref.full))
909-
end
926+
-- Execute direct dependencies (1 level only). Each dep is run without
927+
-- resolving its own sub-dependencies — those must already be cached.
928+
local dep_idx = 1
929+
local function execute_next_dep()
930+
if dep_idx > #pending_deps then
931+
on_complete(substitute_and_finish())
932+
return
910933
end
911934

912-
-- Replace block in full content
913-
local result_lines = {}
914-
local resolved_split = vim.split(resolved_block, "\n", { plain = true })
915-
for i, l in ipairs(all_lines) do
916-
if i >= current_req.start_line and i <= current_req.end_line then
917-
table.insert(result_lines, resolved_split[i - current_req.start_line + 1] or l)
935+
local dep_req = pending_deps[dep_idx]
936+
dep_idx = dep_idx + 1
937+
938+
state.log("INFO", string.format("Auto-executing dependency '%s' (1/1 level)", dep_req.name))
939+
940+
-- Extract dep's block text from original content (no sub-dep resolution)
941+
local dep_block_lines = {}
942+
for i = dep_req.start_line, dep_req.end_line do
943+
table.insert(dep_block_lines, all_lines[i] or "")
944+
end
945+
local dep_block_text = table.concat(dep_block_lines, "\n")
946+
947+
execute_dependent_request_async(binary, file, env_name, dep_req, dep_block_text, function(response)
948+
if response then
949+
state.log("INFO", string.format("Dependency '%s' executed and cached", dep_req.name))
918950
else
919-
table.insert(result_lines, l)
951+
state.log("WARN", string.format("Dependency '%s' failed to execute", dep_req.name))
920952
end
921-
end
953+
execute_next_dep()
954+
end)
955+
end
922956

923-
on_complete(table.concat(result_lines, "\n"))
924-
end)
957+
execute_next_dep()
925958
end
926959

927960
--- Cache the current request's response for use by subsequent request variables
@@ -998,28 +1031,44 @@ function M.resolve_content_dependencies(binary, file_path, env_name, content, bl
9981031

9991032
state.log("INFO", string.format("Found %d request variable reference(s) in target block", #refs))
10001033

1001-
-- Filter refs: only process uncached refs pointing to requests in this content
1002-
local local_refs = {}
1034+
-- Build list of pending dependencies (1 level only, no transitive resolution).
1035+
-- Skip deps that are already cached or contain interactive prompt directives (<<).
1036+
local pending_deps = {}
10031037
for _, ref in ipairs(refs) do
1004-
if not request_response_cache[ref.request_name] then
1038+
if request_response_cache[ref.request_name] then
1039+
-- Already cached, skip execution
1040+
else
10051041
for _, req in ipairs(requests) do
10061042
if req.name == ref.request_name then
1007-
table.insert(local_refs, ref)
1043+
-- Check if this dependency has prompt directives (<<variable)
1044+
-- Auto-execution cannot handle interactive prompts
1045+
local dep_block_lines = {}
1046+
for i = req.start_line, req.end_line do
1047+
table.insert(dep_block_lines, lines[i] or "")
1048+
end
1049+
local dep_text = table.concat(dep_block_lines, "\n")
1050+
if dep_text:match("<<%S") then
1051+
state.log("WARN", string.format("Dependency '%s' has prompt directives (<<), cannot auto-execute. Run it manually first.", ref.request_name))
1052+
else
1053+
table.insert(pending_deps, req)
1054+
end
10081055
break
10091056
end
10101057
end
10111058
end
10121059
end
10131060

1014-
if #local_refs == 0 then
1015-
-- All deps already cached, substitute immediately
1061+
-- Substitution helper: replace all refs with cached values in the block
1062+
local function substitute_and_finish()
10161063
local resolved_block = block_text
1064+
local all_resolved = true
10171065
for _, ref in ipairs(refs) do
10181066
local value = resolve_request_variable(ref.full:sub(3, -3), request_response_cache)
10191067
if value then
10201068
resolved_block = resolved_block:gsub(vim.pesc(ref.full), tostring(value))
10211069
else
1022-
state.log("WARN", string.format("Could not resolve variable: %s", ref.full))
1070+
state.log("WARN", string.format("Could not resolve variable: %s — value may be missing from the response", ref.full))
1071+
all_resolved = false
10231072
end
10241073
end
10251074
local result_lines = {}
@@ -1031,35 +1080,49 @@ function M.resolve_content_dependencies(binary, file_path, env_name, content, bl
10311080
table.insert(result_lines, l)
10321081
end
10331082
end
1034-
on_complete(table.concat(result_lines, "\n"))
1083+
return table.concat(result_lines, "\n")
1084+
end
1085+
1086+
if #pending_deps == 0 then
1087+
-- All deps already cached or skipped — substitute immediately
1088+
on_complete(substitute_and_finish())
10351089
return
10361090
end
10371091

1038-
-- Build dependency order (topological via DFS)
1039-
local dep_order = build_dep_order(local_refs, requests, content)
1092+
-- Execute direct dependencies (1 level only). Each dep is run without
1093+
-- resolving its own sub-dependencies — those must already be cached.
1094+
local dep_idx = 1
1095+
local function execute_next_dep()
1096+
if dep_idx > #pending_deps then
1097+
-- All deps done: substitute and complete
1098+
on_complete(substitute_and_finish())
1099+
return
1100+
end
10401101

1041-
-- Execute dependencies sequentially, then substitute
1042-
execute_deps_sequential(binary, file_path, env_name, dep_order, content, requests, 1, function()
1043-
local resolved_block = block_text
1044-
for _, ref in ipairs(refs) do
1045-
local value = resolve_request_variable(ref.full:sub(3, -3), request_response_cache)
1046-
if value then
1047-
resolved_block = resolved_block:gsub(vim.pesc(ref.full), tostring(value))
1048-
else
1049-
state.log("WARN", string.format("Could not resolve variable: %s", ref.full))
1050-
end
1102+
local dep_req = pending_deps[dep_idx]
1103+
dep_idx = dep_idx + 1
1104+
1105+
state.log("INFO", string.format("Auto-executing dependency '%s' (1/1 level)", dep_req.name))
1106+
1107+
-- Extract dep's block text from original content (no sub-dep resolution)
1108+
local dep_block_lines = {}
1109+
for i = dep_req.start_line, dep_req.end_line do
1110+
table.insert(dep_block_lines, lines[i] or "")
10511111
end
1052-
local result_lines = {}
1053-
local resolved_split = vim.split(resolved_block, "\n", { plain = true })
1054-
for i, l in ipairs(lines) do
1055-
if i >= block_line and i <= block_end then
1056-
table.insert(result_lines, resolved_split[i - block_line + 1] or l)
1112+
local dep_block_text = table.concat(dep_block_lines, "\n")
1113+
1114+
execute_dependent_request_async(binary, file_path, env_name, dep_req, dep_block_text, function(response)
1115+
if response then
1116+
state.log("INFO", string.format("Dependency '%s' executed and cached", dep_req.name))
10571117
else
1058-
table.insert(result_lines, l)
1118+
state.log("WARN", string.format("Dependency '%s' failed to execute", dep_req.name))
10591119
end
1060-
end
1061-
on_complete(table.concat(result_lines, "\n"))
1062-
end)
1120+
-- Continue to next dep regardless of success/failure
1121+
execute_next_dep()
1122+
end)
1123+
end
1124+
1125+
execute_next_dep()
10631126
end
10641127

10651128
---------------------------------------------------------------------------

0 commit comments

Comments
 (0)