@enricai/barnacle 1.12.70 → 1.12.71

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.
Files changed (32) hide show
  1. package/dist/scraper/browser-click-expr.d.ts +3 -4
  2. package/dist/scraper/browser-click-expr.d.ts.map +1 -1
  3. package/dist/scraper/browser-click-expr.js +3 -4
  4. package/dist/scraper/browser-click-expr.js.map +1 -1
  5. package/dist/scraper/flow-runner.d.ts +27 -6
  6. package/dist/scraper/flow-runner.d.ts.map +1 -1
  7. package/dist/scraper/flow-runner.js +332 -61
  8. package/dist/scraper/flow-runner.js.map +1 -1
  9. package/dist/scraper/frame-target.d.ts +12 -0
  10. package/dist/scraper/frame-target.d.ts.map +1 -1
  11. package/dist/scraper/frame-target.js +1 -0
  12. package/dist/scraper/frame-target.js.map +1 -1
  13. package/dist/scraper/submit-control.d.ts +30 -2
  14. package/dist/scraper/submit-control.d.ts.map +1 -1
  15. package/dist/scraper/submit-control.js +79 -20
  16. package/dist/scraper/submit-control.js.map +1 -1
  17. package/dist/scraper/target-resolution-diagnostic.d.ts +53 -0
  18. package/dist/scraper/target-resolution-diagnostic.d.ts.map +1 -0
  19. package/dist/scraper/target-resolution-diagnostic.js +153 -0
  20. package/dist/scraper/target-resolution-diagnostic.js.map +1 -0
  21. package/dist/scripts/recon-browser.d.ts +32 -2
  22. package/dist/scripts/recon-browser.d.ts.map +1 -1
  23. package/dist/scripts/recon-browser.js +2 -0
  24. package/dist/scripts/recon-browser.js.map +1 -1
  25. package/dist/scripts/recon-generate.d.ts.map +1 -1
  26. package/dist/scripts/recon-generate.js +84 -27
  27. package/dist/scripts/recon-generate.js.map +1 -1
  28. package/package.json +1 -1
  29. package/dist/scraper/deep-query.d.ts +0 -44
  30. package/dist/scraper/deep-query.d.ts.map +0 -1
  31. package/dist/scraper/deep-query.js +0 -93
  32. package/dist/scraper/deep-query.js.map +0 -1
@@ -8646,6 +8646,65 @@ function compileFoldReturnResultsMatcher(spec) {
8646
8646
  const rawResultsPath = spec.resultsPath.split(".");
8647
8647
  return (capture) => objectItemsAtPath(capture.responseBody, resolveDeclaredArrayPath(rawResultsPath, capture.responseBody)) !== null;
8648
8648
  }
8649
+ /** Response-side counterpart of {@link buildRequestValueIndex}, restricted to
8650
+ * the endpoint-matching drill candidates: maps each response leaf value to the
8651
+ * ascending drill indices carrying it, so the response-only fallback can find
8652
+ * the drills a primary's join values could resolve against without testing
8653
+ * every primary/drill pair. Built eagerly per call, like
8654
+ * {@link buildRequestValueIndex}; the per-capture leaf sets it reads are
8655
+ * memoized, so repeated calls only re-pay the indexing. */
8656
+ function buildDrillResponseValueIndex(actions, drillStepIndices) {
8657
+ const index = new Map();
8658
+ for (const drillStepIndex of drillStepIndices) {
8659
+ for (const value of collectResponseLeafValues(actions[drillStepIndex].capture)) {
8660
+ const indices = index.get(value);
8661
+ if (indices)
8662
+ indices.push(drillStepIndex);
8663
+ else
8664
+ index.set(value, [drillStepIndex]);
8665
+ }
8666
+ }
8667
+ return index;
8668
+ }
8669
+ /** Descending drill indices after a primary that can possibly resolve: a drill
8670
+ * resolves only via a request-carried join value (some candidate entry index at
8671
+ * or before it) or a response-carried one, so every other drill is skipped
8672
+ * without changing which one wins. `unpruned` (no joinFields, where any drill
8673
+ * trivially matches) keeps the full freshest-first scan. */
8674
+ function collectDrillStepIndicesWorthTrying(allDrillStepIndices, firstDrillOffset, drillResponseValueIndex, joinValues, candidateEntryIndicesDescending, unpruned) {
8675
+ if (unpruned)
8676
+ return allDrillStepIndices.slice(firstDrillOffset).reverse();
8677
+ const worthTrying = new Set();
8678
+ const earliestEntryIndex = candidateEntryIndicesDescending.at(-1);
8679
+ if (earliestEntryIndex !== undefined) {
8680
+ const requestStart = Math.max(firstDrillOffset, firstIndexGreaterThan(allDrillStepIndices, earliestEntryIndex - 1));
8681
+ for (let i = requestStart; i < allDrillStepIndices.length; i++) {
8682
+ worthTrying.add(allDrillStepIndices[i]);
8683
+ }
8684
+ }
8685
+ const firstEligibleDrillStepIndex = allDrillStepIndices[firstDrillOffset];
8686
+ for (const value of joinValues) {
8687
+ for (const drillStepIndex of drillResponseValueIndex.get(value) ?? []) {
8688
+ if (firstEligibleDrillStepIndex !== undefined &&
8689
+ drillStepIndex >= firstEligibleDrillStepIndex) {
8690
+ worthTrying.add(drillStepIndex);
8691
+ }
8692
+ }
8693
+ }
8694
+ return [...worthTrying].sort((a, b) => b - a);
8695
+ }
8696
+ function firstIndexGreaterThan(sortedAscending, value) {
8697
+ let low = 0;
8698
+ let high = sortedAscending.length;
8699
+ while (low < high) {
8700
+ const mid = (low + high) >>> 1;
8701
+ if (sortedAscending[mid] <= value)
8702
+ low = mid + 1;
8703
+ else
8704
+ high = mid;
8705
+ }
8706
+ return low;
8707
+ }
8649
8708
  function buildFoldPlanFromSpec(actions, spec,
8650
8709
  // See detectDrillDownFoldPlan's identical parameter — constrains this
8651
8710
  // function's own primaryStepIndex loop to the same anchor identity.
@@ -8677,6 +8736,14 @@ restrictToDrillEndpointKey = null) {
8677
8736
  // paying for anything else, instead of scanning its own drill candidates
8678
8737
  // one by one only to discover none of them can ever match.
8679
8738
  const requestValueIndex = buildRequestValueIndex(actions);
8739
+ // Endpoint matching never depends on the primary, so it is evaluated once
8740
+ // here; testing every later action per primary made the scan quadratic.
8741
+ const allMatchingDrillStepIndices = actions.flatMap((action, index) => matchesFoldReturnEndpoint(action.capture) &&
8742
+ (restrictToDrillEndpointKey === null ||
8743
+ endpointKey(action.capture.url) === restrictToDrillEndpointKey)
8744
+ ? [index]
8745
+ : []);
8746
+ const drillResponseValueIndex = buildDrillResponseValueIndex(actions, allMatchingDrillStepIndices);
8680
8747
  let freshestPlan = null;
8681
8748
  for (let primaryStepIndex = 0; primaryStepIndex < actions.length; primaryStepIndex++) {
8682
8749
  if (exactPrimaryStepIndex !== null && primaryStepIndex !== exactPrimaryStepIndex) {
@@ -8692,42 +8759,32 @@ restrictToDrillEndpointKey = null) {
8692
8759
  continue;
8693
8760
  const joinValues = collectPrimaryJoinValues(primaryItems, spec.joinFields);
8694
8761
  const candidateEntryIndicesDescending = collectCandidateEntryIndicesDescending(requestValueIndex, joinValues, primaryStepIndex);
8695
- // Cheap regex-only pass first: collects every endpointPattern-matching
8696
- // drillStepIndex without paying for the expensive backward-walk +
8697
- // computeFoldChain resolution below. Scanned from the freshest (highest)
8698
- // index down so the expensive resolution — tried only on the entries
8699
- // this loop actually visits — runs on the candidate that would win
8700
- // ties in the original last-write-wins scan first, falling through to
8701
- // the next-freshest only when a candidate fails to resolve, instead of
8702
- // resolving every earlier occurrence just to have it overwritten.
8762
+ // Drills are tried from the freshest (highest) index down so the
8763
+ // expensive backward-walk + computeFoldChain resolution runs on the
8764
+ // candidate that would win ties in the original last-write-wins scan
8765
+ // first, falling through to the next-freshest only when a candidate
8766
+ // fails to resolve.
8703
8767
  //
8704
- // Computed even when `candidateEntryIndicesDescending` is empty (unlike
8705
- // the request-based path below, which bails immediately in that case):
8706
- // an endpointPattern match is a single regex test per action, O(actions)
8707
- // regardless — not the O(actions)-per-candidate backward walk the
8708
- // request-based prune exists to avoid — so it stays cheap even when no
8709
- // request anywhere carries this primary's join values, which is exactly
8710
- // the case {@link resolveSpecMatchedPrimaryItemIndexFromResponse}'s
8711
- // response-only fallback below exists to resolve.
8712
- const matchingDrillStepIndices = [];
8713
- for (let drillStepIndex = primaryStepIndex + 1; drillStepIndex < actions.length; drillStepIndex++) {
8714
- if (matchesFoldReturnEndpoint(actions[drillStepIndex].capture) &&
8715
- (restrictToDrillEndpointKey === null ||
8716
- endpointKey(actions[drillStepIndex].capture.url) === restrictToDrillEndpointKey)) {
8717
- matchingDrillStepIndices.push(drillStepIndex);
8718
- }
8719
- }
8720
- if (candidateEntryIndicesDescending.length === 0 && matchingDrillStepIndices.length === 0) {
8768
+ // A primary with no request-carried join value must still reach
8769
+ // {@link resolveSpecMatchedPrimaryItemIndexFromResponse}'s response-only
8770
+ // fallback, so an empty `candidateEntryIndicesDescending` alone never
8771
+ // skips it; the drills tried are instead pruned to those whose response
8772
+ // carries one of this primary's join values (see
8773
+ // {@link collectDrillStepIndicesWorthTrying}), because testing every
8774
+ // primary/drill pair is quadratic in the capture count.
8775
+ const firstDrillOffset = firstIndexGreaterThan(allMatchingDrillStepIndices, primaryStepIndex);
8776
+ if (candidateEntryIndicesDescending.length === 0 &&
8777
+ firstDrillOffset === allMatchingDrillStepIndices.length) {
8721
8778
  continue;
8722
8779
  }
8780
+ const drillStepIndicesToTry = collectDrillStepIndicesWorthTrying(allMatchingDrillStepIndices, firstDrillOffset, drillResponseValueIndex, joinValues, candidateEntryIndicesDescending, spec.joinFields.length === 0);
8723
8781
  // Shared across every matching-drill candidate tried below for THIS
8724
8782
  // primaryStepIndex — see resolveSpecMatchedPrimaryItemIndexAlongChain's
8725
8783
  // docstring on why a fresh cache per primary (not per drill candidate)
8726
8784
  // is what collapses the backward-walk from O(candidates * chain length)
8727
8785
  // to O(chain length).
8728
8786
  const matchedItemIndexCache = new Map();
8729
- for (let i = matchingDrillStepIndices.length - 1; i >= 0; i--) {
8730
- const drillStepIndex = matchingDrillStepIndices[i];
8787
+ for (const drillStepIndex of drillStepIndicesToTry) {
8731
8788
  const drill = actions[drillStepIndex];
8732
8789
  // Widened to a flat (non-array) object response the same way the
8733
8790
  // structural heuristic is (see findAllObjectArrayFieldsOrWholeObject):