@apilift/cli 1.5.5 → 1.5.6

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/apilift.js CHANGED
@@ -30696,6 +30696,10 @@ var init_drafts = __esm({
30696
30696
  const row = this.state.db.prepare("SELECT state,response_path,response_digest FROM discovery_request_attempt WHERE draft_id=? AND fingerprint=?").get(draftId, fingerprint2);
30697
30697
  return row ? { state: row.state, responsePath: row.response_path, responseDigest: row.response_digest } : null;
30698
30698
  }
30699
+ hasDispatchedRequestAttempt(draftId) {
30700
+ this.requestAttempts();
30701
+ return Boolean(this.state.db.prepare("SELECT 1 FROM discovery_request_attempt WHERE draft_id=? AND state!='client-rejected' LIMIT 1").get(draftId));
30702
+ }
30699
30703
  saveRequestAttempt(draftId, fingerprint2, state, responsePath, responseDigest) {
30700
30704
  this.requestAttempts();
30701
30705
  this.state.db.prepare("INSERT INTO discovery_request_attempt (draft_id,fingerprint,state,response_path,response_digest) VALUES (?,?,?,?,?) ON CONFLICT(draft_id,fingerprint) DO UPDATE SET state=excluded.state,response_path=CASE WHEN excluded.state='possibly-dispatched' AND excluded.response_path IS NULL THEN NULL ELSE COALESCE(excluded.response_path,discovery_request_attempt.response_path) END,response_digest=CASE WHEN excluded.state='possibly-dispatched' AND excluded.response_path IS NULL THEN NULL ELSE COALESCE(excluded.response_digest,discovery_request_attempt.response_digest) END").run(draftId, fingerprint2, state, responsePath ?? null, responseDigest ?? null);
@@ -34699,6 +34703,15 @@ Abandon removal: ${command4(invocation, "discover discard")}
34699
34703
  release();
34700
34704
  }
34701
34705
  }
34706
+ function primaryInputSchema(schema) {
34707
+ if (typeof schema === "boolean" || !schema.properties) return schema;
34708
+ return { ...schema, properties: Object.fromEntries(Object.entries(schema.properties).map(([name, property]) => {
34709
+ if (typeof property !== "object" || property === null) return [name, property];
34710
+ const input2 = { ...property };
34711
+ delete input2["x-source"];
34712
+ return [name, input2];
34713
+ })) };
34714
+ }
34702
34715
  async function verificationIdentity(api, checked) {
34703
34716
  const kind = backendKind(checked.draft.kind), definition2 = checked.definition;
34704
34717
  const executable = await executableHash(kind, checked.draft.name, definition2);
@@ -34717,8 +34730,9 @@ async function verificationIdentity(api, checked) {
34717
34730
  }
34718
34731
  const closure = await closureHash(executable, deps);
34719
34732
  const fingerprint3 = await hashJson({ executable, closure, inputs: checked.envelope?.verification?.inputs ?? {} });
34720
- const primary = await hashJson({ appId: checked.draft.appId, name: checked.draft.name, effect: "effect" in action ? action.effect : null, request: "request" in action ? action.request : null, inputs: "inputs" in action ? action.inputs ? semanticSchema(action.inputs) : null : null, generated: "generated" in action ? action.generated ?? null : null, samples: checked.envelope?.verification?.inputs ?? {}, deps });
34721
- return { executable, closure, fingerprint: fingerprint3, primary };
34733
+ const primaryIdentity = { appId: checked.draft.appId, name: checked.draft.name, effect: "effect" in action ? action.effect : null, request: "request" in action ? action.request : null, inputs: "inputs" in action ? action.inputs ? semanticSchema(action.inputs) : null : null, generated: "generated" in action ? action.generated ?? null : null, samples: checked.envelope?.verification?.inputs ?? {}, deps };
34734
+ const primary = await hashJson({ ...primaryIdentity, inputs: primaryIdentity.inputs === null ? null : primaryInputSchema(primaryIdentity.inputs) });
34735
+ return { executable, closure, fingerprint: fingerprint3, primary, legacyPrimary: await hashJson(primaryIdentity) };
34722
34736
  }
34723
34737
  const fingerprint2 = await hashJson({ executable, inputs: checked.envelope?.verification?.inputs ?? {} });
34724
34738
  return { executable, fingerprint: fingerprint2 };
@@ -34912,15 +34926,16 @@ async function verifyDraft(invocation, api, state, store, session, fetcher, root
34912
34926
  const write = Boolean(action && "effect" in action && action.effect !== "read");
34913
34927
  const proofOnly = invocation.flags.includes("proof-only");
34914
34928
  if (proofOnly && !write) return fail5("--proof-only requires a mutation draft with a saved successful primary response. No app request was sent.\n");
34915
- const primaryFingerprint = await hashJson({ primary: "primary" in identity2 ? identity2.primary : identity2.fingerprint, browserTab: transport.mode === "browser" ? envelope.capture?.tab || state.browserTab(session.id, draft.appId) || null : null, credential: state.useSession(session.id, draft.appId)?.credentials[action && "transport" in action ? action.transport : draft.name] ?? null });
34916
- const previous = store.requestAttempt(draft.id, primaryFingerprint);
34929
+ const primaryContext = { browserTab: transport.mode === "browser" ? envelope.capture?.tab || state.browserTab(session.id, draft.appId) || null : null, credential: state.useSession(session.id, draft.appId)?.credentials[action && "transport" in action ? action.transport : draft.name] ?? null };
34930
+ const primaryFingerprint = await hashJson({ primary: "primary" in identity2 ? identity2.primary : identity2.fingerprint, ...primaryContext });
34931
+ const previous = store.requestAttempt(draft.id, primaryFingerprint) ?? ("legacyPrimary" in identity2 ? store.requestAttempt(draft.id, await hashJson({ primary: identity2.legacyPrimary, ...primaryContext })) : null);
34917
34932
  const legacy = store.attempt(draft.id);
34918
34933
  const proofCommand = command4(invocation, "discover run --proof-only");
34919
34934
  const evidence = store.evidence(draft.id);
34920
34935
  if (write && !proofOnly && evidence?.fingerprint === identity2.fingerprint && (previous?.state === "success" || legacy?.fingerprint === identity2.fingerprint && legacy.state === "success")) return ok4(`This configured mutation was already tested successfully. Saved output: ${evidence.resultPath}
34921
34936
  Review it and publish; no app request was sent.
34922
34937
  `);
34923
- if (write && !proofOnly && (previous && previous.state !== "client-rejected" || !previous && legacy && legacy.state !== "client-rejected")) return fail5(`This mutation was already sent or may have reached the app. Do not repeat it. ${previous?.responsePath ? `Inspect the saved response: ${previous.responsePath}. After repairing response/proof, check without repeating the write: ${proofCommand}` : "No reusable response is available; inspect the app through an authorized read before choosing another test."}
34938
+ if (write && !proofOnly && (store.hasDispatchedRequestAttempt(draft.id) || legacy && legacy.state !== "client-rejected")) return fail5(`This mutation was already sent or may have reached the app. Do not repeat it. ${previous?.responsePath ? `Inspect the saved response: ${previous.responsePath}. After repairing response/proof, check without repeating the write: ${proofCommand}` : "No saved response matches the current contract. Restore the original test definition for proof-only recovery, or inspect the app through an authorized read."}
34924
34939
  `, 12);
34925
34940
  let primaryEvidence;
34926
34941
  if (proofOnly) {
@@ -34962,7 +34977,7 @@ ${responsePath ? `Saved primary response and test inputs: ${responsePath}
34962
34977
  ` : ""}`, executed.exitCode, executed.dispatched);
34963
34978
  }
34964
34979
  withBaselines(root, (baselines) => baselines.finishPreparation(session.id, draft.appSlug, draft.appId, transport.mode));
34965
- if (write) store.saveRequestAttempt(draft.id, primaryFingerprint, "success");
34980
+ if (write) store.saveRequestAttempt(draft.id, primaryFingerprint, "success", proofOnly ? previous?.responsePath ?? void 0 : void 0, proofOnly ? previous?.responseDigest ?? void 0 : void 0);
34966
34981
  const resultPath = store.artifactPath(draft.id, "verification.json");
34967
34982
  writeFileSync8(resultPath, `${JSON.stringify(executed.output, null, 2)}
34968
34983
  `, { flag: "wx", mode: 384 });
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "apilift",
3
- "version": "1.5.5",
3
+ "version": "1.5.6",
4
4
  "description": "Use logged-in apps and discover missing actions with Apilift.",
5
5
  "author": {
6
6
  "name": "Apilift"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "apilift",
3
- "version": "1.5.5",
3
+ "version": "1.5.6",
4
4
  "description": "Use logged-in apps and discover missing actions with Apilift.",
5
5
  "author": {
6
6
  "name": "Apilift"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@apilift/cli",
3
- "version": "1.5.5",
3
+ "version": "1.5.6",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "bin": {
@@ -36,7 +36,7 @@
36
36
  "engines": {
37
37
  "node": ">=24.16.0 <27"
38
38
  },
39
- "gitHead": "37eceded937e76934ee957dae4e8ea4e72935825",
39
+ "gitHead": "96ed48a06d2c8cb1e147eada2a56b2e5f4f8cb92",
40
40
  "repository": {
41
41
  "type": "git",
42
42
  "url": "git+https://github.com/egor-sergeev/apilift.git",