@knpkv/jira-api-client 0.2.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/.specs/VERSION_V3 +1 -0
- package/.specs/jira-v3-1001.0.0-SNAPSHOT-ea9913a8c4412d2bfb1911480b023a04544e19d8.json +55618 -0
- package/CHANGELOG.md +11 -0
- package/LICENSE +21 -0
- package/README.md +94 -0
- package/dist/JiraApiClient.d.ts +99 -0
- package/dist/JiraApiClient.d.ts.map +1 -0
- package/dist/JiraApiClient.js +94 -0
- package/dist/JiraApiClient.js.map +1 -0
- package/dist/JiraApiConfig.d.ts +57 -0
- package/dist/JiraApiConfig.d.ts.map +1 -0
- package/dist/JiraApiConfig.js +37 -0
- package/dist/JiraApiConfig.js.map +1 -0
- package/dist/OpenApiFetchClient.d.ts +55 -0
- package/dist/OpenApiFetchClient.d.ts.map +1 -0
- package/dist/OpenApiFetchClient.js +63 -0
- package/dist/OpenApiFetchClient.js.map +1 -0
- package/dist/generated/v3/index.d.ts +7 -0
- package/dist/generated/v3/index.d.ts.map +1 -0
- package/dist/generated/v3/index.js +2 -0
- package/dist/generated/v3/index.js.map +1 -0
- package/dist/generated/v3/schema.d.ts +64353 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +17 -0
- package/dist/index.js.map +1 -0
- package/package.json +78 -0
- package/scripts/regenerate.ts +141 -0
- package/src/JiraApiClient.ts +129 -0
- package/src/JiraApiConfig.ts +51 -0
- package/src/OpenApiFetchClient.ts +92 -0
- package/src/generated/v3/index.ts +6 -0
- package/src/generated/v3/schema.d.ts +64353 -0
- package/src/index.ts +22 -0
- package/test/JiraApiClient.test.ts +145 -0
- package/tsconfig.json +11 -0
- package/tsconfig.tsbuildinfo +1 -0
- package/vitest.config.ts +7 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# @knpkv/jira-api-client
|
|
2
|
+
|
|
3
|
+
## 0.2.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- [#61](https://github.com/knpkv/npm/pull/61) [`fc7be8f`](https://github.com/knpkv/npm/commit/fc7be8ffaf5b6b094c7f81551e8ace6f2a8f2c4c) Thanks @konopkov! - feat: add jira-api-client and atlassian-common packages
|
|
8
|
+
- New @knpkv/atlassian-common: shared AST types, serializers, auth, and config
|
|
9
|
+
- New @knpkv/jira-api-client: Effect-based Jira REST API client (openapi-gen)
|
|
10
|
+
- Updated @knpkv/confluence-api-client: regenerated with openapi-gen
|
|
11
|
+
- Updated @knpkv/confluence-to-markdown: use new generated API client
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 knpkv
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
# @knpkv/jira-api-client
|
|
2
|
+
|
|
3
|
+
Effect-based Jira Cloud REST API v3 client. Type-safe paths and responses generated from the official OpenAPI schema via `openapi-typescript` + `openapi-fetch`.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pnpm add @knpkv/jira-api-client
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Peer dependencies: `effect`
|
|
12
|
+
|
|
13
|
+
## Usage
|
|
14
|
+
|
|
15
|
+
```typescript
|
|
16
|
+
import { Effect, Layer } from "effect"
|
|
17
|
+
import * as Redacted from "effect/Redacted"
|
|
18
|
+
import { JiraApiClient, JiraApiConfig, toEffect } from "@knpkv/jira-api-client"
|
|
19
|
+
|
|
20
|
+
const program = Effect.gen(function* () {
|
|
21
|
+
const client = yield* JiraApiClient
|
|
22
|
+
|
|
23
|
+
// Get issue by key
|
|
24
|
+
const issue = yield* toEffect(
|
|
25
|
+
client.v3.client.GET("/rest/api/3/issue/{issueIdOrKey}", {
|
|
26
|
+
params: { path: { issueIdOrKey: "PROJ-123" } }
|
|
27
|
+
})
|
|
28
|
+
)
|
|
29
|
+
console.log(issue.fields?.summary)
|
|
30
|
+
|
|
31
|
+
// Search with JQL
|
|
32
|
+
const results = yield* toEffect(
|
|
33
|
+
client.v3.client.POST("/rest/api/3/search/jql", {
|
|
34
|
+
body: { jql: "project = PROJ AND status != Done", maxResults: 50 }
|
|
35
|
+
})
|
|
36
|
+
)
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
// Basic auth
|
|
40
|
+
const configLayer = Layer.succeed(JiraApiConfig, {
|
|
41
|
+
baseUrl: "https://mysite.atlassian.net",
|
|
42
|
+
auth: {
|
|
43
|
+
type: "basic",
|
|
44
|
+
email: "user@example.com",
|
|
45
|
+
apiToken: Redacted.make("your-api-token")
|
|
46
|
+
}
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
// Or OAuth2
|
|
50
|
+
const oauthConfigLayer = Layer.succeed(JiraApiConfig, {
|
|
51
|
+
baseUrl: "",
|
|
52
|
+
auth: {
|
|
53
|
+
type: "oauth2",
|
|
54
|
+
accessToken: Redacted.make("oauth-access-token"),
|
|
55
|
+
cloudId: "your-cloud-id"
|
|
56
|
+
}
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
Effect.runPromise(program.pipe(Effect.provide(JiraApiClient.layer), Effect.provide(configLayer)))
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## API Pattern
|
|
63
|
+
|
|
64
|
+
All API calls go through `toEffect()` which wraps the openapi-fetch promise in an Effect:
|
|
65
|
+
|
|
66
|
+
```typescript
|
|
67
|
+
toEffect(client.v3.client.GET("/rest/api/3/..."))
|
|
68
|
+
// → Effect<ResponseData, FetchClientError>
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
Paths are fully type-safe — autocomplete and compile-time errors for invalid paths, params, and bodies.
|
|
72
|
+
|
|
73
|
+
## Errors
|
|
74
|
+
|
|
75
|
+
`FetchClientError` is a tagged error with `error`, `status`, and `message` fields:
|
|
76
|
+
|
|
77
|
+
```typescript
|
|
78
|
+
import { FetchClientError } from "@knpkv/jira-api-client"
|
|
79
|
+
|
|
80
|
+
program.pipe(Effect.catchTag("FetchClientError", (e) => Console.log(`HTTP ${e.status}: ${e.message}`)))
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
## Subpath Exports
|
|
84
|
+
|
|
85
|
+
| Export | Contents |
|
|
86
|
+
| -------------------------------------- | ------------------------------- |
|
|
87
|
+
| `@knpkv/jira-api-client` | Client, config, toEffect, types |
|
|
88
|
+
| `@knpkv/jira-api-client/v3` | Generated V3 OpenAPI types |
|
|
89
|
+
| `@knpkv/jira-api-client/JiraApiClient` | Client service only |
|
|
90
|
+
| `@knpkv/jira-api-client/JiraApiConfig` | Config service only |
|
|
91
|
+
|
|
92
|
+
## License
|
|
93
|
+
|
|
94
|
+
MIT
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Effect Layer wrapping openapi-fetch Jira V3 client with auth and base URL.
|
|
3
|
+
*
|
|
4
|
+
* **Mental model**
|
|
5
|
+
*
|
|
6
|
+
* - **Auth-polymorphic**: Supports both Basic (email + API token) and OAuth2 (access token +
|
|
7
|
+
* cloud ID) auth. The layer reads {@link JiraApiConfig} to build the Authorization header
|
|
8
|
+
* and derive the correct base URL.
|
|
9
|
+
* - **openapi-fetch wrapper**: Uses {@link OpenApiFetchClient} for Effect-based methods.
|
|
10
|
+
*
|
|
11
|
+
* **Common tasks**
|
|
12
|
+
*
|
|
13
|
+
* - Use the client: `const jira = yield* JiraApiClient; jira.v3.GET(...)`
|
|
14
|
+
* - Provide the layer: `Effect.provide(JiraApiClient.layer)`
|
|
15
|
+
*
|
|
16
|
+
* @module
|
|
17
|
+
*/
|
|
18
|
+
import * as Context from "effect/Context";
|
|
19
|
+
import * as Layer from "effect/Layer";
|
|
20
|
+
import type { paths as V3Paths } from "./generated/v3/schema.js";
|
|
21
|
+
import { JiraApiConfig } from "./JiraApiConfig.js";
|
|
22
|
+
import { type OpenApiFetchClient } from "./OpenApiFetchClient.js";
|
|
23
|
+
/**
|
|
24
|
+
* Jira API client shape (v3 only).
|
|
25
|
+
*
|
|
26
|
+
* @example
|
|
27
|
+
* ```typescript
|
|
28
|
+
* import { toEffect } from "@knpkv/jira-api-client"
|
|
29
|
+
*
|
|
30
|
+
* // Get issue by key
|
|
31
|
+
* toEffect(client.v3.client.GET("/rest/api/3/issue/{issueIdOrKey}", {
|
|
32
|
+
* params: { path: { issueIdOrKey: "PROJ-123" } }
|
|
33
|
+
* }))
|
|
34
|
+
*
|
|
35
|
+
* // Search issues with JQL
|
|
36
|
+
* toEffect(client.v3.client.POST("/rest/api/3/search/jql", {
|
|
37
|
+
* body: { jql: "project = PROJ", maxResults: 50 }
|
|
38
|
+
* }))
|
|
39
|
+
* ```
|
|
40
|
+
*
|
|
41
|
+
* @category Client
|
|
42
|
+
*/
|
|
43
|
+
export interface JiraApiClientShape {
|
|
44
|
+
readonly v3: OpenApiFetchClient<V3Paths>;
|
|
45
|
+
}
|
|
46
|
+
declare const JiraApiClient_base: Context.TagClass<JiraApiClient, "@knpkv/jira-api-client/JiraApiClient", JiraApiClientShape>;
|
|
47
|
+
/**
|
|
48
|
+
* Jira API client service.
|
|
49
|
+
*
|
|
50
|
+
* @example
|
|
51
|
+
* ```typescript
|
|
52
|
+
* import { JiraApiClient, JiraApiConfig } from "@knpkv/jira-api-client"
|
|
53
|
+
* import * as Redacted from "effect/Redacted"
|
|
54
|
+
* import * as Effect from "effect/Effect"
|
|
55
|
+
* import * as Layer from "effect/Layer"
|
|
56
|
+
*
|
|
57
|
+
* const program = Effect.gen(function* () {
|
|
58
|
+
* const client = yield* JiraApiClient
|
|
59
|
+
* const issue = yield* toEffect(client.v3.client.GET("/rest/api/3/issue/{issueIdOrKey}", {
|
|
60
|
+
* params: { path: { issueIdOrKey: "PROJ-123" } }
|
|
61
|
+
* }))
|
|
62
|
+
* console.log(issue.fields?.summary)
|
|
63
|
+
* })
|
|
64
|
+
*
|
|
65
|
+
* const configLayer = Layer.succeed(JiraApiConfig, {
|
|
66
|
+
* baseUrl: "https://mysite.atlassian.net",
|
|
67
|
+
* auth: {
|
|
68
|
+
* type: "basic",
|
|
69
|
+
* email: "user@example.com",
|
|
70
|
+
* apiToken: Redacted.make("token")
|
|
71
|
+
* }
|
|
72
|
+
* })
|
|
73
|
+
*
|
|
74
|
+
* Effect.runPromise(
|
|
75
|
+
* program.pipe(
|
|
76
|
+
* Effect.provide(JiraApiClient.layer),
|
|
77
|
+
* Effect.provide(configLayer)
|
|
78
|
+
* )
|
|
79
|
+
* )
|
|
80
|
+
* ```
|
|
81
|
+
*
|
|
82
|
+
* @category Client
|
|
83
|
+
*/
|
|
84
|
+
export declare class JiraApiClient extends JiraApiClient_base {
|
|
85
|
+
/**
|
|
86
|
+
* Layer that provides JiraApiClient.
|
|
87
|
+
*
|
|
88
|
+
* Requires: JiraApiConfig
|
|
89
|
+
*/
|
|
90
|
+
static readonly layer: Layer.Layer<JiraApiClient, never, JiraApiConfig>;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Layer that provides JiraApiClient.
|
|
94
|
+
*
|
|
95
|
+
* @category Layers
|
|
96
|
+
*/
|
|
97
|
+
export declare const layer: Layer.Layer<JiraApiClient, never, JiraApiConfig>;
|
|
98
|
+
export {};
|
|
99
|
+
//# sourceMappingURL=JiraApiClient.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"JiraApiClient.d.ts","sourceRoot":"","sources":["../src/JiraApiClient.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AACH,OAAO,KAAK,OAAO,MAAM,gBAAgB,CAAA;AAGzC,OAAO,KAAK,KAAK,MAAM,cAAc,CAAA;AAErC,OAAO,KAAK,EAAE,KAAK,IAAI,OAAO,EAAE,MAAM,0BAA0B,CAAA;AAChE,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAA;AAClD,OAAO,EAA0B,KAAK,kBAAkB,EAAE,MAAM,yBAAyB,CAAA;AAEzF;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,EAAE,EAAE,kBAAkB,CAAC,OAAO,CAAC,CAAA;CACzC;;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AACH,qBAAa,aAAc,SAAQ,kBAEG;IACpC;;;;OAIG;IACH,MAAM,CAAC,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC,KAAK,CAAC,aAAa,EAAE,KAAK,EAAE,aAAa,CAAC,CAyBtE;CACF;AAED;;;;GAIG;AACH,eAAO,MAAM,KAAK,kDAAsB,CAAA"}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Effect Layer wrapping openapi-fetch Jira V3 client with auth and base URL.
|
|
3
|
+
*
|
|
4
|
+
* **Mental model**
|
|
5
|
+
*
|
|
6
|
+
* - **Auth-polymorphic**: Supports both Basic (email + API token) and OAuth2 (access token +
|
|
7
|
+
* cloud ID) auth. The layer reads {@link JiraApiConfig} to build the Authorization header
|
|
8
|
+
* and derive the correct base URL.
|
|
9
|
+
* - **openapi-fetch wrapper**: Uses {@link OpenApiFetchClient} for Effect-based methods.
|
|
10
|
+
*
|
|
11
|
+
* **Common tasks**
|
|
12
|
+
*
|
|
13
|
+
* - Use the client: `const jira = yield* JiraApiClient; jira.v3.GET(...)`
|
|
14
|
+
* - Provide the layer: `Effect.provide(JiraApiClient.layer)`
|
|
15
|
+
*
|
|
16
|
+
* @module
|
|
17
|
+
*/
|
|
18
|
+
import * as Context from "effect/Context";
|
|
19
|
+
import * as Effect from "effect/Effect";
|
|
20
|
+
import * as Encoding from "effect/Encoding";
|
|
21
|
+
import * as Layer from "effect/Layer";
|
|
22
|
+
import * as Redacted from "effect/Redacted";
|
|
23
|
+
import { JiraApiConfig } from "./JiraApiConfig.js";
|
|
24
|
+
import { makeOpenApiFetchClient } from "./OpenApiFetchClient.js";
|
|
25
|
+
/**
|
|
26
|
+
* Jira API client service.
|
|
27
|
+
*
|
|
28
|
+
* @example
|
|
29
|
+
* ```typescript
|
|
30
|
+
* import { JiraApiClient, JiraApiConfig } from "@knpkv/jira-api-client"
|
|
31
|
+
* import * as Redacted from "effect/Redacted"
|
|
32
|
+
* import * as Effect from "effect/Effect"
|
|
33
|
+
* import * as Layer from "effect/Layer"
|
|
34
|
+
*
|
|
35
|
+
* const program = Effect.gen(function* () {
|
|
36
|
+
* const client = yield* JiraApiClient
|
|
37
|
+
* const issue = yield* toEffect(client.v3.client.GET("/rest/api/3/issue/{issueIdOrKey}", {
|
|
38
|
+
* params: { path: { issueIdOrKey: "PROJ-123" } }
|
|
39
|
+
* }))
|
|
40
|
+
* console.log(issue.fields?.summary)
|
|
41
|
+
* })
|
|
42
|
+
*
|
|
43
|
+
* const configLayer = Layer.succeed(JiraApiConfig, {
|
|
44
|
+
* baseUrl: "https://mysite.atlassian.net",
|
|
45
|
+
* auth: {
|
|
46
|
+
* type: "basic",
|
|
47
|
+
* email: "user@example.com",
|
|
48
|
+
* apiToken: Redacted.make("token")
|
|
49
|
+
* }
|
|
50
|
+
* })
|
|
51
|
+
*
|
|
52
|
+
* Effect.runPromise(
|
|
53
|
+
* program.pipe(
|
|
54
|
+
* Effect.provide(JiraApiClient.layer),
|
|
55
|
+
* Effect.provide(configLayer)
|
|
56
|
+
* )
|
|
57
|
+
* )
|
|
58
|
+
* ```
|
|
59
|
+
*
|
|
60
|
+
* @category Client
|
|
61
|
+
*/
|
|
62
|
+
export class JiraApiClient extends Context.Tag("@knpkv/jira-api-client/JiraApiClient")() {
|
|
63
|
+
/**
|
|
64
|
+
* Layer that provides JiraApiClient.
|
|
65
|
+
*
|
|
66
|
+
* Requires: JiraApiConfig
|
|
67
|
+
*/
|
|
68
|
+
static layer = Layer.effect(JiraApiClient, Effect.gen(function* () {
|
|
69
|
+
const config = yield* JiraApiConfig;
|
|
70
|
+
// Build auth header
|
|
71
|
+
const authHeader = config.auth.type === "basic"
|
|
72
|
+
? `Basic ${Encoding.encodeBase64(`${config.auth.email}:${Redacted.value(config.auth.apiToken)}`)}`
|
|
73
|
+
: `Bearer ${Redacted.value(config.auth.accessToken)}`;
|
|
74
|
+
// Base URL differs by auth type
|
|
75
|
+
const baseUrl = config.auth.type === "oauth2"
|
|
76
|
+
? `https://api.atlassian.com/ex/jira/${config.auth.cloudId}`
|
|
77
|
+
: config.baseUrl;
|
|
78
|
+
const headers = {
|
|
79
|
+
Authorization: authHeader,
|
|
80
|
+
Accept: "application/json",
|
|
81
|
+
"Content-Type": "application/json"
|
|
82
|
+
};
|
|
83
|
+
return {
|
|
84
|
+
v3: makeOpenApiFetchClient(baseUrl, headers)
|
|
85
|
+
};
|
|
86
|
+
}));
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Layer that provides JiraApiClient.
|
|
90
|
+
*
|
|
91
|
+
* @category Layers
|
|
92
|
+
*/
|
|
93
|
+
export const layer = JiraApiClient.layer;
|
|
94
|
+
//# sourceMappingURL=JiraApiClient.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"JiraApiClient.js","sourceRoot":"","sources":["../src/JiraApiClient.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AACH,OAAO,KAAK,OAAO,MAAM,gBAAgB,CAAA;AACzC,OAAO,KAAK,MAAM,MAAM,eAAe,CAAA;AACvC,OAAO,KAAK,QAAQ,MAAM,iBAAiB,CAAA;AAC3C,OAAO,KAAK,KAAK,MAAM,cAAc,CAAA;AACrC,OAAO,KAAK,QAAQ,MAAM,iBAAiB,CAAA;AAE3C,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAA;AAClD,OAAO,EAAE,sBAAsB,EAA2B,MAAM,yBAAyB,CAAA;AA0BzF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AACH,MAAM,OAAO,aAAc,SAAQ,OAAO,CAAC,GAAG,CAC5C,sCAAsC,CACvC,EAAqC;IACpC;;;;OAIG;IACH,MAAM,CAAU,KAAK,GAAqD,KAAK,CAAC,MAAM,CACpF,aAAa,EACb,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;QAClB,MAAM,MAAM,GAAG,KAAK,CAAC,CAAC,aAAa,CAAA;QAEnC,oBAAoB;QACpB,MAAM,UAAU,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,KAAK,OAAO;YAC7C,CAAC,CAAC,SAAS,QAAQ,CAAC,YAAY,CAAC,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,IAAI,QAAQ,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,EAAE;YAClG,CAAC,CAAC,UAAU,QAAQ,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC,EAAE,CAAA;QAEvD,gCAAgC;QAChC,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,KAAK,QAAQ;YAC3C,CAAC,CAAC,qCAAqC,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE;YAC5D,CAAC,CAAC,MAAM,CAAC,OAAO,CAAA;QAElB,MAAM,OAAO,GAAG;YACd,aAAa,EAAE,UAAU;YACzB,MAAM,EAAE,kBAAkB;YAC1B,cAAc,EAAE,kBAAkB;SACnC,CAAA;QAED,OAAO;YACL,EAAE,EAAE,sBAAsB,CAAU,OAAO,EAAE,OAAO,CAAC;SACtD,CAAA;IACH,CAAC,CAAC,CACH,CAAA;;AAGH;;;;GAIG;AACH,MAAM,CAAC,MAAM,KAAK,GAAG,aAAa,CAAC,KAAK,CAAA"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Configuration service tag for the Jira API client (basic or OAuth2 auth).
|
|
3
|
+
*
|
|
4
|
+
* **Mental model**
|
|
5
|
+
*
|
|
6
|
+
* - **Discriminated auth union**: The `auth` field is `{ type: "basic", ... } | { type: "oauth2", ... }`.
|
|
7
|
+
* Basic auth uses email + Redacted API token; OAuth2 uses Redacted access token + cloud ID.
|
|
8
|
+
* - **Base URL routing**: For OAuth2, the base URL is derived from cloud ID (`api.atlassian.com/ex/jira/{cloudId}`);
|
|
9
|
+
* for basic auth, `baseUrl` is used directly.
|
|
10
|
+
*
|
|
11
|
+
* @module
|
|
12
|
+
*/
|
|
13
|
+
import * as Context from "effect/Context";
|
|
14
|
+
import type * as Redacted from "effect/Redacted";
|
|
15
|
+
/**
|
|
16
|
+
* Configuration shape for Jira API client.
|
|
17
|
+
*
|
|
18
|
+
* @category Config
|
|
19
|
+
*/
|
|
20
|
+
export interface JiraApiConfigShape {
|
|
21
|
+
readonly baseUrl: string;
|
|
22
|
+
readonly auth: {
|
|
23
|
+
readonly type: "basic";
|
|
24
|
+
readonly email: string;
|
|
25
|
+
readonly apiToken: Redacted.Redacted<string>;
|
|
26
|
+
} | {
|
|
27
|
+
readonly type: "oauth2";
|
|
28
|
+
readonly accessToken: Redacted.Redacted<string>;
|
|
29
|
+
readonly cloudId: string;
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
declare const JiraApiConfig_base: Context.TagClass<JiraApiConfig, "@knpkv/jira-api-client/JiraApiConfig", JiraApiConfigShape>;
|
|
33
|
+
/**
|
|
34
|
+
* Jira API configuration service.
|
|
35
|
+
*
|
|
36
|
+
* @example
|
|
37
|
+
* ```typescript
|
|
38
|
+
* import { JiraApiConfig } from "@knpkv/jira-api-client"
|
|
39
|
+
* import * as Redacted from "effect/Redacted"
|
|
40
|
+
* import * as Layer from "effect/Layer"
|
|
41
|
+
*
|
|
42
|
+
* const configLayer = Layer.succeed(JiraApiConfig, {
|
|
43
|
+
* baseUrl: "https://mysite.atlassian.net",
|
|
44
|
+
* auth: {
|
|
45
|
+
* type: "basic",
|
|
46
|
+
* email: "user@example.com",
|
|
47
|
+
* apiToken: Redacted.make("token")
|
|
48
|
+
* }
|
|
49
|
+
* })
|
|
50
|
+
* ```
|
|
51
|
+
*
|
|
52
|
+
* @category Config
|
|
53
|
+
*/
|
|
54
|
+
export declare class JiraApiConfig extends JiraApiConfig_base {
|
|
55
|
+
}
|
|
56
|
+
export {};
|
|
57
|
+
//# sourceMappingURL=JiraApiConfig.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"JiraApiConfig.d.ts","sourceRoot":"","sources":["../src/JiraApiConfig.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,OAAO,KAAK,OAAO,MAAM,gBAAgB,CAAA;AACzC,OAAO,KAAK,KAAK,QAAQ,MAAM,iBAAiB,CAAA;AAEhD;;;;GAIG;AACH,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,IAAI,EACT;QAAE,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;QAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAA;KAAE,GAChG;QAAE,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC;QAAC,QAAQ,CAAC,WAAW,EAAE,QAAQ,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;QAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAA;CAC3G;;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,qBAAa,aAAc,SAAQ,kBAEG;CAAG"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Configuration service tag for the Jira API client (basic or OAuth2 auth).
|
|
3
|
+
*
|
|
4
|
+
* **Mental model**
|
|
5
|
+
*
|
|
6
|
+
* - **Discriminated auth union**: The `auth` field is `{ type: "basic", ... } | { type: "oauth2", ... }`.
|
|
7
|
+
* Basic auth uses email + Redacted API token; OAuth2 uses Redacted access token + cloud ID.
|
|
8
|
+
* - **Base URL routing**: For OAuth2, the base URL is derived from cloud ID (`api.atlassian.com/ex/jira/{cloudId}`);
|
|
9
|
+
* for basic auth, `baseUrl` is used directly.
|
|
10
|
+
*
|
|
11
|
+
* @module
|
|
12
|
+
*/
|
|
13
|
+
import * as Context from "effect/Context";
|
|
14
|
+
/**
|
|
15
|
+
* Jira API configuration service.
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* ```typescript
|
|
19
|
+
* import { JiraApiConfig } from "@knpkv/jira-api-client"
|
|
20
|
+
* import * as Redacted from "effect/Redacted"
|
|
21
|
+
* import * as Layer from "effect/Layer"
|
|
22
|
+
*
|
|
23
|
+
* const configLayer = Layer.succeed(JiraApiConfig, {
|
|
24
|
+
* baseUrl: "https://mysite.atlassian.net",
|
|
25
|
+
* auth: {
|
|
26
|
+
* type: "basic",
|
|
27
|
+
* email: "user@example.com",
|
|
28
|
+
* apiToken: Redacted.make("token")
|
|
29
|
+
* }
|
|
30
|
+
* })
|
|
31
|
+
* ```
|
|
32
|
+
*
|
|
33
|
+
* @category Config
|
|
34
|
+
*/
|
|
35
|
+
export class JiraApiConfig extends Context.Tag("@knpkv/jira-api-client/JiraApiConfig")() {
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=JiraApiConfig.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"JiraApiConfig.js","sourceRoot":"","sources":["../src/JiraApiConfig.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,OAAO,KAAK,OAAO,MAAM,gBAAgB,CAAA;AAezC;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,OAAO,aAAc,SAAQ,OAAO,CAAC,GAAG,CAC5C,sCAAsC,CACvC,EAAqC;CAAG"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import * as Effect from "effect/Effect";
|
|
2
|
+
import { type Client, type FetchResponse } from "openapi-fetch";
|
|
3
|
+
import type { MediaType } from "openapi-typescript-helpers";
|
|
4
|
+
declare const FetchClientError_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => import("effect/Cause").YieldableError & {
|
|
5
|
+
readonly _tag: "FetchClientError";
|
|
6
|
+
} & Readonly<A>;
|
|
7
|
+
/**
|
|
8
|
+
* Error from openapi-fetch operations.
|
|
9
|
+
*
|
|
10
|
+
* @category Errors
|
|
11
|
+
*/
|
|
12
|
+
export declare class FetchClientError extends FetchClientError_base<{
|
|
13
|
+
readonly error: unknown;
|
|
14
|
+
readonly status: number;
|
|
15
|
+
readonly message: string;
|
|
16
|
+
}> {
|
|
17
|
+
}
|
|
18
|
+
/** Extract success `data` from a FetchResponse discriminated union. */
|
|
19
|
+
export type SuccessData<T> = T extends {
|
|
20
|
+
data: infer D;
|
|
21
|
+
error?: undefined;
|
|
22
|
+
} ? D : T extends {
|
|
23
|
+
data?: infer D;
|
|
24
|
+
} ? NonNullable<D> : never;
|
|
25
|
+
/**
|
|
26
|
+
* Wrap an openapi-fetch `Promise<FetchResponse>` in Effect.
|
|
27
|
+
*
|
|
28
|
+
* Extracts `data` on success, maps errors to `FetchClientError`.
|
|
29
|
+
* Fully type-safe — path/body constraints come from the `Client<Paths>` call site.
|
|
30
|
+
*
|
|
31
|
+
* @example
|
|
32
|
+
* ```typescript
|
|
33
|
+
* const page = yield* toEffect(client.GET("/pages/{id}", { params: { path: { id: 123 } } }))
|
|
34
|
+
* ```
|
|
35
|
+
*
|
|
36
|
+
* @category Utilities
|
|
37
|
+
*/
|
|
38
|
+
export declare const toEffect: <T extends Record<string | number, any>, O, M extends MediaType>(promise: Promise<FetchResponse<T, O, M>>) => Effect.Effect<SuccessData<FetchResponse<T, O, M>>, FetchClientError>;
|
|
39
|
+
/**
|
|
40
|
+
* openapi-fetch client paired with the `toEffect` helper.
|
|
41
|
+
*
|
|
42
|
+
* @category Client
|
|
43
|
+
*/
|
|
44
|
+
export interface OpenApiFetchClient<Paths extends {}> {
|
|
45
|
+
/** Type-safe openapi-fetch client. Use with `toEffect()` to get Effect. */
|
|
46
|
+
readonly client: Client<Paths, "application/json">;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Create an openapi-fetch client with auth headers pre-configured.
|
|
50
|
+
*
|
|
51
|
+
* @category Constructors
|
|
52
|
+
*/
|
|
53
|
+
export declare const makeOpenApiFetchClient: <Paths extends {}>(baseUrl: string, headers: Record<string, string>) => OpenApiFetchClient<Paths>;
|
|
54
|
+
export {};
|
|
55
|
+
//# sourceMappingURL=OpenApiFetchClient.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"OpenApiFetchClient.d.ts","sourceRoot":"","sources":["../src/OpenApiFetchClient.ts"],"names":[],"mappings":"AAgBA,OAAO,KAAK,MAAM,MAAM,eAAe,CAAA;AACvC,OAAqB,EAAE,KAAK,MAAM,EAAE,KAAK,aAAa,EAAE,MAAM,eAAe,CAAA;AAC7E,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,4BAA4B,CAAA;;;;AAE3D;;;;GAIG;AACH,qBAAa,gBAAiB,SAAQ,sBAAqC;IACzE,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAA;IACvB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;CACzB,CAAC;CAAG;AAEL,uEAAuE;AACvE,MAAM,MAAM,WAAW,CAAC,CAAC,IAAI,CAAC,SAAS;IAAE,IAAI,EAAE,MAAM,CAAC,CAAC;IAAC,KAAK,CAAC,EAAE,SAAS,CAAA;CAAE,GAAG,CAAC,GAC3E,CAAC,SAAS;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC,CAAA;CAAE,GAAG,WAAW,CAAC,CAAC,CAAC,GAC7C,KAAK,CAAA;AAET;;;;;;;;;;;;GAYG;AAEH,eAAO,MAAM,QAAQ,GAAI,CAAC,SAAS,MAAM,CAAC,MAAM,GAAG,MAAM,EAAE,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC,SAAS,SAAS,EACrF,SAAS,OAAO,CAAC,aAAa,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,KACvC,MAAM,CAAC,MAAM,CAAC,WAAW,CAAC,aAAa,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,gBAAgB,CAiBlE,CAAA;AAEJ;;;;GAIG;AACH,MAAM,WAAW,kBAAkB,CAAC,KAAK,SAAS,EAAE;IAClD,2EAA2E;IAC3E,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC,KAAK,EAAE,kBAAkB,CAAC,CAAA;CACnD;AAED;;;;GAIG;AACH,eAAO,MAAM,sBAAsB,GAAI,KAAK,SAAS,EAAE,EACrD,SAAS,MAAM,EACf,SAAS,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,KAC9B,kBAAkB,CAAC,KAAK,CAEzB,CAAA"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generic Effect wrapper for openapi-fetch clients.
|
|
3
|
+
*
|
|
4
|
+
* Exposes a type-safe `Client<Paths>` and a `toEffect` helper that wraps
|
|
5
|
+
* any `Promise<FetchResponse>` in Effect with error mapping.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```typescript
|
|
9
|
+
* const page = yield* toEffect(client.GET("/pages/{id}", {
|
|
10
|
+
* params: { path: { id: 123 } }
|
|
11
|
+
* }))
|
|
12
|
+
* ```
|
|
13
|
+
*
|
|
14
|
+
* @module
|
|
15
|
+
*/
|
|
16
|
+
import * as Data from "effect/Data";
|
|
17
|
+
import * as Effect from "effect/Effect";
|
|
18
|
+
import createClient, {} from "openapi-fetch";
|
|
19
|
+
/**
|
|
20
|
+
* Error from openapi-fetch operations.
|
|
21
|
+
*
|
|
22
|
+
* @category Errors
|
|
23
|
+
*/
|
|
24
|
+
export class FetchClientError extends Data.TaggedError("FetchClientError") {
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Wrap an openapi-fetch `Promise<FetchResponse>` in Effect.
|
|
28
|
+
*
|
|
29
|
+
* Extracts `data` on success, maps errors to `FetchClientError`.
|
|
30
|
+
* Fully type-safe — path/body constraints come from the `Client<Paths>` call site.
|
|
31
|
+
*
|
|
32
|
+
* @example
|
|
33
|
+
* ```typescript
|
|
34
|
+
* const page = yield* toEffect(client.GET("/pages/{id}", { params: { path: { id: 123 } } }))
|
|
35
|
+
* ```
|
|
36
|
+
*
|
|
37
|
+
* @category Utilities
|
|
38
|
+
*/
|
|
39
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- matches FetchResponse generic constraint
|
|
40
|
+
export const toEffect = (promise) => Effect.tryPromise({
|
|
41
|
+
try: () => promise.then(({ data, error, response }) => {
|
|
42
|
+
if (error !== undefined || !response.ok) {
|
|
43
|
+
throw { error, status: response.status };
|
|
44
|
+
}
|
|
45
|
+
return data;
|
|
46
|
+
}),
|
|
47
|
+
catch: (e) => new FetchClientError({
|
|
48
|
+
error: e.error ?? e,
|
|
49
|
+
status: e.status ?? 0,
|
|
50
|
+
message: typeof e.error === "string"
|
|
51
|
+
? e.error
|
|
52
|
+
: JSON.stringify(e.error ?? e)
|
|
53
|
+
})
|
|
54
|
+
});
|
|
55
|
+
/**
|
|
56
|
+
* Create an openapi-fetch client with auth headers pre-configured.
|
|
57
|
+
*
|
|
58
|
+
* @category Constructors
|
|
59
|
+
*/
|
|
60
|
+
export const makeOpenApiFetchClient = (baseUrl, headers) => ({
|
|
61
|
+
client: createClient({ baseUrl, headers })
|
|
62
|
+
});
|
|
63
|
+
//# sourceMappingURL=OpenApiFetchClient.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"OpenApiFetchClient.js","sourceRoot":"","sources":["../src/OpenApiFetchClient.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,OAAO,KAAK,IAAI,MAAM,aAAa,CAAA;AACnC,OAAO,KAAK,MAAM,MAAM,eAAe,CAAA;AACvC,OAAO,YAAY,EAAE,EAAmC,MAAM,eAAe,CAAA;AAG7E;;;;GAIG;AACH,MAAM,OAAO,gBAAiB,SAAQ,IAAI,CAAC,WAAW,CAAC,kBAAkB,CAIvE;CAAG;AAOL;;;;;;;;;;;;GAYG;AACH,0GAA0G;AAC1G,MAAM,CAAC,MAAM,QAAQ,GAAG,CACtB,OAAwC,EAC8B,EAAE,CACxE,MAAM,CAAC,UAAU,CAAC;IAChB,GAAG,EAAE,GAAG,EAAE,CACR,OAAO,CAAC,IAAI,CAAC,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,QAAQ,EAAE,EAAE,EAAE;QACzC,IAAI,KAAK,KAAK,SAAS,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;YACxC,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,CAAC,MAAM,EAAE,CAAA;QAC1C,CAAC;QACD,OAAO,IAA2C,CAAA;IACpD,CAAC,CAAC;IACJ,KAAK,EAAE,CAAC,CAAC,EAAE,EAAE,CACX,IAAI,gBAAgB,CAAC;QACnB,KAAK,EAAG,CAA6B,CAAC,KAAK,IAAI,CAAC;QAChD,MAAM,EAAI,CAA6B,CAAC,MAA6B,IAAI,CAAC;QAC1E,OAAO,EAAE,OAAQ,CAA6B,CAAC,KAAK,KAAK,QAAQ;YAC/D,CAAC,CAAE,CAA6B,CAAC,KAAe;YAChD,CAAC,CAAC,IAAI,CAAC,SAAS,CAAE,CAA6B,CAAC,KAAK,IAAI,CAAC,CAAC;KAC9D,CAAC;CACL,CAAC,CAAA;AAYJ;;;;GAIG;AACH,MAAM,CAAC,MAAM,sBAAsB,GAAG,CACpC,OAAe,EACf,OAA+B,EACJ,EAAE,CAAC,CAAC;IAC/B,MAAM,EAAE,YAAY,CAA4B,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC;CACtE,CAAC,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/generated/v3/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,YAAY,EAAE,UAAU,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,aAAa,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/generated/v3/index.ts"],"names":[],"mappings":""}
|