import { Effect } from 'effect'; import { Schema } from 'effect'; import { Subject } from '@voltro/protocol'; /** * Resolved per-request Atlassian credentials. The app's * `credentialsResolver(subject)` produces these — the plugin never reads * the app's schema. `patApplication` / `patEnvironment` populate the * mandatory `X-PAT-Application` / `X-PAT-Environment` tracking headers. */ declare interface AtlassianCredentials { /** The JIRA site root (`https://jira.example.com`). */ readonly baseUrl: string; /** * The CONFLUENCE site root — `https://wiki.example.com` on Data Center, or * `https://.atlassian.net/wiki` on Cloud. Distinct from `baseUrl` * because the two products never share a REST root: Confluence's * `/rest/api/content` lives under `/wiki` on Cloud and on a separate host on * Data Center, so a `ConfluenceService` call built on the Jira root reached * the wrong server in every environment. Absent ⇒ every `ConfluenceService` * method fails with a typed `ConfluenceError` (`code: 'not_configured'`) * rather than a 404 from the wrong host. */ readonly confluenceBaseUrl?: string; readonly token: string; readonly patApplication: string; readonly patEnvironment: string; } /** * Build a {@link CredentialsResolver} backed by the framework's connection * vault. * * A failure to resolve becomes a NON-transient `JiraError` carrying * `code: 'credential_unusable'` — "the stored credential is no longer usable, * re-auth rather than retry". That mapping is the point of this adapter: * without it, a revoked grant would surface as an opaque rejection that the * plugin's retry policy would happily hammer. * * Distinct from `'unauthorized'`, which is what an upstream 401 produces. This * one is a claim we can actually make — the vault knows it has no usable * credential — while a 401 only says the far end refused, without a reason. */ export declare const connectionCredentials: (options: ConnectionCredentialsOptions) => CredentialsResolver; export declare interface ConnectionCredentialsOptions { /** The `defineConnection({ id })` this plugin instance draws from. */ readonly connectionId: string; /** The JIRA site base URL. Not part of the credential — a connection * stores WHO you are to the provider, not WHICH host the app talks to; a * second site is a second plugin instance with the same connection or a * different one, and that stays the app's decision. */ readonly baseUrl: string; /** The CONFLUENCE site base URL — see `AtlassianCredentials.confluenceBaseUrl`. * Omit for a Jira-only integration; every `ConfluenceService` call then * fails typed (`not_configured`) instead of hitting the Jira host. */ readonly confluenceBaseUrl?: string; /** `X-PAT-Application` header. Default `'voltro'`. */ readonly patApplication?: string; /** `X-PAT-Environment` header. Default `NODE_ENV` or `'development'`. */ readonly patEnvironment?: string; /** Override the vault lookup (tests / a custom credential source). */ readonly resolve?: ConnectionResolveFn; } export declare type ConnectionResolveFn = (subject: Pick, connectionId: string) => Promise; /** * App-supplied: maps the request's caller to Atlassian credentials. * Fail with the matching typed error (e.g. no PAT on file). * * **It takes a CONTEXT rather than a bare Subject, and the reason is a live * credential leak.** With `subject` as the only input, an app doing per-user * Atlassian auth had nowhere to put the caller's PAT except `subject.metadata` — * so the token travelled with the identity into everything that persists a * Subject. A reporter found a working Jira PAT in plaintext in 12 of 23 rows of * their `_voltro_audit_log`, and neither plugin was wrong on its own: this one * REQUIRED the credential to be in the Subject, and the audit plugin serialised * the Subject verbatim. * * `@voltro/plugin-audit` gained a default-on `redactSubject` in the same change, * which stops that particular bleed. This is the other half, and the more * important one: with `store` here the credential never has to enter the Subject * at all — which is the only version that also keeps it out of whatever * persists one NEXT. */ declare type CredentialsResolver = (ctx: CredentialsResolverContext) => Effect.Effect; /** * What an app-supplied resolver receives. * * `store` is absent when the app bound no data store (tests, a store-less * deployment), so a resolver that needs it has to say what happens then. The * optionality is the point: a runtime `undefined` here would be discovered in * production. */ declare interface CredentialsResolverContext { readonly subject: Subject; readonly store?: CredentialsStore; } /** * The narrow store surface a resolver needs: one read. * * Deliberately not `DataStore`. This seam exists so an app can LOOK a credential * up; being able to write through it is not part of that, and a narrow type says * so in the signature rather than in a comment. */ declare interface CredentialsStore { readonly query: (descriptor: unknown) => Promise>>; } /** * Jira failure. `transient` drives retry — `true` for 408/425/429/5xx + * network blips (worth retrying), `false` for 4xx / config errors. * * Two non-transient credential codes, and the split is the point. Both used to * be `session_expired`, which asserted a cause one of them cannot know: * * - `'unauthorized'` — the UPSTREAM answered 401. It refused the credential * and did not say why: expired, revoked, insufficient scope and MALFORMED * are all this status. Do not conclude "re-auth" from it alone. * - `'credential_unusable'` — the connection VAULT could not produce a * credential (no grant, revoked grant, refresh failed). Here we do know, * because the failure is ours rather than the upstream's, and re-auth is * the right action. * * The rename came from a deployment whose plugin sent ciphertext as a bearer * token — a configuration error that arrived as a 401, was named * `session_expired`, and made their health check delete a perfectly valid * session. A name that asserts a cause gets acted on. */ declare class JiraError extends JiraError_base { } declare const JiraError_base: Schema.TaggedErrorClass; } & { message: typeof Schema.String; transient: typeof Schema.Boolean; status: Schema.optional; code: Schema.optional>; }>; /** The slice of a resolved connection this adapter needs. Structurally * satisfied by `@voltro/runtime`'s `ResolvedConnection`. */ export declare interface ResolvedConnectionLike { readonly accessToken: string; } export { }