@stoneforge/quarry 1.12.0 → 1.14.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/README.md +2 -0
- package/dist/api/quarry-api.d.ts +9 -1
- package/dist/api/quarry-api.d.ts.map +1 -1
- package/dist/api/quarry-api.js +21 -2
- package/dist/api/quarry-api.js.map +1 -1
- package/dist/api/types.d.ts +8 -1
- package/dist/api/types.d.ts.map +1 -1
- package/dist/api/types.js.map +1 -1
- package/dist/cli/commands/auto-link-helper.d.ts +33 -0
- package/dist/cli/commands/auto-link-helper.d.ts.map +1 -0
- package/dist/cli/commands/auto-link-helper.js +74 -0
- package/dist/cli/commands/auto-link-helper.js.map +1 -0
- package/dist/cli/commands/crud.d.ts +3 -0
- package/dist/cli/commands/crud.d.ts.map +1 -1
- package/dist/cli/commands/crud.js +144 -15
- package/dist/cli/commands/crud.js.map +1 -1
- package/dist/cli/commands/docs.js +2 -2
- package/dist/cli/commands/docs.js.map +1 -1
- package/dist/cli/commands/document.js +1 -1
- package/dist/cli/commands/document.js.map +1 -1
- package/dist/cli/commands/entity.js +1 -1
- package/dist/cli/commands/entity.js.map +1 -1
- package/dist/cli/commands/external-sync.d.ts +18 -0
- package/dist/cli/commands/external-sync.d.ts.map +1 -0
- package/dist/cli/commands/external-sync.js +2499 -0
- package/dist/cli/commands/external-sync.js.map +1 -0
- package/dist/cli/commands/library.js +1 -1
- package/dist/cli/commands/library.js.map +1 -1
- package/dist/cli/commands/message.js +2 -2
- package/dist/cli/commands/message.js.map +1 -1
- package/dist/cli/commands/serve.d.ts.map +1 -1
- package/dist/cli/commands/serve.js +2 -0
- package/dist/cli/commands/serve.js.map +1 -1
- package/dist/cli/commands/task.d.ts.map +1 -1
- package/dist/cli/commands/task.js +7 -4
- package/dist/cli/commands/task.js.map +1 -1
- package/dist/cli/commands/team.js +1 -1
- package/dist/cli/commands/team.js.map +1 -1
- package/dist/cli/commands/workflow.js +1 -1
- package/dist/cli/commands/workflow.js.map +1 -1
- package/dist/cli/runner.d.ts.map +1 -1
- package/dist/cli/runner.js +3 -0
- package/dist/cli/runner.js.map +1 -1
- package/dist/cli/utils/progress.d.ts +30 -0
- package/dist/cli/utils/progress.d.ts.map +1 -0
- package/dist/cli/utils/progress.js +47 -0
- package/dist/cli/utils/progress.js.map +1 -0
- package/dist/config/config.d.ts.map +1 -1
- package/dist/config/config.js +34 -0
- package/dist/config/config.js.map +1 -1
- package/dist/config/defaults.d.ts +13 -1
- package/dist/config/defaults.d.ts.map +1 -1
- package/dist/config/defaults.js +22 -0
- package/dist/config/defaults.js.map +1 -1
- package/dist/config/file.d.ts.map +1 -1
- package/dist/config/file.js +71 -0
- package/dist/config/file.js.map +1 -1
- package/dist/config/index.d.ts +3 -3
- package/dist/config/index.d.ts.map +1 -1
- package/dist/config/index.js +2 -2
- package/dist/config/index.js.map +1 -1
- package/dist/config/merge.d.ts.map +1 -1
- package/dist/config/merge.js +52 -1
- package/dist/config/merge.js.map +1 -1
- package/dist/config/types.d.ts +68 -1
- package/dist/config/types.d.ts.map +1 -1
- package/dist/config/types.js +33 -0
- package/dist/config/types.js.map +1 -1
- package/dist/config/validation.d.ts.map +1 -1
- package/dist/config/validation.js +64 -1
- package/dist/config/validation.js.map +1 -1
- package/dist/external-sync/adapters/document-sync-adapter.d.ts +150 -0
- package/dist/external-sync/adapters/document-sync-adapter.d.ts.map +1 -0
- package/dist/external-sync/adapters/document-sync-adapter.js +325 -0
- package/dist/external-sync/adapters/document-sync-adapter.js.map +1 -0
- package/dist/external-sync/adapters/task-sync-adapter.d.ts +177 -0
- package/dist/external-sync/adapters/task-sync-adapter.d.ts.map +1 -0
- package/dist/external-sync/adapters/task-sync-adapter.js +353 -0
- package/dist/external-sync/adapters/task-sync-adapter.js.map +1 -0
- package/dist/external-sync/auto-link.d.ts +66 -0
- package/dist/external-sync/auto-link.d.ts.map +1 -0
- package/dist/external-sync/auto-link.js +98 -0
- package/dist/external-sync/auto-link.js.map +1 -0
- package/dist/external-sync/conflict-resolver.d.ts +170 -0
- package/dist/external-sync/conflict-resolver.d.ts.map +1 -0
- package/dist/external-sync/conflict-resolver.js +580 -0
- package/dist/external-sync/conflict-resolver.js.map +1 -0
- package/dist/external-sync/index.d.ts +23 -0
- package/dist/external-sync/index.d.ts.map +1 -0
- package/dist/external-sync/index.js +24 -0
- package/dist/external-sync/index.js.map +1 -0
- package/dist/external-sync/provider-registry.d.ts +113 -0
- package/dist/external-sync/provider-registry.d.ts.map +1 -0
- package/dist/external-sync/provider-registry.js +205 -0
- package/dist/external-sync/provider-registry.js.map +1 -0
- package/dist/external-sync/providers/folder/folder-document-adapter.d.ts +97 -0
- package/dist/external-sync/providers/folder/folder-document-adapter.d.ts.map +1 -0
- package/dist/external-sync/providers/folder/folder-document-adapter.js +261 -0
- package/dist/external-sync/providers/folder/folder-document-adapter.js.map +1 -0
- package/dist/external-sync/providers/folder/folder-fs.d.ts +146 -0
- package/dist/external-sync/providers/folder/folder-fs.d.ts.map +1 -0
- package/dist/external-sync/providers/folder/folder-fs.js +300 -0
- package/dist/external-sync/providers/folder/folder-fs.js.map +1 -0
- package/dist/external-sync/providers/folder/folder-provider.d.ts +28 -0
- package/dist/external-sync/providers/folder/folder-provider.d.ts.map +1 -0
- package/dist/external-sync/providers/folder/folder-provider.js +87 -0
- package/dist/external-sync/providers/folder/folder-provider.js.map +1 -0
- package/dist/external-sync/providers/folder/index.d.ts +11 -0
- package/dist/external-sync/providers/folder/index.d.ts.map +1 -0
- package/dist/external-sync/providers/folder/index.js +13 -0
- package/dist/external-sync/providers/folder/index.js.map +1 -0
- package/dist/external-sync/providers/github/github-api.d.ts +271 -0
- package/dist/external-sync/providers/github/github-api.d.ts.map +1 -0
- package/dist/external-sync/providers/github/github-api.js +366 -0
- package/dist/external-sync/providers/github/github-api.js.map +1 -0
- package/dist/external-sync/providers/github/github-field-map.d.ts +76 -0
- package/dist/external-sync/providers/github/github-field-map.d.ts.map +1 -0
- package/dist/external-sync/providers/github/github-field-map.js +157 -0
- package/dist/external-sync/providers/github/github-field-map.js.map +1 -0
- package/dist/external-sync/providers/github/github-provider.d.ts +36 -0
- package/dist/external-sync/providers/github/github-provider.d.ts.map +1 -0
- package/dist/external-sync/providers/github/github-provider.js +212 -0
- package/dist/external-sync/providers/github/github-provider.js.map +1 -0
- package/dist/external-sync/providers/github/github-task-adapter.d.ts +135 -0
- package/dist/external-sync/providers/github/github-task-adapter.d.ts.map +1 -0
- package/dist/external-sync/providers/github/github-task-adapter.js +374 -0
- package/dist/external-sync/providers/github/github-task-adapter.js.map +1 -0
- package/dist/external-sync/providers/github/index.d.ts +12 -0
- package/dist/external-sync/providers/github/index.d.ts.map +1 -0
- package/dist/external-sync/providers/github/index.js +15 -0
- package/dist/external-sync/providers/github/index.js.map +1 -0
- package/dist/external-sync/providers/index.d.ts +13 -0
- package/dist/external-sync/providers/index.d.ts.map +1 -0
- package/dist/external-sync/providers/index.js +15 -0
- package/dist/external-sync/providers/index.js.map +1 -0
- package/dist/external-sync/providers/linear/index.d.ts +19 -0
- package/dist/external-sync/providers/linear/index.d.ts.map +1 -0
- package/dist/external-sync/providers/linear/index.js +19 -0
- package/dist/external-sync/providers/linear/index.js.map +1 -0
- package/dist/external-sync/providers/linear/linear-api.d.ts +252 -0
- package/dist/external-sync/providers/linear/linear-api.d.ts.map +1 -0
- package/dist/external-sync/providers/linear/linear-api.js +522 -0
- package/dist/external-sync/providers/linear/linear-api.js.map +1 -0
- package/dist/external-sync/providers/linear/linear-field-map.d.ts +135 -0
- package/dist/external-sync/providers/linear/linear-field-map.d.ts.map +1 -0
- package/dist/external-sync/providers/linear/linear-field-map.js +338 -0
- package/dist/external-sync/providers/linear/linear-field-map.js.map +1 -0
- package/dist/external-sync/providers/linear/linear-provider.d.ts +52 -0
- package/dist/external-sync/providers/linear/linear-provider.d.ts.map +1 -0
- package/dist/external-sync/providers/linear/linear-provider.js +169 -0
- package/dist/external-sync/providers/linear/linear-provider.js.map +1 -0
- package/dist/external-sync/providers/linear/linear-task-adapter.d.ts +190 -0
- package/dist/external-sync/providers/linear/linear-task-adapter.d.ts.map +1 -0
- package/dist/external-sync/providers/linear/linear-task-adapter.js +521 -0
- package/dist/external-sync/providers/linear/linear-task-adapter.js.map +1 -0
- package/dist/external-sync/providers/linear/linear-types.d.ts +114 -0
- package/dist/external-sync/providers/linear/linear-types.d.ts.map +1 -0
- package/dist/external-sync/providers/linear/linear-types.js +10 -0
- package/dist/external-sync/providers/linear/linear-types.js.map +1 -0
- package/dist/external-sync/providers/notion/index.d.ts +19 -0
- package/dist/external-sync/providers/notion/index.d.ts.map +1 -0
- package/dist/external-sync/providers/notion/index.js +20 -0
- package/dist/external-sync/providers/notion/index.js.map +1 -0
- package/dist/external-sync/providers/notion/notion-api.d.ts +253 -0
- package/dist/external-sync/providers/notion/notion-api.d.ts.map +1 -0
- package/dist/external-sync/providers/notion/notion-api.js +492 -0
- package/dist/external-sync/providers/notion/notion-api.js.map +1 -0
- package/dist/external-sync/providers/notion/notion-blocks.d.ts +93 -0
- package/dist/external-sync/providers/notion/notion-blocks.d.ts.map +1 -0
- package/dist/external-sync/providers/notion/notion-blocks.js +773 -0
- package/dist/external-sync/providers/notion/notion-blocks.js.map +1 -0
- package/dist/external-sync/providers/notion/notion-document-adapter.d.ts +176 -0
- package/dist/external-sync/providers/notion/notion-document-adapter.d.ts.map +1 -0
- package/dist/external-sync/providers/notion/notion-document-adapter.js +413 -0
- package/dist/external-sync/providers/notion/notion-document-adapter.js.map +1 -0
- package/dist/external-sync/providers/notion/notion-provider.d.ts +57 -0
- package/dist/external-sync/providers/notion/notion-provider.d.ts.map +1 -0
- package/dist/external-sync/providers/notion/notion-provider.js +159 -0
- package/dist/external-sync/providers/notion/notion-provider.js.map +1 -0
- package/dist/external-sync/providers/notion/notion-types.d.ts +388 -0
- package/dist/external-sync/providers/notion/notion-types.d.ts.map +1 -0
- package/dist/external-sync/providers/notion/notion-types.js +47 -0
- package/dist/external-sync/providers/notion/notion-types.js.map +1 -0
- package/dist/external-sync/sync-engine.d.ts +364 -0
- package/dist/external-sync/sync-engine.d.ts.map +1 -0
- package/dist/external-sync/sync-engine.js +1154 -0
- package/dist/external-sync/sync-engine.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/server/index.js +8 -8
- package/dist/server/index.js.map +1 -1
- package/dist/services/inbox.js +1 -1
- package/dist/sync/hash.d.ts +5 -0
- package/dist/sync/hash.d.ts.map +1 -1
- package/dist/sync/hash.js +21 -2
- package/dist/sync/hash.js.map +1 -1
- package/package.json +10 -12
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GitHub REST API Client
|
|
3
|
+
*
|
|
4
|
+
* Pure fetch-based client for GitHub issue operations.
|
|
5
|
+
* Supports Personal Access Token (PAT) auth, rate limit handling,
|
|
6
|
+
* and configurable base URL for GitHub Enterprise.
|
|
7
|
+
*
|
|
8
|
+
* No external dependencies — uses only the standard fetch API.
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* Minimal type matching the fields we use from GitHub's issue response.
|
|
12
|
+
* Does not attempt to type the full GitHub API response.
|
|
13
|
+
*/
|
|
14
|
+
export interface GitHubIssue {
|
|
15
|
+
/** GitHub's internal issue ID */
|
|
16
|
+
readonly id: number;
|
|
17
|
+
/** Issue number within the repository */
|
|
18
|
+
readonly number: number;
|
|
19
|
+
/** Issue title */
|
|
20
|
+
readonly title: string;
|
|
21
|
+
/** Issue body/description (markdown) */
|
|
22
|
+
readonly body: string | null;
|
|
23
|
+
/** Issue state */
|
|
24
|
+
readonly state: 'open' | 'closed';
|
|
25
|
+
/** Labels attached to the issue */
|
|
26
|
+
readonly labels: readonly GitHubLabel[];
|
|
27
|
+
/** Users assigned to the issue */
|
|
28
|
+
readonly assignees: readonly GitHubUser[];
|
|
29
|
+
/** URL to view the issue in a browser */
|
|
30
|
+
readonly html_url: string;
|
|
31
|
+
/** Creation timestamp (ISO 8601) */
|
|
32
|
+
readonly created_at: string;
|
|
33
|
+
/** Last update timestamp (ISO 8601) */
|
|
34
|
+
readonly updated_at: string;
|
|
35
|
+
/** Closure timestamp (ISO 8601), null if still open */
|
|
36
|
+
readonly closed_at: string | null;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Minimal GitHub label representation
|
|
40
|
+
*/
|
|
41
|
+
export interface GitHubLabel {
|
|
42
|
+
readonly id: number;
|
|
43
|
+
readonly name: string;
|
|
44
|
+
readonly color: string;
|
|
45
|
+
readonly description: string | null;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Minimal GitHub user representation
|
|
49
|
+
*/
|
|
50
|
+
export interface GitHubUser {
|
|
51
|
+
readonly login: string;
|
|
52
|
+
readonly id: number;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Options for listing issues
|
|
56
|
+
*/
|
|
57
|
+
export interface ListIssuesOptions {
|
|
58
|
+
/** Only return issues updated after this ISO 8601 timestamp */
|
|
59
|
+
since?: string;
|
|
60
|
+
/** Filter by state */
|
|
61
|
+
state?: 'open' | 'closed' | 'all';
|
|
62
|
+
/** Results per page (max 100, default 30) */
|
|
63
|
+
per_page?: number;
|
|
64
|
+
/** Page number for manual pagination */
|
|
65
|
+
page?: number;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Input for creating a new label on a repository
|
|
69
|
+
*/
|
|
70
|
+
export interface CreateLabelInput {
|
|
71
|
+
/** Label name (required) */
|
|
72
|
+
name: string;
|
|
73
|
+
/** Label color as hex string without '#' prefix (e.g. '0075ca') */
|
|
74
|
+
color: string;
|
|
75
|
+
/** Optional description for the label */
|
|
76
|
+
description?: string;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Input for creating a new issue
|
|
80
|
+
*/
|
|
81
|
+
export interface CreateIssueInput {
|
|
82
|
+
/** Issue title (required) */
|
|
83
|
+
title: string;
|
|
84
|
+
/** Issue body/description */
|
|
85
|
+
body?: string;
|
|
86
|
+
/** Label names to attach */
|
|
87
|
+
labels?: string[];
|
|
88
|
+
/** Usernames to assign */
|
|
89
|
+
assignees?: string[];
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Input for updating an existing issue
|
|
93
|
+
*/
|
|
94
|
+
export interface UpdateIssueInput {
|
|
95
|
+
/** Updated title */
|
|
96
|
+
title?: string;
|
|
97
|
+
/** Updated body */
|
|
98
|
+
body?: string;
|
|
99
|
+
/** Updated state */
|
|
100
|
+
state?: 'open' | 'closed';
|
|
101
|
+
/** Updated label names (replaces all labels) */
|
|
102
|
+
labels?: string[];
|
|
103
|
+
/** Updated assignees (replaces all assignees) */
|
|
104
|
+
assignees?: string[];
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Rate limit information parsed from GitHub response headers
|
|
108
|
+
*/
|
|
109
|
+
export interface RateLimitInfo {
|
|
110
|
+
/** Total requests allowed per hour */
|
|
111
|
+
readonly limit: number;
|
|
112
|
+
/** Requests remaining in the current window */
|
|
113
|
+
readonly remaining: number;
|
|
114
|
+
/** UTC epoch timestamp when the rate limit resets */
|
|
115
|
+
readonly reset: number;
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Configuration for creating a GitHubApiClient
|
|
119
|
+
*/
|
|
120
|
+
export interface GitHubApiClientOptions {
|
|
121
|
+
/** Personal Access Token for authentication */
|
|
122
|
+
token: string;
|
|
123
|
+
/** Base URL for GitHub API (default: https://api.github.com) */
|
|
124
|
+
apiBaseUrl?: string;
|
|
125
|
+
/** Remaining requests threshold to trigger warnings (default: 10) */
|
|
126
|
+
rateLimitWarningThreshold?: number;
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Typed error for GitHub API failures.
|
|
130
|
+
* Wraps fetch errors with status code, message, and rate limit info.
|
|
131
|
+
*/
|
|
132
|
+
export declare class GitHubApiError extends Error {
|
|
133
|
+
/** HTTP status code from GitHub's response */
|
|
134
|
+
readonly status: number;
|
|
135
|
+
/** Human-readable error message from GitHub */
|
|
136
|
+
readonly statusText: string;
|
|
137
|
+
/** Rate limit information at the time of the error */
|
|
138
|
+
readonly rateLimit: RateLimitInfo | null;
|
|
139
|
+
/** Parsed error body from GitHub (if available) */
|
|
140
|
+
readonly responseBody: Record<string, unknown> | null;
|
|
141
|
+
constructor(message: string, status: number, statusText: string, rateLimit?: RateLimitInfo | null, responseBody?: Record<string, unknown> | null, cause?: Error);
|
|
142
|
+
/**
|
|
143
|
+
* Whether this error is due to rate limiting
|
|
144
|
+
*/
|
|
145
|
+
get isRateLimited(): boolean;
|
|
146
|
+
/**
|
|
147
|
+
* Whether this error is due to authentication failure
|
|
148
|
+
*/
|
|
149
|
+
get isAuthError(): boolean;
|
|
150
|
+
/**
|
|
151
|
+
* Whether this error is a not-found response
|
|
152
|
+
*/
|
|
153
|
+
get isNotFound(): boolean;
|
|
154
|
+
/**
|
|
155
|
+
* Returns a JSON-serializable representation of the error
|
|
156
|
+
*/
|
|
157
|
+
toJSON(): {
|
|
158
|
+
name: string;
|
|
159
|
+
message: string;
|
|
160
|
+
status: number;
|
|
161
|
+
statusText: string;
|
|
162
|
+
rateLimit: RateLimitInfo | null;
|
|
163
|
+
responseBody: Record<string, unknown> | null;
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* Type guard for GitHubApiError
|
|
168
|
+
*/
|
|
169
|
+
export declare function isGitHubApiError(error: unknown): error is GitHubApiError;
|
|
170
|
+
/**
|
|
171
|
+
* Fetch-based GitHub REST API client for issue operations.
|
|
172
|
+
*
|
|
173
|
+
* Features:
|
|
174
|
+
* - Personal Access Token (PAT) authentication
|
|
175
|
+
* - Rate limit tracking with warnings when approaching limit
|
|
176
|
+
* - Configurable base URL for GitHub Enterprise
|
|
177
|
+
* - Typed errors with status code and rate limit info
|
|
178
|
+
* - Automatic pagination for listing issues
|
|
179
|
+
*
|
|
180
|
+
* @example
|
|
181
|
+
* ```typescript
|
|
182
|
+
* const client = new GitHubApiClient({ token: 'ghp_...' });
|
|
183
|
+
* const issue = await client.getIssue('owner', 'repo', 42);
|
|
184
|
+
* const issues = await client.listIssues('owner', 'repo', { state: 'open' });
|
|
185
|
+
* ```
|
|
186
|
+
*/
|
|
187
|
+
export declare class GitHubApiClient {
|
|
188
|
+
private readonly token;
|
|
189
|
+
private readonly apiBaseUrl;
|
|
190
|
+
private readonly rateLimitWarningThreshold;
|
|
191
|
+
/** Most recently observed rate limit info (updated after each request) */
|
|
192
|
+
private lastRateLimit;
|
|
193
|
+
constructor(options: GitHubApiClientOptions);
|
|
194
|
+
/**
|
|
195
|
+
* Returns the most recently observed rate limit info, or null if no requests have been made.
|
|
196
|
+
*/
|
|
197
|
+
getRateLimit(): RateLimitInfo | null;
|
|
198
|
+
/**
|
|
199
|
+
* Fetch a single issue by number.
|
|
200
|
+
*
|
|
201
|
+
* GET /repos/{owner}/{repo}/issues/{issue_number}
|
|
202
|
+
*/
|
|
203
|
+
getIssue(owner: string, repo: string, issueNumber: number): Promise<GitHubIssue>;
|
|
204
|
+
/**
|
|
205
|
+
* List issues for a repository.
|
|
206
|
+
*
|
|
207
|
+
* GET /repos/{owner}/{repo}/issues?since=...&state=all
|
|
208
|
+
*
|
|
209
|
+
* When no explicit `page` is provided, this method automatically iterates
|
|
210
|
+
* through all pages using the Link header and returns every matching issue.
|
|
211
|
+
* When a `page` is specified, only that single page is returned.
|
|
212
|
+
*/
|
|
213
|
+
listIssues(owner: string, repo: string, options?: ListIssuesOptions): Promise<GitHubIssue[]>;
|
|
214
|
+
/**
|
|
215
|
+
* Create a new issue.
|
|
216
|
+
*
|
|
217
|
+
* POST /repos/{owner}/{repo}/issues
|
|
218
|
+
*/
|
|
219
|
+
createIssue(owner: string, repo: string, input: CreateIssueInput): Promise<GitHubIssue>;
|
|
220
|
+
/**
|
|
221
|
+
* Update an existing issue.
|
|
222
|
+
*
|
|
223
|
+
* PATCH /repos/{owner}/{repo}/issues/{issue_number}
|
|
224
|
+
*/
|
|
225
|
+
updateIssue(owner: string, repo: string, issueNumber: number, updates: UpdateIssueInput): Promise<GitHubIssue>;
|
|
226
|
+
/**
|
|
227
|
+
* List all labels for a repository.
|
|
228
|
+
*
|
|
229
|
+
* GET /repos/{owner}/{repo}/labels
|
|
230
|
+
*
|
|
231
|
+
* Auto-paginates through all pages to return the complete label set.
|
|
232
|
+
*/
|
|
233
|
+
getLabels(owner: string, repo: string): Promise<GitHubLabel[]>;
|
|
234
|
+
/**
|
|
235
|
+
* Create a new label on a repository.
|
|
236
|
+
*
|
|
237
|
+
* POST /repos/{owner}/{repo}/labels
|
|
238
|
+
*/
|
|
239
|
+
createLabel(owner: string, repo: string, input: CreateLabelInput): Promise<GitHubLabel>;
|
|
240
|
+
/**
|
|
241
|
+
* Performs a single HTTP request to the GitHub API.
|
|
242
|
+
*/
|
|
243
|
+
private request;
|
|
244
|
+
/**
|
|
245
|
+
* Performs a paginated request, following Link headers to collect all pages.
|
|
246
|
+
*/
|
|
247
|
+
private paginatedRequest;
|
|
248
|
+
/**
|
|
249
|
+
* Handles non-OK responses by throwing a GitHubApiError.
|
|
250
|
+
*/
|
|
251
|
+
private handleErrorResponse;
|
|
252
|
+
/**
|
|
253
|
+
* Logs a warning when rate limit is approaching exhaustion.
|
|
254
|
+
*/
|
|
255
|
+
private checkRateLimitWarning;
|
|
256
|
+
}
|
|
257
|
+
/**
|
|
258
|
+
* Parses rate limit information from GitHub response headers.
|
|
259
|
+
* Returns null if the headers are not present.
|
|
260
|
+
*/
|
|
261
|
+
declare function parseRateLimitHeaders(headers: Headers): RateLimitInfo | null;
|
|
262
|
+
/**
|
|
263
|
+
* Parses the GitHub Link header to find the "next" page URL.
|
|
264
|
+
* Returns null if there is no next page.
|
|
265
|
+
*
|
|
266
|
+
* The Link header format is:
|
|
267
|
+
* <https://api.github.com/...?page=2>; rel="next", <https://api.github.com/...?page=5>; rel="last"
|
|
268
|
+
*/
|
|
269
|
+
declare function parseLinkHeaderNext(linkHeader: string | null): string | null;
|
|
270
|
+
export { parseRateLimitHeaders, parseLinkHeaderNext };
|
|
271
|
+
//# sourceMappingURL=github-api.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"github-api.d.ts","sourceRoot":"","sources":["../../../../src/external-sync/providers/github/github-api.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,366 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GitHub REST API Client
|
|
3
|
+
*
|
|
4
|
+
* Pure fetch-based client for GitHub issue operations.
|
|
5
|
+
* Supports Personal Access Token (PAT) auth, rate limit handling,
|
|
6
|
+
* and configurable base URL for GitHub Enterprise.
|
|
7
|
+
*
|
|
8
|
+
* No external dependencies — uses only the standard fetch API.
|
|
9
|
+
*/
|
|
10
|
+
// ============================================================================
|
|
11
|
+
// Error Types
|
|
12
|
+
// ============================================================================
|
|
13
|
+
/**
|
|
14
|
+
* Typed error for GitHub API failures.
|
|
15
|
+
* Wraps fetch errors with status code, message, and rate limit info.
|
|
16
|
+
*/
|
|
17
|
+
export class GitHubApiError extends Error {
|
|
18
|
+
/** HTTP status code from GitHub's response */
|
|
19
|
+
status;
|
|
20
|
+
/** Human-readable error message from GitHub */
|
|
21
|
+
statusText;
|
|
22
|
+
/** Rate limit information at the time of the error */
|
|
23
|
+
rateLimit;
|
|
24
|
+
/** Parsed error body from GitHub (if available) */
|
|
25
|
+
responseBody;
|
|
26
|
+
constructor(message, status, statusText, rateLimit = null, responseBody = null, cause) {
|
|
27
|
+
super(message);
|
|
28
|
+
this.name = 'GitHubApiError';
|
|
29
|
+
this.status = status;
|
|
30
|
+
this.statusText = statusText;
|
|
31
|
+
this.rateLimit = rateLimit;
|
|
32
|
+
this.responseBody = responseBody;
|
|
33
|
+
this.cause = cause;
|
|
34
|
+
if (Error.captureStackTrace) {
|
|
35
|
+
Error.captureStackTrace(this, GitHubApiError);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Whether this error is due to rate limiting
|
|
40
|
+
*/
|
|
41
|
+
get isRateLimited() {
|
|
42
|
+
return this.status === 403 && this.rateLimit !== null && this.rateLimit.remaining === 0;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Whether this error is due to authentication failure
|
|
46
|
+
*/
|
|
47
|
+
get isAuthError() {
|
|
48
|
+
return this.status === 401;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Whether this error is a not-found response
|
|
52
|
+
*/
|
|
53
|
+
get isNotFound() {
|
|
54
|
+
return this.status === 404;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Returns a JSON-serializable representation of the error
|
|
58
|
+
*/
|
|
59
|
+
toJSON() {
|
|
60
|
+
return {
|
|
61
|
+
name: this.name,
|
|
62
|
+
message: this.message,
|
|
63
|
+
status: this.status,
|
|
64
|
+
statusText: this.statusText,
|
|
65
|
+
rateLimit: this.rateLimit,
|
|
66
|
+
responseBody: this.responseBody,
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Type guard for GitHubApiError
|
|
72
|
+
*/
|
|
73
|
+
export function isGitHubApiError(error) {
|
|
74
|
+
return error instanceof GitHubApiError;
|
|
75
|
+
}
|
|
76
|
+
// ============================================================================
|
|
77
|
+
// Rate Limit Warning Threshold
|
|
78
|
+
// ============================================================================
|
|
79
|
+
/** Default threshold of remaining requests before logging a warning */
|
|
80
|
+
const DEFAULT_RATE_LIMIT_WARNING_THRESHOLD = 10;
|
|
81
|
+
// ============================================================================
|
|
82
|
+
// Client Implementation
|
|
83
|
+
// ============================================================================
|
|
84
|
+
/**
|
|
85
|
+
* Fetch-based GitHub REST API client for issue operations.
|
|
86
|
+
*
|
|
87
|
+
* Features:
|
|
88
|
+
* - Personal Access Token (PAT) authentication
|
|
89
|
+
* - Rate limit tracking with warnings when approaching limit
|
|
90
|
+
* - Configurable base URL for GitHub Enterprise
|
|
91
|
+
* - Typed errors with status code and rate limit info
|
|
92
|
+
* - Automatic pagination for listing issues
|
|
93
|
+
*
|
|
94
|
+
* @example
|
|
95
|
+
* ```typescript
|
|
96
|
+
* const client = new GitHubApiClient({ token: 'ghp_...' });
|
|
97
|
+
* const issue = await client.getIssue('owner', 'repo', 42);
|
|
98
|
+
* const issues = await client.listIssues('owner', 'repo', { state: 'open' });
|
|
99
|
+
* ```
|
|
100
|
+
*/
|
|
101
|
+
export class GitHubApiClient {
|
|
102
|
+
token;
|
|
103
|
+
apiBaseUrl;
|
|
104
|
+
rateLimitWarningThreshold;
|
|
105
|
+
/** Most recently observed rate limit info (updated after each request) */
|
|
106
|
+
lastRateLimit = null;
|
|
107
|
+
constructor(options) {
|
|
108
|
+
if (!options.token) {
|
|
109
|
+
throw new Error('GitHub API token is required');
|
|
110
|
+
}
|
|
111
|
+
this.token = options.token;
|
|
112
|
+
this.apiBaseUrl = (options.apiBaseUrl ?? 'https://api.github.com').replace(/\/$/, '');
|
|
113
|
+
this.rateLimitWarningThreshold =
|
|
114
|
+
options.rateLimitWarningThreshold ?? DEFAULT_RATE_LIMIT_WARNING_THRESHOLD;
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Returns the most recently observed rate limit info, or null if no requests have been made.
|
|
118
|
+
*/
|
|
119
|
+
getRateLimit() {
|
|
120
|
+
return this.lastRateLimit;
|
|
121
|
+
}
|
|
122
|
+
// --------------------------------------------------------------------------
|
|
123
|
+
// Public API Methods
|
|
124
|
+
// --------------------------------------------------------------------------
|
|
125
|
+
/**
|
|
126
|
+
* Fetch a single issue by number.
|
|
127
|
+
*
|
|
128
|
+
* GET /repos/{owner}/{repo}/issues/{issue_number}
|
|
129
|
+
*/
|
|
130
|
+
async getIssue(owner, repo, issueNumber) {
|
|
131
|
+
return this.request('GET', `/repos/${enc(owner)}/${enc(repo)}/issues/${issueNumber}`);
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* List issues for a repository.
|
|
135
|
+
*
|
|
136
|
+
* GET /repos/{owner}/{repo}/issues?since=...&state=all
|
|
137
|
+
*
|
|
138
|
+
* When no explicit `page` is provided, this method automatically iterates
|
|
139
|
+
* through all pages using the Link header and returns every matching issue.
|
|
140
|
+
* When a `page` is specified, only that single page is returned.
|
|
141
|
+
*/
|
|
142
|
+
async listIssues(owner, repo, options = {}) {
|
|
143
|
+
const params = new URLSearchParams();
|
|
144
|
+
if (options.since)
|
|
145
|
+
params.set('since', options.since);
|
|
146
|
+
if (options.state)
|
|
147
|
+
params.set('state', options.state);
|
|
148
|
+
if (options.per_page)
|
|
149
|
+
params.set('per_page', String(options.per_page));
|
|
150
|
+
// If a specific page was requested, fetch only that page
|
|
151
|
+
if (options.page !== undefined) {
|
|
152
|
+
params.set('page', String(options.page));
|
|
153
|
+
const path = `/repos/${enc(owner)}/${enc(repo)}/issues`;
|
|
154
|
+
const queryString = params.toString();
|
|
155
|
+
const url = queryString ? `${path}?${queryString}` : path;
|
|
156
|
+
return this.request('GET', url);
|
|
157
|
+
}
|
|
158
|
+
// Otherwise, auto-paginate through all pages
|
|
159
|
+
return this.paginatedRequest(`/repos/${enc(owner)}/${enc(repo)}/issues`, params);
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Create a new issue.
|
|
163
|
+
*
|
|
164
|
+
* POST /repos/{owner}/{repo}/issues
|
|
165
|
+
*/
|
|
166
|
+
async createIssue(owner, repo, input) {
|
|
167
|
+
return this.request('POST', `/repos/${enc(owner)}/${enc(repo)}/issues`, input);
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Update an existing issue.
|
|
171
|
+
*
|
|
172
|
+
* PATCH /repos/{owner}/{repo}/issues/{issue_number}
|
|
173
|
+
*/
|
|
174
|
+
async updateIssue(owner, repo, issueNumber, updates) {
|
|
175
|
+
return this.request('PATCH', `/repos/${enc(owner)}/${enc(repo)}/issues/${issueNumber}`, updates);
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* List all labels for a repository.
|
|
179
|
+
*
|
|
180
|
+
* GET /repos/{owner}/{repo}/labels
|
|
181
|
+
*
|
|
182
|
+
* Auto-paginates through all pages to return the complete label set.
|
|
183
|
+
*/
|
|
184
|
+
async getLabels(owner, repo) {
|
|
185
|
+
return this.paginatedRequest(`/repos/${enc(owner)}/${enc(repo)}/labels`, new URLSearchParams());
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* Create a new label on a repository.
|
|
189
|
+
*
|
|
190
|
+
* POST /repos/{owner}/{repo}/labels
|
|
191
|
+
*/
|
|
192
|
+
async createLabel(owner, repo, input) {
|
|
193
|
+
return this.request('POST', `/repos/${enc(owner)}/${enc(repo)}/labels`, input);
|
|
194
|
+
}
|
|
195
|
+
// --------------------------------------------------------------------------
|
|
196
|
+
// Internal: HTTP Request Handling
|
|
197
|
+
// --------------------------------------------------------------------------
|
|
198
|
+
/**
|
|
199
|
+
* Performs a single HTTP request to the GitHub API.
|
|
200
|
+
*/
|
|
201
|
+
async request(method, path, body) {
|
|
202
|
+
const url = `${this.apiBaseUrl}${path}`;
|
|
203
|
+
const headers = {
|
|
204
|
+
Authorization: `Bearer ${this.token}`,
|
|
205
|
+
Accept: 'application/vnd.github+json',
|
|
206
|
+
'X-GitHub-Api-Version': '2022-11-28',
|
|
207
|
+
};
|
|
208
|
+
if (body !== undefined) {
|
|
209
|
+
headers['Content-Type'] = 'application/json';
|
|
210
|
+
}
|
|
211
|
+
let response;
|
|
212
|
+
try {
|
|
213
|
+
response = await fetch(url, {
|
|
214
|
+
method,
|
|
215
|
+
headers,
|
|
216
|
+
body: body !== undefined ? JSON.stringify(body) : undefined,
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
catch (err) {
|
|
220
|
+
throw new GitHubApiError(`Network error requesting ${method} ${path}: ${err instanceof Error ? err.message : String(err)}`, 0, 'Network Error', null, null, err instanceof Error ? err : undefined);
|
|
221
|
+
}
|
|
222
|
+
// Parse rate limit headers from every response
|
|
223
|
+
const rateLimit = parseRateLimitHeaders(response.headers);
|
|
224
|
+
if (rateLimit) {
|
|
225
|
+
this.lastRateLimit = rateLimit;
|
|
226
|
+
this.checkRateLimitWarning(rateLimit, method, path);
|
|
227
|
+
}
|
|
228
|
+
if (!response.ok) {
|
|
229
|
+
await this.handleErrorResponse(response, rateLimit, method, path);
|
|
230
|
+
}
|
|
231
|
+
return (await response.json());
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* Performs a paginated request, following Link headers to collect all pages.
|
|
235
|
+
*/
|
|
236
|
+
async paginatedRequest(basePath, params) {
|
|
237
|
+
const allItems = [];
|
|
238
|
+
// Start at page 1 if not set
|
|
239
|
+
if (!params.has('page')) {
|
|
240
|
+
params.set('page', '1');
|
|
241
|
+
}
|
|
242
|
+
// Default to 100 per page for efficiency if not set
|
|
243
|
+
if (!params.has('per_page')) {
|
|
244
|
+
params.set('per_page', '100');
|
|
245
|
+
}
|
|
246
|
+
let nextUrl = `${this.apiBaseUrl}${basePath}?${params.toString()}`;
|
|
247
|
+
while (nextUrl) {
|
|
248
|
+
const headers = {
|
|
249
|
+
Authorization: `Bearer ${this.token}`,
|
|
250
|
+
Accept: 'application/vnd.github+json',
|
|
251
|
+
'X-GitHub-Api-Version': '2022-11-28',
|
|
252
|
+
};
|
|
253
|
+
let response;
|
|
254
|
+
try {
|
|
255
|
+
response = await fetch(nextUrl, { method: 'GET', headers });
|
|
256
|
+
}
|
|
257
|
+
catch (err) {
|
|
258
|
+
throw new GitHubApiError(`Network error during paginated request to ${nextUrl}: ${err instanceof Error ? err.message : String(err)}`, 0, 'Network Error', null, null, err instanceof Error ? err : undefined);
|
|
259
|
+
}
|
|
260
|
+
const rateLimit = parseRateLimitHeaders(response.headers);
|
|
261
|
+
if (rateLimit) {
|
|
262
|
+
this.lastRateLimit = rateLimit;
|
|
263
|
+
this.checkRateLimitWarning(rateLimit, 'GET', basePath);
|
|
264
|
+
}
|
|
265
|
+
if (!response.ok) {
|
|
266
|
+
await this.handleErrorResponse(response, rateLimit, 'GET', basePath);
|
|
267
|
+
}
|
|
268
|
+
const items = (await response.json());
|
|
269
|
+
allItems.push(...items);
|
|
270
|
+
// Parse Link header for next page
|
|
271
|
+
nextUrl = parseLinkHeaderNext(response.headers.get('Link'));
|
|
272
|
+
}
|
|
273
|
+
return allItems;
|
|
274
|
+
}
|
|
275
|
+
// --------------------------------------------------------------------------
|
|
276
|
+
// Internal: Error and Rate Limit Handling
|
|
277
|
+
// --------------------------------------------------------------------------
|
|
278
|
+
/**
|
|
279
|
+
* Handles non-OK responses by throwing a GitHubApiError.
|
|
280
|
+
*/
|
|
281
|
+
async handleErrorResponse(response, rateLimit, method, path) {
|
|
282
|
+
let responseBody = null;
|
|
283
|
+
let errorMessage;
|
|
284
|
+
try {
|
|
285
|
+
responseBody = (await response.json());
|
|
286
|
+
errorMessage =
|
|
287
|
+
typeof responseBody.message === 'string'
|
|
288
|
+
? responseBody.message
|
|
289
|
+
: `GitHub API error: ${response.status} ${response.statusText}`;
|
|
290
|
+
}
|
|
291
|
+
catch {
|
|
292
|
+
errorMessage = `GitHub API error: ${response.status} ${response.statusText}`;
|
|
293
|
+
}
|
|
294
|
+
// Special messaging for rate limit exhaustion
|
|
295
|
+
if (response.status === 403 && rateLimit && rateLimit.remaining === 0) {
|
|
296
|
+
const resetDate = new Date(rateLimit.reset * 1000);
|
|
297
|
+
errorMessage = `GitHub API rate limit exhausted. Resets at ${resetDate.toISOString()} (${rateLimit.reset}). ${errorMessage}`;
|
|
298
|
+
}
|
|
299
|
+
throw new GitHubApiError(`${method} ${path} failed: ${errorMessage}`, response.status, response.statusText, rateLimit, responseBody);
|
|
300
|
+
}
|
|
301
|
+
/**
|
|
302
|
+
* Logs a warning when rate limit is approaching exhaustion.
|
|
303
|
+
*/
|
|
304
|
+
checkRateLimitWarning(rateLimit, method, path) {
|
|
305
|
+
if (rateLimit.remaining <= this.rateLimitWarningThreshold && rateLimit.remaining > 0) {
|
|
306
|
+
const resetDate = new Date(rateLimit.reset * 1000);
|
|
307
|
+
console.warn(`[GitHubApiClient] Rate limit warning: ${rateLimit.remaining}/${rateLimit.limit} requests remaining. ` +
|
|
308
|
+
`Resets at ${resetDate.toISOString()}. ` +
|
|
309
|
+
`Triggered by ${method} ${path}`);
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
// ============================================================================
|
|
314
|
+
// Utility Functions
|
|
315
|
+
// ============================================================================
|
|
316
|
+
/**
|
|
317
|
+
* URI-encodes a path segment for safe inclusion in URLs.
|
|
318
|
+
*/
|
|
319
|
+
function enc(value) {
|
|
320
|
+
return encodeURIComponent(value);
|
|
321
|
+
}
|
|
322
|
+
/**
|
|
323
|
+
* Parses rate limit information from GitHub response headers.
|
|
324
|
+
* Returns null if the headers are not present.
|
|
325
|
+
*/
|
|
326
|
+
function parseRateLimitHeaders(headers) {
|
|
327
|
+
const limit = headers.get('X-RateLimit-Limit');
|
|
328
|
+
const remaining = headers.get('X-RateLimit-Remaining');
|
|
329
|
+
const reset = headers.get('X-RateLimit-Reset');
|
|
330
|
+
if (limit === null || remaining === null || reset === null) {
|
|
331
|
+
return null;
|
|
332
|
+
}
|
|
333
|
+
const parsedLimit = parseInt(limit, 10);
|
|
334
|
+
const parsedRemaining = parseInt(remaining, 10);
|
|
335
|
+
const parsedReset = parseInt(reset, 10);
|
|
336
|
+
if (isNaN(parsedLimit) || isNaN(parsedRemaining) || isNaN(parsedReset)) {
|
|
337
|
+
return null;
|
|
338
|
+
}
|
|
339
|
+
return {
|
|
340
|
+
limit: parsedLimit,
|
|
341
|
+
remaining: parsedRemaining,
|
|
342
|
+
reset: parsedReset,
|
|
343
|
+
};
|
|
344
|
+
}
|
|
345
|
+
/**
|
|
346
|
+
* Parses the GitHub Link header to find the "next" page URL.
|
|
347
|
+
* Returns null if there is no next page.
|
|
348
|
+
*
|
|
349
|
+
* The Link header format is:
|
|
350
|
+
* <https://api.github.com/...?page=2>; rel="next", <https://api.github.com/...?page=5>; rel="last"
|
|
351
|
+
*/
|
|
352
|
+
function parseLinkHeaderNext(linkHeader) {
|
|
353
|
+
if (!linkHeader)
|
|
354
|
+
return null;
|
|
355
|
+
const links = linkHeader.split(',');
|
|
356
|
+
for (const link of links) {
|
|
357
|
+
const match = link.match(/<([^>]+)>;\s*rel="next"/);
|
|
358
|
+
if (match) {
|
|
359
|
+
return match[1];
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
return null;
|
|
363
|
+
}
|
|
364
|
+
// Export utility functions for testing
|
|
365
|
+
export { parseRateLimitHeaders, parseLinkHeaderNext };
|
|
366
|
+
//# sourceMappingURL=github-api.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"github-api.js","sourceRoot":"","sources":["../../../../src/external-sync/providers/github/github-api.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAqIH,+EAA+E;AAC/E,cAAc;AACd,+EAA+E;AAE/E;;;GAGG;AACH,MAAM,OAAO,cAAe,SAAQ,KAAK;IACvC,8CAA8C;IACrC,MAAM,CAAS;IACxB,+CAA+C;IACtC,UAAU,CAAS;IAC5B,sDAAsD;IAC7C,SAAS,CAAuB;IACzC,mDAAmD;IAC1C,YAAY,CAAiC;IAEtD,YACE,OAAe,EACf,MAAc,EACd,UAAkB,EAClB,YAAkC,IAAI,EACtC,eAA+C,IAAI,EACnD,KAAa;QAEb,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,gBAAgB,CAAC;QAC7B,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,UAAU,GAAG,UAAU,CAAC;QAC7B,IAAI,CAAC,SAAS,GAAG,SAAS,CAAC;QAC3B,IAAI,CAAC,YAAY,GAAG,YAAY,CAAC;QACjC,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;QAEnB,IAAI,KAAK,CAAC,iBAAiB,EAAE,CAAC;YAC5B,KAAK,CAAC,iBAAiB,CAAC,IAAI,EAAE,cAAc,CAAC,CAAC;QAChD,CAAC;IACH,CAAC;IAED;;OAEG;IACH,IAAI,aAAa;QACf,OAAO,IAAI,CAAC,MAAM,KAAK,GAAG,IAAI,IAAI,CAAC,SAAS,KAAK,IAAI,IAAI,IAAI,CAAC,SAAS,CAAC,SAAS,KAAK,CAAC,CAAC;IAC1F,CAAC;IAED;;OAEG;IACH,IAAI,WAAW;QACb,OAAO,IAAI,CAAC,MAAM,KAAK,GAAG,CAAC;IAC7B,CAAC;IAED;;OAEG;IACH,IAAI,UAAU;QACZ,OAAO,IAAI,CAAC,MAAM,KAAK,GAAG,CAAC;IAC7B,CAAC;IAED;;OAEG;IACH,MAAM;QAQJ,OAAO;YACL,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,OAAO,EAAE,IAAI,CAAC,OAAO;YACrB,MAAM,EAAE,IAAI,CAAC,MAAM;YACnB,UAAU,EAAE,IAAI,CAAC,UAAU;YAC3B,SAAS,EAAE,IAAI,CAAC,SAAS;YACzB,YAAY,EAAE,IAAI,CAAC,YAAY;SAChC,CAAC;IACJ,CAAC;CACF;AAED;;GAEG;AACH,MAAM,UAAU,gBAAgB,CAAC,KAAc;IAC7C,OAAO,KAAK,YAAY,cAAc,CAAC;AACzC,CAAC;AAED,+EAA+E;AAC/E,+BAA+B;AAC/B,+EAA+E;AAE/E,uEAAuE;AACvE,MAAM,oCAAoC,GAAG,EAAE,CAAC;AAEhD,+EAA+E;AAC/E,wBAAwB;AACxB,+EAA+E;AAE/E;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,OAAO,eAAe;IACT,KAAK,CAAS;IACd,UAAU,CAAS;IACnB,yBAAyB,CAAS;IAEnD,0EAA0E;IAClE,aAAa,GAAyB,IAAI,CAAC;IAEnD,YAAY,OAA+B;QACzC,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC;YACnB,MAAM,IAAI,KAAK,CAAC,8BAA8B,CAAC,CAAC;QAClD,CAAC;QAED,IAAI,CAAC,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC;QAC3B,IAAI,CAAC,UAAU,GAAG,CAAC,OAAO,CAAC,UAAU,IAAI,wBAAwB,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;QACtF,IAAI,CAAC,yBAAyB;YAC5B,OAAO,CAAC,yBAAyB,IAAI,oCAAoC,CAAC;IAC9E,CAAC;IAED;;OAEG;IACH,YAAY;QACV,OAAO,IAAI,CAAC,aAAa,CAAC;IAC5B,CAAC;IAED,6EAA6E;IAC7E,qBAAqB;IACrB,6EAA6E;IAE7E;;;;OAIG;IACH,KAAK,CAAC,QAAQ,CAAC,KAAa,EAAE,IAAY,EAAE,WAAmB;QAC7D,OAAO,IAAI,CAAC,OAAO,CAAc,KAAK,EAAE,UAAU,GAAG,CAAC,KAAK,CAAC,IAAI,GAAG,CAAC,IAAI,CAAC,WAAW,WAAW,EAAE,CAAC,CAAC;IACrG,CAAC;IAED;;;;;;;;OAQG;IACH,KAAK,CAAC,UAAU,CACd,KAAa,EACb,IAAY,EACZ,UAA6B,EAAE;QAE/B,MAAM,MAAM,GAAG,IAAI,eAAe,EAAE,CAAC;QAErC,IAAI,OAAO,CAAC,KAAK;YAAE,MAAM,CAAC,GAAG,CAAC,OAAO,EAAE,OAAO,CAAC,KAAK,CAAC,CAAC;QACtD,IAAI,OAAO,CAAC,KAAK;YAAE,MAAM,CAAC,GAAG,CAAC,OAAO,EAAE,OAAO,CAAC,KAAK,CAAC,CAAC;QACtD,IAAI,OAAO,CAAC,QAAQ;YAAE,MAAM,CAAC,GAAG,CAAC,UAAU,EAAE,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC;QAEvE,yDAAyD;QACzD,IAAI,OAAO,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;YAC/B,MAAM,CAAC,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;YACzC,MAAM,IAAI,GAAG,UAAU,GAAG,CAAC,KAAK,CAAC,IAAI,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC;YACxD,MAAM,WAAW,GAAG,MAAM,CAAC,QAAQ,EAAE,CAAC;YACtC,MAAM,GAAG,GAAG,WAAW,CAAC,CAAC,CAAC,GAAG,IAAI,IAAI,WAAW,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;YAC1D,OAAO,IAAI,CAAC,OAAO,CAAgB,KAAK,EAAE,GAAG,CAAC,CAAC;QACjD,CAAC;QAED,6CAA6C;QAC7C,OAAO,IAAI,CAAC,gBAAgB,CAC1B,UAAU,GAAG,CAAC,KAAK,CAAC,IAAI,GAAG,CAAC,IAAI,CAAC,SAAS,EAC1C,MAAM,CACP,CAAC;IACJ,CAAC;IAED;;;;OAIG;IACH,KAAK,CAAC,WAAW,CACf,KAAa,EACb,IAAY,EACZ,KAAuB;QAEvB,OAAO,IAAI,CAAC,OAAO,CACjB,MAAM,EACN,UAAU,GAAG,CAAC,KAAK,CAAC,IAAI,GAAG,CAAC,IAAI,CAAC,SAAS,EAC1C,KAAK,CACN,CAAC;IACJ,CAAC;IAED;;;;OAIG;IACH,KAAK,CAAC,WAAW,CACf,KAAa,EACb,IAAY,EACZ,WAAmB,EACnB,OAAyB;QAEzB,OAAO,IAAI,CAAC,OAAO,CACjB,OAAO,EACP,UAAU,GAAG,CAAC,KAAK,CAAC,IAAI,GAAG,CAAC,IAAI,CAAC,WAAW,WAAW,EAAE,EACzD,OAAO,CACR,CAAC;IACJ,CAAC;IAED;;;;;;OAMG;IACH,KAAK,CAAC,SAAS,CAAC,KAAa,EAAE,IAAY;QACzC,OAAO,IAAI,CAAC,gBAAgB,CAC1B,UAAU,GAAG,CAAC,KAAK,CAAC,IAAI,GAAG,CAAC,IAAI,CAAC,SAAS,EAC1C,IAAI,eAAe,EAAE,CACtB,CAAC;IACJ,CAAC;IAED;;;;OAIG;IACH,KAAK,CAAC,WAAW,CACf,KAAa,EACb,IAAY,EACZ,KAAuB;QAEvB,OAAO,IAAI,CAAC,OAAO,CACjB,MAAM,EACN,UAAU,GAAG,CAAC,KAAK,CAAC,IAAI,GAAG,CAAC,IAAI,CAAC,SAAS,EAC1C,KAAK,CACN,CAAC;IACJ,CAAC;IAED,6EAA6E;IAC7E,kCAAkC;IAClC,6EAA6E;IAE7E;;OAEG;IACK,KAAK,CAAC,OAAO,CAAI,MAAc,EAAE,IAAY,EAAE,IAAc;QACnE,MAAM,GAAG,GAAG,GAAG,IAAI,CAAC,UAAU,GAAG,IAAI,EAAE,CAAC;QAExC,MAAM,OAAO,GAA2B;YACtC,aAAa,EAAE,UAAU,IAAI,CAAC,KAAK,EAAE;YACrC,MAAM,EAAE,6BAA6B;YACrC,sBAAsB,EAAE,YAAY;SACrC,CAAC;QAEF,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;YACvB,OAAO,CAAC,cAAc,CAAC,GAAG,kBAAkB,CAAC;QAC/C,CAAC;QAED,IAAI,QAAkB,CAAC;QACvB,IAAI,CAAC;YACH,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE;gBAC1B,MAAM;gBACN,OAAO;gBACP,IAAI,EAAE,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS;aAC5D,CAAC,CAAC;QACL,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,MAAM,IAAI,cAAc,CACtB,4BAA4B,MAAM,IAAI,IAAI,KAAK,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,EACjG,CAAC,EACD,eAAe,EACf,IAAI,EACJ,IAAI,EACJ,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,SAAS,CACvC,CAAC;QACJ,CAAC;QAED,+CAA+C;QAC/C,MAAM,SAAS,GAAG,qBAAqB,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;QAC1D,IAAI,SAAS,EAAE,CAAC;YACd,IAAI,CAAC,aAAa,GAAG,SAAS,CAAC;YAC/B,IAAI,CAAC,qBAAqB,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,CAAC,CAAC;QACtD,CAAC;QAED,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;YACjB,MAAM,IAAI,CAAC,mBAAmB,CAAC,QAAQ,EAAE,SAAS,EAAE,MAAM,EAAE,IAAI,CAAC,CAAC;QACpE,CAAC;QAED,OAAO,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAM,CAAC;IACtC,CAAC;IAED;;OAEG;IACK,KAAK,CAAC,gBAAgB,CAC5B,QAAgB,EAChB,MAAuB;QAEvB,MAAM,QAAQ,GAAQ,EAAE,CAAC;QAEzB,6BAA6B;QAC7B,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC;YACxB,MAAM,CAAC,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;QAC1B,CAAC;QACD,oDAAoD;QACpD,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,UAAU,CAAC,EAAE,CAAC;YAC5B,MAAM,CAAC,GAAG,CAAC,UAAU,EAAE,KAAK,CAAC,CAAC;QAChC,CAAC;QAED,IAAI,OAAO,GAAkB,GAAG,IAAI,CAAC,UAAU,GAAG,QAAQ,IAAI,MAAM,CAAC,QAAQ,EAAE,EAAE,CAAC;QAElF,OAAO,OAAO,EAAE,CAAC;YACf,MAAM,OAAO,GAA2B;gBACtC,aAAa,EAAE,UAAU,IAAI,CAAC,KAAK,EAAE;gBACrC,MAAM,EAAE,6BAA6B;gBACrC,sBAAsB,EAAE,YAAY;aACrC,CAAC;YAEF,IAAI,QAAkB,CAAC;YACvB,IAAI,CAAC;gBACH,QAAQ,GAAG,MAAM,KAAK,CAAC,OAAO,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,CAAC;YAC9D,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,MAAM,IAAI,cAAc,CACtB,6CAA6C,OAAO,KAAK,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,EAC3G,CAAC,EACD,eAAe,EACf,IAAI,EACJ,IAAI,EACJ,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,SAAS,CACvC,CAAC;YACJ,CAAC;YAED,MAAM,SAAS,GAAG,qBAAqB,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;YAC1D,IAAI,SAAS,EAAE,CAAC;gBACd,IAAI,CAAC,aAAa,GAAG,SAAS,CAAC;gBAC/B,IAAI,CAAC,qBAAqB,CAAC,SAAS,EAAE,KAAK,EAAE,QAAQ,CAAC,CAAC;YACzD,CAAC;YAED,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;gBACjB,MAAM,IAAI,CAAC,mBAAmB,CAAC,QAAQ,EAAE,SAAS,EAAE,KAAK,EAAE,QAAQ,CAAC,CAAC;YACvE,CAAC;YAED,MAAM,KAAK,GAAG,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAQ,CAAC;YAC7C,QAAQ,CAAC,IAAI,CAAC,GAAG,KAAK,CAAC,CAAC;YAExB,kCAAkC;YAClC,OAAO,GAAG,mBAAmB,CAAC,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC;QAC9D,CAAC;QAED,OAAO,QAAQ,CAAC;IAClB,CAAC;IAED,6EAA6E;IAC7E,0CAA0C;IAC1C,6EAA6E;IAE7E;;OAEG;IACK,KAAK,CAAC,mBAAmB,CAC/B,QAAkB,EAClB,SAA+B,EAC/B,MAAc,EACd,IAAY;QAEZ,IAAI,YAAY,GAAmC,IAAI,CAAC;QACxD,IAAI,YAAoB,CAAC;QAEzB,IAAI,CAAC;YACH,YAAY,GAAG,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAA4B,CAAC;YAClE,YAAY;gBACV,OAAO,YAAY,CAAC,OAAO,KAAK,QAAQ;oBACtC,CAAC,CAAC,YAAY,CAAC,OAAO;oBACtB,CAAC,CAAC,qBAAqB,QAAQ,CAAC,MAAM,IAAI,QAAQ,CAAC,UAAU,EAAE,CAAC;QACtE,CAAC;QAAC,MAAM,CAAC;YACP,YAAY,GAAG,qBAAqB,QAAQ,CAAC,MAAM,IAAI,QAAQ,CAAC,UAAU,EAAE,CAAC;QAC/E,CAAC;QAED,8CAA8C;QAC9C,IAAI,QAAQ,CAAC,MAAM,KAAK,GAAG,IAAI,SAAS,IAAI,SAAS,CAAC,SAAS,KAAK,CAAC,EAAE,CAAC;YACtE,MAAM,SAAS,GAAG,IAAI,IAAI,CAAC,SAAS,CAAC,KAAK,GAAG,IAAI,CAAC,CAAC;YACnD,YAAY,GAAG,8CAA8C,SAAS,CAAC,WAAW,EAAE,KAAK,SAAS,CAAC,KAAK,MAAM,YAAY,EAAE,CAAC;QAC/H,CAAC;QAED,MAAM,IAAI,cAAc,CACtB,GAAG,MAAM,IAAI,IAAI,YAAY,YAAY,EAAE,EAC3C,QAAQ,CAAC,MAAM,EACf,QAAQ,CAAC,UAAU,EACnB,SAAS,EACT,YAAY,CACb,CAAC;IACJ,CAAC;IAED;;OAEG;IACK,qBAAqB,CAC3B,SAAwB,EACxB,MAAc,EACd,IAAY;QAEZ,IAAI,SAAS,CAAC,SAAS,IAAI,IAAI,CAAC,yBAAyB,IAAI,SAAS,CAAC,SAAS,GAAG,CAAC,EAAE,CAAC;YACrF,MAAM,SAAS,GAAG,IAAI,IAAI,CAAC,SAAS,CAAC,KAAK,GAAG,IAAI,CAAC,CAAC;YACnD,OAAO,CAAC,IAAI,CACV,yCAAyC,SAAS,CAAC,SAAS,IAAI,SAAS,CAAC,KAAK,uBAAuB;gBACpG,aAAa,SAAS,CAAC,WAAW,EAAE,IAAI;gBACxC,gBAAgB,MAAM,IAAI,IAAI,EAAE,CACnC,CAAC;QACJ,CAAC;IACH,CAAC;CACF;AAED,+EAA+E;AAC/E,oBAAoB;AACpB,+EAA+E;AAE/E;;GAEG;AACH,SAAS,GAAG,CAAC,KAAa;IACxB,OAAO,kBAAkB,CAAC,KAAK,CAAC,CAAC;AACnC,CAAC;AAED;;;GAGG;AACH,SAAS,qBAAqB,CAAC,OAAgB;IAC7C,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,mBAAmB,CAAC,CAAC;IAC/C,MAAM,SAAS,GAAG,OAAO,CAAC,GAAG,CAAC,uBAAuB,CAAC,CAAC;IACvD,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,mBAAmB,CAAC,CAAC;IAE/C,IAAI,KAAK,KAAK,IAAI,IAAI,SAAS,KAAK,IAAI,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;QAC3D,OAAO,IAAI,CAAC;IACd,CAAC;IAED,MAAM,WAAW,GAAG,QAAQ,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;IACxC,MAAM,eAAe,GAAG,QAAQ,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC;IAChD,MAAM,WAAW,GAAG,QAAQ,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;IAExC,IAAI,KAAK,CAAC,WAAW,CAAC,IAAI,KAAK,CAAC,eAAe,CAAC,IAAI,KAAK,CAAC,WAAW,CAAC,EAAE,CAAC;QACvE,OAAO,IAAI,CAAC;IACd,CAAC;IAED,OAAO;QACL,KAAK,EAAE,WAAW;QAClB,SAAS,EAAE,eAAe;QAC1B,KAAK,EAAE,WAAW;KACnB,CAAC;AACJ,CAAC;AAED;;;;;;GAMG;AACH,SAAS,mBAAmB,CAAC,UAAyB;IACpD,IAAI,CAAC,UAAU;QAAE,OAAO,IAAI,CAAC;IAE7B,MAAM,KAAK,GAAG,UAAU,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACpC,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,yBAAyB,CAAC,CAAC;QACpD,IAAI,KAAK,EAAE,CAAC;YACV,OAAO,KAAK,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;IACH,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC;AAED,uCAAuC;AACvC,OAAO,EAAE,qBAAqB,EAAE,mBAAmB,EAAE,CAAC"}
|