@voltro/plugin-atlassian 0.1.0
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/CHANGELOG.md +52 -0
- package/LICENSE +57 -0
- package/README.md +26 -0
- package/SECURITY.md +56 -0
- package/THIRD-PARTY-NOTICES.md +347 -0
- package/dist/index.d.ts +386 -0
- package/dist/index.js +377 -0
- package/dist/types-CVyqPJzY.js +22 -0
- package/dist/webhook.d.ts +86 -0
- package/dist/webhook.js +75 -0
- package/package.json +49 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,386 @@
|
|
|
1
|
+
import { Context } from 'effect';
|
|
2
|
+
import { DEFAULT_POLICY } from '@voltro/integration-http';
|
|
3
|
+
import { Effect } from 'effect';
|
|
4
|
+
import { FetchLike } from '@voltro/integration-http';
|
|
5
|
+
import { HttpPolicy } from '@voltro/integration-http';
|
|
6
|
+
import { Option } from 'effect';
|
|
7
|
+
import { Schema } from 'effect';
|
|
8
|
+
import { Subject } from '@voltro/protocol';
|
|
9
|
+
import { SubjectService } from '@voltro/protocol';
|
|
10
|
+
import { VoltroPlugin } from '@voltro/protocol';
|
|
11
|
+
|
|
12
|
+
/** Atlassian's fixed 3LO endpoints (identical for every Cloud site). */
|
|
13
|
+
export declare const ATLASSIAN_AUTHORIZE_URL = "https://auth.atlassian.com/authorize";
|
|
14
|
+
|
|
15
|
+
export declare const ATLASSIAN_TOKEN_URL = "https://auth.atlassian.com/oauth/token";
|
|
16
|
+
|
|
17
|
+
/** The slice of `@voltro/cache`'s CacheStore the plugin uses — kept
|
|
18
|
+
* structural so the app can pass its own store without a type dep dance. */
|
|
19
|
+
export declare interface AtlassianCache {
|
|
20
|
+
readonly get: (key: string) => Effect.Effect<Option.Option<unknown>, unknown>;
|
|
21
|
+
readonly set: (key: string, value: unknown, options?: {
|
|
22
|
+
readonly ttlMs?: number;
|
|
23
|
+
}) => Effect.Effect<void, unknown>;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/** The cacheable read namespaces — the reference-data GETs the plugin memoises
|
|
27
|
+
* (board config, project, field list, status list). Issue/board *content* is
|
|
28
|
+
* never cached (it changes too often to serve stale). */
|
|
29
|
+
export declare type AtlassianCacheNamespace = 'boardConfig' | 'project' | 'field' | 'status';
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Resolved per-request Atlassian credentials. The app's
|
|
33
|
+
* `credentialsResolver(subject)` produces these — the plugin never reads
|
|
34
|
+
* the app's schema. `patApplication` / `patEnvironment` populate the
|
|
35
|
+
* mandatory `X-PAT-Application` / `X-PAT-Environment` tracking headers.
|
|
36
|
+
*/
|
|
37
|
+
export declare interface AtlassianCredentials {
|
|
38
|
+
readonly baseUrl: string;
|
|
39
|
+
readonly token: string;
|
|
40
|
+
readonly patApplication: string;
|
|
41
|
+
readonly patEnvironment: string;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export declare type AtlassianError = JiraError | ConfluenceError | AtlassianOAuthError | AtlassianWebhookError;
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* OAuth 2.0 (3LO) failure — a rejected/expired grant, a malformed token
|
|
48
|
+
* response, or a missing-refresh-token re-consent signal. `transient` is
|
|
49
|
+
* `true` only for a 5xx / network blip at the token endpoint. The
|
|
50
|
+
* `client_secret` and tokens are NEVER included in `message`.
|
|
51
|
+
*/
|
|
52
|
+
export declare class AtlassianOAuthError extends AtlassianOAuthError_base {
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
declare const AtlassianOAuthError_base: Schema.TaggedErrorClass<AtlassianOAuthError, "AtlassianOAuthError", {
|
|
56
|
+
readonly _tag: Schema.tag<"AtlassianOAuthError">;
|
|
57
|
+
} & {
|
|
58
|
+
message: typeof Schema.String;
|
|
59
|
+
transient: typeof Schema.Boolean;
|
|
60
|
+
status: Schema.optional<typeof Schema.Number>;
|
|
61
|
+
}>;
|
|
62
|
+
|
|
63
|
+
export declare const atlassianPlugin: (options: AtlassianPluginOptions) => VoltroPlugin;
|
|
64
|
+
|
|
65
|
+
export declare interface AtlassianPluginOptions {
|
|
66
|
+
/** Resolve the caller's Atlassian credentials from their Subject. */
|
|
67
|
+
readonly credentialsResolver: CredentialsResolver;
|
|
68
|
+
/** Distinguishes multiple instances (e.g. two Atlassian sites). */
|
|
69
|
+
readonly name?: string;
|
|
70
|
+
/** Override the HTTP client (tests / custom transport). Default global fetch. */
|
|
71
|
+
readonly fetchImpl?: FetchLike;
|
|
72
|
+
/** Retry/backoff/timeout overrides (merged over defaults). */
|
|
73
|
+
readonly policy?: Partial<HttpPolicy>;
|
|
74
|
+
/** Optional response cache (per-namespace TTLs). */
|
|
75
|
+
readonly cache?: CacheConfig;
|
|
76
|
+
/** Enable the public avatar proxy. `resolveCredentials` is a SERVICE
|
|
77
|
+
* token (the route is public — no per-user PAT). */
|
|
78
|
+
readonly avatar?: {
|
|
79
|
+
readonly resolveCredentials: () => Promise<AtlassianCredentials> | AtlassianCredentials;
|
|
80
|
+
readonly pathPrefix?: string;
|
|
81
|
+
readonly fetchImpl?: FetchLike;
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/** Inbound-webhook verification failure — a missing/invalid signature or an
|
|
86
|
+
* unparseable envelope. Reject the request with a 401/400. */
|
|
87
|
+
export declare class AtlassianWebhookError extends AtlassianWebhookError_base {
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
declare const AtlassianWebhookError_base: Schema.TaggedErrorClass<AtlassianWebhookError, "AtlassianWebhookError", {
|
|
91
|
+
readonly _tag: Schema.tag<"AtlassianWebhookError">;
|
|
92
|
+
} & {
|
|
93
|
+
message: typeof Schema.String;
|
|
94
|
+
/** `'signature'` → 401 (bad/missing signature); `'payload'` → 400 (bad body). */
|
|
95
|
+
reason: Schema.Literal<["signature", "payload"]>;
|
|
96
|
+
}>;
|
|
97
|
+
|
|
98
|
+
export declare interface AuthorizeUrlOptions {
|
|
99
|
+
/** Scopes to request. `offline_access` is required to receive a refresh
|
|
100
|
+
* token. Common: `['read:jira-work', 'write:jira-work', 'offline_access']`. */
|
|
101
|
+
readonly scopes: ReadonlyArray<string>;
|
|
102
|
+
/** Opaque anti-CSRF value echoed back on the redirect — the app generates,
|
|
103
|
+
* stores, and verifies it. */
|
|
104
|
+
readonly state: string;
|
|
105
|
+
/** `prompt=consent` forces the consent screen (needed to (re)issue a refresh
|
|
106
|
+
* token). Default omitted. */
|
|
107
|
+
readonly prompt?: 'consent';
|
|
108
|
+
/** `audience`. Default `'api.atlassian.com'` (the Atlassian Cloud gateway). */
|
|
109
|
+
readonly audience?: string;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Build the URL to redirect a user to for consent. Pure string construction —
|
|
114
|
+
* no IO. The caller stores `state` and verifies it on the callback.
|
|
115
|
+
*/
|
|
116
|
+
export declare const buildAuthorizeUrl: (config: OAuthConfig, options: AuthorizeUrlOptions) => string;
|
|
117
|
+
|
|
118
|
+
declare type C<A> = Effect.Effect<A, ConfluenceError, SubjectService>;
|
|
119
|
+
|
|
120
|
+
export declare interface CacheConfig {
|
|
121
|
+
readonly store: AtlassianCache;
|
|
122
|
+
/** Per-namespace TTL in ms (e.g. `{ boardConfig: 60_000, project: 30_000 }`).
|
|
123
|
+
* Missing namespace → no TTL (store default). Only the four reference-data
|
|
124
|
+
* namespaces are cached — see {@link AtlassianCacheNamespace}. */
|
|
125
|
+
readonly ttlMs?: Partial<Readonly<Record<AtlassianCacheNamespace, number>>>;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/** Confluence failure — same shape/semantics as {@link JiraError}. */
|
|
129
|
+
export declare class ConfluenceError extends ConfluenceError_base {
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
declare const ConfluenceError_base: Schema.TaggedErrorClass<ConfluenceError, "ConfluenceError", {
|
|
133
|
+
readonly _tag: Schema.tag<"ConfluenceError">;
|
|
134
|
+
} & {
|
|
135
|
+
message: typeof Schema.String;
|
|
136
|
+
transient: typeof Schema.Boolean;
|
|
137
|
+
status: Schema.optional<typeof Schema.Number>;
|
|
138
|
+
code: Schema.optional<Schema.Literal<["session_expired"]>>;
|
|
139
|
+
}>;
|
|
140
|
+
|
|
141
|
+
export declare class ConfluenceService extends ConfluenceService_base {
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
declare const ConfluenceService_base: Context.TagClass<ConfluenceService, "@voltro/plugin-atlassian/ConfluenceService", ConfluenceServiceShape>;
|
|
145
|
+
|
|
146
|
+
export declare interface ConfluenceServiceShape {
|
|
147
|
+
/** GET /rest/api/content/{id}?expand=body.view,version */
|
|
148
|
+
readonly getContent: (id: string, options?: {
|
|
149
|
+
readonly expand?: ReadonlyArray<string>;
|
|
150
|
+
}) => C<unknown>;
|
|
151
|
+
/** GET /rest/api/content/search?cql=&expand=version,space */
|
|
152
|
+
readonly searchContent: (cql: string, options?: {
|
|
153
|
+
readonly limit?: number;
|
|
154
|
+
readonly expand?: ReadonlyArray<string>;
|
|
155
|
+
}) => C<ReadonlyArray<unknown>>;
|
|
156
|
+
/** GET /rest/api/space/{key} */
|
|
157
|
+
readonly getSpace: (spaceKey: string) => C<unknown>;
|
|
158
|
+
/** POST /rest/api/content */
|
|
159
|
+
readonly createContent: (payload: unknown) => C<unknown>;
|
|
160
|
+
/** PUT /rest/api/content/{id} (version-bumped) */
|
|
161
|
+
readonly updateContent: (id: string, payload: unknown, options: {
|
|
162
|
+
readonly version: number;
|
|
163
|
+
}) => C<unknown>;
|
|
164
|
+
/** GET /rest/api/content/{id}/child/attachment */
|
|
165
|
+
readonly getAttachments: (contentId: string) => C<ReadonlyArray<unknown>>;
|
|
166
|
+
/** POST /rest/api/content — add a comment to a page. `body` is the comment
|
|
167
|
+
* text (storage-format markup). Returns the created comment content. */
|
|
168
|
+
readonly addComment: (pageId: string, body: string) => C<unknown>;
|
|
169
|
+
/** GET a PAT-gated binary asset on the Confluence host (an embedded internal
|
|
170
|
+
* image / attachment download path that the browser can't fetch
|
|
171
|
+
* unauthenticated). SSRF-guarded to the configured host — pass a same-host
|
|
172
|
+
* absolute URL or a host-relative path. Returns raw bytes + content-type
|
|
173
|
+
* (for inlining as a data URL or re-storing via `@voltro/plugin-storage`). */
|
|
174
|
+
readonly downloadImage: (url: string) => C<{
|
|
175
|
+
readonly bytes: Uint8Array;
|
|
176
|
+
readonly contentType: string | null;
|
|
177
|
+
}>;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/** App-supplied: maps the request's Subject to Atlassian credentials.
|
|
181
|
+
* Fail with the matching typed error (e.g. no PAT on file). */
|
|
182
|
+
export declare type CredentialsResolver = (subject: Subject) => Effect.Effect<AtlassianCredentials, JiraError>;
|
|
183
|
+
|
|
184
|
+
export declare const DEFAULT_AVATAR_PREFIX = "/_voltro/atlassian/avatar";
|
|
185
|
+
|
|
186
|
+
/** Default `X-PAT-Application` when the app doesn't override it. */
|
|
187
|
+
export declare const DEFAULT_PAT_APPLICATION = "voltro";
|
|
188
|
+
|
|
189
|
+
export { DEFAULT_POLICY }
|
|
190
|
+
|
|
191
|
+
/** How close to expiry (ms) a token is considered stale and refreshed early.
|
|
192
|
+
* Default 60s of headroom so an in-flight call doesn't race the deadline. */
|
|
193
|
+
export declare const DEFAULT_REFRESH_SKEW_MS = 60000;
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Exchange an authorization `code` (from the redirect callback) for a token
|
|
197
|
+
* set. Fails with {@link AtlassianOAuthError} on a rejected grant / bad code.
|
|
198
|
+
*/
|
|
199
|
+
export declare const exchangeCode: (config: OAuthExchangeConfig, code: string) => Effect.Effect<OAuthTokens, AtlassianOAuthError>;
|
|
200
|
+
|
|
201
|
+
export { FetchLike }
|
|
202
|
+
|
|
203
|
+
/** Jira board data (Greenhopper xboard allData shape — kept loose; the
|
|
204
|
+
* app maps `raw` in app code). */
|
|
205
|
+
export declare interface GreenhopperBoard {
|
|
206
|
+
readonly issues: ReadonlyArray<unknown>;
|
|
207
|
+
readonly columns: ReadonlyArray<unknown>;
|
|
208
|
+
readonly swimlanes: ReadonlyArray<unknown>;
|
|
209
|
+
readonly raw: unknown;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
export { HttpPolicy }
|
|
213
|
+
|
|
214
|
+
declare type J<A> = Effect.Effect<A, JiraError, SubjectService>;
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* Jira failure. `transient` drives retry — `true` for 408/425/429/5xx +
|
|
218
|
+
* network blips (worth retrying), `false` for 4xx / config errors. A 401
|
|
219
|
+
* carries `code: 'session_expired'` (the stored PAT is stale) and is
|
|
220
|
+
* NON-transient — re-auth, don't retry.
|
|
221
|
+
*/
|
|
222
|
+
export declare class JiraError extends JiraError_base {
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
declare const JiraError_base: Schema.TaggedErrorClass<JiraError, "JiraError", {
|
|
226
|
+
readonly _tag: Schema.tag<"JiraError">;
|
|
227
|
+
} & {
|
|
228
|
+
message: typeof Schema.String;
|
|
229
|
+
transient: typeof Schema.Boolean;
|
|
230
|
+
status: Schema.optional<typeof Schema.Number>;
|
|
231
|
+
code: Schema.optional<Schema.Literal<["session_expired"]>>;
|
|
232
|
+
}>;
|
|
233
|
+
|
|
234
|
+
export declare class JiraService extends JiraService_base {
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
declare const JiraService_base: Context.TagClass<JiraService, "@voltro/plugin-atlassian/JiraService", JiraServiceShape>;
|
|
238
|
+
|
|
239
|
+
export declare interface JiraServiceShape {
|
|
240
|
+
/** POST /rest/api/2/search — JQL, batched 100. Returns all matching
|
|
241
|
+
* issues (follows pagination). */
|
|
242
|
+
readonly searchIssues: (jql: string, options?: SearchIssuesOptions) => J<ReadonlyArray<unknown>>;
|
|
243
|
+
/** GET /rest/greenhopper/1.0/xboard/work/allData.json?rapidViewId= */
|
|
244
|
+
readonly getGreenhopperBoard: (boardId: string | number, options?: {
|
|
245
|
+
readonly quickFilterId?: string | number;
|
|
246
|
+
}) => J<GreenhopperBoard>;
|
|
247
|
+
/** GET /rest/greenhopper/1.0/rapidviewconfig/editmodel?rapidViewId= */
|
|
248
|
+
readonly getBoardConfig: (boardId: string | number) => J<unknown>;
|
|
249
|
+
/** GET /rest/agile/1.0/board/{id}/sprint?state=active → null if none
|
|
250
|
+
* (failures are swallowed to null). */
|
|
251
|
+
readonly getActiveSprint: (boardId: string | number) => J<SprintRef | null>;
|
|
252
|
+
/** GET /rest/agile/1.0/sprint/{id}/issue */
|
|
253
|
+
readonly getSprintIssues: (sprintId: string | number) => J<ReadonlyArray<unknown>>;
|
|
254
|
+
/** GET /rest/agile/1.0/sprint/{id} */
|
|
255
|
+
readonly getSprintInfo: (sprintId: string | number) => J<unknown>;
|
|
256
|
+
/** GET /rest/api/2/issue/{key} */
|
|
257
|
+
readonly getIssue: (key: string, options?: {
|
|
258
|
+
readonly expand?: ReadonlyArray<string>;
|
|
259
|
+
readonly fields?: ReadonlyArray<string>;
|
|
260
|
+
}) => J<unknown>;
|
|
261
|
+
/** GET /rest/api/2/issue/{key}?expand=changelog */
|
|
262
|
+
readonly getIssueChangelog: (key: string) => J<unknown>;
|
|
263
|
+
/** GET /rest/api/2/issue/{key}/comment */
|
|
264
|
+
readonly getComments: (key: string) => J<ReadonlyArray<unknown>>;
|
|
265
|
+
/** POST /rest/api/2/issue/{key}/comment — add a comment. `body` is the comment
|
|
266
|
+
* text (wiki markup on Server/DC). Returns the created comment. */
|
|
267
|
+
readonly addComment: (key: string, body: string) => J<unknown>;
|
|
268
|
+
/** Issue `issuelinks` resolved to linked issues (read tool for the AI loop). */
|
|
269
|
+
readonly getLinkedIssues: (key: string) => J<ReadonlyArray<unknown>>;
|
|
270
|
+
/** GET /rest/api/2/user?key= | /rest/api/2/user/search?username= */
|
|
271
|
+
readonly getUser: (by: {
|
|
272
|
+
readonly key?: string;
|
|
273
|
+
readonly username?: string;
|
|
274
|
+
}) => J<unknown>;
|
|
275
|
+
/** GET /rest/api/2/myself */
|
|
276
|
+
readonly getMyself: () => J<unknown>;
|
|
277
|
+
/** GET /rest/api/2/issue/{key}/transitions */
|
|
278
|
+
readonly getIssueTransitions: (key: string) => J<ReadonlyArray<unknown>>;
|
|
279
|
+
/** POST /rest/api/2/issue/{key}/transitions */
|
|
280
|
+
readonly transitionIssue: (key: string, transitionId: string, options?: {
|
|
281
|
+
readonly fields?: Record<string, unknown>;
|
|
282
|
+
}) => J<void>;
|
|
283
|
+
/** POST /rest/api/2/issue */
|
|
284
|
+
readonly createIssue: (payload: unknown) => J<unknown>;
|
|
285
|
+
/** PUT /rest/api/2/issue/{key} */
|
|
286
|
+
readonly updateIssue: (key: string, payload: unknown) => J<void>;
|
|
287
|
+
/** GET /rest/api/2/project/{key} */
|
|
288
|
+
readonly getProject: (key: string) => J<unknown>;
|
|
289
|
+
/** GET /rest/api/2/field */
|
|
290
|
+
readonly getFields: () => J<ReadonlyArray<unknown>>;
|
|
291
|
+
/** GET /rest/api/2/status */
|
|
292
|
+
readonly getStatuses: () => J<ReadonlyArray<unknown>>;
|
|
293
|
+
/** GET /rest/api/2/filter/{id} (or list) */
|
|
294
|
+
readonly getFilters: () => J<ReadonlyArray<unknown>>;
|
|
295
|
+
/** Build the `/secure/useravatar?ownerId=…` URL for a user/owner. */
|
|
296
|
+
readonly getAvatarUrl: (ownerId: string) => J<string>;
|
|
297
|
+
/** GET /rest/agile/1.0/board/{id} — the board's metadata (name, type, location). */
|
|
298
|
+
readonly getBoard: (boardId: string | number) => J<unknown>;
|
|
299
|
+
/** GET /rest/agile/1.0/board/{id}/quickfilter — the board's configured quick filters (`.values`). */
|
|
300
|
+
readonly getBoardQuickFilters: (boardId: string | number) => J<ReadonlyArray<unknown>>;
|
|
301
|
+
/** PUT /rest/api/2/issue/{key}/assignee — assign (Jira Server/DC body `{ name }`); pass `null` to unassign. */
|
|
302
|
+
readonly assignIssue: (key: string, assignee: string | null) => J<void>;
|
|
303
|
+
/** DELETE /rest/api/2/issue/{key} — remove an issue (optionally its subtasks). */
|
|
304
|
+
readonly deleteIssue: (key: string, options?: {
|
|
305
|
+
readonly deleteSubtasks?: boolean;
|
|
306
|
+
}) => J<void>;
|
|
307
|
+
/** The configured Jira base URL for the caller's tenant — for building issue/board deep-links. No HTTP call. */
|
|
308
|
+
readonly getBaseUrl: () => J<string>;
|
|
309
|
+
/** Resolve a board's SAVED-FILTER JQL: `/board/{id}/configuration` → `filter.id`
|
|
310
|
+
* → `/filter/{id}.jql`. Returns `null` when the board has no resolvable filter.
|
|
311
|
+
* Use to scope a query to exactly what the board shows (e.g. sprint reports). */
|
|
312
|
+
readonly getBoardFilterJql: (boardId: string | number) => J<string | null>;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
/** OAuth 2.0 client registration + endpoint config. `clientSecret` is a
|
|
316
|
+
* secret — sourced from config/env, never logged, never bundled. */
|
|
317
|
+
export declare interface OAuthConfig {
|
|
318
|
+
readonly clientId: string;
|
|
319
|
+
readonly clientSecret: string;
|
|
320
|
+
/** The exact redirect URI registered on the Atlassian app (must match). */
|
|
321
|
+
readonly redirectUri: string;
|
|
322
|
+
/** Override the token endpoint (tests / Atlassian gov-cloud). Default
|
|
323
|
+
* {@link ATLASSIAN_TOKEN_URL}. */
|
|
324
|
+
readonly tokenUrl?: string;
|
|
325
|
+
/** Override the authorize endpoint. Default {@link ATLASSIAN_AUTHORIZE_URL}. */
|
|
326
|
+
readonly authorizeUrl?: string;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
export declare interface OAuthExchangeConfig extends OAuthConfig {
|
|
330
|
+
readonly fetchImpl: FetchLike;
|
|
331
|
+
/** Injectable clock for `expiresAt` (tests). Default `Date.now`. */
|
|
332
|
+
readonly now?: () => number;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
/** A resolved OAuth token set. `expiresAt` is an absolute epoch-ms deadline
|
|
336
|
+
* (computed from the endpoint's `expires_in`), so a consumer can decide when
|
|
337
|
+
* to refresh without re-deriving it. */
|
|
338
|
+
export declare interface OAuthTokens {
|
|
339
|
+
readonly accessToken: string;
|
|
340
|
+
/** Absent when Atlassian didn't return one (e.g. `offline_access` scope not
|
|
341
|
+
* requested — then the app must re-consent to get a new access token). */
|
|
342
|
+
readonly refreshToken?: string;
|
|
343
|
+
/** Absolute epoch-ms the access token expires at. */
|
|
344
|
+
readonly expiresAt: number;
|
|
345
|
+
/** Granted scopes (space-delimited on the wire; split here). */
|
|
346
|
+
readonly scopes: ReadonlyArray<string>;
|
|
347
|
+
readonly tokenType: string;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
/**
|
|
351
|
+
* Swap a refresh token for a fresh token set (rotating refresh — Atlassian
|
|
352
|
+
* returns a NEW refresh token each time, so the app must persist the returned
|
|
353
|
+
* one). Fails with {@link AtlassianOAuthError} on an expired / revoked grant.
|
|
354
|
+
*/
|
|
355
|
+
export declare const refreshTokens: (config: OAuthExchangeConfig, refreshToken: string) => Effect.Effect<OAuthTokens, AtlassianOAuthError>;
|
|
356
|
+
|
|
357
|
+
export declare interface SearchIssuesOptions {
|
|
358
|
+
readonly fields?: ReadonlyArray<string>;
|
|
359
|
+
readonly maxResults?: number;
|
|
360
|
+
readonly startAt?: number;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
export declare interface SprintRef {
|
|
364
|
+
readonly id: number;
|
|
365
|
+
readonly name: string;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
/**
|
|
369
|
+
* Read-refresh-persist in one call: given the currently-stored token set,
|
|
370
|
+
* return a still-valid one — refreshing (and persisting the rotated set via
|
|
371
|
+
* `persist`) when it's within `skewMs` of expiry. This is the seam the app
|
|
372
|
+
* calls before every OAuth-authenticated request; it mirrors the PAT
|
|
373
|
+
* `credentialsResolver`'s "resolve fresh per request" contract.
|
|
374
|
+
*
|
|
375
|
+
* Fails with {@link AtlassianOAuthError} when a refresh is needed but the
|
|
376
|
+
* stored set has no refresh token (the app must re-consent) or the grant is
|
|
377
|
+
* rejected.
|
|
378
|
+
*/
|
|
379
|
+
export declare const withFreshToken: (config: WithFreshTokenConfig, stored: OAuthTokens, persist: (tokens: OAuthTokens) => Effect.Effect<void, never> | void) => Effect.Effect<OAuthTokens, AtlassianOAuthError>;
|
|
380
|
+
|
|
381
|
+
export declare interface WithFreshTokenConfig extends OAuthExchangeConfig {
|
|
382
|
+
/** Refresh when `expiresAt - now < skewMs`. Default {@link DEFAULT_REFRESH_SKEW_MS}. */
|
|
383
|
+
readonly skewMs?: number;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
export { }
|