@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.
- package/dist/recon/capture-filters.d.ts +10 -0
- package/dist/recon/capture-filters.d.ts.map +1 -1
- package/dist/recon/capture-filters.js +113 -0
- package/dist/recon/capture-filters.js.map +1 -1
- package/dist/scripts/recon-generate.d.ts +4 -4
- package/dist/scripts/recon-generate.d.ts.map +1 -1
- package/dist/scripts/recon-generate.js +97 -50
- package/dist/scripts/recon-generate.js.map +1 -1
- package/package.json +1 -1
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
9167
|
-
//
|
|
9168
|
-
//
|
|
9169
|
-
//
|
|
9170
|
-
//
|
|
9171
|
-
//
|
|
9172
|
-
//
|
|
9173
|
-
// emitted
|
|
9174
|
-
//
|
|
9175
|
-
|
|
9176
|
-
|
|
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
|
-
|
|
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)
|
|
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
|