@enricai/barnacle 1.12.44 → 1.12.45

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.
@@ -7078,19 +7078,46 @@ function emitContractTs(opts) {
7078
7078
  //
7079
7079
  // The captured request body (inputBody) is the SITE's internal request
7080
7080
  // shape (a vendor's ddoKey/formData, a GraphQL worklet's variables, …) — not
7081
- // what the real caller sends. The plugin's buildBarnacleFormData posts
7082
- // the standard candidate payload (ApplicantContactSchema's identity/
7083
- // address/resume fields + Email + job-targeting + a JSON Answers block) to
7084
- // every plugin's /run, so that — not a structural inference over
7085
- // inputBody — is the public contract every submission-flow plugin must
7086
- // declare, unconditionally (see recon-generate-payload-schema-mismatch.md
7087
- // fix option (a)). inputBody remains available to the plugin author as the
7088
- // internal request shape the site's own call needs to be built from; it no
7089
- // longer drives the public schema. A missing inputBody means this is a
7090
- // non-submission (query-type) flow, which keeps its own contract untouched.
7091
- const basePayloadSchemaExpr = inputBody
7081
+ // necessarily the real caller's shape. ApplicantContactSchema (the job-
7082
+ // application template: identity/address/resume fields + Email + a JSON
7083
+ // Answers block) only belongs on flows that are actually job/benefits
7084
+ // applications — selecting it purely because SOME body was captured picks
7085
+ // it for unrelated submission flows too. usesApplicantContactSchema is the
7086
+ // real-evidence gate: it requires the captured inputBody to itself carry
7087
+ // one of ApplicantContactSchema's own field names (case-insensitively,
7088
+ // checked at any depth via walkAllPrimitiveLeaves) before the template
7089
+ // applies (see recon-generate-payload-schema-mismatch.md).
7090
+ const applicantContactFieldNames = new Set([
7091
+ "FirstName",
7092
+ "LastName",
7093
+ "Phone",
7094
+ "AddressLine",
7095
+ "City",
7096
+ "State",
7097
+ "PostalCode",
7098
+ "Country",
7099
+ "County",
7100
+ "Resume",
7101
+ "ResumeContentType",
7102
+ "ResumeFilename",
7103
+ "ResumeBase64",
7104
+ ]);
7105
+ const baseContractFieldNames = new Set(["Email", "ClickUrl", "Answers"]);
7106
+ const applicantContactEvidenceFieldNamesLower = new Set([...applicantContactFieldNames, ...baseContractFieldNames].map((name) => name.toLowerCase()));
7107
+ const usesApplicantContactSchema = inputBody != null &&
7108
+ [...walkAllPrimitiveLeaves(inputBody)].some(({ path }) => path.some((segment) => applicantContactEvidenceFieldNamesLower.has(segment.toLowerCase())));
7109
+ const isReservedByApplicantContactSchema = (name) => usesApplicantContactSchema && applicantContactFieldNames.has(name);
7110
+ // A missing inputBody, or one with no ApplicantContactSchema evidence,
7111
+ // means this is not a job-application submission flow. The former keeps
7112
+ // its own read-flow `{ query }` contract untouched; the latter (a body WAS
7113
+ // captured, it just isn't an application) gets a plain empty base instead
7114
+ // — extend() below still layers on every genuinely-discovered field from
7115
+ // the other sources unchanged.
7116
+ const basePayloadSchemaExpr = usesApplicantContactSchema
7092
7117
  ? `ApplicantContactSchema`
7093
- : `z.object({\n query: z.string().min(1),\n})`;
7118
+ : inputBody
7119
+ ? `z.object({})`
7120
+ : `z.object({\n query: z.string().min(1),\n})`;
7094
7121
  // Only the single-endpoint GraphQL read path (a real primary operation, no
7095
7122
  // multi-step flow) is a candidate for a paging signal — multiStepBody
7096
7123
  // already owns its own per-call semantics.
@@ -7153,34 +7180,12 @@ function emitContractTs(opts) {
7153
7180
  if (paginationSignal) {
7154
7181
  addExtendField("maxPages", " maxPages: z.number().int().positive().optional(),");
7155
7182
  }
7156
- // The base extend's own keys — submission flows only.
7157
- if (inputBody) {
7183
+ // The base extend's own keys — job-application submission flows only.
7184
+ if (usesApplicantContactSchema) {
7158
7185
  addExtendField("Email", " Email: z.email(),");
7159
7186
  addExtendField("ClickUrl", " ClickUrl: z.string().min(1),");
7160
7187
  addExtendField("Answers", " Answers: multipartJsonObject(z.record(z.string(), z.unknown())),");
7161
7188
  }
7162
- // ApplicantContactSchema's own merged identity/address/resume field names
7163
- // (see src/lib/application-identity.ts, application-address.ts,
7164
- // application-resume.ts, applicant-payload.ts) — reserved so no discovered/
7165
- // spliced source can redeclare (and silently shadow) a field the base
7166
- // ApplicantContactSchema already supplies. Only relevant for submission
7167
- // flows, where basePayloadSchemaExpr actually is ApplicantContactSchema.
7168
- const applicantContactFieldNames = new Set([
7169
- "FirstName",
7170
- "LastName",
7171
- "Phone",
7172
- "AddressLine",
7173
- "City",
7174
- "State",
7175
- "PostalCode",
7176
- "Country",
7177
- "County",
7178
- "Resume",
7179
- "ResumeContentType",
7180
- "ResumeFilename",
7181
- "ResumeBase64",
7182
- ]);
7183
- const isReservedByApplicantContactSchema = (name) => Boolean(inputBody) && applicantContactFieldNames.has(name);
7184
7189
  // A declared foldReturn.drillParamBindings names drill query params that
7185
7190
  // are caller-driven instead of frozen literals (see
7186
7191
  // recon-generate-foldreturn-cannot-bind-drill-query-param-to-caller-payload.md)
@@ -7201,9 +7206,10 @@ function emitContractTs(opts) {
7201
7206
  addExtendField(binding.payloadField, ` ${binding.payloadField}: ${zod},`);
7202
7207
  }
7203
7208
  // Multi-step flows that include a multipart upload need the binary asset
7204
- // on the payload. A query-type flow (no ApplicantContactSchema base) still
7205
- // needs these fields spelled out explicitly.
7206
- if (hasMultipartStep && !inputBody) {
7209
+ // on the payload. A non-applicant flow (no ApplicantContactSchema base,
7210
+ // whether or not a body was captured) still needs these fields spelled
7211
+ // out explicitly.
7212
+ if (hasMultipartStep && !usesApplicantContactSchema) {
7207
7213
  addExtendField("Resume", " Resume: z.instanceof(Buffer),");
7208
7214
  addExtendField("ResumeContentType", " ResumeContentType: z.string(),");
7209
7215
  addExtendField("ResumeFilename", " ResumeFilename: z.string(),");
@@ -7345,9 +7351,8 @@ function emitContractTs(opts) {
7345
7351
  // comment above) — a GraphQL mutation that happens to declare an
7346
7352
  // unpopulated variable with a matching name (e.g. `$email`) must not
7347
7353
  // downgrade that required base field.
7348
- const baseContractFieldNames = new Set(["Email", "ClickUrl", "Answers"]);
7349
7354
  for (const [fieldName, line] of extendFields) {
7350
- if (inputBody && baseContractFieldNames.has(fieldName))
7355
+ if (usesApplicantContactSchema && baseContractFieldNames.has(fieldName))
7351
7356
  continue;
7352
7357
  if (unpopulatedDeclaredVariables.some((name) => name.toLowerCase() === fieldName.toLowerCase())) {
7353
7358
  extendFields.set(fieldName, line.replace(/,\s*$/, ".optional(),"));
@@ -7356,23 +7361,25 @@ function emitContractTs(opts) {
7356
7361
  const mergedExtension = extendFields.size > 0 ? `.extend({\n${[...extendFields.values()].join("\n")}\n})` : "";
7357
7362
  const payloadSchemaExpr = `${basePayloadSchemaExpr}${mergedExtension}`;
7358
7363
  // basePayloadSchemaExpr's own Answers field always wraps in
7359
- // multipartJsonObject() for submission flows (inputBody set);
7360
- // multipartBoolean() is only imported when a boolean field was actually
7361
- // wrapped in it above (an additional-body-key or an inputBody field under
7362
- // multipartCoerce) — payloadNeedsMultipart alone doesn't imply that.
7364
+ // multipartJsonObject() for job-application submission flows
7365
+ // (usesApplicantContactSchema); multipartBoolean() is only imported when a
7366
+ // boolean field was actually wrapped in it above (an additional-body-key
7367
+ // or an inputBody field under multipartCoerce) — payloadNeedsMultipart
7368
+ // alone doesn't imply that.
7363
7369
  // Named imports from the same module are combined into one import statement.
7364
7370
  const zodMultipartNamedImports = [
7365
7371
  ...(usesMultipartBoolean ? ["multipartBoolean"] : []),
7366
- ...(inputBody || (payloadNeedsMultipart && sortedStructuredEntries.length > 0)
7372
+ ...(usesApplicantContactSchema || (payloadNeedsMultipart && sortedStructuredEntries.length > 0)
7367
7373
  ? ["multipartJsonObject"]
7368
7374
  : []),
7369
7375
  ];
7370
7376
  const multipartBoolImport = zodMultipartNamedImports.length > 0
7371
7377
  ? `import { ${zodMultipartNamedImports.join(", ")} } from "${ENGINE_PKG}/lib/zod-multipart";\n`
7372
7378
  : "";
7373
- // ApplicantContactSchema backs the default submission-flow payload schema
7374
- // (see basePayloadSchemaExpr above); only referenced when inputBody is set.
7375
- const applicantContactImport = inputBody
7379
+ // ApplicantContactSchema backs the job-application submission-flow payload
7380
+ // schema (see basePayloadSchemaExpr above); only referenced when
7381
+ // usesApplicantContactSchema is true.
7382
+ const applicantContactImport = usesApplicantContactSchema
7376
7383
  ? `import { ApplicantContactSchema } from "${ENGINE_PKG}/lib/applicant-payload";\n`
7377
7384
  : "";
7378
7385
  // Content-Type must be absent from multipart fetch calls so FormData can inject the boundary.
@@ -7773,17 +7780,17 @@ export const ${camel}Plugin: SitePlugin<${pascal}Payload, ${pascal}Response> = {
7773
7780
  bodySchema: ${pascal}PayloadSchema,
7774
7781
  responseSchema: ${pascal}ResponseSchema,
7775
7782
  defaultBaseUrl: ${JSON.stringify(baseUrl)},
7776
- ${payloadNeedsMultipart || inputBody
7783
+ ${payloadNeedsMultipart || usesApplicantContactSchema || hasMultipartStep
7777
7784
  ? `// multipart is required whenever the flow itself uploads a file
7778
- // (hasMultipartStep), OR this is a submission flow (inputBody set) since
7779
- // basePayloadSchemaExpr always requires a real Resume Buffer via
7780
- // ApplicantContactSchema regardless of whether the recorded browser flow
7781
- // contained an upload step, OR the payload has a non-scalar
7782
- // discoveredStructuredKeys field (payloadNeedsMultipart), since the
7783
- // multipart wire format is what makes that field's JSON-stringified
7784
- // encoding parseable.
7785
+ // (hasMultipartStep), OR this is a job-application submission flow
7786
+ // (usesApplicantContactSchema) since basePayloadSchemaExpr requires a
7787
+ // real Resume Buffer via ApplicantContactSchema regardless of whether
7788
+ // the recorded browser flow contained an upload step, OR the payload
7789
+ // has a non-scalar discoveredStructuredKeys field (payloadNeedsMultipart),
7790
+ // since the multipart wire format is what makes that field's
7791
+ // JSON-stringified encoding parseable.
7785
7792
  `
7786
- : ""}apiVersion: ${JSON.stringify(plugin_api_version_1.PLUGIN_API_VERSION)},${payloadNeedsMultipart || inputBody ? "\n multipart: true," : ""}
7793
+ : ""}apiVersion: ${JSON.stringify(plugin_api_version_1.PLUGIN_API_VERSION)},${payloadNeedsMultipart || usesApplicantContactSchema || hasMultipartStep ? "\n multipart: true," : ""}
7787
7794
  },
7788
7795
  ${executeHttpMethodBlock}
7789
7796
  /** Browser fallback: Stagehand + Steel — invoked only when hot path fails. */
@@ -7792,7 +7799,7 @@ ${executeHttpMethodBlock}
7792
7799
  session: BrowserSession,
7793
7800
  context: SitePluginContext
7794
7801
  ): Promise<SitePluginResult<${pascal}Response>> {
7795
- const raw = await run${pascal}BrowserFlow(session.stagehand, ${inputBody ? "payload.ClickUrl" : "context.baseUrl"}, payload, session.sessionProxy ?? null);
7802
+ const raw = await run${pascal}BrowserFlow(session.stagehand, ${usesApplicantContactSchema ? "payload.ClickUrl" : "context.baseUrl"}, payload, session.sessionProxy ?? null);
7796
7803
  return { data: raw as ${pascal}Response };
7797
7804
  },
7798
7805
  };