@enricai/barnacle 1.12.8 → 1.12.10
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/README.md +3 -1
- package/dist/config.d.ts +7 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +1 -0
- package/dist/config.js.map +1 -1
- package/dist/scraper/retry.d.ts +1 -1
- package/dist/scraper/retry.d.ts.map +1 -1
- package/dist/scraper/retry.js +3 -2
- package/dist/scraper/retry.js.map +1 -1
- package/dist/scripts/recon-browser.d.ts.map +1 -1
- package/dist/scripts/recon-browser.js +628 -581
- package/dist/scripts/recon-browser.js.map +1 -1
- package/dist/scripts/recon-generate-repeated-section-fixture.d.ts +14 -0
- package/dist/scripts/recon-generate-repeated-section-fixture.d.ts.map +1 -0
- package/dist/scripts/recon-generate-repeated-section-fixture.js +52 -0
- package/dist/scripts/recon-generate-repeated-section-fixture.js.map +1 -0
- package/dist/scripts/recon-generate.d.ts +11 -6
- package/dist/scripts/recon-generate.d.ts.map +1 -1
- package/dist/scripts/recon-generate.js +42 -73
- package/dist/scripts/recon-generate.js.map +1 -1
- package/package.json +1 -1
|
@@ -506,14 +506,14 @@ const IGNORE_REQUEST_HEADERS = new Set([
|
|
|
506
506
|
*/
|
|
507
507
|
function deriveRequestHeaders(captures, replays, baseUrl, submitPatterns = null) {
|
|
508
508
|
const successfulUrls = new Set(replays.filter((r) => r.success).map((r) => endpointKey(r.url)));
|
|
509
|
-
// Prefer ACTION captures (non-GET 2xx
|
|
510
|
-
//
|
|
511
|
-
//
|
|
512
|
-
//
|
|
513
|
-
//
|
|
514
|
-
//
|
|
515
|
-
//
|
|
516
|
-
const actionCaptures = extractActionSequence(captures,
|
|
509
|
+
// Prefer ACTION captures (non-GET 2xx, non-noise) as the authoritative
|
|
510
|
+
// header source. Replay-matched static-asset GETs lack the API-specific
|
|
511
|
+
// headers that REST endpoints require, so falling back to those produces
|
|
512
|
+
// a degenerate baseline-only header set. When action captures exist
|
|
513
|
+
// (multi-step submission flows), use them. For sites where the flow is a
|
|
514
|
+
// single REST call (no detectable action sequence), fall back to the
|
|
515
|
+
// replay-matched captures.
|
|
516
|
+
const actionCaptures = extractActionSequence(captures, submitPatterns).map((a) => a.capture);
|
|
517
517
|
const replayMatchedCaptures = captures.filter((c) => successfulUrls.has(endpointKey(c.url)));
|
|
518
518
|
const relevantCaptures = actionCaptures.length > 0 ? actionCaptures : replayMatchedCaptures;
|
|
519
519
|
const counts = new Map();
|
|
@@ -707,26 +707,23 @@ function resolveManifestActionSequence(runRoot, captures) {
|
|
|
707
707
|
}
|
|
708
708
|
/**
|
|
709
709
|
* Extracts the ordered sequence of meaningful POSTs that represent the
|
|
710
|
-
* transactional flow:
|
|
711
|
-
* sinks. Assets need no filter of their own — they arrive as GETs.
|
|
710
|
+
* transactional flow: non-noise 2xx POSTs, minus telemetry and error-reporting
|
|
711
|
+
* sinks. Assets need no filter of their own — they arrive as GETs. Host is
|
|
712
|
+
* NOT a filter criterion — a multi-step submission routinely bounces to a
|
|
713
|
+
* different host mid-flow (an account-creation redirect, a tenant API
|
|
714
|
+
* subdomain distinct from the landing page's host), so `isNoiseUrl` alone
|
|
715
|
+
* decides what counts as site traffic vs. third-party noise.
|
|
712
716
|
*
|
|
713
717
|
* When the flow declares submit patterns, only POSTs matching them survive —
|
|
714
718
|
* this isolates the submission from same-origin page chrome (bootstrap, chatbot,
|
|
715
719
|
* JWT refresh, reference-lookup) that a browser fires incidentally. Absent
|
|
716
|
-
* patterns preserve the
|
|
720
|
+
* patterns preserve the noise heuristic exactly.
|
|
717
721
|
*
|
|
718
722
|
* Exported for tests: this predicate decides what a generated plugin will POST
|
|
719
723
|
* at a live site, and it is the only gate between a browser's incidental
|
|
720
724
|
* chatter and the emitted hot path.
|
|
721
725
|
*/
|
|
722
|
-
function extractActionSequence(captures,
|
|
723
|
-
let host;
|
|
724
|
-
try {
|
|
725
|
-
host = new URL(baseUrl).host;
|
|
726
|
-
}
|
|
727
|
-
catch {
|
|
728
|
-
host = "";
|
|
729
|
-
}
|
|
726
|
+
function extractActionSequence(captures, submitPatterns = null) {
|
|
730
727
|
const matchesSubmit = compileSubmitMatcher(submitPatterns);
|
|
731
728
|
return captures
|
|
732
729
|
.map((capture, index) => ({ capture, index }))
|
|
@@ -735,15 +732,6 @@ function extractActionSequence(captures, baseUrl, submitPatterns = null) {
|
|
|
735
732
|
return false;
|
|
736
733
|
if (capture.status < 200 || capture.status >= 300)
|
|
737
734
|
return false;
|
|
738
|
-
let captureHost;
|
|
739
|
-
try {
|
|
740
|
-
captureHost = new URL(capture.url).host;
|
|
741
|
-
}
|
|
742
|
-
catch {
|
|
743
|
-
return false;
|
|
744
|
-
}
|
|
745
|
-
if (captureHost !== host)
|
|
746
|
-
return false;
|
|
747
735
|
if ((0, capture_filters_1.isNoiseUrl)(capture.url))
|
|
748
736
|
return false;
|
|
749
737
|
if (!matchesSubmit(capture))
|
|
@@ -759,19 +747,13 @@ function extractActionSequence(captures, baseUrl, submitPatterns = null) {
|
|
|
759
747
|
* make up a transactional flow (`UpsertSavedApplication`, `SubmitForm`, ...)
|
|
760
748
|
* — and drops `query` operations, which are read/bootstrap calls (e.g. a
|
|
761
749
|
* page-load `ListForms`) that carry no state-threading value and are exactly
|
|
762
|
-
* what let a chronologically-first fallback pick an unrelated query.
|
|
750
|
+
* what let a chronologically-first fallback pick an unrelated query. Host is
|
|
751
|
+
* NOT a filter criterion, matching {@link extractActionSequence}.
|
|
763
752
|
*
|
|
764
753
|
* Exported for tests: this predicate decides what a generated GraphQL plugin
|
|
765
754
|
* will send at a live site.
|
|
766
755
|
*/
|
|
767
|
-
function extractGraphQLActionSequence(captures,
|
|
768
|
-
let host;
|
|
769
|
-
try {
|
|
770
|
-
host = new URL(baseUrl).host;
|
|
771
|
-
}
|
|
772
|
-
catch {
|
|
773
|
-
host = "";
|
|
774
|
-
}
|
|
756
|
+
function extractGraphQLActionSequence(captures, submitPatterns = null) {
|
|
775
757
|
const matchesSubmit = compileSubmitMatcher(submitPatterns);
|
|
776
758
|
return captures
|
|
777
759
|
.map((capture, index) => ({ capture, index }))
|
|
@@ -780,15 +762,6 @@ function extractGraphQLActionSequence(captures, baseUrl, submitPatterns = null)
|
|
|
780
762
|
return false;
|
|
781
763
|
if (capture.status < 200 || capture.status >= 300)
|
|
782
764
|
return false;
|
|
783
|
-
let captureHost;
|
|
784
|
-
try {
|
|
785
|
-
captureHost = new URL(capture.url).host;
|
|
786
|
-
}
|
|
787
|
-
catch {
|
|
788
|
-
return false;
|
|
789
|
-
}
|
|
790
|
-
if (captureHost !== host)
|
|
791
|
-
return false;
|
|
792
765
|
if ((0, capture_filters_1.isNoiseUrl)(capture.url))
|
|
793
766
|
return false;
|
|
794
767
|
if (!matchesSubmit(capture))
|
|
@@ -3298,20 +3271,18 @@ function emitContractTs(opts) {
|
|
|
3298
3271
|
// invocation, so heterogeneous per-call shapes are each checked against
|
|
3299
3272
|
// their own inferred schema regardless of what this client-level schema is.
|
|
3300
3273
|
//
|
|
3301
|
-
// For multi-step flows
|
|
3302
|
-
//
|
|
3303
|
-
//
|
|
3304
|
-
//
|
|
3305
|
-
//
|
|
3306
|
-
//
|
|
3307
|
-
//
|
|
3308
|
-
//
|
|
3309
|
-
//
|
|
3310
|
-
//
|
|
3311
|
-
//
|
|
3312
|
-
|
|
3313
|
-
// — same z.unknown() treatment as the multi-step case, for the same reason.
|
|
3314
|
-
const responseSchemaExpr = multiStepBody || omitExecuteHttp ? `z.unknown()` : inferZodSchema(responseBody);
|
|
3274
|
+
// For multi-step flows, `responseBody` here is already
|
|
3275
|
+
// `selectEffectiveResponseBody`'s pick — the SAME call `selectReturnAction`
|
|
3276
|
+
// returns from `executeHttp` (`return { data: r9 }` below, r9 being that
|
|
3277
|
+
// call's own already-validated result). Inferring the client-level schema
|
|
3278
|
+
// from it is therefore not a guess about a field the captures never showed:
|
|
3279
|
+
// it's the literal shape of the value the client hands back, captured by
|
|
3280
|
+
// its own success response (e.g. a `valid`/`errors`-shaped terminal body).
|
|
3281
|
+
// Single-endpoint plugins keep the same inferred-schema treatment, since
|
|
3282
|
+
// there both roles (client default and sole call) coincide.
|
|
3283
|
+
// Browser-flow-only plugins have no HTTP call to infer a shape from —
|
|
3284
|
+
// z.unknown() there is the honest gap, not a narrowing shortcut.
|
|
3285
|
+
const responseSchemaExpr = omitExecuteHttp ? `z.unknown()` : inferZodSchema(responseBody);
|
|
3315
3286
|
// Multi-step flows that include a multipart upload need the binary asset
|
|
3316
3287
|
// on the payload. ApplicantContactSchema (via ApplicantResumeSchema) already
|
|
3317
3288
|
// declares Resume/ResumeContentType/ResumeFilename, so submission flows
|
|
@@ -4178,9 +4149,7 @@ async function main() {
|
|
|
4178
4149
|
// Hoisted so both the primary-operation gate below and rawActionCaptures
|
|
4179
4150
|
// (further down) read the same computed sequence instead of calling the
|
|
4180
4151
|
// extractor twice.
|
|
4181
|
-
const graphqlActionSequence = gql
|
|
4182
|
-
? extractGraphQLActionSequence(captures, baseUrl, submitPatterns)
|
|
4183
|
-
: [];
|
|
4152
|
+
const graphqlActionSequence = gql ? extractGraphQLActionSequence(captures, submitPatterns) : [];
|
|
4184
4153
|
const isReadOnlyFlow = !flowSteps.some((step) => typeof step !== "string" && step.submitStep === true);
|
|
4185
4154
|
const primaryGraphQLOperation = gql && isReadOnlyFlow && graphqlActionSequence.length === 0
|
|
4186
4155
|
? selectPrimaryGraphQLOperation(captures, flowSteps, vocabulary)
|
|
@@ -4201,7 +4170,7 @@ async function main() {
|
|
|
4201
4170
|
// heuristic extraction finds.
|
|
4202
4171
|
const patternedHeuristicActionCaptures = gql
|
|
4203
4172
|
? graphqlActionSequence
|
|
4204
|
-
: collapseRedundantPatches(extractActionSequence(captures,
|
|
4173
|
+
: collapseRedundantPatches(extractActionSequence(captures, submitPatterns));
|
|
4205
4174
|
// The same undercount hazard applies one layer below the manifest: a
|
|
4206
4175
|
// flow-declared submitEndpointPattern that matches only one section's URL
|
|
4207
4176
|
// (the natural way to describe "the button that finishes the wizard")
|
|
@@ -4214,8 +4183,8 @@ async function main() {
|
|
|
4214
4183
|
const unfilteredHeuristicActionCaptures = submitPatterns.endpoint === null && submitPatterns.body === null
|
|
4215
4184
|
? patternedHeuristicActionCaptures
|
|
4216
4185
|
: gql
|
|
4217
|
-
? extractGraphQLActionSequence(captures,
|
|
4218
|
-
: collapseRedundantPatches(extractActionSequence(captures,
|
|
4186
|
+
? extractGraphQLActionSequence(captures, null)
|
|
4187
|
+
: collapseRedundantPatches(extractActionSequence(captures, null));
|
|
4219
4188
|
const patternUndercounts = patternedHeuristicActionCaptures.length < unfilteredHeuristicActionCaptures.length;
|
|
4220
4189
|
if (patternUndercounts) {
|
|
4221
4190
|
logger.info(`submission selection: ignoring submitEndpointPattern/submitBodyPattern (${patternedHeuristicActionCaptures.length} capture(s)) as an undercount of the unfiltered heuristic action sequence (${unfilteredHeuristicActionCaptures.length} capture(s))`);
|
|
@@ -4238,16 +4207,16 @@ async function main() {
|
|
|
4238
4207
|
// The declared submitEndpointPattern's own under-match: unlike manifestUndercounts
|
|
4239
4208
|
// (which compares an authoritative submit-manifest.json against the heuristic
|
|
4240
4209
|
// sequence), this compares the pattern-filtered heuristic sequence against the SAME
|
|
4241
|
-
// captures with no submitPatterns filter at all — the true raw
|
|
4210
|
+
// captures with no submitPatterns filter at all — the true raw non-GET 2xx non-noise
|
|
4242
4211
|
// action set. A pattern that matches conspicuously fewer calls than that raw set is a
|
|
4243
4212
|
// detection failure (the pattern is too narrow), not evidence the flow is read-only,
|
|
4244
4213
|
// and per the no-silent-fallback rule must not be allowed to quietly collapse into the
|
|
4245
4214
|
// generic single-endpoint {query} template below.
|
|
4246
|
-
const
|
|
4215
|
+
const rawUnfilteredActionCaptures = submitEndpointPattern === null
|
|
4247
4216
|
? null
|
|
4248
4217
|
: gql
|
|
4249
|
-
? extractGraphQLActionSequence(captures,
|
|
4250
|
-
: collapseRedundantPatches(extractActionSequence(captures,
|
|
4218
|
+
? extractGraphQLActionSequence(captures, null)
|
|
4219
|
+
: collapseRedundantPatches(extractActionSequence(captures, null));
|
|
4251
4220
|
// Form-schema detection runs BEFORE state-indexing so the field-id/option-id
|
|
4252
4221
|
// UUIDs can be shielded from indexing — those UUIDs are stable schema
|
|
4253
4222
|
// anchors that T2/T3 substitution depends on remaining literal in body
|
|
@@ -4307,10 +4276,10 @@ async function main() {
|
|
|
4307
4276
|
// excluded real action captures the unfiltered raw set still has, emitting the generic
|
|
4308
4277
|
// {query} template would misrepresent a genuine multi-call flow as read-only. Exit
|
|
4309
4278
|
// rather than degrade quietly, per the no-defensive/no-silent-fallback rule.
|
|
4310
|
-
if (
|
|
4279
|
+
if (rawUnfilteredActionCaptures !== null &&
|
|
4311
4280
|
!isSubmissionFlow &&
|
|
4312
|
-
|
|
4313
|
-
logger.error(`ERROR declared submitEndpointPattern ${JSON.stringify(submitEndpointPattern)} matched only ${heuristicActionCaptures.length} of ${
|
|
4281
|
+
rawUnfilteredActionCaptures.length > heuristicActionCaptures.length) {
|
|
4282
|
+
logger.error(`ERROR declared submitEndpointPattern ${JSON.stringify(submitEndpointPattern)} matched only ${heuristicActionCaptures.length} of ${rawUnfilteredActionCaptures.length} raw non-GET 2xx non-noise action capture(s) — this under-match looks like a detection failure, not a read-only flow; refusing to fall back to the generic single-endpoint {query} template. Fix submitEndpointPattern in recon-flow.json to cover the real submission calls.`);
|
|
4314
4283
|
process.exit(1);
|
|
4315
4284
|
}
|
|
4316
4285
|
// Diagnostic for the FAILURE-3 shape (a flowless recon capture): a "submission flow"
|