@enricai/barnacle 1.3.0 → 1.4.1
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 +35 -1
- package/dist/api/errors.d.ts.map +1 -1
- package/dist/api/errors.js.map +1 -1
- package/dist/api/helpers/envelope.js.map +1 -1
- package/dist/api/helpers/reply.js.map +1 -1
- package/dist/api/plugins/auth.d.ts.map +1 -1
- package/dist/api/plugins/auth.js.map +1 -1
- package/dist/api/plugins/error-handler.d.ts.map +1 -1
- package/dist/api/plugins/error-handler.js.map +1 -1
- package/dist/api/plugins/request-context.d.ts.map +1 -1
- package/dist/api/plugins/request-context.js.map +1 -1
- package/dist/api/routes/health.js.map +1 -1
- package/dist/api/routes/plugins-introspection.js.map +1 -1
- package/dist/api/schemas/common.d.ts +12 -12
- package/dist/api/schemas/common.d.ts.map +1 -1
- package/dist/api/schemas/common.js.map +1 -1
- package/dist/cache/keyed-ttl-cache.js.map +1 -1
- package/dist/cache/response-cache.js.map +1 -1
- package/dist/config.js.map +1 -1
- package/dist/lib/applicant-payload.d.ts +3 -3
- package/dist/lib/bedrock.js.map +1 -1
- package/dist/lib/case-insensitive-headers.js.map +1 -1
- package/dist/lib/chromium-client-hints.js.map +1 -1
- package/dist/lib/cookie-value.js.map +1 -1
- package/dist/lib/datadog.js.map +1 -1
- package/dist/lib/dd-metrics.js.map +1 -1
- package/dist/lib/dispatch-metrics.d.ts.map +1 -1
- package/dist/lib/dispatch-metrics.js.map +1 -1
- package/dist/lib/env.js.map +1 -1
- package/dist/lib/errors.js.map +1 -1
- package/dist/lib/http.js.map +1 -1
- package/dist/lib/llm/anthropic-client.js.map +1 -1
- package/dist/lib/llm/judge.js.map +1 -1
- package/dist/lib/llm/judges/error-messages.js.map +1 -1
- package/dist/lib/llm/judges/invalid-fields.js.map +1 -1
- package/dist/lib/llm/judges/modal-priority.js.map +1 -1
- package/dist/lib/llm/judges/select-option.js.map +1 -1
- package/dist/lib/llm/judges/verify-submit.js.map +1 -1
- package/dist/lib/llm/schemas.d.ts +7 -7
- package/dist/lib/llm/schemas.js.map +1 -1
- package/dist/lib/logging.js.map +1 -1
- package/dist/lib/multipart.js.map +1 -1
- package/dist/lib/option-matcher.js.map +1 -1
- package/dist/lib/phone.js.map +1 -1
- package/dist/lib/random.js.map +1 -1
- package/dist/lib/statsd.js.map +1 -1
- package/dist/lib/telemetry/call-capture.d.ts +6 -6
- package/dist/lib/telemetry/call-capture.js.map +1 -1
- package/dist/lib/telemetry/run-state.js.map +1 -1
- package/dist/lib/telemetry/s3-sink.js.map +1 -1
- package/dist/lib/telemetry/submission-capture.js.map +1 -1
- package/dist/lib/telemetry/telemetry-paths.js.map +1 -1
- package/dist/lib/tracking-click.d.ts.map +1 -1
- package/dist/lib/tracking-click.js +5 -1
- package/dist/lib/tracking-click.js.map +1 -1
- package/dist/lib/url-capture.js.map +1 -1
- package/dist/lib/us-states.js.map +1 -1
- package/dist/lib/zod-multipart.js.map +1 -1
- package/dist/plugins/config-plugin.d.ts +1 -0
- package/dist/plugins/config-plugin.d.ts.map +1 -1
- package/dist/plugins/config-plugin.js +4 -0
- package/dist/plugins/config-plugin.js.map +1 -1
- package/dist/plugins/discover.d.ts.map +1 -1
- package/dist/plugins/discover.js +1 -0
- package/dist/plugins/discover.js.map +1 -1
- package/dist/plugins/json-schema-to-zod.d.ts.map +1 -1
- package/dist/plugins/json-schema-to-zod.js.map +1 -1
- package/dist/plugins/loader.d.ts.map +1 -1
- package/dist/plugins/loader.js +12 -2
- package/dist/plugins/loader.js.map +1 -1
- package/dist/recon/load-vocabulary.d.ts +24 -0
- package/dist/recon/load-vocabulary.d.ts.map +1 -0
- package/dist/recon/load-vocabulary.js +71 -0
- package/dist/recon/load-vocabulary.js.map +1 -0
- package/dist/recon/vocabulary.d.ts +62 -0
- package/dist/recon/vocabulary.d.ts.map +1 -0
- package/dist/recon/vocabulary.js +34 -0
- package/dist/recon/vocabulary.js.map +1 -0
- package/dist/scraper/behavioral-signals.js.map +1 -1
- package/dist/scraper/errors.d.ts.map +1 -1
- package/dist/scraper/errors.js.map +1 -1
- package/dist/scraper/fixtures.js.map +1 -1
- package/dist/scraper/flow-runner.js.map +1 -1
- package/dist/scraper/graphql-client.js.map +1 -1
- package/dist/scraper/http-client.js.map +1 -1
- package/dist/scraper/http-status-classifier.js.map +1 -1
- package/dist/scraper/metrics.js.map +1 -1
- package/dist/scraper/navigate.js.map +1 -1
- package/dist/scraper/oracle-sentinels.js.map +1 -1
- package/dist/scraper/parse-json-response.js.map +1 -1
- package/dist/scraper/pool.d.ts +2 -3
- package/dist/scraper/pool.d.ts.map +1 -1
- package/dist/scraper/pool.js.map +1 -1
- package/dist/scraper/rate-limited-json-client.js.map +1 -1
- package/dist/scraper/raw-fetch.js.map +1 -1
- package/dist/scraper/require-response-field.js.map +1 -1
- package/dist/scraper/retry.js.map +1 -1
- package/dist/scraper/session-browserbase.d.ts +6 -10
- package/dist/scraper/session-browserbase.d.ts.map +1 -1
- package/dist/scraper/session-browserbase.js +7 -6
- package/dist/scraper/session-browserbase.js.map +1 -1
- package/dist/scraper/session-shared.d.ts +16 -0
- package/dist/scraper/session-shared.d.ts.map +1 -1
- package/dist/scraper/session-shared.js.map +1 -1
- package/dist/scraper/session-steel.js.map +1 -1
- package/dist/scraper/session-warmup.js.map +1 -1
- package/dist/scraper/session.d.ts +6 -6
- package/dist/scraper/session.d.ts.map +1 -1
- package/dist/scraper/session.js +7 -1
- package/dist/scraper/session.js.map +1 -1
- package/dist/scraper/stagehand-guard.d.ts.map +1 -1
- package/dist/scraper/stagehand-guard.js.map +1 -1
- package/dist/scraper/throttle.js.map +1 -1
- package/dist/scripts/judge-llm-batch.js.map +1 -1
- package/dist/scripts/llm-heal.js.map +1 -1
- package/dist/scripts/migrate-telemetry-dir-names.js.map +1 -1
- package/dist/scripts/recon-browser.js.map +1 -1
- package/dist/scripts/recon-generate.d.ts +26 -1
- package/dist/scripts/recon-generate.d.ts.map +1 -1
- package/dist/scripts/recon-generate.js +159 -56
- package/dist/scripts/recon-generate.js.map +1 -1
- package/dist/scripts/recon-heal.js.map +1 -1
- package/dist/scripts/recon-shared.js.map +1 -1
- package/dist/server.js.map +1 -1
- package/dist/site-plugin.d.ts +7 -0
- package/dist/site-plugin.d.ts.map +1 -1
- package/dist/testing/batch-email-confirmation.js.map +1 -1
- package/dist/testing/batch-report.js.map +1 -1
- package/dist/testing/integration-runner.js.map +1 -1
- package/dist/testing/mock-fetch-response.js.map +1 -1
- package/dist/testing/persona-fixture.d.ts.map +1 -1
- package/dist/testing/resume-fixture.js.map +1 -1
- package/dist/testmail/client.js.map +1 -1
- package/dist/testmail/errors.d.ts.map +1 -1
- package/dist/testmail/errors.js.map +1 -1
- package/dist/types/dispatch-metrics.d.ts +2 -2
- package/package.json +30 -35
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
20
20
|
exports.resolveStepPayloadField = resolveStepPayloadField;
|
|
21
21
|
exports.inferZodSchemaFromSamples = inferZodSchemaFromSamples;
|
|
22
|
+
exports.selectPayloadAction = selectPayloadAction;
|
|
22
23
|
exports.loadQuestionPromptKeywords = loadQuestionPromptKeywords;
|
|
23
24
|
exports.emitMultiStepExecuteHttp = emitMultiStepExecuteHttp;
|
|
24
25
|
exports.emitContractTs = emitContractTs;
|
|
@@ -29,6 +30,8 @@ const node_path_1 = require("node:path");
|
|
|
29
30
|
const errors_1 = require("../lib/errors");
|
|
30
31
|
const logging_1 = require("../lib/logging");
|
|
31
32
|
const plugin_manifest_envelope_1 = require("../plugins/plugin-manifest-envelope");
|
|
33
|
+
const load_vocabulary_1 = require("../recon/load-vocabulary");
|
|
34
|
+
const vocabulary_1 = require("../recon/vocabulary");
|
|
32
35
|
const recon_shared_1 = require("../scripts/recon-shared");
|
|
33
36
|
const logger = (0, logging_1.getScriptLogger)("recon-generate");
|
|
34
37
|
// ── helpers ──────────────────────────────────────────────────────────────────
|
|
@@ -39,46 +42,52 @@ function toPascalCase(siteId) {
|
|
|
39
42
|
.join("");
|
|
40
43
|
}
|
|
41
44
|
/**
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
/employment history/i,
|
|
51
|
-
/\bcompany (name|phone)\b/i,
|
|
52
|
-
/\bemployer\b/i,
|
|
53
|
-
/signature/i,
|
|
54
|
-
/\bfull name\b/i,
|
|
55
|
-
/today'?s date/i,
|
|
56
|
-
/school|institution|degree|major|education/i,
|
|
57
|
-
// Screening-question shapes: "For '<question>' select '<answer>'" and any
|
|
58
|
-
// step framed around a 'question'. Here the first quoted string is the
|
|
59
|
-
// question label, not a candidate value — a label word inside the question
|
|
60
|
-
// (e.g. "...licensed in this state?") must NOT trigger a splice that would
|
|
61
|
-
// overwrite the question quote. Candidate-fill steps say "fill in the X
|
|
62
|
-
// field with '...'" / "Select '...' from the X dropdown" instead.
|
|
63
|
-
/^\s*for\s+'/i,
|
|
64
|
-
/\bquestion\b/i,
|
|
65
|
-
];
|
|
66
|
-
/**
|
|
67
|
-
* Ordered candidate-field label table. Each entry maps an English-label regex
|
|
68
|
-
* to the PascalCase payload field the generator should splice. Order matters:
|
|
69
|
-
* the first matching row wins, so more-specific labels precede broader ones.
|
|
45
|
+
* The recruiting vocabulary the engine used to hardcode, kept only so consumers
|
|
46
|
+
* who have not yet passed `--vocabulary` keep working through the 1.x line.
|
|
47
|
+
*
|
|
48
|
+
* @deprecated Pass `--vocabulary <specifier>` instead. The engine cannot know
|
|
49
|
+
* what any site's forms mean, and guessing mis-fires on every non-recruiting
|
|
50
|
+
* domain (a cruise site's "Select the departure port from the Country dropdown"
|
|
51
|
+
* became `${payload.Country}`). Slated for deletion in 2.0.0, after which an
|
|
52
|
+
* absent vocabulary on a spliceable flow is a hard error. See `src/recon/vocabulary.ts`.
|
|
70
53
|
*/
|
|
71
|
-
const
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
[
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
54
|
+
const DEPRECATED_BUILTIN_ATS_VOCABULARY = {
|
|
55
|
+
// Naming the subject is what separates "…select the test candidate's state"
|
|
56
|
+
// (fill with the caller's data) from "…the departure port from the Country
|
|
57
|
+
// dropdown" (a search facet that merely says Country).
|
|
58
|
+
subject: /\b(the\s+)?(test\s+)?(candidate|applicant)'?s\b/i,
|
|
59
|
+
exclusions: [
|
|
60
|
+
/reference\s*#?\s*\d/i,
|
|
61
|
+
/employment history/i,
|
|
62
|
+
/\bcompany (name|phone)\b/i,
|
|
63
|
+
/\bemployer\b/i,
|
|
64
|
+
/signature/i,
|
|
65
|
+
/\bfull name\b/i,
|
|
66
|
+
/today'?s date/i,
|
|
67
|
+
/school|institution|degree|major|education/i,
|
|
68
|
+
// Screening-question shapes: "For '<question>' select '<answer>'" and any
|
|
69
|
+
// step framed around a 'question'. Here the first quoted string is the
|
|
70
|
+
// question label, not a candidate value — a label word inside the question
|
|
71
|
+
// (e.g. "...licensed in this state?") must NOT trigger a splice that would
|
|
72
|
+
// overwrite the question quote. Candidate-fill steps say "fill in the X
|
|
73
|
+
// field with '...'" / "Select '...' from the X dropdown" instead.
|
|
74
|
+
/^\s*for\s+'/i,
|
|
75
|
+
/\bquestion\b/i,
|
|
76
|
+
// A secondary phone must not fill the primary MobilePhone field.
|
|
77
|
+
/\bsecondary\b[^.]*\bphone\b/i,
|
|
78
|
+
],
|
|
79
|
+
table: [
|
|
80
|
+
[/\bfirst name\b/i, "FirstName"],
|
|
81
|
+
[/\blast name\b/i, "LastName"],
|
|
82
|
+
[/\b(e-?mail|email address)\b/i, "Email"],
|
|
83
|
+
[/\b(mobile phone|primary phone|phone number|mobile)\b/i, "MobilePhone"],
|
|
84
|
+
[/\b(street address|address line 1)\b/i, "AddressLine1"],
|
|
85
|
+
[/\bcity\b/i, "City"],
|
|
86
|
+
[/\b(state|province|state\/region)\b/i, "State"],
|
|
87
|
+
[/\b(zip|postal)\b/i, "PostalCode"],
|
|
88
|
+
[/\bcountry\b/i, "Country"],
|
|
89
|
+
],
|
|
90
|
+
};
|
|
82
91
|
/**
|
|
83
92
|
* Decide whether a flow step should splice a runtime `payload.<field>` value in
|
|
84
93
|
* place of the frozen recon constant baked into its instruction. Exists so
|
|
@@ -91,27 +100,29 @@ const PAYLOAD_FIELD_TABLE = [
|
|
|
91
100
|
* @param instruction the flow step's plain-English instruction
|
|
92
101
|
* @param explicit an optional flow-authored `payloadField` override (wins outright)
|
|
93
102
|
* @param forceNone when true, force a literal step (the `payloadFieldNone` opt-out)
|
|
103
|
+
* @param vocabulary the consumer's domain vocabulary; defaults to the deprecated
|
|
104
|
+
* built-in recruiting table so 1.x consumers who pass nothing keep working
|
|
94
105
|
* @returns the PascalCase payload field name to splice, or null to keep literal
|
|
95
106
|
*/
|
|
96
|
-
function resolveStepPayloadField(instruction, explicit, forceNone) {
|
|
107
|
+
function resolveStepPayloadField(instruction, explicit, forceNone, vocabulary = DEPRECATED_BUILTIN_ATS_VOCABULARY) {
|
|
97
108
|
if (forceNone)
|
|
98
109
|
return null;
|
|
99
110
|
if (explicit)
|
|
100
111
|
return explicit;
|
|
101
|
-
//
|
|
102
|
-
//
|
|
103
|
-
const
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
112
|
+
// A quoted literal or ${RECON_EMAIL} IS the recon constant this step would
|
|
113
|
+
// replace, so it is spliceable on its own.
|
|
114
|
+
const hasQuotedConstant = /'[^']*'/.test(instruction) || /\$\{RECON_EMAIL\}/.test(instruction);
|
|
115
|
+
// A dropdown step carries no constant to replace, so a label match alone can't
|
|
116
|
+
// tell "select the test candidate's state" (the caller's data) from "select the
|
|
117
|
+
// departure port from the Country dropdown" (a facet that merely says Country).
|
|
118
|
+
// Requiring the subject is what keeps this from mis-firing off-domain.
|
|
119
|
+
const isDropdownStep = /\bdropdown\b/i.test(instruction) || /\bselect\b[^.]*\bfrom\b/i.test(instruction);
|
|
120
|
+
const hasSpliceable = hasQuotedConstant || (isDropdownStep && vocabulary.subject.test(instruction));
|
|
107
121
|
if (!hasSpliceable)
|
|
108
122
|
return null;
|
|
109
|
-
if (
|
|
110
|
-
return null;
|
|
111
|
-
// "Secondary Phone Number" must not fill the primary MobilePhone field.
|
|
112
|
-
if (/\bsecondary\b/i.test(instruction) && /\bphone\b/i.test(instruction))
|
|
123
|
+
if (vocabulary.exclusions.some((rx) => rx.test(instruction)))
|
|
113
124
|
return null;
|
|
114
|
-
for (const [rx, field] of
|
|
125
|
+
for (const [rx, field] of vocabulary.table) {
|
|
115
126
|
if (rx.test(instruction))
|
|
116
127
|
return field;
|
|
117
128
|
}
|
|
@@ -223,6 +234,54 @@ function endpointKey(url) {
|
|
|
223
234
|
return url;
|
|
224
235
|
}
|
|
225
236
|
}
|
|
237
|
+
/** Empty or absent response bodies carry no evidence of an endpoint's purpose. */
|
|
238
|
+
function isVoidResponse(body) {
|
|
239
|
+
if (body === null || body === undefined || body === "")
|
|
240
|
+
return true;
|
|
241
|
+
if (typeof body !== "object")
|
|
242
|
+
return false;
|
|
243
|
+
if (Array.isArray(body))
|
|
244
|
+
return body.length === 0;
|
|
245
|
+
return Object.keys(body).length === 0;
|
|
246
|
+
}
|
|
247
|
+
/**
|
|
248
|
+
* Picks the action whose request body should define the payload schema.
|
|
249
|
+
*
|
|
250
|
+
* Defaults to the first action, which is right for a transactional flow: the
|
|
251
|
+
* caller's data goes in with the opening POST and later steps only carry the
|
|
252
|
+
* transaction forward. It is wrong when the flow's real subject is a query the
|
|
253
|
+
* page issues repeatedly — a search or inventory endpoint is re-hit on every
|
|
254
|
+
* filter change, while whatever happened to fire first was incidental (a
|
|
255
|
+
* feature-toggle fetch, a config read). Re-issuing the same endpoint with a
|
|
256
|
+
* different body is the signature of the parameters a caller would want to
|
|
257
|
+
* control, so that wins. Anything less clear-cut falls through to first-action
|
|
258
|
+
* behavior rather than guessing.
|
|
259
|
+
*
|
|
260
|
+
* A transactional flow can re-issue an endpoint too — an applicant record built
|
|
261
|
+
* up across several writes — and lands on those writes for the same reason: the
|
|
262
|
+
* call that merely opened the flow carries none of the caller's fields.
|
|
263
|
+
*/
|
|
264
|
+
function selectPayloadAction(steps) {
|
|
265
|
+
const first = steps[0];
|
|
266
|
+
if (!first)
|
|
267
|
+
return null;
|
|
268
|
+
const bodiesByEndpoint = new Map();
|
|
269
|
+
for (const step of steps) {
|
|
270
|
+
const key = endpointKey(step.capture.url);
|
|
271
|
+
const bodies = bodiesByEndpoint.get(key) ?? new Set();
|
|
272
|
+
bodies.add(step.capture.requestPostData ?? "");
|
|
273
|
+
bodiesByEndpoint.set(key, bodies);
|
|
274
|
+
}
|
|
275
|
+
const requeried = steps.filter((step) => {
|
|
276
|
+
const bodies = bodiesByEndpoint.get(endpointKey(step.capture.url));
|
|
277
|
+
if (!bodies || bodies.size < 2)
|
|
278
|
+
return false;
|
|
279
|
+
// An endpoint re-hit with varying bodies but nothing to show for it is
|
|
280
|
+
// chatter — client-side error reporting, beacons — not the flow's subject.
|
|
281
|
+
return !isVoidResponse(step.capture.responseBody);
|
|
282
|
+
});
|
|
283
|
+
return requeried[0] ?? first;
|
|
284
|
+
}
|
|
226
285
|
function deriveBaseUrl(captures) {
|
|
227
286
|
for (const c of captures) {
|
|
228
287
|
try {
|
|
@@ -2113,12 +2172,12 @@ function buildManifestInstruction(instruction, field) {
|
|
|
2113
2172
|
* omitted; a site that needs it keeps the `.ts` path or wires `spec.httpModule`.
|
|
2114
2173
|
*/
|
|
2115
2174
|
function emitConfigManifest(opts) {
|
|
2116
|
-
const { siteId, displayName, baseUrl, flowSteps } = opts;
|
|
2175
|
+
const { siteId, displayName, baseUrl, flowSteps, vocabulary } = opts;
|
|
2117
2176
|
const payloadFieldNames = new Set();
|
|
2118
2177
|
const steps = flowSteps.map((step) => {
|
|
2119
2178
|
const isObj = typeof step !== "string";
|
|
2120
2179
|
const instruction = isObj ? step.step : step;
|
|
2121
|
-
const field = resolveStepPayloadField(instruction, isObj ? step.payloadField : undefined, isObj ? step.payloadFieldNone : undefined);
|
|
2180
|
+
const field = resolveStepPayloadField(instruction, isObj ? step.payloadField : undefined, isObj ? step.payloadFieldNone : undefined, vocabulary);
|
|
2122
2181
|
if (field !== null)
|
|
2123
2182
|
payloadFieldNames.add(field);
|
|
2124
2183
|
const rewritten = buildManifestInstruction(instruction, field);
|
|
@@ -2162,13 +2221,13 @@ function emitConfigManifest(opts) {
|
|
|
2162
2221
|
* payload schema (both are driven by this same set).
|
|
2163
2222
|
*/
|
|
2164
2223
|
function emitBrowserFlowTs(opts) {
|
|
2165
|
-
const { siteId, pascal, flowSteps, isSubmissionFlow, hasMultipartStep = false } = opts;
|
|
2224
|
+
const { siteId, pascal, flowSteps, isSubmissionFlow, hasMultipartStep = false, vocabulary, } = opts;
|
|
2166
2225
|
const payloadFieldNames = new Set();
|
|
2167
2226
|
const hasUploadStep = flowSteps.some((s) => typeof s !== "string" && s.upload === true);
|
|
2168
2227
|
const stepLiterals = flowSteps.map((step) => {
|
|
2169
2228
|
const isObj = typeof step !== "string";
|
|
2170
2229
|
const instruction = isObj ? step.step : step;
|
|
2171
|
-
const field = resolveStepPayloadField(instruction, isObj ? step.payloadField : undefined, isObj ? step.payloadFieldNone : undefined);
|
|
2230
|
+
const field = resolveStepPayloadField(instruction, isObj ? step.payloadField : undefined, isObj ? step.payloadFieldNone : undefined, vocabulary);
|
|
2172
2231
|
if (field !== null)
|
|
2173
2232
|
payloadFieldNames.add(field);
|
|
2174
2233
|
const instructionExpr = buildStepInstructionExpr(instruction, field);
|
|
@@ -2289,14 +2348,51 @@ export { ${camel}Plugin } from "@/sites/${siteId}/contract";
|
|
|
2289
2348
|
`;
|
|
2290
2349
|
}
|
|
2291
2350
|
// ── main ──────────────────────────────────────────────────────────────────────
|
|
2351
|
+
/**
|
|
2352
|
+
* Resolves the vocabulary for this run and reports which one is in play.
|
|
2353
|
+
*
|
|
2354
|
+
* The deprecation warning is the whole point of the 1.x window: removing a
|
|
2355
|
+
* built-in default that consumers silently depend on is only safe if the
|
|
2356
|
+
* transition is loud. It fires only when the built-in would actually change the
|
|
2357
|
+
* output — a flow with nothing to splice gets no nag it can't act on.
|
|
2358
|
+
*/
|
|
2359
|
+
async function resolveVocabulary(specifier, flowSteps) {
|
|
2360
|
+
if (specifier) {
|
|
2361
|
+
const vocabulary = await (0, load_vocabulary_1.loadReconVocabulary)(specifier, process.cwd());
|
|
2362
|
+
logger.info(`vocabulary: ${specifier === load_vocabulary_1.VOCABULARY_NONE ? "none (splicing disabled)" : `${vocabulary.table.length} row(s) from ${specifier}`}`);
|
|
2363
|
+
return vocabulary;
|
|
2364
|
+
}
|
|
2365
|
+
// Warn only when the built-in table itself changes the outcome. A step with an
|
|
2366
|
+
// explicit payloadField resolves the same under any vocabulary, so nagging
|
|
2367
|
+
// about it would send consumers to fix something that isn't broken.
|
|
2368
|
+
const builtinChangesOutcome = flowSteps.some((step) => {
|
|
2369
|
+
const isObj = typeof step !== "string";
|
|
2370
|
+
const instruction = isObj ? step.step : step;
|
|
2371
|
+
const explicit = isObj ? step.payloadField : undefined;
|
|
2372
|
+
const forceNone = isObj ? step.payloadFieldNone : undefined;
|
|
2373
|
+
const withBuiltin = resolveStepPayloadField(instruction, explicit, forceNone, DEPRECATED_BUILTIN_ATS_VOCABULARY);
|
|
2374
|
+
const withNothing = resolveStepPayloadField(instruction, explicit, forceNone, vocabulary_1.EMPTY_VOCABULARY);
|
|
2375
|
+
return withBuiltin !== withNothing;
|
|
2376
|
+
});
|
|
2377
|
+
if (builtinChangesOutcome) {
|
|
2378
|
+
logger.warn(`DeprecationWarning: no --vocabulary given, falling back to the built-in recruiting table. ` +
|
|
2379
|
+
`It is removed in 2.0.0, after which this is an error. Fix: create a vocabulary module ` +
|
|
2380
|
+
`exporting a ReconVocabulary (see @enricai/barnacle/recon/vocabulary) and pass ` +
|
|
2381
|
+
`--vocabulary ./src/recon/<name>.ts, or --vocabulary none if this site splices no caller data.`);
|
|
2382
|
+
}
|
|
2383
|
+
return DEPRECATED_BUILTIN_ATS_VOCABULARY;
|
|
2384
|
+
}
|
|
2292
2385
|
async function main() {
|
|
2293
2386
|
const args = process.argv.slice(2);
|
|
2294
2387
|
let siteId = "";
|
|
2295
2388
|
let force = false;
|
|
2296
2389
|
let emit = "ts";
|
|
2390
|
+
let vocabularySpecifier = "";
|
|
2297
2391
|
for (let i = 0; i < args.length; i++) {
|
|
2298
2392
|
if (args[i] === "--site-id" && args[i + 1])
|
|
2299
2393
|
siteId = args[++i];
|
|
2394
|
+
else if (args[i] === "--vocabulary" && args[i + 1])
|
|
2395
|
+
vocabularySpecifier = args[++i];
|
|
2300
2396
|
else if (args[i] === "--force")
|
|
2301
2397
|
force = true;
|
|
2302
2398
|
else if (args[i] === "--emit" && args[i + 1]) {
|
|
@@ -2363,6 +2459,10 @@ async function main() {
|
|
|
2363
2459
|
return [];
|
|
2364
2460
|
}
|
|
2365
2461
|
})();
|
|
2462
|
+
// Resolved once and threaded down, never captured into a module const: a
|
|
2463
|
+
// module-level const would freeze at import time, which is the bug that makes
|
|
2464
|
+
// RECON_QUESTION_KEYWORDS silently inert for anyone setting it after load.
|
|
2465
|
+
const vocabulary = await resolveVocabulary(vocabularySpecifier, flowSteps);
|
|
2366
2466
|
const pascal = toPascalCase(siteId);
|
|
2367
2467
|
const baseUrl = deriveBaseUrl(captures);
|
|
2368
2468
|
const baseHeaders = deriveRequestHeaders(captures, replays, baseUrl);
|
|
@@ -2426,7 +2526,8 @@ async function main() {
|
|
|
2426
2526
|
const inputBody = isSubmissionFlow
|
|
2427
2527
|
? (() => {
|
|
2428
2528
|
try {
|
|
2429
|
-
|
|
2529
|
+
const payloadAction = selectPayloadAction(actionSteps);
|
|
2530
|
+
return JSON.parse(payloadAction?.capture.requestPostData ?? "null");
|
|
2430
2531
|
}
|
|
2431
2532
|
catch {
|
|
2432
2533
|
return null;
|
|
@@ -2582,6 +2683,7 @@ async function main() {
|
|
|
2582
2683
|
displayName: pascal,
|
|
2583
2684
|
baseUrl,
|
|
2584
2685
|
flowSteps,
|
|
2686
|
+
vocabulary,
|
|
2585
2687
|
}));
|
|
2586
2688
|
logger.info(`wrote ${manifestPath}`);
|
|
2587
2689
|
logger.info(`done — review ${manifestPath}, fill in response/extract schemas, then load via BARNACLE_PLUGINS or BARNACLE_PLUGINS_CONFIG_DIR (no compile step)`);
|
|
@@ -2598,6 +2700,7 @@ async function main() {
|
|
|
2598
2700
|
flowSteps,
|
|
2599
2701
|
isSubmissionFlow,
|
|
2600
2702
|
hasMultipartStep,
|
|
2703
|
+
vocabulary,
|
|
2601
2704
|
});
|
|
2602
2705
|
(0, node_fs_1.writeFileSync)(`${outDir}/contract.ts`, emitContractTs({
|
|
2603
2706
|
siteId,
|