@knpkv/atlassian-common 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/CHANGELOG.md +11 -0
- package/LICENSE +21 -0
- package/README.md +107 -0
- package/dist/Brand.d.ts +118 -0
- package/dist/Brand.d.ts.map +1 -0
- package/dist/Brand.js +107 -0
- package/dist/Brand.js.map +1 -0
- package/dist/Hash.d.ts +53 -0
- package/dist/Hash.d.ts.map +1 -0
- package/dist/Hash.js +63 -0
- package/dist/Hash.js.map +1 -0
- package/dist/SerializeError.d.ts +48 -0
- package/dist/SerializeError.d.ts.map +1 -0
- package/dist/SerializeError.js +42 -0
- package/dist/SerializeError.js.map +1 -0
- package/dist/ast/BlockNode.d.ts +429 -0
- package/dist/ast/BlockNode.d.ts.map +1 -0
- package/dist/ast/BlockNode.js +279 -0
- package/dist/ast/BlockNode.js.map +1 -0
- package/dist/ast/Document.d.ts +255 -0
- package/dist/ast/Document.d.ts.map +1 -0
- package/dist/ast/Document.js +80 -0
- package/dist/ast/Document.js.map +1 -0
- package/dist/ast/InlineNode.d.ts +483 -0
- package/dist/ast/InlineNode.d.ts.map +1 -0
- package/dist/ast/InlineNode.js +268 -0
- package/dist/ast/InlineNode.js.map +1 -0
- package/dist/ast/MacroNode.d.ts +214 -0
- package/dist/ast/MacroNode.d.ts.map +1 -0
- package/dist/ast/MacroNode.js +110 -0
- package/dist/ast/MacroNode.js.map +1 -0
- package/dist/ast/index.d.ts +10 -0
- package/dist/ast/index.d.ts.map +1 -0
- package/dist/ast/index.js +14 -0
- package/dist/ast/index.js.map +1 -0
- package/dist/auth/OAuthEndpoints.d.ts +95 -0
- package/dist/auth/OAuthEndpoints.d.ts.map +1 -0
- package/dist/auth/OAuthEndpoints.js +112 -0
- package/dist/auth/OAuthEndpoints.js.map +1 -0
- package/dist/auth/OAuthErrors.d.ts +76 -0
- package/dist/auth/OAuthErrors.d.ts.map +1 -0
- package/dist/auth/OAuthErrors.js +85 -0
- package/dist/auth/OAuthErrors.js.map +1 -0
- package/dist/auth/OAuthOperations.d.ts +87 -0
- package/dist/auth/OAuthOperations.d.ts.map +1 -0
- package/dist/auth/OAuthOperations.js +169 -0
- package/dist/auth/OAuthOperations.js.map +1 -0
- package/dist/auth/OAuthResponseSchemas.d.ts +65 -0
- package/dist/auth/OAuthResponseSchemas.d.ts.map +1 -0
- package/dist/auth/OAuthResponseSchemas.js +47 -0
- package/dist/auth/OAuthResponseSchemas.js.map +1 -0
- package/dist/auth/index.d.ts +11 -0
- package/dist/auth/index.d.ts.map +1 -0
- package/dist/auth/index.js +16 -0
- package/dist/auth/index.js.map +1 -0
- package/dist/auth/uuid.d.ts +14 -0
- package/dist/auth/uuid.d.ts.map +1 -0
- package/dist/auth/uuid.js +14 -0
- package/dist/auth/uuid.js.map +1 -0
- package/dist/config/ConfigPaths.d.ts +95 -0
- package/dist/config/ConfigPaths.d.ts.map +1 -0
- package/dist/config/ConfigPaths.js +102 -0
- package/dist/config/ConfigPaths.js.map +1 -0
- package/dist/config/OAuthSchemas.d.ts +144 -0
- package/dist/config/OAuthSchemas.d.ts.map +1 -0
- package/dist/config/OAuthSchemas.js +107 -0
- package/dist/config/OAuthSchemas.js.map +1 -0
- package/dist/config/TokenStorage.d.ts +96 -0
- package/dist/config/TokenStorage.d.ts.map +1 -0
- package/dist/config/TokenStorage.js +128 -0
- package/dist/config/TokenStorage.js.map +1 -0
- package/dist/config/index.d.ts +9 -0
- package/dist/config/index.d.ts.map +1 -0
- package/dist/config/index.js +12 -0
- package/dist/config/index.js.map +1 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +20 -0
- package/dist/index.js.map +1 -0
- package/dist/parsers/index.d.ts +7 -0
- package/dist/parsers/index.d.ts.map +1 -0
- package/dist/parsers/index.js +8 -0
- package/dist/parsers/index.js.map +1 -0
- package/dist/serializers/MarkdownSerializer.d.ts +63 -0
- package/dist/serializers/MarkdownSerializer.d.ts.map +1 -0
- package/dist/serializers/MarkdownSerializer.js +367 -0
- package/dist/serializers/MarkdownSerializer.js.map +1 -0
- package/dist/serializers/index.d.ts +7 -0
- package/dist/serializers/index.d.ts.map +1 -0
- package/dist/serializers/index.js +7 -0
- package/dist/serializers/index.js.map +1 -0
- package/package.json +86 -0
- package/src/Brand.ts +142 -0
- package/src/Hash.ts +68 -0
- package/src/SerializeError.ts +50 -0
- package/src/ast/BlockNode.ts +386 -0
- package/src/ast/Document.ts +101 -0
- package/src/ast/InlineNode.ts +328 -0
- package/src/ast/MacroNode.ts +172 -0
- package/src/ast/index.ts +87 -0
- package/src/auth/OAuthEndpoints.ts +142 -0
- package/src/auth/OAuthErrors.ts +101 -0
- package/src/auth/OAuthOperations.ts +276 -0
- package/src/auth/OAuthResponseSchemas.ts +70 -0
- package/src/auth/index.ts +47 -0
- package/src/auth/uuid.ts +14 -0
- package/src/config/ConfigPaths.ts +179 -0
- package/src/config/OAuthSchemas.ts +146 -0
- package/src/config/TokenStorage.ts +228 -0
- package/src/config/index.ts +43 -0
- package/src/index.ts +34 -0
- package/src/parsers/index.ts +8 -0
- package/src/serializers/MarkdownSerializer.ts +498 -0
- package/src/serializers/index.ts +7 -0
- package/test/Brand.test.ts +90 -0
- package/test/MarkdownSerializer.test.ts +82 -0
- package/test/OAuthEndpoints.test.ts +109 -0
- package/test/OAuthOperations.test.ts +315 -0
- package/tsconfig.json +11 -0
- package/tsconfig.tsbuildinfo +1 -0
- package/vitest.config.ts +12 -0
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared Effect Schema definitions for persisted OAuth credentials and site metadata.
|
|
3
|
+
*
|
|
4
|
+
* **Mental model**
|
|
5
|
+
*
|
|
6
|
+
* - **Storage schemas**: {@link OAuthTokenSchema} and {@link OAuthConfigSchema} define the
|
|
7
|
+
* JSON shape written to `auth.json` / `oauth.json` on disk. They are the single source
|
|
8
|
+
* of truth for token and config structure.
|
|
9
|
+
* - **Type derivation**: All companion types (`OAuthToken`, `OAuthConfig`, etc.) are derived
|
|
10
|
+
* from schemas — never duplicated.
|
|
11
|
+
*
|
|
12
|
+
* @module
|
|
13
|
+
*/
|
|
14
|
+
import * as Schema from "effect/Schema"
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Schema for OAuth user info from Atlassian.
|
|
18
|
+
*
|
|
19
|
+
* @category Schema
|
|
20
|
+
*/
|
|
21
|
+
export const OAuthUserSchema = Schema.Struct({
|
|
22
|
+
/** Atlassian account ID */
|
|
23
|
+
account_id: Schema.String,
|
|
24
|
+
/** Display name */
|
|
25
|
+
name: Schema.String,
|
|
26
|
+
/** Email address */
|
|
27
|
+
email: Schema.String
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Type for OAuth user info.
|
|
32
|
+
*
|
|
33
|
+
* @category Types
|
|
34
|
+
*/
|
|
35
|
+
export type OAuthUser = Schema.Schema.Type<typeof OAuthUserSchema>
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Schema for stored OAuth token.
|
|
39
|
+
*
|
|
40
|
+
* @example
|
|
41
|
+
* ```typescript
|
|
42
|
+
* import { OAuthTokenSchema } from "@knpkv/atlassian-common/config"
|
|
43
|
+
* import * as Schema from "effect/Schema"
|
|
44
|
+
*
|
|
45
|
+
* const token = Schema.decodeUnknownSync(OAuthTokenSchema)({
|
|
46
|
+
* access_token: "eyJ...",
|
|
47
|
+
* refresh_token: "eyJ...",
|
|
48
|
+
* expires_at: Date.now() + 3600000,
|
|
49
|
+
* scope: "read:confluence-content.all",
|
|
50
|
+
* cloud_id: "abc123",
|
|
51
|
+
* site_url: "https://mysite.atlassian.net"
|
|
52
|
+
* })
|
|
53
|
+
* ```
|
|
54
|
+
*
|
|
55
|
+
* @category Schema
|
|
56
|
+
*/
|
|
57
|
+
export const OAuthTokenSchema = Schema.Struct({
|
|
58
|
+
/** OAuth access token */
|
|
59
|
+
access_token: Schema.String,
|
|
60
|
+
/** OAuth refresh token */
|
|
61
|
+
refresh_token: Schema.String,
|
|
62
|
+
/** Token expiration timestamp (Unix ms) */
|
|
63
|
+
expires_at: Schema.Number,
|
|
64
|
+
/** Granted scopes */
|
|
65
|
+
scope: Schema.String,
|
|
66
|
+
/** Atlassian Cloud site ID */
|
|
67
|
+
cloud_id: Schema.String,
|
|
68
|
+
/** Site URL */
|
|
69
|
+
site_url: Schema.String,
|
|
70
|
+
/** Cached user info */
|
|
71
|
+
user: Schema.optional(OAuthUserSchema)
|
|
72
|
+
})
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Type for stored OAuth token.
|
|
76
|
+
*
|
|
77
|
+
* @category Types
|
|
78
|
+
*/
|
|
79
|
+
export type OAuthToken = Schema.Schema.Type<typeof OAuthTokenSchema>
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Schema for OAuth client configuration.
|
|
83
|
+
*
|
|
84
|
+
* @category Schema
|
|
85
|
+
*/
|
|
86
|
+
export const OAuthConfigSchema = Schema.Struct({
|
|
87
|
+
/** OAuth client ID from Atlassian Developer Console */
|
|
88
|
+
clientId: Schema.String,
|
|
89
|
+
/** OAuth client secret */
|
|
90
|
+
clientSecret: Schema.String
|
|
91
|
+
})
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Type for OAuth client configuration.
|
|
95
|
+
*
|
|
96
|
+
* @category Types
|
|
97
|
+
*/
|
|
98
|
+
export type OAuthConfig = Schema.Schema.Type<typeof OAuthConfigSchema>
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Schema for Atlassian Cloud site info.
|
|
102
|
+
*
|
|
103
|
+
* @category Schema
|
|
104
|
+
*/
|
|
105
|
+
export const AtlassianSiteSchema = Schema.Struct({
|
|
106
|
+
/** Cloud ID */
|
|
107
|
+
id: Schema.String,
|
|
108
|
+
/** Site name */
|
|
109
|
+
name: Schema.String,
|
|
110
|
+
/** Site URL */
|
|
111
|
+
url: Schema.String,
|
|
112
|
+
/** Available scopes */
|
|
113
|
+
scopes: Schema.Array(Schema.String),
|
|
114
|
+
/** Avatar URL */
|
|
115
|
+
avatarUrl: Schema.optional(Schema.String)
|
|
116
|
+
})
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Type for Atlassian Cloud site.
|
|
120
|
+
*
|
|
121
|
+
* @category Types
|
|
122
|
+
*/
|
|
123
|
+
export type AtlassianSite = Schema.Schema.Type<typeof AtlassianSiteSchema>
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Schema for Atlassian user profile.
|
|
127
|
+
*
|
|
128
|
+
* @category Schema
|
|
129
|
+
*/
|
|
130
|
+
export const AtlassianUserSchema = Schema.Struct({
|
|
131
|
+
/** Atlassian account ID */
|
|
132
|
+
accountId: Schema.String,
|
|
133
|
+
/** Display name */
|
|
134
|
+
displayName: Schema.String,
|
|
135
|
+
/** Email address (may be empty for privacy) */
|
|
136
|
+
email: Schema.optional(Schema.String),
|
|
137
|
+
/** Public name */
|
|
138
|
+
publicName: Schema.optional(Schema.String)
|
|
139
|
+
})
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Type for Atlassian user profile.
|
|
143
|
+
*
|
|
144
|
+
* @category Types
|
|
145
|
+
*/
|
|
146
|
+
export type AtlassianUser = Schema.Schema.Type<typeof AtlassianUserSchema>
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Persistent token and OAuth config storage with schema validation and secure file I/O.
|
|
3
|
+
*
|
|
4
|
+
* **Mental model**
|
|
5
|
+
*
|
|
6
|
+
* - **Load returns null, not error**: {@link loadToken} and {@link loadOAuthConfig} return
|
|
7
|
+
* `null` for missing/corrupt files — callers decide whether absence is an error.
|
|
8
|
+
* - **Save is atomic**: Writes go through {@link writeSecureFile} with `0o600` permissions.
|
|
9
|
+
* - **Schema-gated reads**: JSON is parsed then validated via `Schema.decodeUnknown` —
|
|
10
|
+
* corrupt data is treated as absent rather than crashing.
|
|
11
|
+
*
|
|
12
|
+
* **Common tasks**
|
|
13
|
+
*
|
|
14
|
+
* - Load stored token: {@link loadToken}
|
|
15
|
+
* - Persist after refresh: {@link saveToken}
|
|
16
|
+
* - Check expiration: {@link isTokenExpired}
|
|
17
|
+
*
|
|
18
|
+
* @module
|
|
19
|
+
*/
|
|
20
|
+
import type * as Error from "@effect/platform/Error"
|
|
21
|
+
import * as FileSystem from "@effect/platform/FileSystem"
|
|
22
|
+
import type * as Path from "@effect/platform/Path"
|
|
23
|
+
import * as Data from "effect/Data"
|
|
24
|
+
import * as Effect from "effect/Effect"
|
|
25
|
+
import * as Schema from "effect/Schema"
|
|
26
|
+
import { type HomeDirectoryError, type HomeDirectoryTag } from "./ConfigPaths.js"
|
|
27
|
+
import { ensureConfigDir, getAuthPath, getOAuthConfigPath, writeSecureFile } from "./ConfigPaths.js"
|
|
28
|
+
import { type OAuthConfig, OAuthConfigSchema, type OAuthToken, OAuthTokenSchema } from "./OAuthSchemas.js"
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Error during file system operations.
|
|
32
|
+
*
|
|
33
|
+
* @category Errors
|
|
34
|
+
*/
|
|
35
|
+
export class FileSystemError extends Data.TaggedError("FileSystemError")<{
|
|
36
|
+
readonly operation: string
|
|
37
|
+
readonly path: string
|
|
38
|
+
readonly cause?: unknown
|
|
39
|
+
}> {
|
|
40
|
+
override get message(): string {
|
|
41
|
+
return `Failed to ${this.operation} file at ${this.path}`
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Load stored OAuth token from disk.
|
|
47
|
+
*
|
|
48
|
+
* @param toolName - The tool name (e.g., "confluence", "jira")
|
|
49
|
+
* @returns The stored token or null if not found/invalid
|
|
50
|
+
*
|
|
51
|
+
* @category Token Storage
|
|
52
|
+
*/
|
|
53
|
+
export const loadToken = (
|
|
54
|
+
toolName: string
|
|
55
|
+
): Effect.Effect<
|
|
56
|
+
OAuthToken | null,
|
|
57
|
+
FileSystemError | HomeDirectoryError,
|
|
58
|
+
FileSystem.FileSystem | Path.Path | HomeDirectoryTag
|
|
59
|
+
> =>
|
|
60
|
+
Effect.gen(function*() {
|
|
61
|
+
const fs = yield* FileSystem.FileSystem
|
|
62
|
+
const tokenPath = yield* getAuthPath(toolName)
|
|
63
|
+
|
|
64
|
+
const exists = yield* fs.exists(tokenPath).pipe(
|
|
65
|
+
Effect.catchAll(() => Effect.succeed(false))
|
|
66
|
+
)
|
|
67
|
+
if (!exists) {
|
|
68
|
+
return null
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const content = yield* fs.readFileString(tokenPath).pipe(
|
|
72
|
+
Effect.mapError((cause) => new FileSystemError({ operation: "read", path: tokenPath, cause }))
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
const parsed = yield* Effect.sync(() => {
|
|
76
|
+
try {
|
|
77
|
+
return JSON.parse(content) as unknown
|
|
78
|
+
} catch {
|
|
79
|
+
return null
|
|
80
|
+
}
|
|
81
|
+
})
|
|
82
|
+
if (parsed === null) {
|
|
83
|
+
yield* Effect.logWarning(`Corrupted auth.json at ${tokenPath} — could not parse JSON`)
|
|
84
|
+
return null
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return yield* Schema.decodeUnknown(OAuthTokenSchema)(parsed).pipe(
|
|
88
|
+
Effect.catchAll((e) =>
|
|
89
|
+
Effect.logWarning(`Invalid token schema in ${tokenPath}: ${e}`).pipe(
|
|
90
|
+
Effect.map(() => null)
|
|
91
|
+
)
|
|
92
|
+
)
|
|
93
|
+
)
|
|
94
|
+
})
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Save OAuth token to disk with secure permissions.
|
|
98
|
+
*
|
|
99
|
+
* @param toolName - The tool name (e.g., "confluence", "jira")
|
|
100
|
+
* @param token - The token to save
|
|
101
|
+
*
|
|
102
|
+
* @category Token Storage
|
|
103
|
+
*/
|
|
104
|
+
export const saveToken = (
|
|
105
|
+
toolName: string,
|
|
106
|
+
token: OAuthToken
|
|
107
|
+
): Effect.Effect<
|
|
108
|
+
void,
|
|
109
|
+
FileSystemError | HomeDirectoryError | Error.PlatformError,
|
|
110
|
+
FileSystem.FileSystem | Path.Path | HomeDirectoryTag
|
|
111
|
+
> =>
|
|
112
|
+
Effect.gen(function*() {
|
|
113
|
+
const fs = yield* FileSystem.FileSystem
|
|
114
|
+
yield* ensureConfigDir(toolName)
|
|
115
|
+
const tokenPath = yield* getAuthPath(toolName)
|
|
116
|
+
|
|
117
|
+
yield* writeSecureFile(tokenPath, JSON.stringify(token, null, 2)).pipe(
|
|
118
|
+
Effect.provideService(FileSystem.FileSystem, fs)
|
|
119
|
+
)
|
|
120
|
+
})
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Delete stored OAuth token.
|
|
124
|
+
*
|
|
125
|
+
* @param toolName - The tool name (e.g., "confluence", "jira")
|
|
126
|
+
*
|
|
127
|
+
* @category Token Storage
|
|
128
|
+
*/
|
|
129
|
+
export const deleteToken = (
|
|
130
|
+
toolName: string
|
|
131
|
+
): Effect.Effect<void, FileSystemError | HomeDirectoryError, FileSystem.FileSystem | Path.Path | HomeDirectoryTag> =>
|
|
132
|
+
Effect.gen(function*() {
|
|
133
|
+
const fs = yield* FileSystem.FileSystem
|
|
134
|
+
const tokenPath = yield* getAuthPath(toolName)
|
|
135
|
+
|
|
136
|
+
yield* fs.remove(tokenPath).pipe(
|
|
137
|
+
Effect.catchAll(() => Effect.void)
|
|
138
|
+
)
|
|
139
|
+
})
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Load OAuth client configuration from disk.
|
|
143
|
+
*
|
|
144
|
+
* @param toolName - The tool name (e.g., "confluence", "jira")
|
|
145
|
+
* @returns The stored config or null if not found/invalid
|
|
146
|
+
*
|
|
147
|
+
* @category OAuth Config
|
|
148
|
+
*/
|
|
149
|
+
export const loadOAuthConfig = (
|
|
150
|
+
toolName: string
|
|
151
|
+
): Effect.Effect<
|
|
152
|
+
OAuthConfig | null,
|
|
153
|
+
FileSystemError | HomeDirectoryError,
|
|
154
|
+
FileSystem.FileSystem | Path.Path | HomeDirectoryTag
|
|
155
|
+
> =>
|
|
156
|
+
Effect.gen(function*() {
|
|
157
|
+
const fs = yield* FileSystem.FileSystem
|
|
158
|
+
const configPath = yield* getOAuthConfigPath(toolName)
|
|
159
|
+
|
|
160
|
+
const exists = yield* fs.exists(configPath).pipe(
|
|
161
|
+
Effect.catchAll(() => Effect.succeed(false))
|
|
162
|
+
)
|
|
163
|
+
if (!exists) {
|
|
164
|
+
return null
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
const content = yield* fs.readFileString(configPath).pipe(
|
|
168
|
+
Effect.mapError((cause) => new FileSystemError({ operation: "read", path: configPath, cause }))
|
|
169
|
+
)
|
|
170
|
+
|
|
171
|
+
const parsed = yield* Effect.sync(() => {
|
|
172
|
+
try {
|
|
173
|
+
return JSON.parse(content) as unknown
|
|
174
|
+
} catch {
|
|
175
|
+
return null
|
|
176
|
+
}
|
|
177
|
+
})
|
|
178
|
+
if (parsed === null) {
|
|
179
|
+
yield* Effect.logWarning(`Corrupted oauth config at ${configPath} — could not parse JSON`)
|
|
180
|
+
return null
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
return yield* Schema.decodeUnknown(OAuthConfigSchema)(parsed).pipe(
|
|
184
|
+
Effect.catchAll((e) =>
|
|
185
|
+
Effect.logWarning(`Invalid OAuth config schema in ${configPath}: ${e}`).pipe(
|
|
186
|
+
Effect.map(() => null)
|
|
187
|
+
)
|
|
188
|
+
)
|
|
189
|
+
)
|
|
190
|
+
})
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Save OAuth client configuration to disk with secure permissions.
|
|
194
|
+
*
|
|
195
|
+
* @param toolName - The tool name (e.g., "confluence", "jira")
|
|
196
|
+
* @param config - The config to save
|
|
197
|
+
*
|
|
198
|
+
* @category OAuth Config
|
|
199
|
+
*/
|
|
200
|
+
export const saveOAuthConfig = (
|
|
201
|
+
toolName: string,
|
|
202
|
+
config: OAuthConfig
|
|
203
|
+
): Effect.Effect<
|
|
204
|
+
void,
|
|
205
|
+
FileSystemError | HomeDirectoryError | Error.PlatformError,
|
|
206
|
+
FileSystem.FileSystem | Path.Path | HomeDirectoryTag
|
|
207
|
+
> =>
|
|
208
|
+
Effect.gen(function*() {
|
|
209
|
+
const fs = yield* FileSystem.FileSystem
|
|
210
|
+
yield* ensureConfigDir(toolName)
|
|
211
|
+
const configPath = yield* getOAuthConfigPath(toolName)
|
|
212
|
+
|
|
213
|
+
yield* writeSecureFile(configPath, JSON.stringify(config, null, 2)).pipe(
|
|
214
|
+
Effect.provideService(FileSystem.FileSystem, fs)
|
|
215
|
+
)
|
|
216
|
+
})
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* Check if token is expired or about to expire.
|
|
220
|
+
*
|
|
221
|
+
* @param token - The token to check
|
|
222
|
+
* @param bufferMs - Buffer time in ms before expiration (default: 5 minutes)
|
|
223
|
+
* @returns True if token is expired or will expire within buffer time
|
|
224
|
+
*
|
|
225
|
+
* @category Utilities
|
|
226
|
+
*/
|
|
227
|
+
export const isTokenExpired = (token: OAuthToken, bufferMs: number = 5 * 60 * 1000): boolean =>
|
|
228
|
+
Date.now() >= token.expires_at - bufferMs
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Barrel export for config path utilities, schemas, and token storage.
|
|
3
|
+
*
|
|
4
|
+
* @module
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
// Config paths
|
|
8
|
+
export {
|
|
9
|
+
ensureConfigDir,
|
|
10
|
+
getAuthPath,
|
|
11
|
+
getConfigDir,
|
|
12
|
+
getOAuthConfigPath,
|
|
13
|
+
type HomeDirectory,
|
|
14
|
+
HomeDirectoryError,
|
|
15
|
+
HomeDirectoryLive,
|
|
16
|
+
HomeDirectoryTag,
|
|
17
|
+
writeSecureFile
|
|
18
|
+
} from "./ConfigPaths.js"
|
|
19
|
+
|
|
20
|
+
// OAuth schemas
|
|
21
|
+
export {
|
|
22
|
+
type AtlassianSite,
|
|
23
|
+
AtlassianSiteSchema,
|
|
24
|
+
type AtlassianUser,
|
|
25
|
+
AtlassianUserSchema,
|
|
26
|
+
type OAuthConfig,
|
|
27
|
+
OAuthConfigSchema,
|
|
28
|
+
type OAuthToken,
|
|
29
|
+
OAuthTokenSchema,
|
|
30
|
+
type OAuthUser,
|
|
31
|
+
OAuthUserSchema
|
|
32
|
+
} from "./OAuthSchemas.js"
|
|
33
|
+
|
|
34
|
+
// Token storage
|
|
35
|
+
export {
|
|
36
|
+
deleteToken,
|
|
37
|
+
FileSystemError,
|
|
38
|
+
isTokenExpired,
|
|
39
|
+
loadOAuthConfig,
|
|
40
|
+
loadToken,
|
|
41
|
+
saveOAuthConfig,
|
|
42
|
+
saveToken
|
|
43
|
+
} from "./TokenStorage.js"
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Root barrel export for `@knpkv/atlassian-common` — AST nodes, auth, config, serializers.
|
|
3
|
+
*
|
|
4
|
+
* @module
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
// AST types
|
|
8
|
+
export * from "./ast/index.js"
|
|
9
|
+
|
|
10
|
+
// Brand utilities
|
|
11
|
+
export {
|
|
12
|
+
ContentHash,
|
|
13
|
+
IssueKey,
|
|
14
|
+
makeBrandedNonEmptyString,
|
|
15
|
+
makeBrandedString,
|
|
16
|
+
PageId,
|
|
17
|
+
ProjectKey,
|
|
18
|
+
SpaceKey
|
|
19
|
+
} from "./Brand.js"
|
|
20
|
+
|
|
21
|
+
// Hash utilities
|
|
22
|
+
export { hashBuffer, hashContent, hashContentSync, hashEquals } from "./Hash.js"
|
|
23
|
+
|
|
24
|
+
// Error types
|
|
25
|
+
export { ParseError, SerializeError } from "./SerializeError.js"
|
|
26
|
+
|
|
27
|
+
// Serializers
|
|
28
|
+
export { serializeInlineNodes, type SerializeOptions, serializeToMarkdown } from "./serializers/index.js"
|
|
29
|
+
|
|
30
|
+
// Config utilities
|
|
31
|
+
export * from "./config/index.js"
|
|
32
|
+
|
|
33
|
+
// Auth utilities
|
|
34
|
+
export * from "./auth/index.js"
|