@enricai/barnacle 1.12.19 → 1.12.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -4,17 +4,63 @@ exports.buildCapture = buildCapture;
4
4
  exports.buildStep = buildStep;
5
5
  exports.buildMulticallHeterogeneousActionSteps = buildMulticallHeterogeneousActionSteps;
6
6
  exports.buildMulticallHeterogeneousActionStepsWithDrillDown = buildMulticallHeterogeneousActionStepsWithDrillDown;
7
+ exports.buildMulticallDependentDrillDownActionSteps = buildMulticallDependentDrillDownActionSteps;
7
8
  exports.buildWizardCheckoutCaptures = buildWizardCheckoutCaptures;
9
+ exports.buildMulticallSingleShotSearchDrillDownActionSteps = buildMulticallSingleShotSearchDrillDownActionSteps;
10
+ exports.buildMulticallSingleShotSearchDrillDownDrillDecoyActionSteps = buildMulticallSingleShotSearchDrillDownDrillDecoyActionSteps;
11
+ exports.buildMulticallSingleShotSearchDrillDownNoDecoyActionSteps = buildMulticallSingleShotSearchDrillDownNoDecoyActionSteps;
12
+ exports.buildMulticallSingleShotSearchDrillDownNestedJoinFieldMultiItemActionSteps = buildMulticallSingleShotSearchDrillDownNestedJoinFieldMultiItemActionSteps;
13
+ exports.buildMulticallSingleShotSearchDrillDownRicherFlatOutranksNestedArrayActionSteps = buildMulticallSingleShotSearchDrillDownRicherFlatOutranksNestedArrayActionSteps;
14
+ exports.buildMulticallSingleShotSearchTwoIndependentDrillDownsActionSteps = buildMulticallSingleShotSearchTwoIndependentDrillDownsActionSteps;
15
+ exports.buildMulticallSingleShotSearchTwoIndependentDrillDownsSharedJoinFieldActionSteps = buildMulticallSingleShotSearchTwoIndependentDrillDownsSharedJoinFieldActionSteps;
16
+ exports.buildMulticallTwoIndependentPrimariesActionSteps = buildMulticallTwoIndependentPrimariesActionSteps;
17
+ exports.buildMulticallTwoIndependentPrimariesSecondHeaderThreadedActionSteps = buildMulticallTwoIndependentPrimariesSecondHeaderThreadedActionSteps;
18
+ exports.buildMulticallSingleShotSearchHeuristicAndSpecTwoTargetActionSteps = buildMulticallSingleShotSearchHeuristicAndSpecTwoTargetActionSteps;
19
+ exports.buildMulticallTwoOccurrencesSamePrimaryDistinctDrillsActionSteps = buildMulticallTwoOccurrencesSamePrimaryDistinctDrillsActionSteps;
20
+ exports.buildMulticallNestedGroupedDrillDownMultiGroupActionSteps = buildMulticallNestedGroupedDrillDownMultiGroupActionSteps;
21
+ exports.buildMulticallSingleShotSearchDrillDownNumericJoinActionSteps = buildMulticallSingleShotSearchDrillDownNumericJoinActionSteps;
22
+ exports.buildMulticallSingleShotSearchDrillDownPathThreadedJoinActionSteps = buildMulticallSingleShotSearchDrillDownPathThreadedJoinActionSteps;
23
+ exports.buildMulticallSingleShotSearchDrillDownNestedJoinFieldActionSteps = buildMulticallSingleShotSearchDrillDownNestedJoinFieldActionSteps;
24
+ exports.buildMulticallSingleShotSearchDrillDownCompositeNumericJoinActionSteps = buildMulticallSingleShotSearchDrillDownCompositeNumericJoinActionSteps;
25
+ exports.buildMulticallSingleShotSearchDrillDownNonFirstItemSkuActionSteps = buildMulticallSingleShotSearchDrillDownNonFirstItemSkuActionSteps;
26
+ exports.buildMulticallSingleShotSearchDrillDownCompositeNumericJoinNonFirstItemActionSteps = buildMulticallSingleShotSearchDrillDownCompositeNumericJoinNonFirstItemActionSteps;
27
+ exports.buildMulticallSingleShotSearchDrillDownHeaderThreadedJoinActionSteps = buildMulticallSingleShotSearchDrillDownHeaderThreadedJoinActionSteps;
28
+ exports.buildMulticallSingleShotSearchDrillDownHeaderThreadedJoinRequeriedPrimaryOverlapActionSteps = buildMulticallSingleShotSearchDrillDownHeaderThreadedJoinRequeriedPrimaryOverlapActionSteps;
29
+ exports.buildMulticallSingleShotSearchDrillDownHeaderThreadedJoinMultiItemActionSteps = buildMulticallSingleShotSearchDrillDownHeaderThreadedJoinMultiItemActionSteps;
30
+ exports.buildMulticallSingleShotSearchDrillDownOutOfOrderItemActionSteps = buildMulticallSingleShotSearchDrillDownOutOfOrderItemActionSteps;
31
+ exports.buildMulticallSingleShotSearchDrillDownMultiMatchActionSteps = buildMulticallSingleShotSearchDrillDownMultiMatchActionSteps;
32
+ exports.buildMulticallSingleShotSearchDrillDownTypeMismatchJoinActionSteps = buildMulticallSingleShotSearchDrillDownTypeMismatchJoinActionSteps;
33
+ exports.buildMulticallSingleShotSearchDrillDownChainedDependentMultipartChainStepActionSteps = buildMulticallSingleShotSearchDrillDownChainedDependentMultipartChainStepActionSteps;
34
+ exports.buildMulticallSingleShotSearchDrillDownChainedDependentActionSteps = buildMulticallSingleShotSearchDrillDownChainedDependentActionSteps;
35
+ exports.buildMulticallSingleShotSearchDrillDownHeaderMintedChainedResponseValueActionSteps = buildMulticallSingleShotSearchDrillDownHeaderMintedChainedResponseValueActionSteps;
36
+ exports.buildMulticallSingleShotSearchDrillDownBooleanChainedResponseValueActionSteps = buildMulticallSingleShotSearchDrillDownBooleanChainedResponseValueActionSteps;
37
+ exports.buildMulticallSingleShotSearchDrillDownNestedJoinFieldChainedDependentActionSteps = buildMulticallSingleShotSearchDrillDownNestedJoinFieldChainedDependentActionSteps;
38
+ exports.buildMulticallSingleShotSearchDrillDownRequeriedPrimaryOverlapActionSteps = buildMulticallSingleShotSearchDrillDownRequeriedPrimaryOverlapActionSteps;
39
+ exports.buildMulticallSingleShotSearchTwoIndependentArraysActionSteps = buildMulticallSingleShotSearchTwoIndependentArraysActionSteps;
40
+ exports.buildMulticallStructuralPlusSpecOnlySameStepActionSteps = buildMulticallStructuralPlusSpecOnlySameStepActionSteps;
41
+ exports.buildMulticallSingleShotSearchDrillDownChainedDecoyOnChainTerminalActionSteps = buildMulticallSingleShotSearchDrillDownChainedDecoyOnChainTerminalActionSteps;
42
+ exports.buildMulticallSingleShotSearchDrillDownOpaqueIntermediateChainedDependentActionSteps = buildMulticallSingleShotSearchDrillDownOpaqueIntermediateChainedDependentActionSteps;
43
+ exports.buildMulticallSingleShotSearchDrillDownRichnessTiedConfirmationHopChainedDependentActionSteps = buildMulticallSingleShotSearchDrillDownRichnessTiedConfirmationHopChainedDependentActionSteps;
44
+ exports.buildMulticallSingleShotSearchDrillDownArrayWrappedChainedDependentActionSteps = buildMulticallSingleShotSearchDrillDownArrayWrappedChainedDependentActionSteps;
45
+ exports.buildMulticallSingleShotSearchDrillDownArrayWrappedNumericChainedJoinFieldActionSteps = buildMulticallSingleShotSearchDrillDownArrayWrappedNumericChainedJoinFieldActionSteps;
46
+ exports.buildMulticallSingleShotSearchDrillDownShortNumericChainedJoinFieldActionSteps = buildMulticallSingleShotSearchDrillDownShortNumericChainedJoinFieldActionSteps;
47
+ exports.buildMulticallSingleShotSearchDrillDownShortCookieChainedJoinFieldActionSteps = buildMulticallSingleShotSearchDrillDownShortCookieChainedJoinFieldActionSteps;
48
+ exports.buildMulticallSingleShotSearchDrillDownArrayWrappedImmediateJoinFieldActionSteps = buildMulticallSingleShotSearchDrillDownArrayWrappedImmediateJoinFieldActionSteps;
49
+ exports.buildMulticallSingleShotSearchDrillDownArrayWrappedNumericImmediateJoinFieldActionSteps = buildMulticallSingleShotSearchDrillDownArrayWrappedNumericImmediateJoinFieldActionSteps;
50
+ exports.buildMulticallSingleShotSearchDrillDownArrayWrappedBooleanImmediateJoinFieldActionSteps = buildMulticallSingleShotSearchDrillDownArrayWrappedBooleanImmediateJoinFieldActionSteps;
51
+ exports.buildMulticallSingleShotSearchDrillDownHeaderThreadedJoinChainedDependentActionSteps = buildMulticallSingleShotSearchDrillDownHeaderThreadedJoinChainedDependentActionSteps;
52
+ exports.buildMulticallSingleShotSearchDrillDownGetEntryHopChainedDependentActionSteps = buildMulticallSingleShotSearchDrillDownGetEntryHopChainedDependentActionSteps;
53
+ exports.buildMulticallSingleShotSearchDrillDownResponseHeaderThreadedJoinChainedDependentActionSteps = buildMulticallSingleShotSearchDrillDownResponseHeaderThreadedJoinChainedDependentActionSteps;
8
54
  function buildCapture(overrides) {
9
55
  return {
10
56
  timestamp: overrides.timestamp,
11
57
  phase: "action",
12
- method: "POST",
58
+ method: overrides.method ?? "POST",
13
59
  url: overrides.url,
14
60
  status: 200,
15
- requestHeaders: { "Content-Type": "application/json" },
61
+ requestHeaders: overrides.requestHeaders ?? { "Content-Type": "application/json" },
16
62
  requestPostData: overrides.requestPostData,
17
- responseHeaders: { "content-type": "application/json" },
63
+ responseHeaders: overrides.responseHeaders ?? { "content-type": "application/json" },
18
64
  responseBody: overrides.responseBody,
19
65
  operationName: null,
20
66
  query: null,
@@ -102,6 +148,53 @@ function buildMulticallHeterogeneousActionStepsWithDrillDown() {
102
148
  }),
103
149
  ];
104
150
  }
151
+ const CATALOG_SEARCH_URL = "https://api.example.com/catalog/search/";
152
+ const CATALOG_ITEM_DETAIL_URL = "https://api.example.com/catalog/item-detail/";
153
+ /**
154
+ * Reproduces a search-then-per-item-drill-down flow whose primary page
155
+ * carries multiple results, each followed by its own drill-down call — the
156
+ * shape {@link detectDrillDownFoldPlan} must resolve by join key rather than
157
+ * by position. `search`/`page 2` is re-queried (distinct body from `page 1`)
158
+ * so it satisfies {@link findRequeriedActions}'s relevance signal, and its
159
+ * response holds two items (`i-b` then `i-a`, deliberately not alphabetical)
160
+ * so a later merge can't assume primary-array order. The two drill-down
161
+ * calls are fired in the OPPOSITE order of the primary array (`i-a`'s
162
+ * drill-down first, `i-b`'s second) — a positional/index-based fold would
163
+ * pair the wrong item with the wrong drill-down response, while a join-key
164
+ * fold (matching each drill request's `itemId` back to the primary item that
165
+ * produced it) pairs them correctly regardless of call order.
166
+ */
167
+ function buildMulticallDependentDrillDownActionSteps() {
168
+ return [
169
+ buildStep("r0", {
170
+ url: CATALOG_SEARCH_URL,
171
+ requestPostData: '{"page":1}',
172
+ responseBody: { totalPages: 2, items: [{ itemId: "solo" }] },
173
+ timestamp: "2024-03-01T00:00:00Z",
174
+ }),
175
+ buildStep("r1", {
176
+ url: CATALOG_SEARCH_URL,
177
+ requestPostData: '{"page":2}',
178
+ responseBody: {
179
+ totalPages: 2,
180
+ items: [{ itemId: "i-b" }, { itemId: "i-a" }],
181
+ },
182
+ timestamp: "2024-03-01T00:00:01Z",
183
+ }),
184
+ buildStep("r2", {
185
+ url: CATALOG_ITEM_DETAIL_URL,
186
+ requestPostData: '{"itemId":"i-a"}',
187
+ responseBody: { details: [{ detailId: "d-a" }] },
188
+ timestamp: "2024-03-01T00:00:02Z",
189
+ }),
190
+ buildStep("r3", {
191
+ url: CATALOG_ITEM_DETAIL_URL,
192
+ requestPostData: '{"itemId":"i-b"}',
193
+ responseBody: { details: [{ detailId: "d-b" }] },
194
+ timestamp: "2024-03-01T00:00:03Z",
195
+ }),
196
+ ];
197
+ }
105
198
  const CHECKOUT_HOST = "https://api.example.com";
106
199
  /**
107
200
  * Reproduces a wizard-style checkout submission on one host: a create-order
@@ -146,4 +239,1701 @@ function buildWizardCheckoutCaptures() {
146
239
  }),
147
240
  ];
148
241
  }
242
+ const CATALOG_PRICING_URL = "https://api.example.com/catalog/pricing/";
243
+ /**
244
+ * A search → per-item drill-down flow whose search endpoint fires exactly
245
+ * ONCE. {@link detectDrillDownFoldPlan} now scans every prior action as a
246
+ * fold-primary candidate, so a single-shot search is just as eligible as a
247
+ * re-queried one — but the primary response here also carries a decoy
248
+ * `facets[]` array ahead of the real `results[]`, so `findObjectArrayField`'s
249
+ * DFS first-match deliberately disagrees with the array a caller would fold
250
+ * onto. `resolveFoldPlan(steps)` on this fixture still returns `null`, but
251
+ * only because of that decoy, not because the search fires once. That
252
+ * decoy-vs-declared-path gap is the shape the flow-declared `foldReturn`
253
+ * (`FoldReturnSpec`/`resolveFoldPlan`) exists to reach.
254
+ *
255
+ * The decoy is load-bearing for a `resolveFoldPlan`/`foldReturn` test
256
+ * elsewhere; {@link buildMulticallSingleShotSearchDrillDownNoDecoyActionSteps}
257
+ * is the decoy-free sibling used to prove `detectDrillDownFoldPlan` alone
258
+ * detects a single-shot primary.
259
+ */
260
+ function buildMulticallSingleShotSearchDrillDownActionSteps() {
261
+ return [
262
+ buildStep("r0", {
263
+ url: CATALOG_SEARCH_URL,
264
+ requestPostData: '{"page":1}',
265
+ responseBody: {
266
+ facets: [{ name: "brand" }],
267
+ results: [{ sku: "sku-a" }, { sku: "sku-b" }],
268
+ },
269
+ timestamp: "2024-04-01T00:00:00Z",
270
+ }),
271
+ buildStep("r1", {
272
+ url: CATALOG_PRICING_URL,
273
+ requestPostData: '{"sku":"sku-a"}',
274
+ responseBody: { prices: [{ sku: "sku-a", amount: 19.99 }] },
275
+ timestamp: "2024-04-01T00:00:01Z",
276
+ }),
277
+ ];
278
+ }
279
+ /**
280
+ * A search → per-item drill-down flow whose search response is unambiguous
281
+ * (a single `results[]` array, no decoy), but whose DRILL step's response
282
+ * carries a decoy `errors[]` array ahead of the real per-item `details[]`
283
+ * array. `findObjectArrayField`'s DFS first-match lands on `errors[]`
284
+ * instead of `details[]` when resolving the drill side of the fold, so a
285
+ * `foldReturn` declaration that only names the primary's `resultsPath`
286
+ * (see {@link FoldReturnSpec}) cannot fix this — the gap is on the drill
287
+ * side, not the primary side. Proves a drill-side results-path declaration
288
+ * (as opposed to {@link buildMulticallSingleShotSearchDrillDownActionSteps}'s
289
+ * primary-side decoy) is what a `drillResultsPath`-style flow declaration
290
+ * exists to reach.
291
+ */
292
+ function buildMulticallSingleShotSearchDrillDownDrillDecoyActionSteps() {
293
+ return [
294
+ buildStep("r0", {
295
+ url: CATALOG_SEARCH_URL,
296
+ requestPostData: '{"page":1}',
297
+ responseBody: {
298
+ results: [{ sku: "sku-a" }, { sku: "sku-b" }],
299
+ },
300
+ timestamp: "2024-04-01T00:00:00Z",
301
+ }),
302
+ buildStep("r1", {
303
+ url: CATALOG_PRICING_URL,
304
+ requestPostData: '{"sku":"sku-a"}',
305
+ responseBody: {
306
+ errors: [{ code: "none" }],
307
+ details: [{ sku: "sku-a", price: 19.99 }],
308
+ },
309
+ timestamp: "2024-04-01T00:00:01Z",
310
+ }),
311
+ ];
312
+ }
313
+ /**
314
+ * A decoy-free sibling of {@link buildMulticallSingleShotSearchDrillDownActionSteps}:
315
+ * the search endpoint still fires exactly once, but `results[]` is the sole
316
+ * object-array field in its response, so `findObjectArrayField`'s DFS
317
+ * first-match lands on the real results array with no declared `foldReturn`
318
+ * needed. Proves the single-shot primary is detected on structure alone.
319
+ */
320
+ function buildMulticallSingleShotSearchDrillDownNoDecoyActionSteps() {
321
+ return [
322
+ buildStep("r0", {
323
+ url: CATALOG_SEARCH_URL,
324
+ requestPostData: '{"page":1}',
325
+ responseBody: {
326
+ results: [{ sku: "sku-a" }, { sku: "sku-b" }],
327
+ },
328
+ timestamp: "2024-04-01T00:00:00Z",
329
+ }),
330
+ buildStep("r1", {
331
+ url: CATALOG_PRICING_URL,
332
+ requestPostData: '{"sku":"sku-a"}',
333
+ responseBody: { prices: [{ sku: "sku-a", amount: 19.99 }] },
334
+ timestamp: "2024-04-01T00:00:01Z",
335
+ }),
336
+ ];
337
+ }
338
+ /**
339
+ * A nested-join-key sibling of {@link buildMulticallSingleShotSearchDrillDownNoDecoyActionSteps}
340
+ * with a SECOND primary item, so a per-item fold loop is actually exercised
341
+ * (not just a single-item detection check): each primary `results[]` item
342
+ * carries its join key under a nested `identifiers` object
343
+ * (`{ identifiers: { sku } }`) instead of as a top-level field, while the
344
+ * drill-down request is still keyed by the plain `sku` value pulled out of
345
+ * that nested field. Proves the fold loop threads a join key found by
346
+ * walking INTO an item's nested objects, not only its own top-level
347
+ * `Object.entries`, across every primary item.
348
+ */
349
+ function buildMulticallSingleShotSearchDrillDownNestedJoinFieldMultiItemActionSteps() {
350
+ return [
351
+ buildStep("r0", {
352
+ url: CATALOG_SEARCH_URL,
353
+ requestPostData: '{"page":1}',
354
+ responseBody: {
355
+ results: [{ identifiers: { sku: "sku-a" } }, { identifiers: { sku: "sku-b" } }],
356
+ },
357
+ timestamp: "2024-04-01T00:00:00Z",
358
+ }),
359
+ buildStep("r1", {
360
+ url: CATALOG_PRICING_URL,
361
+ requestPostData: '{"sku":"sku-a"}',
362
+ responseBody: { prices: [{ sku: "sku-a", amount: 19.99 }] },
363
+ timestamp: "2024-04-01T00:00:01Z",
364
+ }),
365
+ ];
366
+ }
367
+ /**
368
+ * A search → per-item drill-down flow whose DRILL step's response carries
369
+ * BOTH a small real nested object-array field (`tags[]`, one primitive
370
+ * field per item, no join echo) AND richer flat top-level per-item fields
371
+ * (`name`/`price`/`brand`, none of them echoing the join either) — the
372
+ * shape {@link findAllObjectArrayFieldsOrWholeObject}'s old docstring
373
+ * ("a response that already has one [a real object-array field] is never
374
+ * second-guessed") resolved to the tiny `tags[]` array instead of the
375
+ * richer flat object. Neither candidate threads the join value in its
376
+ * response body (only the drill REQUEST carries `sku-a`), so selection must
377
+ * fall through to comparing per-item primitive-field richness, which the
378
+ * flat object wins 3 (`name`/`price`/`brand`) to 1 (`tags[0].id`).
379
+ */
380
+ function buildMulticallSingleShotSearchDrillDownRicherFlatOutranksNestedArrayActionSteps() {
381
+ return [
382
+ buildStep("r0", {
383
+ url: CATALOG_SEARCH_URL,
384
+ requestPostData: '{"page":1}',
385
+ responseBody: {
386
+ results: [{ sku: "sku-a" }, { sku: "sku-b" }],
387
+ },
388
+ timestamp: "2024-04-01T00:00:00Z",
389
+ }),
390
+ buildStep("r1", {
391
+ url: CATALOG_PRICING_URL,
392
+ requestPostData: '{"sku":"sku-a"}',
393
+ responseBody: {
394
+ tags: [{ id: 1 }, { id: 2 }],
395
+ name: "Widget A",
396
+ price: 19.99,
397
+ brand: "Acme",
398
+ },
399
+ timestamp: "2024-04-01T00:00:01Z",
400
+ }),
401
+ ];
402
+ }
403
+ const CATALOG_INVENTORY_URL = "https://api.example.com/catalog/inventory/";
404
+ /**
405
+ * A decoy-free sibling of {@link buildMulticallSingleShotSearchDrillDownNoDecoyActionSteps}
406
+ * with a SECOND, independently-threaded per-item drill-down appended: the
407
+ * primary `results[]` items carry both a `sku` (joined to the pricing drill)
408
+ * and an `itemId` (joined to this inventory drill), and neither drill
409
+ * step's response overlaps the other's fields. Proves the fold loop merges
410
+ * fields from every independent target onto each item, not just the first.
411
+ */
412
+ function buildMulticallSingleShotSearchTwoIndependentDrillDownsActionSteps() {
413
+ return [
414
+ buildStep("r0", {
415
+ url: CATALOG_SEARCH_URL,
416
+ requestPostData: '{"page":1}',
417
+ responseBody: {
418
+ results: [
419
+ { sku: "sku-a", itemId: "item-a" },
420
+ { sku: "sku-b", itemId: "item-b" },
421
+ ],
422
+ },
423
+ timestamp: "2024-04-01T00:00:00Z",
424
+ }),
425
+ buildStep("r1", {
426
+ url: CATALOG_PRICING_URL,
427
+ requestPostData: '{"sku":"sku-a"}',
428
+ responseBody: { prices: [{ sku: "sku-a", amount: 19.99 }] },
429
+ timestamp: "2024-04-01T00:00:01Z",
430
+ }),
431
+ buildStep("r2", {
432
+ url: CATALOG_INVENTORY_URL,
433
+ requestPostData: '{"itemId":"item-a"}',
434
+ responseBody: { stock: [{ itemId: "item-a", qty: 7 }] },
435
+ timestamp: "2024-04-01T00:00:02Z",
436
+ }),
437
+ ];
438
+ }
439
+ /**
440
+ * A sibling of {@link buildMulticallSingleShotSearchTwoIndependentDrillDownsActionSteps}
441
+ * where BOTH per-item drills are threaded off the SAME primary join field
442
+ * (`sku`), rather than disjoint fields (`sku`/`itemId`). Each drill's
443
+ * response echoes that shared `sku` value back alongside its own field —
444
+ * an echo of the primary's own join value, not evidence the second drill
445
+ * depends on the first drill's response. Proves the fold loop still emits
446
+ * and merges BOTH independent drills onto every primary item even when
447
+ * their echoed join values collide, rather than collapsing the second
448
+ * drill into a chain under the first.
449
+ */
450
+ function buildMulticallSingleShotSearchTwoIndependentDrillDownsSharedJoinFieldActionSteps() {
451
+ return [
452
+ buildStep("r0", {
453
+ url: CATALOG_SEARCH_URL,
454
+ requestPostData: '{"page":1}',
455
+ responseBody: {
456
+ results: [{ sku: "sku-a" }, { sku: "sku-b" }],
457
+ },
458
+ timestamp: "2024-04-01T00:00:00Z",
459
+ }),
460
+ buildStep("r1", {
461
+ url: CATALOG_PRICING_URL,
462
+ requestPostData: '{"sku":"sku-a"}',
463
+ responseBody: { prices: [{ sku: "sku-a", amount: 19.99 }] },
464
+ timestamp: "2024-04-01T00:00:01Z",
465
+ }),
466
+ buildStep("r2", {
467
+ url: CATALOG_INVENTORY_URL,
468
+ requestPostData: '{"sku":"sku-a"}',
469
+ responseBody: { stock: [{ sku: "sku-a", qty: 7 }] },
470
+ timestamp: "2024-04-01T00:00:02Z",
471
+ }),
472
+ ];
473
+ }
474
+ const CATALOG_VENDORS_SEARCH_URL = "https://api.example.com/catalog/vendors/search";
475
+ const CATALOG_VENDOR_CONTRACTS_URL = "https://api.example.com/catalog/vendors/contracts/";
476
+ /**
477
+ * TWO independent primary/drill-down pairs, fully disjoint in both endpoint
478
+ * and step range: a products search folded by a per-product reviews drill
479
+ * (r0/r1), and an UNRELATED vendors search folded by a per-vendor contracts
480
+ * drill (r2/r3). Neither primary's items reference the other pair's join key
481
+ * (`productId` vs `vendorId`), so this proves the fold emitter resolves and
482
+ * loops over every independent plan, not only the first one it finds.
483
+ */
484
+ function buildMulticallTwoIndependentPrimariesActionSteps() {
485
+ return [
486
+ buildStep("r0", {
487
+ url: CATALOG_SEARCH_URL,
488
+ requestPostData: '{"page":1}',
489
+ responseBody: {
490
+ products: [{ productId: "p1" }, { productId: "p2" }],
491
+ },
492
+ timestamp: "2024-05-01T00:00:00Z",
493
+ }),
494
+ buildStep("r1", {
495
+ url: CATALOG_PRICING_URL,
496
+ requestPostData: '{"productId":"p1"}',
497
+ responseBody: { reviews: [{ productId: "p1", rating: 5 }] },
498
+ timestamp: "2024-05-01T00:00:01Z",
499
+ }),
500
+ buildStep("r2", {
501
+ url: CATALOG_VENDORS_SEARCH_URL,
502
+ requestPostData: '{"page":1}',
503
+ responseBody: {
504
+ vendors: [{ vendorId: "v1" }, { vendorId: "v2" }],
505
+ },
506
+ timestamp: "2024-05-01T00:00:02Z",
507
+ }),
508
+ buildStep("r3", {
509
+ url: CATALOG_VENDOR_CONTRACTS_URL,
510
+ requestPostData: '{"vendorId":"v1"}',
511
+ responseBody: { contracts: [{ vendorId: "v1", contractId: "c1" }] },
512
+ timestamp: "2024-05-01T00:00:03Z",
513
+ }),
514
+ ];
515
+ }
516
+ /**
517
+ * Same disjoint two-pair shape as
518
+ * {@link buildMulticallTwoIndependentPrimariesActionSteps}, except the SECOND
519
+ * pair's join value threads only through a request HEADER
520
+ * (`collectRequestStringValues` never scans headers), so
521
+ * `detectDrillDownFoldPlan`'s structural heuristic resolves only the FIRST
522
+ * pair (r0/r1) on its own; the second pair (r2/r3) is only reachable via a
523
+ * flow-declared `foldReturn` spec naming `vendors` as its `resultsPath`.
524
+ * Since the second pair's primary/drill steps (2, 3) are entirely outside the
525
+ * first pair's own consumed indices ({@link buildMulticallTwoIndependentPrimariesActionSteps}'s
526
+ * r0/r1), a correctly-behaving `resolveFoldPlan` must APPEND the spec's plan
527
+ * as a second, independent entry rather than discarding it just because its
528
+ * primary differs from the one the heuristic already resolved.
529
+ */
530
+ function buildMulticallTwoIndependentPrimariesSecondHeaderThreadedActionSteps() {
531
+ return [
532
+ buildStep("r0", {
533
+ url: CATALOG_SEARCH_URL,
534
+ requestPostData: '{"page":1}',
535
+ responseBody: {
536
+ products: [{ productId: "p1" }, { productId: "p2" }],
537
+ },
538
+ timestamp: "2024-05-01T00:00:00Z",
539
+ }),
540
+ buildStep("r1", {
541
+ url: CATALOG_PRICING_URL,
542
+ requestPostData: '{"productId":"p1"}',
543
+ responseBody: { reviews: [{ productId: "p1", rating: 5 }] },
544
+ timestamp: "2024-05-01T00:00:01Z",
545
+ }),
546
+ buildStep("r2", {
547
+ url: CATALOG_VENDORS_SEARCH_URL,
548
+ requestPostData: '{"page":1}',
549
+ responseBody: {
550
+ vendors: [{ vendorId: "v1" }, { vendorId: "v2" }],
551
+ },
552
+ timestamp: "2024-05-01T00:00:02Z",
553
+ }),
554
+ buildStep("r3", {
555
+ url: CATALOG_VENDOR_CONTRACTS_URL,
556
+ requestPostData: '{"lookup":true}',
557
+ responseBody: { contracts: [{ vendorId: "v1", contractId: "c1" }] },
558
+ timestamp: "2024-05-01T00:00:03Z",
559
+ requestHeaders: { "Content-Type": "application/json", "X-Vendor-Id": "v1" },
560
+ }),
561
+ ];
562
+ }
563
+ const CATALOG_STOCK_URL = "https://api.example.com/catalog/stock/";
564
+ /** A single-shot search drilled by two INDEPENDENT later calls whose join
565
+ * values thread through different surfaces: the pricing step's `sku` lands
566
+ * in its JSON body (heuristically detectable by `collectRequestStringValues`),
567
+ * while the stock step's `itemId` is carried ONLY in a request header —
568
+ * `collectRequestStringValues` never scans headers, so that fold can only be
569
+ * resolved via a flow-declared `foldReturn` spec. Every downstream unit and
570
+ * runtime test that needs one mixed-source, two-target fold scenario should
571
+ * share this fixture rather than inventing its own. */
572
+ function buildMulticallSingleShotSearchHeuristicAndSpecTwoTargetActionSteps() {
573
+ return [
574
+ buildStep("r0", {
575
+ url: CATALOG_SEARCH_URL,
576
+ requestPostData: '{"page":1}',
577
+ responseBody: {
578
+ results: [
579
+ { sku: "sku-a", itemId: "item-a" },
580
+ { sku: "sku-b", itemId: "item-b" },
581
+ ],
582
+ },
583
+ timestamp: "2024-04-01T00:00:00Z",
584
+ }),
585
+ buildStep("r1", {
586
+ url: CATALOG_PRICING_URL,
587
+ requestPostData: '{"sku":"sku-a"}',
588
+ responseBody: { prices: [{ sku: "sku-a", amount: 19.99 }] },
589
+ timestamp: "2024-04-01T00:00:01Z",
590
+ }),
591
+ buildStep("r2", {
592
+ url: CATALOG_STOCK_URL,
593
+ requestPostData: '{"lookup":true}',
594
+ responseBody: { stock: [{ itemId: "item-a", qty: 7 }] },
595
+ timestamp: "2024-04-01T00:00:02Z",
596
+ requestHeaders: { "Content-Type": "application/json", "X-Item-Id": "item-a" },
597
+ }),
598
+ ];
599
+ }
600
+ /**
601
+ * TWO occurrences of the SAME primary endpoint (same origin+pathname,
602
+ * differing only by query string), each independently drilling a
603
+ * DIFFERENT item into a DIFFERENT dependent call — unlike
604
+ * {@link buildMulticallSingleShotSearchDrillDownRequeriedPrimaryOverlapActionSteps}
605
+ * (whose two occurrences thread the SAME drill), these thread two distinct
606
+ * drills, so {@link detectDrillDownFoldPlan}'s freshest-wins collapse never
607
+ * applies and both occurrences resolve as their own independent
608
+ * {@link FoldPlan}. Both plans' primary responses share the SAME top-level
609
+ * array key (`results`) — the exact shape a plain `{ ...a, ...b }`
610
+ * object-spread silently collapses to the later plan's array alone, since
611
+ * the emitted return statement folds every resolved plan's own primary var
612
+ * together into the runtime response.
613
+ */
614
+ function buildMulticallTwoOccurrencesSamePrimaryDistinctDrillsActionSteps() {
615
+ return [
616
+ buildStep("r0", {
617
+ url: `${CATALOG_SEARCH_URL}?q=widgets`,
618
+ requestPostData: '{"page":1}',
619
+ responseBody: { results: [{ sku: "sku-a" }] },
620
+ timestamp: "2025-01-01T00:00:00Z",
621
+ }),
622
+ buildStep("r1", {
623
+ url: CATALOG_PRICING_URL,
624
+ requestPostData: '{"sku":"sku-a"}',
625
+ responseBody: { prices: [{ sku: "sku-a", amount: 19.99 }] },
626
+ timestamp: "2025-01-01T00:00:01Z",
627
+ }),
628
+ buildStep("r2", {
629
+ url: `${CATALOG_SEARCH_URL}?q=gadgets`,
630
+ requestPostData: '{"page":1}',
631
+ responseBody: { results: [{ sku: "sku-c" }] },
632
+ timestamp: "2025-01-01T00:00:02Z",
633
+ }),
634
+ buildStep("r3", {
635
+ url: CATALOG_INVENTORY_URL,
636
+ requestPostData: '{"sku":"sku-c"}',
637
+ responseBody: { stock: [{ sku: "sku-c", qty: 4 }] },
638
+ timestamp: "2025-01-01T00:00:03Z",
639
+ }),
640
+ ];
641
+ }
642
+ const CATALOG_SECTIONS_URL = "https://api.example.com/catalog/sections";
643
+ const CATALOG_ENTRY_DETAIL_URL = "https://api.example.com/catalog/entries/e2/details";
644
+ /**
645
+ * A grouped, nested-primary drill-down whose primary response carries TWO
646
+ * outer `sections[]` groups rather than the single group every other nested
647
+ * fixture in this file uses, with the drilled entry living in the SECOND
648
+ * group's `entries[]`, not the first. A fold that assumes the matched item
649
+ * always lives in group 0 (as a single-group fixture can never disprove)
650
+ * would resolve the fold onto `sections[0]` and silently attach the drill
651
+ * response's `description` to the wrong entry.
652
+ */
653
+ function buildMulticallNestedGroupedDrillDownMultiGroupActionSteps() {
654
+ return [
655
+ buildStep("r0", {
656
+ url: CATALOG_SECTIONS_URL,
657
+ requestPostData: null,
658
+ responseBody: {
659
+ sections: [
660
+ {
661
+ label: "featured",
662
+ entries: [
663
+ { entryId: "e1", name: "Widget" },
664
+ { entryId: "e3", name: "Doohickey" },
665
+ ],
666
+ },
667
+ {
668
+ label: "clearance",
669
+ entries: [
670
+ { entryId: "e2", name: "Gadget" },
671
+ { entryId: "e4", name: "Thingamajig" },
672
+ ],
673
+ },
674
+ ],
675
+ },
676
+ timestamp: "2024-11-01T00:00:00Z",
677
+ }),
678
+ buildStep("r1", {
679
+ url: CATALOG_ENTRY_DETAIL_URL,
680
+ requestPostData: null,
681
+ responseBody: { details: [{ entryId: "e2", description: "A gadget." }] },
682
+ timestamp: "2024-11-01T00:00:01Z",
683
+ }),
684
+ ];
685
+ }
686
+ const ACCOUNT_SEARCH_URL = "https://api.example.com/accounts/search";
687
+ const ACCOUNT_DETAIL_URL = "https://api.example.com/accounts/detail";
688
+ /**
689
+ * A single-shot search whose primary item join field (`accountId`) is a
690
+ * NUMBER rather than a string, threaded into the drill-down call via a URL
691
+ * query parameter (`?accountId=42`). `URLSearchParams` always stringifies its
692
+ * values, so the request-collection side already captures `"42"` as a
693
+ * string; the item side of the join is what must widen to accept a numeric
694
+ * leaf for {@link detectDrillDownFoldPlan} to resolve `accountId` as a join
695
+ * field at all.
696
+ */
697
+ function buildMulticallSingleShotSearchDrillDownNumericJoinActionSteps() {
698
+ return [
699
+ buildStep("r0", {
700
+ url: ACCOUNT_SEARCH_URL,
701
+ requestPostData: JSON.stringify({ page: 1 }),
702
+ responseBody: { accounts: [{ accountId: 42, name: "Acme" }] },
703
+ timestamp: "2024-05-01T00:00:00Z",
704
+ }),
705
+ buildStep("r1", {
706
+ url: `${ACCOUNT_DETAIL_URL}?accountId=42`,
707
+ requestPostData: null,
708
+ responseBody: { transactions: [{ transactionId: "t1" }] },
709
+ timestamp: "2024-05-01T00:00:01Z",
710
+ }),
711
+ ];
712
+ }
713
+ /**
714
+ * A single-shot search whose primary item join field (`accountId`) is
715
+ * threaded into the drill-down call ONLY as a URL path segment
716
+ * (`/accounts/42/transactions`) — never as a query param or a JSON body
717
+ * value. REST-style APIs commonly shape drill-down calls this way, but
718
+ * `collectRequestStringValues` only harvested query params and JSON body
719
+ * leaves, so this join field was invisible to `findThreadedJoinFields` and
720
+ * {@link detectDrillDownFoldPlan} returned null for this shape.
721
+ */
722
+ function buildMulticallSingleShotSearchDrillDownPathThreadedJoinActionSteps() {
723
+ return [
724
+ buildStep("r0", {
725
+ url: ACCOUNT_SEARCH_URL,
726
+ requestPostData: JSON.stringify({ page: 1 }),
727
+ responseBody: { accounts: [{ accountId: 42, name: "Acme" }] },
728
+ timestamp: "2024-07-01T00:00:00Z",
729
+ }),
730
+ buildStep("r1", {
731
+ url: "https://api.example.com/accounts/42/transactions",
732
+ requestPostData: null,
733
+ responseBody: { transactions: [{ transactionId: "t1" }] },
734
+ timestamp: "2024-07-01T00:00:01Z",
735
+ }),
736
+ ];
737
+ }
738
+ /**
739
+ * A single-shot search whose primary item join field (`sku`) sits inside a
740
+ * NESTED object (`{ identifiers: { sku } }`) rather than as a top-level
741
+ * property. `findThreadedJoinFields` must walk into nested plain objects to
742
+ * find it, and the resulting joinFields entry is the dot-separated path
743
+ * `"identifiers.sku"`, not the bare leaf name `"sku"`.
744
+ */
745
+ function buildMulticallSingleShotSearchDrillDownNestedJoinFieldActionSteps() {
746
+ return [
747
+ buildStep("r0", {
748
+ url: ACCOUNT_SEARCH_URL,
749
+ requestPostData: JSON.stringify({ page: 1 }),
750
+ responseBody: { accounts: [{ identifiers: { sku: "SKU-1" }, name: "Acme" }] },
751
+ timestamp: "2024-08-01T00:00:00Z",
752
+ }),
753
+ buildStep("r1", {
754
+ url: ACCOUNT_DETAIL_URL,
755
+ requestPostData: JSON.stringify({ sku: "SKU-1" }),
756
+ responseBody: { transactions: [{ transactionId: "t1" }] },
757
+ timestamp: "2024-08-01T00:00:01Z",
758
+ }),
759
+ ];
760
+ }
761
+ /**
762
+ * A single-shot search whose primary items carry a COMPOSITE join key mixing
763
+ * a string field (`region`) and a numeric field (`accountId`), both threaded
764
+ * into the drill-down request. `findThreadedJoinFields` filters candidate
765
+ * fields independently per entry, so a numeric field dropped alongside a
766
+ * present string field would silently degrade the fold to a partial
767
+ * (string-only) join key rather than failing outright — this fixture proves
768
+ * both survive together, in the item's own key order.
769
+ */
770
+ function buildMulticallSingleShotSearchDrillDownCompositeNumericJoinActionSteps() {
771
+ return [
772
+ buildStep("r0", {
773
+ url: ACCOUNT_SEARCH_URL,
774
+ requestPostData: JSON.stringify({ page: 1 }),
775
+ responseBody: { accounts: [{ region: "us", accountId: 7, name: "Acme" }] },
776
+ timestamp: "2024-06-01T00:00:00Z",
777
+ }),
778
+ buildStep("r1", {
779
+ url: `${ACCOUNT_DETAIL_URL}?region=us&accountId=7`,
780
+ requestPostData: null,
781
+ responseBody: { transactions: [{ transactionId: "t1" }] },
782
+ timestamp: "2024-06-01T00:00:01Z",
783
+ }),
784
+ ];
785
+ }
786
+ /**
787
+ * A decoy-free single-shot search sibling of
788
+ * {@link buildMulticallSingleShotSearchDrillDownNoDecoyActionSteps} whose
789
+ * `results[]` has TWO items, but whose only captured drill-down call was
790
+ * made for the SECOND item (`sku-b`), not the first. A heuristic that always
791
+ * pairs the primary's `results[0]` with the sole drill step would thread
792
+ * `sku-a` into the fold even though the captured request body only ever
793
+ * mentions `sku-b` — proving the fold must select the drill call's item by
794
+ * matching the join field's actual value, not by array position.
795
+ */
796
+ function buildMulticallSingleShotSearchDrillDownNonFirstItemSkuActionSteps() {
797
+ return [
798
+ buildStep("r0", {
799
+ url: CATALOG_SEARCH_URL,
800
+ requestPostData: '{"page":1}',
801
+ responseBody: {
802
+ results: [{ sku: "sku-a" }, { sku: "sku-b" }],
803
+ },
804
+ timestamp: "2024-09-01T00:00:00Z",
805
+ }),
806
+ buildStep("r1", {
807
+ url: CATALOG_PRICING_URL,
808
+ requestPostData: '{"sku":"sku-b"}',
809
+ responseBody: { prices: [{ sku: "sku-b", amount: 24.99 }] },
810
+ timestamp: "2024-09-01T00:00:01Z",
811
+ }),
812
+ ];
813
+ }
814
+ /**
815
+ * A composite-join sibling of
816
+ * {@link buildMulticallSingleShotSearchDrillDownCompositeNumericJoinActionSteps}
817
+ * whose `accounts[]` has TWO items, but whose only captured drill-down call
818
+ * threads the SECOND item's composite join key (`region: "eu"`,
819
+ * `accountId: 9`), never the first item's (`region: "us"`, `accountId: 7`).
820
+ * Proves the same non-first-item selection requirement holds when the join
821
+ * key is composite, not just when it's a single field.
822
+ */
823
+ function buildMulticallSingleShotSearchDrillDownCompositeNumericJoinNonFirstItemActionSteps() {
824
+ return [
825
+ buildStep("r0", {
826
+ url: ACCOUNT_SEARCH_URL,
827
+ requestPostData: JSON.stringify({ page: 1 }),
828
+ responseBody: {
829
+ accounts: [
830
+ { region: "us", accountId: 7, name: "Acme" },
831
+ { region: "eu", accountId: 9, name: "Globex" },
832
+ ],
833
+ },
834
+ timestamp: "2024-09-01T00:00:00Z",
835
+ }),
836
+ buildStep("r1", {
837
+ url: `${ACCOUNT_DETAIL_URL}?region=eu&accountId=9`,
838
+ requestPostData: null,
839
+ responseBody: { transactions: [{ transactionId: "t2" }] },
840
+ timestamp: "2024-09-01T00:00:01Z",
841
+ }),
842
+ ];
843
+ }
844
+ /**
845
+ * A single-shot search whose primary item join field (`accountId`) is
846
+ * threaded into the drill-down call ONLY via a custom request header
847
+ * (`X-Account-Id`) — never a query param, JSON body value, or URL path
848
+ * segment. `collectRequestStringValues` deliberately never scans
849
+ * `requestHeaders` (recon-generate.ts:4787-4815), so this join is invisible
850
+ * to `findThreadedJoinFields` and {@link detectDrillDownFoldPlan} must return
851
+ * `null` here regardless of how many other threading channels it learns to
852
+ * scan — only a flow-declared `foldReturn` naming `accountId` can resolve
853
+ * this fold.
854
+ */
855
+ function buildMulticallSingleShotSearchDrillDownHeaderThreadedJoinActionSteps() {
856
+ return [
857
+ buildStep("r0", {
858
+ url: ACCOUNT_SEARCH_URL,
859
+ requestPostData: JSON.stringify({ page: 1 }),
860
+ responseBody: { accounts: [{ accountId: 42, name: "Acme" }] },
861
+ timestamp: "2024-08-01T00:00:00Z",
862
+ }),
863
+ buildStep("r1", {
864
+ url: ACCOUNT_DETAIL_URL,
865
+ requestPostData: null,
866
+ requestHeaders: { "Content-Type": "application/json", "X-Account-Id": "42" },
867
+ responseBody: { transactions: [{ transactionId: "t1" }] },
868
+ timestamp: "2024-08-01T00:00:01Z",
869
+ }),
870
+ ];
871
+ }
872
+ /**
873
+ * A sibling of {@link buildMulticallSingleShotSearchDrillDownHeaderThreadedJoinActionSteps}
874
+ * whose primary endpoint is hit TWICE (`r0`, `r1`), each occurrence's single
875
+ * `accounts[]` item independently satisfying the drill-down's `X-Account-Id`
876
+ * join on its own — mirroring
877
+ * {@link buildMulticallSingleShotSearchDrillDownRequeriedPrimaryOverlapActionSteps}'s
878
+ * requeried-primary-overlap shape, but with the join threaded ONLY through a
879
+ * request header rather than a URL query param, so `detectDrillDownFoldPlan`'s
880
+ * structural heuristic can never see it (same blind spot documented on
881
+ * {@link buildMulticallSingleShotSearchDrillDownHeaderThreadedJoinActionSteps}).
882
+ * Only a flow-declared `foldReturn` naming `accountId` can resolve this fold,
883
+ * isolating `buildFoldPlanFromSpec`'s own freshest-wins primary lookahead:
884
+ * the resolved plan must anchor on `r1` (the LATER occurrence) and its
885
+ * differing `name` value ("Acme Corp"), not `r0`'s stale "Acme".
886
+ */
887
+ function buildMulticallSingleShotSearchDrillDownHeaderThreadedJoinRequeriedPrimaryOverlapActionSteps() {
888
+ return [
889
+ buildStep("r0", {
890
+ url: ACCOUNT_SEARCH_URL,
891
+ requestPostData: JSON.stringify({ page: 1 }),
892
+ responseBody: { accounts: [{ accountId: 42, name: "Acme" }] },
893
+ timestamp: "2024-08-01T00:00:00Z",
894
+ }),
895
+ buildStep("r1", {
896
+ url: ACCOUNT_SEARCH_URL,
897
+ requestPostData: JSON.stringify({ page: 2 }),
898
+ responseBody: { accounts: [{ accountId: 42, name: "Acme Corp" }] },
899
+ timestamp: "2024-08-01T00:00:01Z",
900
+ }),
901
+ buildStep("r2", {
902
+ url: ACCOUNT_DETAIL_URL,
903
+ requestPostData: null,
904
+ requestHeaders: { "Content-Type": "application/json", "X-Account-Id": "42" },
905
+ responseBody: { transactions: [{ transactionId: "t1" }] },
906
+ timestamp: "2024-08-01T00:00:02Z",
907
+ }),
908
+ ];
909
+ }
910
+ /**
911
+ * Sibling of {@link buildMulticallSingleShotSearchDrillDownHeaderThreadedJoinActionSteps}
912
+ * whose primary `accounts[]` array holds TWO items rather than one, so a
913
+ * runtime fold loop built from this capture must re-key the drill-down's
914
+ * request header per iteration rather than replaying the single captured
915
+ * header value.
916
+ *
917
+ * Threaded on `API-Token` rather than the sibling fixture's `X-Account-Id`:
918
+ * `emitMultiStepExecuteHttp`'s per-call header emission only ever renders a
919
+ * captured header back into the generated request when it recognizes the
920
+ * header name (`API-Token`/`Authorization`, or a base-URL-/tenant-derived
921
+ * header passed in separately — recon-generate.ts:4273-4279) — an arbitrary
922
+ * custom header like `X-Account-Id` is captured but never re-emitted, so a
923
+ * fold loop built from it would have nothing to re-key at runtime. `API-Token`
924
+ * is still a request header the join value reaches through NO OTHER channel
925
+ * (not the URL, which is identical on every call, and not the body, which is
926
+ * empty), so it still exercises the same header-threaded-join runtime path
927
+ * {@link buildMulticallSingleShotSearchDrillDownHeaderThreadedJoinActionSteps}
928
+ * demonstrates is invisible to the structural heuristic.
929
+ *
930
+ * The captured drill call was made for `accountId: 43` (the SECOND item),
931
+ * pinning `primaryMatchedItemIndex` away from the index-0 default.
932
+ */
933
+ function buildMulticallSingleShotSearchDrillDownHeaderThreadedJoinMultiItemActionSteps() {
934
+ return [
935
+ buildStep("r0", {
936
+ url: ACCOUNT_SEARCH_URL,
937
+ requestPostData: JSON.stringify({ page: 1 }),
938
+ responseBody: {
939
+ accounts: [
940
+ { accountId: 42, name: "Acme" },
941
+ { accountId: 43, name: "Globex" },
942
+ ],
943
+ },
944
+ timestamp: "2024-08-01T00:00:00Z",
945
+ }),
946
+ buildStep("r1", {
947
+ url: ACCOUNT_DETAIL_URL,
948
+ requestPostData: null,
949
+ requestHeaders: { "Content-Type": "application/json", "API-Token": "43" },
950
+ responseBody: { transactions: [{ transactionId: "t2" }] },
951
+ timestamp: "2024-08-01T00:00:01Z",
952
+ }),
953
+ ];
954
+ }
955
+ /**
956
+ * A single-shot search whose primary results array is NOT drilled into at
957
+ * item 0 — only a single later drill call exists, and it threads the second
958
+ * item's (`itemId: "i-a"`) join value, never the first's (`itemId: "i-b"`).
959
+ * With no coincidental extra call re-drilling item 0 (unlike
960
+ * {@link buildMulticallDependentDrillDownActionSteps}, whose items are also
961
+ * out of order but which happens to pass a items[0]-only match because a
962
+ * third step re-drills item 0), this pins {@link detectDrillDownFoldPlan}'s
963
+ * item search to the actually-drilled item rather than an index-0 default —
964
+ * the search must find the match at `primaryMatchedItemIndex` 1.
965
+ * Named "OutOfOrder" (rather than "NonFirstItem") to avoid colliding with
966
+ * {@link buildMulticallSingleShotSearchDrillDownNonFirstItemSkuActionSteps}.
967
+ */
968
+ function buildMulticallSingleShotSearchDrillDownOutOfOrderItemActionSteps() {
969
+ return [
970
+ buildStep("r0", {
971
+ url: CATALOG_SEARCH_URL,
972
+ requestPostData: '{"page":1}',
973
+ responseBody: {
974
+ results: [{ itemId: "i-b" }, { itemId: "i-a" }],
975
+ },
976
+ timestamp: "2024-09-01T00:00:00Z",
977
+ }),
978
+ buildStep("r1", {
979
+ url: CATALOG_PRICING_URL,
980
+ requestPostData: '{"itemId":"i-a"}',
981
+ responseBody: { prices: [{ itemId: "i-a", amount: 19.99 }] },
982
+ timestamp: "2024-09-01T00:00:01Z",
983
+ }),
984
+ ];
985
+ }
986
+ /**
987
+ * A single-shot search sibling of
988
+ * {@link buildMulticallSingleShotSearchDrillDownNonFirstItemSkuActionSteps}
989
+ * whose axis is on the DRILL side rather than the primary side: the primary
990
+ * `results[]` has a single item (`sku-a`), but the drill-down's own
991
+ * `prices[]` array comes back holding TWO entries — a decoy for an unrelated
992
+ * sku (`sku-z`, listed first) alongside the real match (`sku-a`, listed
993
+ * second). A fold that merges `foldMatches[0]`/`drillItems?.[0]` would
994
+ * silently splice the decoy's `amount` onto the primary item; only matching
995
+ * the drill array's own entries against the threaded join field (`sku`)
996
+ * picks the correct one, at a non-zero index within that array.
997
+ */
998
+ function buildMulticallSingleShotSearchDrillDownMultiMatchActionSteps() {
999
+ return [
1000
+ buildStep("r0", {
1001
+ url: CATALOG_SEARCH_URL,
1002
+ requestPostData: '{"page":1}',
1003
+ responseBody: {
1004
+ results: [{ sku: "sku-a" }],
1005
+ },
1006
+ timestamp: "2024-09-01T00:00:00Z",
1007
+ }),
1008
+ buildStep("r1", {
1009
+ url: CATALOG_PRICING_URL,
1010
+ requestPostData: '{"sku":"sku-a"}',
1011
+ responseBody: {
1012
+ prices: [
1013
+ { sku: "sku-z", amount: 5.0 },
1014
+ { sku: "sku-a", amount: 19.99 },
1015
+ ],
1016
+ },
1017
+ timestamp: "2024-09-01T00:00:01Z",
1018
+ }),
1019
+ ];
1020
+ }
1021
+ /**
1022
+ * A multi-match sibling of
1023
+ * {@link buildMulticallSingleShotSearchDrillDownMultiMatchActionSteps} whose
1024
+ * join field's type DIFFERS across the two sides: the primary item's
1025
+ * `accountId` is a NUMBER, while every item in the drill-down response's
1026
+ * array carries `accountId` as a STRING (as request-threaded values always
1027
+ * are). A decoy entry is listed first with a string that never equals the
1028
+ * primary's numeric value even after coercion; only the second entry's
1029
+ * string stringifies to the primary's value. A fold that matches via strict
1030
+ * equality (`m["accountId"] === item.accountId`, i.e. `"42" === 42`) fails
1031
+ * for every candidate here, proving the fold must compare join values by
1032
+ * their string representation, not by strict identity.
1033
+ */
1034
+ function buildMulticallSingleShotSearchDrillDownTypeMismatchJoinActionSteps() {
1035
+ return [
1036
+ buildStep("r0", {
1037
+ url: ACCOUNT_SEARCH_URL,
1038
+ requestPostData: JSON.stringify({ page: 1 }),
1039
+ responseBody: { accounts: [{ accountId: 42, name: "Acme" }] },
1040
+ timestamp: "2024-10-01T00:00:00Z",
1041
+ }),
1042
+ buildStep("r1", {
1043
+ url: `${ACCOUNT_DETAIL_URL}?accountId=42`,
1044
+ requestPostData: null,
1045
+ responseBody: {
1046
+ transactions: [
1047
+ { accountId: "99", transactionId: "decoy" },
1048
+ { accountId: "42", transactionId: "t-real" },
1049
+ ],
1050
+ },
1051
+ timestamp: "2024-10-01T00:00:01Z",
1052
+ }),
1053
+ ];
1054
+ }
1055
+ /**
1056
+ * Same shape as {@link buildMulticallSingleShotSearchDrillDownChainedDependentActionSteps},
1057
+ * but the CHAIN's second step (the price-history call `r2`, not the drill
1058
+ * step `r1` itself) is a multipart upload rather than a JSON POST.
1059
+ * `emitMultiStepExecuteHttp`'s fold loop re-issues EVERY chain step's
1060
+ * request per item by re-keying its rendered JSON body template — a
1061
+ * multipart step anywhere in `chain`, not just at `drillStepIndex`, has no
1062
+ * such template to re-key, so `resolveFoldPlan` must disqualify the whole
1063
+ * plan here exactly as it already does when the drill step itself is
1064
+ * multipart.
1065
+ */
1066
+ function buildMulticallSingleShotSearchDrillDownChainedDependentMultipartChainStepActionSteps() {
1067
+ const steps = buildMulticallSingleShotSearchDrillDownChainedDependentActionSteps();
1068
+ return steps.map((step, index) => (index === 2 ? { ...step, isMultipart: true } : step));
1069
+ }
1070
+ const CATALOG_PRICE_HISTORY_URL = "https://api.example.com/catalog/price-history";
1071
+ /**
1072
+ * A single-shot search → per-item drill-down flow whose drill step's OWN
1073
+ * response is foldable on its own terms — it carries `prices[]`, the same
1074
+ * per-item results shape {@link buildMulticallSingleShotSearchDrillDownNoDecoyActionSteps}
1075
+ * would fold directly — but is ALSO depended on by a THIRD step: the drill
1076
+ * response's `priceToken` threads into a price-history call whose response
1077
+ * carries the real per-item array this flow means to fold. `computeFoldChain`
1078
+ * must extend the plan's chain to `[drillStepIndex, historyStepIndex]`
1079
+ * instead of stopping at the drill step's own array, so
1080
+ * `emitMultiStepExecuteHttp`'s fold loop renders BOTH calls per item and
1081
+ * shape inference ({@link foldResponseBodyForShapeInference}) folds the
1082
+ * chain's TERMINAL (history) response, not the drill step's own `prices[]`.
1083
+ */
1084
+ function buildMulticallSingleShotSearchDrillDownChainedDependentActionSteps() {
1085
+ return [
1086
+ buildStep("r0", {
1087
+ url: CATALOG_SEARCH_URL,
1088
+ requestPostData: '{"page":1}',
1089
+ responseBody: {
1090
+ results: [{ sku: "sku-a" }, { sku: "sku-b" }],
1091
+ },
1092
+ timestamp: "2024-11-15T00:00:00Z",
1093
+ }),
1094
+ buildStep("r1", {
1095
+ url: CATALOG_PRICING_URL,
1096
+ requestPostData: '{"sku":"sku-a"}',
1097
+ responseBody: {
1098
+ priceToken: "tok-a1",
1099
+ prices: [{ sku: "sku-a", amount: 19.99 }],
1100
+ },
1101
+ timestamp: "2024-11-15T00:00:01Z",
1102
+ }),
1103
+ buildStep("r2", {
1104
+ url: CATALOG_PRICE_HISTORY_URL,
1105
+ requestPostData: '{"priceToken":"tok-a1"}',
1106
+ responseBody: {
1107
+ history: [{ sku: "sku-a", amount: 18.5, asOf: "2024-11-01" }],
1108
+ },
1109
+ timestamp: "2024-11-15T00:00:02Z",
1110
+ }),
1111
+ ];
1112
+ }
1113
+ /**
1114
+ * A response-header sibling of {@link buildMulticallSingleShotSearchDrillDownChainedDependentActionSteps}:
1115
+ * the drill step's (`r1`) response mints its join token (`tok-a1`) ONLY in a
1116
+ * custom response HEADER (`X-Price-Token`) — its body is empty, unlike the
1117
+ * sibling's `priceToken` body field. A third step (`r2`) threads that header
1118
+ * value into its own request body and carries the real per-item `history[]`
1119
+ * array. `computeFoldChain`'s `dependsOnChain` check must walk `r1`'s
1120
+ * response headers (not just its body) to see the shared value, extending
1121
+ * the chain to `[drillStepIndex, historyStepIndex]` rather than stopping at
1122
+ * `r1`.
1123
+ */
1124
+ function buildMulticallSingleShotSearchDrillDownHeaderMintedChainedResponseValueActionSteps() {
1125
+ return [
1126
+ buildStep("r0", {
1127
+ url: CATALOG_SEARCH_URL,
1128
+ requestPostData: '{"page":1}',
1129
+ responseBody: {
1130
+ results: [{ sku: "sku-a" }, { sku: "sku-b" }],
1131
+ },
1132
+ timestamp: "2024-11-16T00:00:00Z",
1133
+ }),
1134
+ buildStep("r1", {
1135
+ url: CATALOG_PRICING_URL,
1136
+ requestPostData: '{"sku":"sku-a"}',
1137
+ responseBody: {},
1138
+ responseHeaders: { "content-type": "application/json", "X-Price-Token": "tok-a1" },
1139
+ timestamp: "2024-11-16T00:00:01Z",
1140
+ }),
1141
+ buildStep("r2", {
1142
+ url: CATALOG_PRICE_HISTORY_URL,
1143
+ requestPostData: '{"priceToken":"tok-a1"}',
1144
+ responseBody: {
1145
+ history: [{ sku: "sku-a", amount: 18.5, asOf: "2024-11-01" }],
1146
+ },
1147
+ timestamp: "2024-11-16T00:00:02Z",
1148
+ }),
1149
+ ];
1150
+ }
1151
+ const CATALOG_VERIFICATION_STATUS_URL = "https://api.example.com/catalog/verification-status";
1152
+ /**
1153
+ * A boolean-threading sibling of {@link buildMulticallSingleShotSearchDrillDownChainedDependentActionSteps}:
1154
+ * the drill step's (`r1`) response mints ONLY a boolean value
1155
+ * (`{ verified: true }`) and carries no array of its own — unlike the
1156
+ * `priceToken`/`prices[]` sibling, this drill step is NOT foldable in
1157
+ * isolation. A third step (`r2`) threads that boolean (rendered as the
1158
+ * string `"true"`) into its request and carries the real per-item
1159
+ * `history[]` array in its response. `computeFoldChain` must recognize the
1160
+ * boolean leaf as a genuine chain-dependency source and extend the chain to
1161
+ * `[drillStepIndex, historyStepIndex]` rather than stopping at `r1`.
1162
+ */
1163
+ function buildMulticallSingleShotSearchDrillDownBooleanChainedResponseValueActionSteps() {
1164
+ return [
1165
+ buildStep("r0", {
1166
+ url: CATALOG_SEARCH_URL,
1167
+ requestPostData: '{"page":1}',
1168
+ responseBody: {
1169
+ results: [{ sku: "sku-a" }, { sku: "sku-b" }],
1170
+ },
1171
+ timestamp: "2024-12-01T00:00:00Z",
1172
+ }),
1173
+ buildStep("r1", {
1174
+ url: CATALOG_PRICING_URL,
1175
+ requestPostData: '{"sku":"sku-a"}',
1176
+ responseBody: {
1177
+ verified: true,
1178
+ },
1179
+ timestamp: "2024-12-01T00:00:01Z",
1180
+ }),
1181
+ buildStep("r2", {
1182
+ url: CATALOG_VERIFICATION_STATUS_URL,
1183
+ requestPostData: '{"verified":"true"}',
1184
+ responseBody: {
1185
+ history: [{ sku: "sku-a", amount: 18.5, asOf: "2024-11-01" }],
1186
+ },
1187
+ timestamp: "2024-12-01T00:00:02Z",
1188
+ }),
1189
+ ];
1190
+ }
1191
+ /**
1192
+ * A nested-join-key sibling of {@link buildMulticallSingleShotSearchDrillDownChainedDependentActionSteps}:
1193
+ * each primary `results[]` item carries its join key under a nested
1194
+ * `identifiers` object (`{ identifiers: { sku } }`, matching
1195
+ * {@link buildMulticallSingleShotSearchDrillDownNestedJoinFieldMultiItemActionSteps})
1196
+ * rather than as a top-level field, AND (as in the chained-dependent fixture)
1197
+ * the drill step's own response is foldable on its own terms but is ALSO
1198
+ * depended on by a further step whose response carries the real per-item
1199
+ * data. The primary item that threads the nested join is the SECOND item
1200
+ * (`sku-b`), not the first, so `primaryMatchedItemIndex` must resolve by
1201
+ * walking into each item's nested fields rather than assuming index 0, while
1202
+ * `computeFoldChain` must still extend the chain past the drill step to its
1203
+ * dependent follow-up rather than stopping at the drill step's own array.
1204
+ */
1205
+ function buildMulticallSingleShotSearchDrillDownNestedJoinFieldChainedDependentActionSteps() {
1206
+ return [
1207
+ buildStep("r0", {
1208
+ url: CATALOG_SEARCH_URL,
1209
+ requestPostData: '{"page":1}',
1210
+ responseBody: {
1211
+ results: [{ identifiers: { sku: "sku-a" } }, { identifiers: { sku: "sku-b" } }],
1212
+ },
1213
+ timestamp: "2024-11-20T00:00:00Z",
1214
+ }),
1215
+ buildStep("r1", {
1216
+ url: CATALOG_PRICING_URL,
1217
+ requestPostData: '{"sku":"sku-b"}',
1218
+ responseBody: {
1219
+ priceToken: "tok-b1",
1220
+ prices: [{ sku: "sku-b", amount: 24.99 }],
1221
+ },
1222
+ timestamp: "2024-11-20T00:00:01Z",
1223
+ }),
1224
+ buildStep("r2", {
1225
+ url: CATALOG_PRICE_HISTORY_URL,
1226
+ requestPostData: '{"priceToken":"tok-b1"}',
1227
+ responseBody: {
1228
+ history: [{ sku: "sku-b", amount: 22.5, asOf: "2024-11-01" }],
1229
+ },
1230
+ timestamp: "2024-11-20T00:00:02Z",
1231
+ }),
1232
+ ];
1233
+ }
1234
+ /**
1235
+ * A search endpoint re-queried with two distinct bodies (`page 1`/`page 2`,
1236
+ * satisfying {@link findRequeriedActions}'s re-query signature) whose
1237
+ * responses BOTH independently contain a `results[]` item sharing the same
1238
+ * `sku` — but at a different `price` (10 vs 12) — followed by a pricing
1239
+ * drill-down call threading that `sku` via its URL query param. Every prior
1240
+ * action is a fold-primary candidate for {@link detectDrillDownFoldPlan}, so
1241
+ * both step 0 and step 1 satisfy the drill's join; the primary-candidate
1242
+ * scan currently commits to the FIRST match (step 0, `price: 10`) and
1243
+ * globally consumes the drill step, so step 1 — the fresher, `price: 12`
1244
+ * re-queried occurrence — is never considered. `amount` in the drill
1245
+ * response is the object-array field distinct from `price` that a fold
1246
+ * assertion reads to observe which occurrence was actually folded onto.
1247
+ */
1248
+ function buildMulticallSingleShotSearchDrillDownRequeriedPrimaryOverlapActionSteps() {
1249
+ return [
1250
+ buildStep("r0", {
1251
+ url: CATALOG_SEARCH_URL,
1252
+ requestPostData: '{"page":1}',
1253
+ responseBody: {
1254
+ results: [{ sku: "sku-a", price: 10 }],
1255
+ },
1256
+ timestamp: "2024-12-01T00:00:00Z",
1257
+ }),
1258
+ buildStep("r1", {
1259
+ url: CATALOG_SEARCH_URL,
1260
+ requestPostData: '{"page":2}',
1261
+ responseBody: {
1262
+ results: [{ sku: "sku-a", price: 12 }],
1263
+ },
1264
+ timestamp: "2024-12-01T00:00:01Z",
1265
+ }),
1266
+ buildStep("r2", {
1267
+ url: `${CATALOG_PRICING_URL}?sku=sku-a`,
1268
+ requestPostData: null,
1269
+ responseBody: {
1270
+ prices: [{ sku: "sku-a", amount: 19.99 }],
1271
+ },
1272
+ timestamp: "2024-12-01T00:00:02Z",
1273
+ }),
1274
+ ];
1275
+ }
1276
+ const CATALOG_VENDOR_DETAIL_URL = "https://api.example.com/catalog/vendors/detail/";
1277
+ /**
1278
+ * A single primary search response carrying TWO genuinely independent
1279
+ * object-array fields — `products[]` and `vendors[]` — each drilled by its
1280
+ * own, unrelated later step (`sku` into a pricing lookup, `vendorId` into a
1281
+ * vendor-detail lookup). `scanPrimaryCandidate`'s old candidatePool lock
1282
+ * (recon-generate.ts:5411-5413) resolved only whichever array its FIRST
1283
+ * qualifying drill-down threaded from (`products[]`, via r1) and then
1284
+ * restricted every later candidate scan to that SAME array, so r2's
1285
+ * `vendorId` thread into `vendors[]` was silently discarded — this fixture
1286
+ * proves BOTH arrays fold independently, one FoldPlan per distinct
1287
+ * `primaryArrayPath`.
1288
+ */
1289
+ function buildMulticallSingleShotSearchTwoIndependentArraysActionSteps() {
1290
+ return [
1291
+ buildStep("r0", {
1292
+ url: CATALOG_SEARCH_URL,
1293
+ requestPostData: '{"page":1}',
1294
+ responseBody: {
1295
+ products: [{ sku: "sku-a" }],
1296
+ vendors: [{ vendorId: "v1" }],
1297
+ },
1298
+ timestamp: "2025-01-01T00:00:00Z",
1299
+ }),
1300
+ buildStep("r1", {
1301
+ url: `${CATALOG_PRICING_URL}?sku=sku-a`,
1302
+ requestPostData: null,
1303
+ responseBody: {
1304
+ prices: [{ sku: "sku-a", amount: 9.99 }],
1305
+ },
1306
+ timestamp: "2025-01-01T00:00:01Z",
1307
+ }),
1308
+ buildStep("r2", {
1309
+ url: `${CATALOG_VENDOR_DETAIL_URL}?vendorId=v1`,
1310
+ requestPostData: null,
1311
+ responseBody: {
1312
+ contracts: [{ vendorId: "v1", contractId: "c1" }],
1313
+ },
1314
+ timestamp: "2025-01-01T00:00:02Z",
1315
+ }),
1316
+ ];
1317
+ }
1318
+ /**
1319
+ * A single primary search response carrying TWO independent object-array
1320
+ * fields — `products[]`, whose `sku` join threads through the pricing call's
1321
+ * own JSON body and so is structurally detectable, and `vendors[]`, whose
1322
+ * `vendorId` join threads ONLY through the vendor-detail call's
1323
+ * `X-Vendor-Id` request header, invisible to `collectRequestStringValues`
1324
+ * and resolvable only via a flow-declared `foldReturn` spec naming `vendors`
1325
+ * as its `resultsPath`. `mergeSpecPlanOntoSamePrimary` used to key its
1326
+ * consumed-index check off `primaryStepIndex` alone, so the spec's plan —
1327
+ * anchored on the SAME step 0 the structural heuristic had already resolved,
1328
+ * but naming a DIFFERENT array — was wrongly treated as already consumed and
1329
+ * silently dropped instead of appended.
1330
+ */
1331
+ function buildMulticallStructuralPlusSpecOnlySameStepActionSteps() {
1332
+ return [
1333
+ buildStep("r0", {
1334
+ url: CATALOG_SEARCH_URL,
1335
+ requestPostData: '{"page":1}',
1336
+ responseBody: {
1337
+ products: [{ sku: "sku-a" }],
1338
+ vendors: [{ vendorId: "v1" }],
1339
+ },
1340
+ timestamp: "2025-03-01T00:00:00Z",
1341
+ }),
1342
+ buildStep("r1", {
1343
+ url: `${CATALOG_PRICING_URL}?sku=sku-a`,
1344
+ requestPostData: null,
1345
+ responseBody: {
1346
+ prices: [{ sku: "sku-a", amount: 9.99 }],
1347
+ },
1348
+ timestamp: "2025-03-01T00:00:01Z",
1349
+ }),
1350
+ buildStep("r2", {
1351
+ url: CATALOG_VENDOR_DETAIL_URL,
1352
+ requestPostData: '{"lookup":true}',
1353
+ responseBody: {
1354
+ contracts: [{ vendorId: "v1", contractId: "c1" }],
1355
+ },
1356
+ timestamp: "2025-03-01T00:00:02Z",
1357
+ requestHeaders: { "Content-Type": "application/json", "X-Vendor-Id": "v1" },
1358
+ }),
1359
+ ];
1360
+ }
1361
+ const ORDER_HISTORY_URL = "https://api.example.com/orders/history/";
1362
+ /**
1363
+ * A 3-step chain where the drill step (`r1`) is a bare threading step — its
1364
+ * response carries the join value (`statusToken`) but no object-array field
1365
+ * at all, so it can't itself be the decoy. The decoy instead sits on the
1366
+ * chain's terminal step (`r2`): a `warnings[]` array positioned BEFORE the
1367
+ * real per-item `entries[]` array in key order, where `entries[]`'s items
1368
+ * thread `r2`'s own `statusToken` request value and `warnings[]`'s items do
1369
+ * not. {@link computeFoldChain}'s `selectDisambiguatedCandidate` call on
1370
+ * this (non-immediate) chain hop must resolve `chainArrayPath` to `entries`
1371
+ * on the threaded-join-fields match, not `warnings` for being found first.
1372
+ */
1373
+ function buildMulticallSingleShotSearchDrillDownChainedDecoyOnChainTerminalActionSteps() {
1374
+ return [
1375
+ buildStep("r0", {
1376
+ url: CATALOG_SEARCH_URL,
1377
+ requestPostData: '{"page":1}',
1378
+ responseBody: {
1379
+ results: [{ orderId: "order-7" }],
1380
+ },
1381
+ timestamp: "2024-09-01T00:00:00Z",
1382
+ }),
1383
+ buildStep("r1", {
1384
+ url: "https://api.example.com/orders/order-7/status",
1385
+ requestPostData: null,
1386
+ responseBody: {
1387
+ statusToken: "tok-99",
1388
+ },
1389
+ timestamp: "2024-09-01T00:00:01Z",
1390
+ }),
1391
+ buildStep("r2", {
1392
+ url: ORDER_HISTORY_URL,
1393
+ requestPostData: '{"statusToken":"tok-99"}',
1394
+ responseBody: {
1395
+ warnings: [{ code: "stale-cache" }],
1396
+ entries: [{ statusToken: "tok-99", ts: "2024-09-01T00:00:02Z", event: "shipped" }],
1397
+ },
1398
+ timestamp: "2024-09-01T00:00:02Z",
1399
+ }),
1400
+ ];
1401
+ }
1402
+ const CATALOG_ORDER_STATUS_URL = "https://api.example.com/catalog/order-status";
1403
+ /**
1404
+ * A single-shot search → per-item drill-down flow where the drill step's
1405
+ * OWN response (`r1`) is entirely OPAQUE — a bare array holding a single
1406
+ * token, satisfying neither `isObjectArrayItem` nor
1407
+ * `findAllObjectArrayFields`, so `selectDisambiguatedCandidate` finds no
1408
+ * candidate there at all — and is depended on by a THIRD step (`r2`) whose
1409
+ * response carries the real per-item array this flow means to fold. Unlike
1410
+ * {@link buildMulticallSingleShotSearchDrillDownChainedDependentActionSteps},
1411
+ * whose `r1` is ALSO independently foldable, this fixture isolates the case
1412
+ * where `computeFoldChain` has nothing to fall back on at the immediate hop
1413
+ * and must extend the chain past it to `r2` to find any candidate at all.
1414
+ */
1415
+ function buildMulticallSingleShotSearchDrillDownOpaqueIntermediateChainedDependentActionSteps() {
1416
+ return [
1417
+ buildStep("r0", {
1418
+ url: CATALOG_SEARCH_URL,
1419
+ requestPostData: '{"page":1}',
1420
+ responseBody: {
1421
+ results: [{ orderId: "order-a" }, { orderId: "order-b" }],
1422
+ },
1423
+ timestamp: "2024-10-01T00:00:00Z",
1424
+ }),
1425
+ buildStep("r1", {
1426
+ url: CATALOG_ORDER_STATUS_URL,
1427
+ requestPostData: '{"orderId":"order-a"}',
1428
+ responseBody: ["status-token-order-a"],
1429
+ timestamp: "2024-10-01T00:00:01Z",
1430
+ }),
1431
+ buildStep("r2", {
1432
+ url: ORDER_HISTORY_URL,
1433
+ requestPostData: '{"statusToken":"status-token-order-a"}',
1434
+ responseBody: {
1435
+ entries: [
1436
+ { statusToken: "status-token-order-a", ts: "2024-10-01T00:00:02Z", event: "shipped" },
1437
+ ],
1438
+ },
1439
+ timestamp: "2024-10-01T00:00:02Z",
1440
+ }),
1441
+ ];
1442
+ }
1443
+ /**
1444
+ * A 4-step sibling of
1445
+ * {@link buildMulticallSingleShotSearchDrillDownOpaqueIntermediateChainedDependentActionSteps}
1446
+ * that inserts one more chain hop between the opaque token step and the real
1447
+ * per-item terminal: a flat confirmation object (`r2`) that echoes back the
1448
+ * token it was called with (excluded from its richness by
1449
+ * `directPrimitiveChildCountExcludingEchoed`) alongside a boolean status flag
1450
+ * AND a second, non-echoed token that threads onward into the real terminal
1451
+ * step (`r3`). Both `r2` and `r3` land on the SAME richness — two non-echoed
1452
+ * primitive fields apiece (`held`/`receiptToken` on `r2`, `event`/`ts` on
1453
+ * `r3`) — so `computeFoldChain`'s strict `candidateRichness > chainTerminalRichness`
1454
+ * comparison never lets `r3` displace `r2` as the chain terminal: a tie is
1455
+ * exactly as "not richer" as a loss. This isolates the case where a
1456
+ * side-effect confirmation hop that merely happens to match the real
1457
+ * terminal's field count silently masks it, as opposed to
1458
+ * {@link buildMulticallSingleShotSearchDrillDownOpaqueIntermediateChainedDependentActionSteps}'s
1459
+ * opaque hop, which offers no candidate at all.
1460
+ */
1461
+ function buildMulticallSingleShotSearchDrillDownRichnessTiedConfirmationHopChainedDependentActionSteps() {
1462
+ return [
1463
+ buildStep("r0", {
1464
+ url: CATALOG_SEARCH_URL,
1465
+ requestPostData: '{"page":1}',
1466
+ responseBody: {
1467
+ results: [{ orderId: "order-a" }, { orderId: "order-b" }],
1468
+ },
1469
+ timestamp: "2024-10-02T00:00:00Z",
1470
+ }),
1471
+ buildStep("r1", {
1472
+ url: CATALOG_ORDER_STATUS_URL,
1473
+ requestPostData: '{"orderId":"order-a"}',
1474
+ responseBody: ["status-token-order-a"],
1475
+ timestamp: "2024-10-02T00:00:01Z",
1476
+ }),
1477
+ buildStep("r2", {
1478
+ url: ORDER_HISTORY_URL,
1479
+ requestPostData: '{"statusToken":"status-token-order-a"}',
1480
+ responseBody: {
1481
+ statusToken: "status-token-order-a",
1482
+ held: true,
1483
+ receiptToken: "receipt-token-order-a",
1484
+ },
1485
+ timestamp: "2024-10-02T00:00:02Z",
1486
+ }),
1487
+ buildStep("r3", {
1488
+ url: `${ORDER_HISTORY_URL}receipt/`,
1489
+ requestPostData: '{"receiptToken":"receipt-token-order-a"}',
1490
+ responseBody: {
1491
+ entries: [
1492
+ {
1493
+ receiptToken: "receipt-token-order-a",
1494
+ event: "shipped",
1495
+ ts: "2024-10-02T00:00:03Z",
1496
+ },
1497
+ ],
1498
+ },
1499
+ timestamp: "2024-10-02T00:00:03Z",
1500
+ }),
1501
+ ];
1502
+ }
1503
+ const ORDER_HISTORY_BULK_URL = "https://api.example.com/orders/history/bulk";
1504
+ /**
1505
+ * An array-wrapped sibling of
1506
+ * {@link buildMulticallSingleShotSearchDrillDownOpaqueIntermediateChainedDependentActionSteps}:
1507
+ * the opaque intermediate hop (`r1`) is identical, but the chain's TERMINAL
1508
+ * hop (`r2`) threads `r1`'s produced `statusToken` wrapped inside a
1509
+ * single-element request-body ARRAY (`{"tokens":["status-token-order-a"]}`,
1510
+ * a bulk/batch-lookup request shape) instead of as a flat top-level scalar
1511
+ * field. `computeFoldChain`'s structural `dependsOnChain` detection (which
1512
+ * walks every request-body leaf regardless of nesting) resolves the chain
1513
+ * correctly either way, so this isolates a DIFFERENT failure: the
1514
+ * request-body render must still be able to thread `statusToken` into that
1515
+ * array slot instead of freezing the whole array as an opaque caller-payload
1516
+ * blob (`applyStructuredValuePayloadSubstitutions`'s "swallow whole
1517
+ * caller-supplied array/object" mechanism, which runs BEFORE state
1518
+ * threading and has no concept of "this array actually wraps a threaded
1519
+ * dependent-drill-down join value").
1520
+ */
1521
+ function buildMulticallSingleShotSearchDrillDownArrayWrappedChainedDependentActionSteps() {
1522
+ return [
1523
+ buildStep("r0", {
1524
+ url: CATALOG_SEARCH_URL,
1525
+ requestPostData: '{"page":1}',
1526
+ responseBody: {
1527
+ results: [{ orderId: "order-a" }, { orderId: "order-b" }],
1528
+ },
1529
+ timestamp: "2024-10-03T00:00:00Z",
1530
+ }),
1531
+ buildStep("r1", {
1532
+ url: CATALOG_ORDER_STATUS_URL,
1533
+ requestPostData: '{"orderId":"order-a"}',
1534
+ responseBody: ["status-token-order-a"],
1535
+ timestamp: "2024-10-03T00:00:01Z",
1536
+ }),
1537
+ buildStep("r2", {
1538
+ url: ORDER_HISTORY_BULK_URL,
1539
+ requestPostData: '{"tokens":["status-token-order-a"]}',
1540
+ responseBody: {
1541
+ entries: [
1542
+ { statusToken: "status-token-order-a", ts: "2024-10-03T00:00:02Z", event: "shipped" },
1543
+ ],
1544
+ },
1545
+ timestamp: "2024-10-03T00:00:02Z",
1546
+ }),
1547
+ ];
1548
+ }
1549
+ /**
1550
+ * Same array-wrapped-chained shape as
1551
+ * {@link buildMulticallSingleShotSearchDrillDownArrayWrappedChainedDependentActionSteps},
1552
+ * but the chain-produced value threaded from `r1` into `r2` is a bare JSON
1553
+ * NUMBER (`12345678`) rather than a string. Unlike the primary-item join
1554
+ * field covered by
1555
+ * {@link buildMulticallSingleShotSearchDrillDownArrayWrappedNumericImmediateJoinFieldActionSteps},
1556
+ * a chain-produced value must first be indexed as a threadable STATE value by
1557
+ * `indexStateValues` before any array-wrap rendering question can even arise
1558
+ * — and `indexStateValues` walks response bodies via `walkStringLeaves`,
1559
+ * which yields string leaves only. A bare-number response leaf is therefore
1560
+ * never indexed and never appears in `compileActionSteps`' `produces[]` at
1561
+ * all, so `r2`'s templated body has no accessor to substitute and renders
1562
+ * `{"tokens":[undefined]}` — invalid JSON, thrown as a fetch failure. This is
1563
+ * a distinct, more fundamental gap than the array-wrap fix
1564
+ * (`applyStructuredValuePayloadSubstitutions`, bugfix-001) addresses: it sits
1565
+ * upstream, in state INDEXING, not in payload-substitution SPARING, and
1566
+ * reproduces identically whether or not the terminal body is array-wrapped.
1567
+ */
1568
+ function buildMulticallSingleShotSearchDrillDownArrayWrappedNumericChainedJoinFieldActionSteps() {
1569
+ return [
1570
+ buildStep("r0", {
1571
+ url: CATALOG_SEARCH_URL,
1572
+ requestPostData: '{"page":1}',
1573
+ responseBody: {
1574
+ results: [{ orderId: "order-a" }, { orderId: "order-b" }],
1575
+ },
1576
+ timestamp: "2024-10-03T00:00:00Z",
1577
+ }),
1578
+ buildStep("r1", {
1579
+ url: CATALOG_ORDER_STATUS_URL,
1580
+ requestPostData: '{"orderId":"order-a"}',
1581
+ responseBody: [12345678],
1582
+ timestamp: "2024-10-03T00:00:01Z",
1583
+ }),
1584
+ buildStep("r2", {
1585
+ url: ORDER_HISTORY_BULK_URL,
1586
+ requestPostData: '{"tokens":[12345678]}',
1587
+ responseBody: {
1588
+ entries: [{ statusToken: 12345678, ts: "2024-10-03T00:00:02Z", event: "shipped" }],
1589
+ },
1590
+ timestamp: "2024-10-03T00:00:02Z",
1591
+ }),
1592
+ ];
1593
+ }
1594
+ /**
1595
+ * A sibling of
1596
+ * {@link buildMulticallSingleShotSearchDrillDownArrayWrappedNumericChainedJoinFieldActionSteps}
1597
+ * isolating a DIFFERENT gap in the numeric chain-produced join field family:
1598
+ * `r1`'s produced token is a bare (non-array-wrapped) JSON number, and — the
1599
+ * shape that matters here — it is SHORT (`42`/`43`, two digits) rather than
1600
+ * an 8-digit token. `indexStateValues` gates every candidate state value on
1601
+ * `MIN_STATE_VALUE_LENGTH` (8 chars), a threshold sized to keep an unrelated
1602
+ * short STRING (an enum code, a page number) from colliding with arbitrary
1603
+ * substrings elsewhere; applied uniformly to a bare NUMBER's stringified
1604
+ * length, it also silently excludes a short numeric id/token — exactly the
1605
+ * common shape for an order/account/status id — from ever being indexed as
1606
+ * producible state. Without that index entry, `compileActionSteps` never
1607
+ * emits a `produces[]` accessor for `r1`'s response, so `r2`'s templated
1608
+ * body has nothing to substitute and falls back to treating `{"token":...}`
1609
+ * as an opaque caller-supplied payload field (`payload.token`) instead of
1610
+ * the per-item threaded value — every iteration would send whatever (or
1611
+ * nothing) the caller passed, not each primary item's own token.
1612
+ */
1613
+ function buildMulticallSingleShotSearchDrillDownShortNumericChainedJoinFieldActionSteps() {
1614
+ return [
1615
+ buildStep("r0", {
1616
+ url: CATALOG_SEARCH_URL,
1617
+ requestPostData: '{"page":1}',
1618
+ responseBody: {
1619
+ results: [{ orderId: "order-a" }, { orderId: "order-b" }],
1620
+ },
1621
+ timestamp: "2024-10-04T00:00:00Z",
1622
+ }),
1623
+ buildStep("r1", {
1624
+ url: CATALOG_ORDER_STATUS_URL,
1625
+ requestPostData: '{"orderId":"order-a"}',
1626
+ responseBody: [42],
1627
+ timestamp: "2024-10-04T00:00:01Z",
1628
+ }),
1629
+ buildStep("r2", {
1630
+ url: ORDER_HISTORY_URL,
1631
+ requestPostData: '{"token":42}',
1632
+ responseBody: {
1633
+ entries: [{ token: 42, ts: "2024-10-04T00:00:02Z", event: "shipped" }],
1634
+ },
1635
+ timestamp: "2024-10-04T00:00:02Z",
1636
+ }),
1637
+ ];
1638
+ }
1639
+ /**
1640
+ * Cookie-origin sibling of
1641
+ * {@link buildMulticallSingleShotSearchDrillDownShortNumericChainedJoinFieldActionSteps}:
1642
+ * here `r1` mints the short chain-produced token (`tok1`, under
1643
+ * `MIN_STATE_VALUE_LENGTH`) via `Set-Cookie` rather than the response body,
1644
+ * AND echoes the same value in its response body (the "body-level echo" that
1645
+ * lets {@link collectDependentDrillDownChainValues} confirm the cookie value
1646
+ * is chain-produced, exactly as a body-sourced token would be). `r2` threads
1647
+ * `tok1` back via its request body. Exercises `indexStateValues`' Set-Cookie
1648
+ * branch's `chainForceIncludeValues`/`forceIncludeValues` exemption, mirroring
1649
+ * the body-value floor exemption.
1650
+ */
1651
+ function buildMulticallSingleShotSearchDrillDownShortCookieChainedJoinFieldActionSteps() {
1652
+ return [
1653
+ buildStep("r0", {
1654
+ url: CATALOG_SEARCH_URL,
1655
+ requestPostData: '{"page":1}',
1656
+ responseBody: {
1657
+ results: [{ orderId: "order-a" }, { orderId: "order-b" }],
1658
+ },
1659
+ timestamp: "2024-10-05T00:00:00Z",
1660
+ }),
1661
+ buildStep("r1", {
1662
+ url: CATALOG_ORDER_STATUS_URL,
1663
+ requestPostData: '{"orderId":"order-a"}',
1664
+ responseHeaders: { "set-cookie": "sess=tok1; Path=/; HttpOnly" },
1665
+ responseBody: { echoedToken: "tok1" },
1666
+ timestamp: "2024-10-05T00:00:01Z",
1667
+ }),
1668
+ buildStep("r2", {
1669
+ url: ORDER_HISTORY_URL,
1670
+ requestPostData: '{"token":"tok1"}',
1671
+ responseBody: {
1672
+ entries: [{ token: "tok1", ts: "2024-10-05T00:00:02Z", event: "shipped" }],
1673
+ },
1674
+ timestamp: "2024-10-05T00:00:02Z",
1675
+ }),
1676
+ ];
1677
+ }
1678
+ /**
1679
+ * A different sibling of the same array-wrapped-join-field failure family:
1680
+ * here it's the IMMEDIATE drill step (`r1`), not a later chain hop, whose
1681
+ * request body wraps the value it threads — but that value is the PRIMARY
1682
+ * ITEM's own join field (`orderId`), not a prior step's produced response
1683
+ * value. `applyStructuredValuePayloadSubstitutions`'s "spare a candidate
1684
+ * already threaded" exception only recognized prior-step state values, so it
1685
+ * still froze `r1`'s `{"orderIds":["order-a"]}` into an opaque
1686
+ * `${JSON.stringify(payload.orderIds)}` blob, destroying the literal
1687
+ * `"order-a"` text the fold-loop's own per-item `parameterize` pass (which
1688
+ * runs even later, once rendering enters the loop) needs to find and swap
1689
+ * for `${item.orderId}` — every iteration replayed the SAME captured order
1690
+ * instead of each primary item's own.
1691
+ */
1692
+ function buildMulticallSingleShotSearchDrillDownArrayWrappedImmediateJoinFieldActionSteps() {
1693
+ return [
1694
+ buildStep("r0", {
1695
+ url: CATALOG_SEARCH_URL,
1696
+ requestPostData: '{"page":1}',
1697
+ responseBody: {
1698
+ results: [{ orderId: "order-a" }, { orderId: "order-b" }],
1699
+ },
1700
+ timestamp: "2024-10-03T00:00:00Z",
1701
+ }),
1702
+ buildStep("r1", {
1703
+ url: CATALOG_ORDER_STATUS_URL,
1704
+ requestPostData: '{"orderIds":["order-a"]}',
1705
+ responseBody: ["status-token-order-a"],
1706
+ timestamp: "2024-10-03T00:00:01Z",
1707
+ }),
1708
+ buildStep("r2", {
1709
+ url: ORDER_HISTORY_BULK_URL,
1710
+ requestPostData: '{"tokens":["status-token-order-a"]}',
1711
+ responseBody: {
1712
+ entries: [
1713
+ { statusToken: "status-token-order-a", ts: "2024-10-03T00:00:02Z", event: "shipped" },
1714
+ ],
1715
+ },
1716
+ timestamp: "2024-10-03T00:00:02Z",
1717
+ }),
1718
+ ];
1719
+ }
1720
+ /**
1721
+ * Same array-wrapped-immediate-join-field shape as
1722
+ * {@link buildMulticallSingleShotSearchDrillDownArrayWrappedImmediateJoinFieldActionSteps},
1723
+ * but the primary item's join field is a bare JSON NUMBER (`orderId: 101`)
1724
+ * rather than a string. `walkStringLeaves` (used by the spare check in
1725
+ * `applyStructuredValuePayloadSubstitutions`) silently skips number leaves,
1726
+ * so a numeric join value wrapped in `{"orderIds":[101]}` was frozen into an
1727
+ * opaque `${JSON.stringify(payload.orderIds)}` blob instead of being spared
1728
+ * for the fold-loop's later per-item `parameterize` pass.
1729
+ */
1730
+ function buildMulticallSingleShotSearchDrillDownArrayWrappedNumericImmediateJoinFieldActionSteps() {
1731
+ return [
1732
+ buildStep("r0", {
1733
+ url: CATALOG_SEARCH_URL,
1734
+ requestPostData: '{"page":1}',
1735
+ responseBody: {
1736
+ results: [{ orderId: 101 }, { orderId: 102 }],
1737
+ },
1738
+ timestamp: "2024-10-03T00:00:00Z",
1739
+ }),
1740
+ buildStep("r1", {
1741
+ url: CATALOG_ORDER_STATUS_URL,
1742
+ requestPostData: '{"orderIds":[101]}',
1743
+ responseBody: ["status-token-101"],
1744
+ timestamp: "2024-10-03T00:00:01Z",
1745
+ }),
1746
+ buildStep("r2", {
1747
+ url: ORDER_HISTORY_BULK_URL,
1748
+ requestPostData: '{"tokens":["status-token-101"]}',
1749
+ responseBody: {
1750
+ entries: [
1751
+ { statusToken: "status-token-101", ts: "2024-10-03T00:00:02Z", event: "shipped" },
1752
+ ],
1753
+ },
1754
+ timestamp: "2024-10-03T00:00:02Z",
1755
+ }),
1756
+ ];
1757
+ }
1758
+ /**
1759
+ * Same array-wrapped-immediate-join-field shape as
1760
+ * {@link buildMulticallSingleShotSearchDrillDownArrayWrappedNumericImmediateJoinFieldActionSteps},
1761
+ * but the primary item's join field is a bare JSON BOOLEAN (`flag: true`)
1762
+ * rather than a number or string. `walkStringLeaves` (used by the spare
1763
+ * check in `applyStructuredValuePayloadSubstitutions`) silently skips
1764
+ * boolean leaves the same way it skips number leaves, so a boolean join
1765
+ * value wrapped in `{"flags":[true]}` was frozen into an opaque
1766
+ * `${JSON.stringify(payload.flags)}` blob instead of being spared for the
1767
+ * fold-loop's later per-item `parameterize` pass.
1768
+ */
1769
+ function buildMulticallSingleShotSearchDrillDownArrayWrappedBooleanImmediateJoinFieldActionSteps() {
1770
+ return [
1771
+ buildStep("r0", {
1772
+ url: CATALOG_SEARCH_URL,
1773
+ requestPostData: '{"page":1}',
1774
+ responseBody: {
1775
+ results: [{ flag: true }, { flag: false }],
1776
+ },
1777
+ timestamp: "2024-10-03T00:00:00Z",
1778
+ }),
1779
+ buildStep("r1", {
1780
+ url: CATALOG_ORDER_STATUS_URL,
1781
+ requestPostData: '{"flags":[true]}',
1782
+ responseBody: ["status-token-true"],
1783
+ timestamp: "2024-10-03T00:00:01Z",
1784
+ }),
1785
+ buildStep("r2", {
1786
+ url: ORDER_HISTORY_BULK_URL,
1787
+ requestPostData: '{"tokens":["status-token-true"]}',
1788
+ responseBody: {
1789
+ entries: [
1790
+ { statusToken: "status-token-true", ts: "2024-10-03T00:00:02Z", event: "shipped" },
1791
+ ],
1792
+ },
1793
+ timestamp: "2024-10-03T00:00:02Z",
1794
+ }),
1795
+ ];
1796
+ }
1797
+ const ACCOUNT_STATUS_URL = "https://api.example.com/accounts/status";
1798
+ const ACCOUNT_TRANSACTIONS_URL = "https://api.example.com/accounts/transactions";
1799
+ /**
1800
+ * A dependent (chained) drill-down whose join key is threaded ONLY through a
1801
+ * request HEADER (`API-Token`) on the chain's ENTRY hop (`r1`) — invisible to
1802
+ * `detectDrillDownFoldPlan`'s structural heuristic exactly like
1803
+ * {@link buildMulticallSingleShotSearchDrillDownHeaderThreadedJoinActionSteps}
1804
+ * — so only a flow-declared `foldReturn` can resolve this fold at all. Unlike
1805
+ * every existing header-threaded-join fixture, though, a site author writing
1806
+ * that `foldReturn` naturally points `endpointPattern` at `r2`
1807
+ * (`/accounts/transactions`) — the call whose OWN response actually carries
1808
+ * the per-item data (`transactions[]`) they want folded — not at `r1`
1809
+ * (`/accounts/status`), the opaque intermediate hop that merely carries the
1810
+ * `accountId`→`API-Token` header thread onward as a `statusToken`. `r2`'s own
1811
+ * request only ever carries that threaded `statusToken`, never `accountId`
1812
+ * in any form (body, URL, or header) — the join key is resolvable only by
1813
+ * walking back to `r1`, exactly the way `computeFoldChain` already walks
1814
+ * FORWARD from a resolved entry hop to a richer terminal. `resultsPath`
1815
+ * anchors on `accounts`, `joinFields` on `accountId`.
1816
+ */
1817
+ function buildMulticallSingleShotSearchDrillDownHeaderThreadedJoinChainedDependentActionSteps() {
1818
+ return [
1819
+ buildStep("r0", {
1820
+ url: ACCOUNT_SEARCH_URL,
1821
+ requestPostData: JSON.stringify({ page: 1 }),
1822
+ responseBody: {
1823
+ accounts: [
1824
+ { accountId: 42, name: "Acme" },
1825
+ { accountId: 43, name: "Globex" },
1826
+ ],
1827
+ },
1828
+ timestamp: "2024-08-01T00:00:00Z",
1829
+ }),
1830
+ buildStep("r1", {
1831
+ url: ACCOUNT_STATUS_URL,
1832
+ requestPostData: null,
1833
+ requestHeaders: { "Content-Type": "application/json", "API-Token": "42" },
1834
+ responseBody: { statusToken: "status-token-42" },
1835
+ timestamp: "2024-08-01T00:00:01Z",
1836
+ }),
1837
+ buildStep("r2", {
1838
+ url: ACCOUNT_TRANSACTIONS_URL,
1839
+ requestPostData: JSON.stringify({ statusToken: "status-token-42" }),
1840
+ responseBody: {
1841
+ transactions: [{ statusToken: "status-token-42", transactionId: "t-42", amount: 19.99 }],
1842
+ },
1843
+ timestamp: "2024-08-01T00:00:02Z",
1844
+ }),
1845
+ ];
1846
+ }
1847
+ const ORDER_STATUS_LOOKUP_URL = "https://api.example.com/orders/status-lookup";
1848
+ const ORDER_EVENTS_URL = "https://api.example.com/orders/events";
1849
+ /**
1850
+ * A dependent (chained) drill-down whose ENTRY hop (`r1`) is a `GET`
1851
+ * request — not the `POST` every other chained-dependent fixture in this
1852
+ * file uses — and whose response produces the chain's join value
1853
+ * (`statusToken`) as a non-UUID string. `indexStateValues` indexes a `GET`
1854
+ * capture's response leaves ONLY when they are UUID-shaped
1855
+ * (`isGet && !UUID_REGEX.test(value)` skips everything else, a filter aimed
1856
+ * at excluding noisy short non-UUID strings a GET-only telemetry/schema
1857
+ * fetch surfaces); that filter runs unconditionally, with no exemption for a
1858
+ * value `collectDependentDrillDownChainValues` has already confirmed is
1859
+ * threaded from this exact hop into a later chain hop's own request — unlike
1860
+ * the `MIN_STATE_VALUE_LENGTH` floor a few lines above it, which DOES carry
1861
+ * that exemption. So a chain-produced token minted by a GET response is
1862
+ * never indexed as producible state at all, `compileActionSteps` never
1863
+ * emits a `produces[]` accessor for it, and `r2`'s templated body has
1864
+ * nothing to substitute — every iteration renders the literal string
1865
+ * `"undefined"` instead of threading each primary item's own token.
1866
+ */
1867
+ function buildMulticallSingleShotSearchDrillDownGetEntryHopChainedDependentActionSteps() {
1868
+ return [
1869
+ buildStep("r0", {
1870
+ url: CATALOG_SEARCH_URL,
1871
+ requestPostData: '{"page":1}',
1872
+ responseBody: {
1873
+ results: [{ orderId: "order-a" }, { orderId: "order-b" }],
1874
+ },
1875
+ timestamp: "2024-10-05T00:00:00Z",
1876
+ }),
1877
+ buildStep("r1", {
1878
+ method: "GET",
1879
+ url: `${ORDER_STATUS_LOOKUP_URL}?orderId=order-a`,
1880
+ requestPostData: null,
1881
+ responseBody: { statusToken: "status-token-order-a" },
1882
+ timestamp: "2024-10-05T00:00:01Z",
1883
+ }),
1884
+ buildStep("r2", {
1885
+ url: ORDER_EVENTS_URL,
1886
+ requestPostData: '{"token":"status-token-order-a"}',
1887
+ responseBody: {
1888
+ entries: [{ token: "status-token-order-a", ts: "2024-10-05T00:00:02Z", event: "shipped" }],
1889
+ },
1890
+ timestamp: "2024-10-05T00:00:02Z",
1891
+ }),
1892
+ ];
1893
+ }
1894
+ /**
1895
+ * A response-header sibling of {@link buildMulticallSingleShotSearchDrillDownGetEntryHopChainedDependentActionSteps}:
1896
+ * the chain's drill hop (`r1`) mints its join token (`tok-a1`) ONLY in a
1897
+ * custom response HEADER (`X-Price-Token`) — its body is empty ({}), unlike
1898
+ * {@link buildMulticallSingleShotSearchDrillDownHeaderMintedChainedResponseValueActionSteps}'s
1899
+ * body-echo-free sibling, which threads the header value forward via the
1900
+ * request BODY. Here the terminal hop (`r2`) instead threads that value back
1901
+ * as a REQUEST HEADER of its own (also named `X-Price-Token`) — the only
1902
+ * shape `createHttpClient`'s `bind` option can actually thread a
1903
+ * header-origin value into, since the emitted response variable never
1904
+ * exposes response headers to the rest of the generated code. Exercises
1905
+ * `compileActionSteps`' non-cookie response-header produce block: without it,
1906
+ * `tok-a1` is never captured into a `produces[]`/`bind` entry at all, and
1907
+ * `r2`'s per-item call goes out with no `X-Price-Token` header, so the
1908
+ * terminal `history[]` fold silently collapses to whichever primary item the
1909
+ * stub happens to answer first instead of each item's own token.
1910
+ */
1911
+ function buildMulticallSingleShotSearchDrillDownResponseHeaderThreadedJoinChainedDependentActionSteps() {
1912
+ return [
1913
+ buildStep("r0", {
1914
+ url: CATALOG_SEARCH_URL,
1915
+ requestPostData: '{"page":1}',
1916
+ responseBody: {
1917
+ results: [{ sku: "sku-a" }, { sku: "sku-b" }],
1918
+ },
1919
+ timestamp: "2024-12-10T00:00:00Z",
1920
+ }),
1921
+ buildStep("r1", {
1922
+ url: CATALOG_PRICING_URL,
1923
+ requestPostData: '{"sku":"sku-a"}',
1924
+ responseBody: {},
1925
+ responseHeaders: { "content-type": "application/json", "X-Price-Token": "tok-a1" },
1926
+ timestamp: "2024-12-10T00:00:01Z",
1927
+ }),
1928
+ buildStep("r2", {
1929
+ url: CATALOG_PRICE_HISTORY_URL,
1930
+ requestPostData: "{}",
1931
+ requestHeaders: { "Content-Type": "application/json", "X-Price-Token": "tok-a1" },
1932
+ responseBody: {
1933
+ history: [{ sku: "sku-a", amount: 18.5, asOf: "2024-11-01" }],
1934
+ },
1935
+ timestamp: "2024-12-10T00:00:02Z",
1936
+ }),
1937
+ ];
1938
+ }
149
1939
  //# sourceMappingURL=recon-generate-multicall-fixture.js.map