@enricai/barnacle 1.12.73 → 1.12.75
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/scraper/browser-click-expr.d.ts +68 -0
- package/dist/scraper/browser-click-expr.d.ts.map +1 -1
- package/dist/scraper/browser-click-expr.js +121 -1
- package/dist/scraper/browser-click-expr.js.map +1 -1
- package/dist/scraper/flow-runner.d.ts +1 -6
- package/dist/scraper/flow-runner.d.ts.map +1 -1
- package/dist/scraper/flow-runner.js +113 -44
- package/dist/scraper/flow-runner.js.map +1 -1
- package/dist/scraper/phantom-click.d.ts +8 -0
- package/dist/scraper/phantom-click.d.ts.map +1 -1
- package/dist/scraper/phantom-click.js +21 -1
- package/dist/scraper/phantom-click.js.map +1 -1
- package/dist/scraper/submit-control.d.ts.map +1 -1
- package/dist/scraper/submit-control.js +2 -19
- package/dist/scraper/submit-control.js.map +1 -1
- package/dist/scripts/recon-browser.d.ts +9 -5
- package/dist/scripts/recon-browser.d.ts.map +1 -1
- package/dist/scripts/recon-browser.js +21 -9
- package/dist/scripts/recon-browser.js.map +1 -1
- package/dist/scripts/recon-generate.d.ts +77 -1
- package/dist/scripts/recon-generate.d.ts.map +1 -1
- package/dist/scripts/recon-generate.js +141 -22
- package/dist/scripts/recon-generate.js.map +1 -1
- package/package.json +1 -1
|
@@ -24,6 +24,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
24
24
|
exports.resolveStepPayloadField = resolveStepPayloadField;
|
|
25
25
|
exports.buildKnownFieldValues = buildKnownFieldValues;
|
|
26
26
|
exports.extractStepPersonaValue = extractStepPersonaValue;
|
|
27
|
+
exports.extractNavigateToHashFragmentValue = extractNavigateToHashFragmentValue;
|
|
27
28
|
exports.deriveFillLabelField = deriveFillLabelField;
|
|
28
29
|
exports.harvestPersonaBindings = harvestPersonaBindings;
|
|
29
30
|
exports.resolveCompositePersonaFields = resolveCompositePersonaFields;
|
|
@@ -60,6 +61,7 @@ exports.ancestorScopedAccessor = ancestorScopedAccessor;
|
|
|
60
61
|
exports.compileActionSteps = compileActionSteps;
|
|
61
62
|
exports.collectHeaderBindings = collectHeaderBindings;
|
|
62
63
|
exports.deriveProducerBoundaryBindings = deriveProducerBoundaryBindings;
|
|
64
|
+
exports.collectUnboundLiterals = collectUnboundLiterals;
|
|
63
65
|
exports.emitMultiStepExecuteHttp = emitMultiStepExecuteHttp;
|
|
64
66
|
exports.countSpliceableFacets = countSpliceableFacets;
|
|
65
67
|
exports.spliceFacetsIntoStringVariable = spliceFacetsIntoStringVariable;
|
|
@@ -359,6 +361,34 @@ function extractStepPersonaValue(instruction, env) {
|
|
|
359
361
|
const value = pickValueSpan(instruction, spans).value;
|
|
360
362
|
return value.length > 0 ? value : null;
|
|
361
363
|
}
|
|
364
|
+
/**
|
|
365
|
+
* Extracts the trailing path segment of a navigateTo URL's hash fragment —
|
|
366
|
+
* e.g. `.../#/jobs/category-widgets` yields `widgets` — as the persona value
|
|
367
|
+
* an explicit `payloadField` annotation on that step names. Site-agnostic:
|
|
368
|
+
* reads only the URL's own hash shape, no domain knowledge.
|
|
369
|
+
*
|
|
370
|
+
* When `previousHash` is given and the current hash is a strict extension of
|
|
371
|
+
* it (the accumulating-hash case, e.g. `#a` -> `#a,b`), the persona value is
|
|
372
|
+
* only the NEW suffix the step added, not the whole cumulative string — a
|
|
373
|
+
* cumulative value would never recur verbatim in any single request literal.
|
|
374
|
+
*
|
|
375
|
+
* @returns the segment (or delta), or null when the URL has no non-empty
|
|
376
|
+
* hash segment
|
|
377
|
+
*/
|
|
378
|
+
function extractNavigateToHashFragmentValue(url, previousHash) {
|
|
379
|
+
const hashIndex = url.indexOf("#");
|
|
380
|
+
if (hashIndex === -1)
|
|
381
|
+
return null;
|
|
382
|
+
const hash = url.slice(hashIndex + 1);
|
|
383
|
+
if (previousHash && hash.length > previousHash.length && hash.startsWith(previousHash)) {
|
|
384
|
+
const suffix = hash.slice(previousHash.length);
|
|
385
|
+
const delta = /^[,/]/.test(suffix) ? suffix.slice(1) : suffix;
|
|
386
|
+
if (delta.length > 0)
|
|
387
|
+
return delta;
|
|
388
|
+
}
|
|
389
|
+
const segments = hash.split("/").filter((s) => s.length > 0);
|
|
390
|
+
return segments.length > 0 ? segments[segments.length - 1] : null;
|
|
391
|
+
}
|
|
362
392
|
/**
|
|
363
393
|
* Derives a payload field name from the field LABEL in a fill/enter/type
|
|
364
394
|
* instruction, for steps the consumer vocabulary does not cover.
|
|
@@ -405,9 +435,32 @@ function deriveFillLabelField(instruction) {
|
|
|
405
435
|
function harvestPersonaBindings(flowSteps, vocabulary, env) {
|
|
406
436
|
const bindings = new Map();
|
|
407
437
|
const knownFieldValues = buildKnownFieldValues(flowSteps, vocabulary, env);
|
|
438
|
+
let previousNavigateToHash;
|
|
408
439
|
for (const step of flowSteps) {
|
|
409
440
|
const isObj = typeof step !== "string";
|
|
410
441
|
const instruction = isObj ? step.step : step;
|
|
442
|
+
// A navigateTo step carries no prose instruction to extract a persona
|
|
443
|
+
// value FROM — its own text is a captured page.goto, not a fill/select
|
|
444
|
+
// label. The facet an explicit `payloadField` annotation names instead
|
|
445
|
+
// lives in the URL itself (recon-browser captures navigation targets like
|
|
446
|
+
// `.../#/jobs/category-widgets`), so the value to correlate against
|
|
447
|
+
// downstream header/body templates is the URL's own hash-derived
|
|
448
|
+
// fragment, not anything extractStepPersonaValue could find in `step.step`.
|
|
449
|
+
if (isObj && step.navigateTo !== undefined) {
|
|
450
|
+
const hashIndex = step.navigateTo.indexOf("#");
|
|
451
|
+
const currentHash = hashIndex === -1 ? undefined : step.navigateTo.slice(hashIndex + 1);
|
|
452
|
+
if (!step.payloadField) {
|
|
453
|
+
previousNavigateToHash = currentHash;
|
|
454
|
+
continue;
|
|
455
|
+
}
|
|
456
|
+
const value = extractNavigateToHashFragmentValue(step.navigateTo, previousNavigateToHash);
|
|
457
|
+
previousNavigateToHash = currentHash;
|
|
458
|
+
if (value === null)
|
|
459
|
+
continue;
|
|
460
|
+
if (!bindings.has(value))
|
|
461
|
+
bindings.set(value, `payload.${step.payloadField}`);
|
|
462
|
+
continue;
|
|
463
|
+
}
|
|
411
464
|
const vocabField = resolveStepPayloadField(instruction, isObj ? step.payloadField : undefined, isObj ? step.payloadFieldNone : undefined, vocabulary, knownFieldValues);
|
|
412
465
|
// Vocabulary wins outright. Only on a miss do we fall back to deriving the
|
|
413
466
|
// field from the instruction's own label — and never when the author opted
|
|
@@ -3913,7 +3966,7 @@ function locateFormEnvelopePath(parsedBody) {
|
|
|
3913
3966
|
* search there stops an earlier element's already-rewritten (or still-frozen)
|
|
3914
3967
|
* span from swallowing a later element's identically-named key.
|
|
3915
3968
|
*/
|
|
3916
|
-
function applyStructuredValuePayloadSubstitutionsForEnvelope(template, envelope, outStructuredKeys,
|
|
3969
|
+
function applyStructuredValuePayloadSubstitutionsForEnvelope(template, envelope, outStructuredKeys, unconditionalExcludeValues, restrictedExcludeSourceByValue, searchFrom, envelopePath, rootBody) {
|
|
3917
3970
|
let result = template;
|
|
3918
3971
|
let cursor = searchFrom;
|
|
3919
3972
|
// The body root's own entries are eligible too, whenever the located
|
|
@@ -3935,8 +3988,29 @@ function applyStructuredValuePayloadSubstitutionsForEnvelope(template, envelope,
|
|
|
3935
3988
|
Object.keys(value).length > 0;
|
|
3936
3989
|
if (!isNonEmptyArray && !isNestedObject)
|
|
3937
3990
|
continue;
|
|
3938
|
-
if (
|
|
3939
|
-
|
|
3991
|
+
if (unconditionalExcludeValues.size > 0 || restrictedExcludeSourceByValue.size > 0) {
|
|
3992
|
+
// A name-free join/chain value (unconditionalExcludeValues) excludes the
|
|
3993
|
+
// whole field on ANY leaf coincidence, same as before. A RESTRICTED prior-
|
|
3994
|
+
// step value (one with a real source field name) additionally requires
|
|
3995
|
+
// keyNamesCorrelate(sourceName, key) — mirroring the discipline
|
|
3996
|
+
// replaceGuardedAgainstExistingPlaceholders/findThreadedJoinFields already
|
|
3997
|
+
// apply to the exact same class of value-coincidence false positive.
|
|
3998
|
+
// Without this, a broken field's own unrelated short scalar leaf (a
|
|
3999
|
+
// count/id) merely equalling SOME other step's produced value gets the
|
|
4000
|
+
// ENTIRE array/object excluded from threading, even though nothing about
|
|
4001
|
+
// that leaf's key names the same concept as the matched value's source.
|
|
4002
|
+
const carriesThreadedValue = [...walkAllPrimitiveLeaves(value)].some(({ value: leaf, path: leafPath }) => {
|
|
4003
|
+
if (leaf === null)
|
|
4004
|
+
return false;
|
|
4005
|
+
const leafValue = String(leaf);
|
|
4006
|
+
if (unconditionalExcludeValues.has(leafValue))
|
|
4007
|
+
return true;
|
|
4008
|
+
const sourceName = restrictedExcludeSourceByValue.get(leafValue);
|
|
4009
|
+
if (sourceName === undefined)
|
|
4010
|
+
return false;
|
|
4011
|
+
const leafKey = leafPath.at(-1);
|
|
4012
|
+
return leafKey !== undefined && keyNamesCorrelate(sourceName, leafKey);
|
|
4013
|
+
});
|
|
3940
4014
|
if (carriesThreadedValue)
|
|
3941
4015
|
continue;
|
|
3942
4016
|
}
|
|
@@ -3993,7 +4067,7 @@ function applyStructuredValuePayloadSubstitutionsForEnvelope(template, envelope,
|
|
|
3993
4067
|
* pass to it. Shared by the top-level-object and top-level-array branches of
|
|
3994
4068
|
* {@link applyStructuredValuePayloadSubstitutions} below.
|
|
3995
4069
|
*/
|
|
3996
|
-
function applyStructuredValuePayloadSubstitutionsForObjectBody(template, objectBody, outStructuredKeys,
|
|
4070
|
+
function applyStructuredValuePayloadSubstitutionsForObjectBody(template, objectBody, outStructuredKeys, unconditionalExcludeValues, restrictedExcludeSourceByValue, searchFrom) {
|
|
3997
4071
|
const envelopePath = locateFormEnvelopePath(objectBody);
|
|
3998
4072
|
let envelope = objectBody;
|
|
3999
4073
|
for (const seg of envelopePath) {
|
|
@@ -4004,12 +4078,33 @@ function applyStructuredValuePayloadSubstitutionsForObjectBody(template, objectB
|
|
|
4004
4078
|
if (envelope === null || typeof envelope !== "object" || Array.isArray(envelope)) {
|
|
4005
4079
|
return { result: template, nextSearchFrom: searchFrom };
|
|
4006
4080
|
}
|
|
4007
|
-
return applyStructuredValuePayloadSubstitutionsForEnvelope(template, envelope, outStructuredKeys,
|
|
4081
|
+
return applyStructuredValuePayloadSubstitutionsForEnvelope(template, envelope, outStructuredKeys, unconditionalExcludeValues, restrictedExcludeSourceByValue, searchFrom, envelopePath, objectBody);
|
|
4008
4082
|
}
|
|
4009
|
-
|
|
4083
|
+
/**
|
|
4084
|
+
* `priorStepStateBindings` carries the SAME value→binding map
|
|
4085
|
+
* {@link deriveStateVarByValue} produces. A binding with `restricted: true`
|
|
4086
|
+
* (a value whose source has a real field name) only excludes a candidate
|
|
4087
|
+
* field when its own leaf's key {@link keyNamesCorrelate}s with that source
|
|
4088
|
+
* name — an unrestricted binding (name-free, e.g. a bare array-index chain
|
|
4089
|
+
* hop) or a `joinFieldValues` entry excludes unconditionally on any leaf
|
|
4090
|
+
* match, same as before. Without the correlation requirement, an array/
|
|
4091
|
+
* object field whose own unrelated short scalar leaf (a count/id) merely
|
|
4092
|
+
* EQUALS some other step's produced value gets the whole field wrongly
|
|
4093
|
+
* excluded from threading — the same value-coincidence class of bug
|
|
4094
|
+
* {@link keyNamesCorrelate}'s other call sites already guard against.
|
|
4095
|
+
*/
|
|
4096
|
+
function applyStructuredValuePayloadSubstitutions(template, parsedBody, outStructuredKeys, priorStepStateBindings = new Map(), joinFieldValues = new Set()) {
|
|
4010
4097
|
if (parsedBody === null || typeof parsedBody !== "object") {
|
|
4011
4098
|
return template;
|
|
4012
4099
|
}
|
|
4100
|
+
const unconditionalExcludeValues = new Set(joinFieldValues);
|
|
4101
|
+
const restrictedExcludeSourceByValue = new Map();
|
|
4102
|
+
for (const [value, binding] of priorStepStateBindings) {
|
|
4103
|
+
if (binding.restricted)
|
|
4104
|
+
restrictedExcludeSourceByValue.set(value, binding.sourceName);
|
|
4105
|
+
else
|
|
4106
|
+
unconditionalExcludeValues.add(value);
|
|
4107
|
+
}
|
|
4013
4108
|
// A cruise-line-style multi-room/multi-guest search body commonly batches
|
|
4014
4109
|
// per-element criteria as a top-level JSON ARRAY rather than a single
|
|
4015
4110
|
// object. Walk each element in textual order (via the growing searchFrom
|
|
@@ -4022,13 +4117,13 @@ function applyStructuredValuePayloadSubstitutions(template, parsedBody, outStruc
|
|
|
4022
4117
|
for (const element of parsedBody) {
|
|
4023
4118
|
if (element === null || typeof element !== "object" || Array.isArray(element))
|
|
4024
4119
|
continue;
|
|
4025
|
-
const { result: nextResult, nextSearchFrom } = applyStructuredValuePayloadSubstitutionsForObjectBody(result, element, outStructuredKeys,
|
|
4120
|
+
const { result: nextResult, nextSearchFrom } = applyStructuredValuePayloadSubstitutionsForObjectBody(result, element, outStructuredKeys, unconditionalExcludeValues, restrictedExcludeSourceByValue, cursor);
|
|
4026
4121
|
result = nextResult;
|
|
4027
4122
|
cursor = nextSearchFrom;
|
|
4028
4123
|
}
|
|
4029
4124
|
return result;
|
|
4030
4125
|
}
|
|
4031
|
-
const { result } = applyStructuredValuePayloadSubstitutionsForObjectBody(template, parsedBody, outStructuredKeys,
|
|
4126
|
+
const { result } = applyStructuredValuePayloadSubstitutionsForObjectBody(template, parsedBody, outStructuredKeys, unconditionalExcludeValues, restrictedExcludeSourceByValue, 0);
|
|
4032
4127
|
return result;
|
|
4033
4128
|
}
|
|
4034
4129
|
/** Maximum length to guard against indexing massive blobs (HTML fragments,
|
|
@@ -5742,13 +5837,14 @@ function applyVolatileFieldSubstitutions(template, parsedBody, shieldedUuids, bo
|
|
|
5742
5837
|
return result;
|
|
5743
5838
|
}
|
|
5744
5839
|
/**
|
|
5745
|
-
* Collects captured string leaves
|
|
5746
|
-
* still-literal — the values a reviewer must
|
|
5747
|
-
* traced to a payload field, a generator, or
|
|
5748
|
-
* key names (deduped, in first-seen order)
|
|
5749
|
-
* `// TODO: unbound captured literal`
|
|
5750
|
-
* file still compiles. Short
|
|
5751
|
-
* skipped — they are the
|
|
5840
|
+
* Collects captured string leaves AND whole array/object values that survived
|
|
5841
|
+
* every binding/generation pass as still-literal — the values a reviewer must
|
|
5842
|
+
* look at because they couldn't be traced to a payload field, a generator, or
|
|
5843
|
+
* a schema anchor. Returns the JSON key names (deduped, in first-seen order)
|
|
5844
|
+
* so the emitter can prepend a single `// TODO: unbound captured literal`
|
|
5845
|
+
* marker; it never mutates the body, so the file still compiles. Short string
|
|
5846
|
+
* values (< {@link MIN_STATE_VALUE_LENGTH}) are skipped — they are the
|
|
5847
|
+
* legitimately-constant enum-like fields.
|
|
5752
5848
|
*/
|
|
5753
5849
|
function collectUnboundLiterals(finalTemplate, parsedBody, shieldedUuids) {
|
|
5754
5850
|
const unbound = [];
|
|
@@ -5767,6 +5863,20 @@ function collectUnboundLiterals(finalTemplate, parsedBody, shieldedUuids) {
|
|
|
5767
5863
|
unbound.push(key);
|
|
5768
5864
|
}
|
|
5769
5865
|
}
|
|
5866
|
+
if (parsedBody !== null && typeof parsedBody === "object") {
|
|
5867
|
+
for (const [key, value] of Object.entries(parsedBody)) {
|
|
5868
|
+
if (value === null || typeof value !== "object")
|
|
5869
|
+
continue;
|
|
5870
|
+
if (seen.has(key))
|
|
5871
|
+
continue;
|
|
5872
|
+
// Whole array/object value never got threaded as a unit — ${JSON.stringify(...)}
|
|
5873
|
+
// never replaced it, so it still sits verbatim in the emitted template.
|
|
5874
|
+
if (finalTemplate.includes(JSON.stringify(value))) {
|
|
5875
|
+
seen.add(key);
|
|
5876
|
+
unbound.push(key);
|
|
5877
|
+
}
|
|
5878
|
+
}
|
|
5879
|
+
}
|
|
5770
5880
|
return unbound;
|
|
5771
5881
|
}
|
|
5772
5882
|
/** Builds the multi-step `executeHttp` body as a single template-literal string.
|
|
@@ -6280,10 +6390,7 @@ flowSteps = [], vocabulary = undefined) {
|
|
|
6280
6390
|
// dependent-drill-down join field (e.g. a bulk `{"tokens":[...]}` lookup)
|
|
6281
6391
|
// must stay reachable for state threading, not get frozen as caller data.
|
|
6282
6392
|
const rawBodyWithStructuredSubs = parsedBody !== null
|
|
6283
|
-
? applyStructuredValuePayloadSubstitutions(rawBodyWithFacetSplices, parsedBody, outStructuredKeys, new Set(
|
|
6284
|
-
...deriveStateVarByValue(prior, cap).keys(),
|
|
6285
|
-
...(joinFieldValuesByStep.get(i) ?? []),
|
|
6286
|
-
]))
|
|
6393
|
+
? applyStructuredValuePayloadSubstitutions(rawBodyWithFacetSplices, parsedBody, outStructuredKeys, deriveStateVarByValue(prior, cap), joinFieldValuesByStep.get(i) ?? new Set())
|
|
6287
6394
|
: rawBodyWithFormSubs;
|
|
6288
6395
|
// Whole-value caller coordinates bind here — after structured subs, BEFORE
|
|
6289
6396
|
// state threading — so a composite coordinate (a jobLocation or jobSeqNo
|
|
@@ -11491,10 +11598,22 @@ function computeFlowPayloadFieldNames(flowSteps, vocabulary, env) {
|
|
|
11491
11598
|
for (const step of flowSteps) {
|
|
11492
11599
|
const isObj = typeof step !== "string";
|
|
11493
11600
|
const instruction = isObj ? step.step : step;
|
|
11494
|
-
// navigateTo
|
|
11495
|
-
//
|
|
11496
|
-
|
|
11601
|
+
// A navigateTo step's own instruction text bypasses field resolution —
|
|
11602
|
+
// it's a captured page.goto, not a prose instruction, so there is no
|
|
11603
|
+
// label/quoted-constant to correlate (see emitBrowserFlowTs's stepLiterals
|
|
11604
|
+
// pass). An explicit `payloadField` annotation is a different thing: it is
|
|
11605
|
+
// the author directly naming the facet the navigation target encodes, and
|
|
11606
|
+
// resolveStepPayloadField's own `explicit` short-circuit would honor it
|
|
11607
|
+
// unconditionally — so it must still be registered here, or every
|
|
11608
|
+
// downstream consumer of this accumulator (REST facet-splice, schema
|
|
11609
|
+
// declaration) silently drops the facet the author explicitly declared.
|
|
11610
|
+
if (isObj && step.navigateTo !== undefined) {
|
|
11611
|
+
if (step.payloadField) {
|
|
11612
|
+
payloadFieldNames.add(step.payloadField);
|
|
11613
|
+
registerFieldOptionality(step.payloadField, step.optional === true);
|
|
11614
|
+
}
|
|
11497
11615
|
continue;
|
|
11616
|
+
}
|
|
11498
11617
|
if (isObj && step.emailStep === true)
|
|
11499
11618
|
continue;
|
|
11500
11619
|
if (instruction.includes(RECON_PASSWORD_TOKEN))
|