import type { FetchLike } from '@substrat-run/kernel'; /** * Scrive, in memory. * * ## What this is for * * A connector cannot be exercised end to end without a provider, and a provider * account is not always available. This implements the documented endpoints so * the seam — credential resolution, egress, health, retry, the document * lifecycle — can be tested today. * * ## What it proves, and what it does not * * It proves OUR shape works. It cannot prove our reading of Scrive's API is * right, because it *is* our reading of Scrive's API: same author, same * misunderstandings, in both halves. A green suite here means "ready to check * against a testbed account", never "verified". * * The specific things a mock like this will always get wrong until someone runs * the real thing: auth handshakes, exact response shapes, error bodies, rate * limits, and every asynchronous timing behaviour that matters. * * It stays useful afterwards: a real provider will not return 503 on demand, or * let you fast-forward two days to a signature. */ interface MockDocument { id: string; status: 'preparation' | 'pending' | 'closed' | 'canceled' | 'timedout' | 'rejected'; title: string; callbackUrl: string | null; /** * The uploaded file as the multipart envelope declared it: the filename from the * `content-disposition`, and the payload's own length — NOT the envelope's. * * Both matter since #711. The connector now sends either the vertical's rendered * document or its own attestation sheet, and "which one went out" is exactly the * question a test has to be able to ask; an envelope length shared by both, or a * hardcoded filename, cannot answer it. */ file: { name: string; bytes: number; } | null; parties: { id: string; name: string; signTime: string | null; auth: string; /** Whether `update` carried a `personal_number` field — value irrelevant (#687). */ hasPersonalNumber: boolean; /** The `email` field's value, or null when the party carried none. */ email: string | null; /** The sending account. Scrive rewrites this party to the account holder (#852). */ isAuthor: boolean; /** An author that does not sign is a `viewer`; every named party signs. */ isSignatory: boolean; }[]; } export interface ScriveMockOptions { /** Reject every call with this HTTP status — the failure path on demand. */ failWith?: number; /** * Validate `start`'s DELIVERY rule as the real testbed does: a party who must * be INVITED and carries no `email` field cannot be reached, and `start` * answers 409 `invalid_invitation_delivery_info` (probed live, #687). * * The author is exempt — it is the sending account, and Scrive never invites * it. That exemption is the whole shape of the bug: because this connector * sends no address on any party, a set with a real counterparty is refused * loudly, while a set whose only party is the author STARTS and delivers to * nobody. Production reached the second case without anyone choosing it. * * Default OFF: no party this connector builds carries an address, so enforcing * the rule by default would fail every dispatch test with a gap none of them is * about. `test/dispatch.test.ts` turns it on for the two tests that state the * gap — the refusal AND the control case that starts. When a party can carry a * contact (#687 item 1), this option should become the behaviour and disappear. */ strictDelivery?: boolean; /** * Fired when a signing event lands on a document that has an * `api_callback_url` — the provider-side POST the real Scrive makes on status * changes. The mock only reports WHERE (the capability URL the connector set) * and what document, never a trustworthy body, matching the real callback's * standing as a hint. Errors are swallowed, as a provider's delivery failures * would be — the poll floor is what covers a lost callback. */ onCallback?: (cb: { url: string; documentId: string; status: string; }) => void | Promise; } export declare class ScriveMock { readonly documents: Map; private seq; failWith: number | undefined; private readonly onCallback; private readonly strictDelivery; /** * Who this mock's credential belongs to — the identity Scrive stamps onto the * author party regardless of what the caller sent (#852). Matches the values * `/getprofile` answers, because at the provider they are the same account. */ readonly accountHolder: { name: string; email: string; }; constructor(options?: ScriveMockOptions); /** Simulate a party completing BankID. The provider-side event we cannot cause for real. */ sign(documentId: string, partyIndex: number, at: string): void; decline(documentId: string): void; /** The provider-side POST on a signing event — fire and forget, like the real one. */ private fireCallback; private mustGet; private wire; /** The `fetch` to hand a host. */ get fetch(): FetchLike; } export {}; //# sourceMappingURL=mock.d.ts.map