@enricai/barnacle 1.12.56 → 1.12.58

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.
@@ -932,8 +932,11 @@ function selectReturnAction(steps) {
932
932
  * getGql/httpClient emission's own fold-merge loop (see `emitContractTs`)
933
933
  * adds at runtime.
934
934
  */
935
- function selectEffectiveResponseBody(isSubmissionFlow, actionSteps, replayResponseBody, foldReturnSpec = null) {
936
- const foldPlans = resolveFoldPlan(actionSteps, foldReturnSpec);
935
+ function selectEffectiveResponseBody(isSubmissionFlow, actionSteps, replayResponseBody, foldReturnSpec = null,
936
+ // See resolveFoldPlan's identical parameter — threaded through so shape
937
+ // inference can't disagree with emitContractTs's own emitted-primary anchor.
938
+ primaryIdentityAnchor = null) {
939
+ const foldPlans = resolveFoldPlan(actionSteps, foldReturnSpec, primaryIdentityAnchor);
937
940
  const lastFoldPlan = foldPlans[foldPlans.length - 1] ?? null;
938
941
  if (foldPlans.length <= 1) {
939
942
  if (lastFoldPlan)
@@ -1116,7 +1119,7 @@ function deriveRequestHeaders(captures, replays, baseUrl, submitPatterns = null,
1116
1119
  return baseline;
1117
1120
  }
1118
1121
  function isGraphQL(captures) {
1119
- return captures.some((c) => c.operationName !== null);
1122
+ return captures.some((c) => c.operationName !== null || (0, capture_filters_1.parsedOperationName)(c.query ?? "") !== null);
1120
1123
  }
1121
1124
  /**
1122
1125
  * The single own-backend capture the raw GraphQL fallback (no
@@ -1168,15 +1171,6 @@ function declaredOperationVariableNames(query) {
1168
1171
  return [];
1169
1172
  return Array.from(signature[1].matchAll(/\$(\w+)\s*:/g), (m) => m[1]);
1170
1173
  }
1171
- /**
1172
- * Parses the operation name out of the query body itself, for captures whose
1173
- * top-level `operationName` field is null (an inline document with no
1174
- * separate operationName was still sent with a named `query`/`mutation`).
1175
- */
1176
- function parsedOperationName(query) {
1177
- const signature = /^\s*(?:query|mutation)\s+(\w+)/.exec(stripLeadingGraphQLComments(query));
1178
- return signature?.[1] ?? null;
1179
- }
1180
1174
  /**
1181
1175
  * Groups a capture for recurrence counting by endpoint path + operation
1182
1176
  * name (falling back to the name parsed out of the query body when
@@ -1193,7 +1187,23 @@ function operationGroupKey(capture) {
1193
1187
  return "";
1194
1188
  }
1195
1189
  })();
1196
- const name = capture.operationName ?? parsedOperationName(capture.query ?? "") ?? "anonymous";
1190
+ const name = capture.operationName ?? (0, capture_filters_1.parsedOperationName)(capture.query ?? "") ?? "anonymous";
1191
+ return `${endpointPath}::${name}`;
1192
+ }
1193
+ /**
1194
+ * The identity (in {@link operationGroupKey}'s `endpointPath::operationName`
1195
+ * format) of the GraphQL operation already selected upstream as this run's
1196
+ * emitted primary — `null` for a REST primary, since fold-plan/emitted-primary
1197
+ * drift is a GraphQL-only concern (a REST primary has no operationName to
1198
+ * diverge on in the first place). Shared verbatim by {@link emitContractTs}
1199
+ * and the "no fold plan resolved" diagnostic in `main()` so both anchor
1200
+ * `resolveFoldPlan`/`resolveApplicableFoldPlans` to the SAME identity and can
1201
+ * never independently resolve a different primary.
1202
+ */
1203
+ function computeEmittedPrimaryAnchor(gql, endpointPath, gqlOperationName, gqlQuery) {
1204
+ if (!gql)
1205
+ return null;
1206
+ const name = gqlOperationName ?? (0, capture_filters_1.parsedOperationName)(gqlQuery ?? "") ?? "anonymous";
1197
1207
  return `${endpointPath}::${name}`;
1198
1208
  }
1199
1209
  /**
@@ -1610,7 +1620,12 @@ function extractActionSequence(captures, submitPatterns = null, foldReturnSpec =
1610
1620
  const otherPaths = hostGated
1611
1621
  .filter((h, j) => denselyNameSpaced ? safeUrlPathname(h.capture.url) !== path : j !== i)
1612
1622
  .map((h) => safeUrlPathname(h.capture.url));
1613
- return !(0, capture_filters_1.isStructurallyIsolatedCapture)(path, otherPaths);
1623
+ // A capture admitted via the flow's own declared foldReturnSpec
1624
+ // endpointPattern (the drill-down target) is authoritative by
1625
+ // construction — the flow author named it explicitly — so it is
1626
+ // exempt from token-overlap isolation, which exists to catch
1627
+ // INCIDENTAL same-host captures the flow never declared.
1628
+ return matchesFoldReturn(capture) || !(0, capture_filters_1.isStructurallyIsolatedCapture)(path, otherPaths);
1614
1629
  })
1615
1630
  : hostGated;
1616
1631
  // Narrow further using structural relevance, anchored on whichever
@@ -7585,7 +7600,14 @@ function scanPrimaryCandidateGroups(actions, primaryIndex, globallyConsumedIndic
7585
7600
  }
7586
7601
  return groups;
7587
7602
  }
7588
- function detectDrillDownFoldPlan(actions) {
7603
+ function detectDrillDownFoldPlan(actions,
7604
+ // When set (see computeEmittedPrimaryAnchor), constrains the outer loop's
7605
+ // primary candidate search to only the action whose own operationGroupKey
7606
+ // matches this identity, so this can never resolve a fold plan anchored on
7607
+ // a DIFFERENT operation than the one already selected upstream as this
7608
+ // run's emitted primary. `null`/omitted preserves today's unconstrained,
7609
+ // order-dependent structural search for every other call site.
7610
+ primaryIdentityAnchor = null) {
7589
7611
  const plans = [];
7590
7612
  // Spans every primary candidate, not just the current one's own drill
7591
7613
  // scan: a step already folded into an earlier plan's chain — as either
@@ -7626,6 +7648,10 @@ function detectDrillDownFoldPlan(actions) {
7626
7648
  if (globallyConsumedIndices.has(primaryIndex))
7627
7649
  continue;
7628
7650
  const primary = actions[primaryIndex];
7651
+ if (primaryIdentityAnchor !== null &&
7652
+ operationGroupKey(primary.capture) !== primaryIdentityAnchor) {
7653
+ continue;
7654
+ }
7629
7655
  const groups = scanCached(primaryIndex);
7630
7656
  if (groups.length === 0)
7631
7657
  continue;
@@ -8136,7 +8162,10 @@ function compileFoldReturnResultsMatcher(spec) {
8136
8162
  const resultsPath = spec.resultsPath.split(".");
8137
8163
  return (capture) => objectItemsAtPath(capture.responseBody, resultsPath) !== null;
8138
8164
  }
8139
- function buildFoldPlanFromSpec(actions, spec) {
8165
+ function buildFoldPlanFromSpec(actions, spec,
8166
+ // See detectDrillDownFoldPlan's identical parameter — constrains this
8167
+ // function's own primaryStepIndex loop to the same anchor identity.
8168
+ primaryIdentityAnchor = null) {
8140
8169
  const primaryArrayPath = spec.resultsPath.split(".");
8141
8170
  const matchesFoldReturnEndpoint = compileFoldReturnEndpointMatcher(spec);
8142
8171
  // Built once and shared across every primaryStepIndex — see
@@ -8147,6 +8176,10 @@ function buildFoldPlanFromSpec(actions, spec) {
8147
8176
  const requestValueIndex = buildRequestValueIndex(actions);
8148
8177
  let freshestPlan = null;
8149
8178
  for (let primaryStepIndex = 0; primaryStepIndex < actions.length; primaryStepIndex++) {
8179
+ if (primaryIdentityAnchor !== null &&
8180
+ operationGroupKey(actions[primaryStepIndex].capture) !== primaryIdentityAnchor) {
8181
+ continue;
8182
+ }
8150
8183
  const primaryItems = objectItemsAtPath(actions[primaryStepIndex].capture.responseBody, primaryArrayPath);
8151
8184
  if (!primaryItems)
8152
8185
  continue;
@@ -8305,10 +8338,15 @@ function isDescendantArrayPath(candidate, base) {
8305
8338
  * spec's own drill indices look independent, so the heuristic's wrong-level
8306
8339
  * guess never survives alongside the corrected one.
8307
8340
  */
8308
- function mergeSpecPlanOntoSamePrimary(structuralPlans, actions, foldReturnSpec) {
8341
+ function mergeSpecPlanOntoSamePrimary(structuralPlans, actions, foldReturnSpec,
8342
+ // See detectDrillDownFoldPlan's identical parameter. Threaded into this
8343
+ // function's own buildFoldPlanFromSpec call so a spec plan resolved here
8344
+ // can never anchor on a DIFFERENT primary than the (already anchor-
8345
+ // constrained) structuralPlans it's about to be merged onto.
8346
+ primaryIdentityAnchor = null) {
8309
8347
  if (foldReturnSpec === null)
8310
8348
  return [...structuralPlans];
8311
- const specPlan = buildFoldPlanFromSpec(actions, foldReturnSpec);
8349
+ const specPlan = buildFoldPlanFromSpec(actions, foldReturnSpec, primaryIdentityAnchor);
8312
8350
  if (specPlan === null)
8313
8351
  return [...structuralPlans];
8314
8352
  const specPrimaryEndpointKey = endpointKey(actions[specPlan.primaryStepIndex].capture.url);
@@ -8660,14 +8698,17 @@ function collectDependentDrillDownChainValues(actions, foldReturnSpec) {
8660
8698
  }
8661
8699
  return consumersByValue;
8662
8700
  }
8663
- function resolveFoldPlan(actions, foldReturnSpec = null) {
8664
- const structuralPlans = detectDrillDownFoldPlan(actions);
8701
+ function resolveFoldPlan(actions, foldReturnSpec = null,
8702
+ // See detectDrillDownFoldPlan's identical parameter — threaded through to
8703
+ // both the structural and spec-driven resolution paths below.
8704
+ primaryIdentityAnchor = null) {
8705
+ const structuralPlans = detectDrillDownFoldPlan(actions, primaryIdentityAnchor);
8665
8706
  const plans = structuralPlans.length > 0
8666
- ? mergeSpecPlanOntoSamePrimary(structuralPlans, actions, foldReturnSpec)
8707
+ ? mergeSpecPlanOntoSamePrimary(structuralPlans, actions, foldReturnSpec, primaryIdentityAnchor)
8667
8708
  : (() => {
8668
8709
  if (foldReturnSpec === null)
8669
8710
  return [];
8670
- const specPlan = buildFoldPlanFromSpec(actions, foldReturnSpec);
8711
+ const specPlan = buildFoldPlanFromSpec(actions, foldReturnSpec, primaryIdentityAnchor);
8671
8712
  return specPlan === null ? [] : [specPlan];
8672
8713
  })();
8673
8714
  return plans.flatMap((plan) => {
@@ -8694,10 +8735,14 @@ function resolveFoldPlan(actions, foldReturnSpec = null) {
8694
8735
  * `primaryStepIndex`) per distinct primaryArrayPath/endpoint before
8695
8736
  * returning, keeping `resolveFoldPlan`'s own multi-plan output untouched.
8696
8737
  */
8697
- function resolveApplicableFoldPlans(actions, foldReturnSpec, multiStepBody) {
8738
+ function resolveApplicableFoldPlans(actions, foldReturnSpec, multiStepBody,
8739
+ // See detectDrillDownFoldPlan's identical parameter — threaded through so
8740
+ // this function's single-primary output can never diverge from the
8741
+ // caller's already-selected emitted primary.
8742
+ primaryIdentityAnchor = null) {
8698
8743
  if (multiStepBody)
8699
8744
  return [];
8700
- const plans = resolveFoldPlan(actions, foldReturnSpec);
8745
+ const plans = resolveFoldPlan(actions, foldReturnSpec, primaryIdentityAnchor);
8701
8746
  const primaryKey = (plan) => `${endpointKey(actions[plan.primaryStepIndex].capture.url)}\u0000${plan.primaryArrayPath.join(".")}`;
8702
8747
  const freshestStepIndexByPrimary = new Map();
8703
8748
  for (const plan of plans) {
@@ -9163,28 +9208,17 @@ function emitContractTs(opts) {
9163
9208
  // buildPaginatedFetchLoopExecuteHttpBody) — the fold runs against the final
9164
9209
  // assembled/de-duplicated page items, not just the first page's captured
9165
9210
  // sample, so a paginated primary is no longer excluded from folding.
9166
- const singlePrimaryFoldPlans = resolveApplicableFoldPlans(actionSteps, foldReturnSpec, multiStepBody);
9167
- // A fold plan's primaryStepIndex is resolved purely structurally (does the
9168
- // declared resultsPath resolve to an object array on that capture), with
9169
- // no comparison against the operation actually selected as primary for
9170
- // emission (gqlOperationName/gqlQuery, computed independently upstream).
9171
- // When they diverge, buildFoldMergeLines below would cast `data` to the
9172
- // fold plan's operation's shape even though `data` at runtime holds the
9173
- // emitted primary operation's response — a cast that can never typecheck.
9174
- // Scoped to gql: REST fold-plan/emitted-primary drift is a distinct,
9175
- // undocumented concern out of scope here.
9176
- if (gql) {
9177
- const emittedPrimaryIdentity = `${endpointPath}::${gqlOperationName ?? parsedOperationName(gqlQuery ?? "") ?? "anonymous"}`;
9178
- for (const plan of singlePrimaryFoldPlans) {
9179
- const primaryStep = actionSteps[plan.primaryStepIndex];
9180
- if (!primaryStep)
9181
- continue;
9182
- const foldPrimaryIdentity = operationGroupKey(primaryStep.capture);
9183
- if (foldPrimaryIdentity !== emittedPrimaryIdentity) {
9184
- throw new Error(`emitContractTs: fold plan primary operation ${foldPrimaryIdentity} differs from the emitted primary operation ${emittedPrimaryIdentity} — the declared foldReturn no longer applies to this run's selected primary; drop or re-resolve the foldReturn spec against the emitted primary operation`);
9185
- }
9186
- }
9187
- }
9211
+ // Constrains the resolution below to the operation already selected as
9212
+ // this run's emitted primary (gqlOperationName/gqlQuery, computed
9213
+ // independently upstream) — see computeEmittedPrimaryAnchor. Without this,
9214
+ // a fold plan's primaryStepIndex was resolved purely structurally (does
9215
+ // the declared resultsPath resolve to an object array on that capture),
9216
+ // with no anchor to the emitted primary, so it could resolve onto a
9217
+ // DIFFERENT operation than the one `data` holds at runtime. Scoped to gql:
9218
+ // REST fold-plan/emitted-primary drift is a distinct, undocumented concern
9219
+ // out of scope here.
9220
+ const emittedPrimaryAnchor = computeEmittedPrimaryAnchor(gql, endpointPath, gqlOperationName, gqlQuery);
9221
+ const singlePrimaryFoldPlans = resolveApplicableFoldPlans(actionSteps, foldReturnSpec, multiStepBody, emittedPrimaryAnchor);
9188
9222
  // A GraphQL primary with a resolved drill-down fold has no other REST
9189
9223
  // client to issue the drill request(s) with — getGql only ever speaks
9190
9224
  // GraphQL to the primary endpoint.
@@ -11187,9 +11221,21 @@ async function main() {
11187
11221
  }
11188
11222
  const hasMultipartStep = actionSteps.some((s) => s.isMultipart);
11189
11223
  const headerBindings = collectHeaderBindings(actionSteps);
11224
+ // Mirrors the exact gqlOperationName expression fed into contractOpts
11225
+ // below (see the `gqlOperationName:` field), so this anchor can never
11226
+ // disagree with the one emitContractTs computes from that same
11227
+ // contractOpts — see computeEmittedPrimaryAnchor.
11228
+ const emittedPrimaryAnchor = computeEmittedPrimaryAnchor(gql, endpointPath, primaryGraphQLOperation
11229
+ ? (primaryGraphQLOperation.capture.operationName ??
11230
+ (0, capture_filters_1.parsedOperationName)(primaryGraphQLOperation.capture.query ?? ""))
11231
+ : (fallbackGraphQLCapture?.operationName ??
11232
+ (0, capture_filters_1.parsedOperationName)(fallbackGraphQLCapture?.query ?? "")), gqlQuery);
11190
11233
  // Shape inference targets the SAME call executeHttp returns — see
11191
11234
  // selectEffectiveResponseBody — so the two surfaces can't describe different calls.
11192
- const effectiveResponseBody = selectEffectiveResponseBody(isSubmissionFlow, actionSteps, responseBody, foldReturnSpec);
11235
+ // A submission flow's fold plan must stay byte-identical to
11236
+ // emitMultiStepExecuteHttp's own unanchored resolveFoldPlan call, which the
11237
+ // actual multi-step runtime loop uses — so the anchor is withheld there.
11238
+ const effectiveResponseBody = selectEffectiveResponseBody(isSubmissionFlow, actionSteps, responseBody, foldReturnSpec, isSubmissionFlow ? null : emittedPrimaryAnchor);
11193
11239
  // A declared foldReturn that resolves to no plan is a silent no-op otherwise
11194
11240
  // — the flow author gets the discarding selectReturnAction path with nothing
11195
11241
  // in the output saying their declaration never applied. A multi-step
@@ -11202,7 +11248,8 @@ async function main() {
11202
11248
  // with a working foldReturn.
11203
11249
  const effectiveFoldPlanCount = multiStepBody
11204
11250
  ? resolveFoldPlan(actionSteps, foldReturnSpec).length
11205
- : resolveApplicableFoldPlans(actionSteps, foldReturnSpec, multiStepBody).length;
11251
+ : resolveApplicableFoldPlans(actionSteps, foldReturnSpec, multiStepBody, emittedPrimaryAnchor)
11252
+ .length;
11206
11253
  if (foldReturnSpec !== null && effectiveFoldPlanCount === 0) {
11207
11254
  logger.warn(`flow declares foldReturn (endpointPattern: ${foldReturnSpec.endpointPattern}, resultsPath: ${foldReturnSpec.resultsPath}, joinFields: ${foldReturnSpec.joinFields.join(", ")}) but no fold plan resolved — no later capture matched the endpoint pattern, resultsPath resolved to no object array, or the matched drill-down is multipart; the drill-down's response will not be folded`);
11208
11255
  }
@@ -11265,9 +11312,9 @@ async function main() {
11265
11312
  endpointPath,
11266
11313
  gqlOperationName: primaryGraphQLOperation
11267
11314
  ? (primaryGraphQLOperation.capture.operationName ??
11268
- parsedOperationName(primaryGraphQLOperation.capture.query ?? ""))
11315
+ (0, capture_filters_1.parsedOperationName)(primaryGraphQLOperation.capture.query ?? ""))
11269
11316
  : (fallbackGraphQLCapture?.operationName ??
11270
- parsedOperationName(fallbackGraphQLCapture?.query ?? "")),
11317
+ (0, capture_filters_1.parsedOperationName)(fallbackGraphQLCapture?.query ?? "")),
11271
11318
  // For a REST primary (no primaryGraphQLOperation), the winning
11272
11319
  // capture's own `variables`/`decodedParams` is the REST equivalent of
11273
11320
  // a GraphQL operation's captured variables — the object