forges 0.0.0 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENCE +21 -0
- package/README.md +72 -0
- package/dist/azure-devops/index.d.mts +2 -0
- package/dist/azure-devops/index.mjs +1 -0
- package/dist/azure-devops-DwQvL4TV.mjs +1355 -0
- package/dist/bitbucket/index.d.mts +2 -0
- package/dist/bitbucket/index.mjs +1 -0
- package/dist/bitbucket-CpcfJlYi.mjs +1171 -0
- package/dist/crypto-CQNEyYHD.mjs +156 -0
- package/dist/cursor-origin/index.d.mts +2 -0
- package/dist/cursor-origin/index.mjs +1 -0
- package/dist/cursor-origin-BhqYx2oW.mjs +980 -0
- package/dist/define-B2_4ob2i.d.mts +307 -0
- package/dist/define-D3yEXtKK.mjs +2276 -0
- package/dist/env.d.mts +46 -0
- package/dist/env.mjs +283 -0
- package/dist/fake/index.d.mts +227 -0
- package/dist/fake/index.mjs +1001 -0
- package/dist/fetch-D4pSRvvA.d.mts +1723 -0
- package/dist/forgejo/index.d.mts +2 -0
- package/dist/forgejo/index.mjs +1 -0
- package/dist/forgejo-BCefbHUN.mjs +2005 -0
- package/dist/gitea/index.d.mts +2 -0
- package/dist/gitea/index.mjs +22 -0
- package/dist/gitee/index.d.mts +2 -0
- package/dist/gitee/index.mjs +1 -0
- package/dist/gitee-CA-f7Rs8.mjs +1272 -0
- package/dist/github/index.d.mts +2 -0
- package/dist/github/index.mjs +1 -0
- package/dist/github-B0umL8_f.mjs +3259 -0
- package/dist/gitlab/index.d.mts +2 -0
- package/dist/gitlab/index.mjs +1 -0
- package/dist/gitlab-Bsr__Z0C.mjs +2378 -0
- package/dist/graphql-D7uGzZTf.mjs +366 -0
- package/dist/index-BQ6VS31s.d.mts +32 -0
- package/dist/index-C-jz9Mo3.d.mts +16 -0
- package/dist/index-C4fYzmK3.d.mts +102 -0
- package/dist/index-CQiIX4SH.d.mts +27 -0
- package/dist/index-CTj5gQzl.d.mts +35 -0
- package/dist/index-CliBWzOF.d.mts +23 -0
- package/dist/index-Cz_y-t3d.d.mts +36 -0
- package/dist/index-DEt25E1w.d.mts +77 -0
- package/dist/index-DIuS0EOy.d.mts +43 -0
- package/dist/index-uSpsMCrC.d.mts +26 -0
- package/dist/index.d.mts +41 -0
- package/dist/index.mjs +13 -0
- package/dist/kit.d.mts +183 -0
- package/dist/kit.mjs +4 -0
- package/dist/provider-CoZ7cCxp.d.mts +1439 -0
- package/dist/provider-Ed1YhkQN.mjs +169 -0
- package/dist/pushin/index.d.mts +2 -0
- package/dist/pushin/index.mjs +1 -0
- package/dist/pushin-CkxgJ2cU.mjs +343 -0
- package/dist/schema/Actor.json +96 -0
- package/dist/schema/AlertSupport.json +49 -0
- package/dist/schema/AuthKind.json +15 -0
- package/dist/schema/Branch.json +31 -0
- package/dist/schema/ChangedFile.json +58 -0
- package/dist/schema/Check.json +169 -0
- package/dist/schema/CheckRef.json +114 -0
- package/dist/schema/CheckState.json +14 -0
- package/dist/schema/ChecksSummary.json +34 -0
- package/dist/schema/CiJob.json +186 -0
- package/dist/schema/CiJobRef.json +132 -0
- package/dist/schema/CiRun.json +234 -0
- package/dist/schema/CiRunRef.json +103 -0
- package/dist/schema/CodeFragment.json +19 -0
- package/dist/schema/CodeMatch.json +128 -0
- package/dist/schema/Collaborator.json +169 -0
- package/dist/schema/Comment.json +336 -0
- package/dist/schema/CommentRef.json +158 -0
- package/dist/schema/Commit.json +307 -0
- package/dist/schema/CommitRef.json +104 -0
- package/dist/schema/CommitSignature.json +118 -0
- package/dist/schema/Comparison.json +355 -0
- package/dist/schema/Cursor.json +21 -0
- package/dist/schema/EventAction.json +33 -0
- package/dist/schema/EventDetail.json +649 -0
- package/dist/schema/EventKind.json +26 -0
- package/dist/schema/FileContent.json +73 -0
- package/dist/schema/FileStatus.json +15 -0
- package/dist/schema/ForgeCapabilities.json +737 -0
- package/dist/schema/ForgeEvent.json +789 -0
- package/dist/schema/ForgeInstance.json +7 -0
- package/dist/schema/ForgeKind.json +32 -0
- package/dist/schema/ForgeWarning.json +49 -0
- package/dist/schema/ForgeWarningCode.json +7 -0
- package/dist/schema/GetManyResult.json +699 -0
- package/dist/schema/Installation.json +131 -0
- package/dist/schema/InstallationToken.json +31 -0
- package/dist/schema/KnownForgeKind.json +19 -0
- package/dist/schema/Label.json +22 -0
- package/dist/schema/MergeMethod.json +14 -0
- package/dist/schema/Milestone.json +40 -0
- package/dist/schema/Notification.json +498 -0
- package/dist/schema/NotificationReason.json +28 -0
- package/dist/schema/NotificationRef.json +60 -0
- package/dist/schema/NotificationSubject.json +376 -0
- package/dist/schema/PerKind.json +49 -0
- package/dist/schema/PullBranches.json +125 -0
- package/dist/schema/PushCommit.json +24 -0
- package/dist/schema/Reaction.json +148 -0
- package/dist/schema/ReactionContent.json +17 -0
- package/dist/schema/ReactionSummary.json +70 -0
- package/dist/schema/Release.json +286 -0
- package/dist/schema/ReleaseAsset.json +169 -0
- package/dist/schema/ReleaseAssetRef.json +138 -0
- package/dist/schema/ReleaseRef.json +107 -0
- package/dist/schema/Repo.json +354 -0
- package/dist/schema/RepoFeatures.json +35 -0
- package/dist/schema/RepoPermissions.json +35 -0
- package/dist/schema/RepoRef.json +77 -0
- package/dist/schema/RepoRole.json +15 -0
- package/dist/schema/RepoVisibility.json +12 -0
- package/dist/schema/Review.json +371 -0
- package/dist/schema/ReviewComment.json +280 -0
- package/dist/schema/ReviewCommentInput.json +34 -0
- package/dist/schema/ReviewRef.json +158 -0
- package/dist/schema/ReviewState.json +15 -0
- package/dist/schema/Reviewer.json +133 -0
- package/dist/schema/SecurityAlert.json +219 -0
- package/dist/schema/SecurityAlertKind.json +14 -0
- package/dist/schema/SecurityAlertRef.json +122 -0
- package/dist/schema/SecuritySeverity.json +14 -0
- package/dist/schema/SourceKind.json +12 -0
- package/dist/schema/SubscriptionState.json +12 -0
- package/dist/schema/Support.json +23 -0
- package/dist/schema/Tag.json +25 -0
- package/dist/schema/TextLimits.json +18 -0
- package/dist/schema/Thread.json +653 -0
- package/dist/schema/ThreadKind.json +14 -0
- package/dist/schema/ThreadRef.json +131 -0
- package/dist/schema/ThreadStack.json +146 -0
- package/dist/schema/ThreadState.json +13 -0
- package/dist/schema/TreeEntry.json +46 -0
- package/dist/schema/TreeEntryType.json +13 -0
- package/dist/schema/UpsertCommentResult.json +355 -0
- package/dist/schema/User.json +123 -0
- package/dist/schema/Webhook.json +188 -0
- package/dist/schema/WebhookDeliveryRecord.json +173 -0
- package/dist/schema/WebhookDeliveryRef.json +135 -0
- package/dist/schema/WebhookRef.json +105 -0
- package/dist/schema/index.d.mts +4 -0
- package/dist/schema/index.mjs +3219 -0
- package/dist/tangled/index.d.mts +2 -0
- package/dist/tangled/index.mjs +1 -0
- package/dist/tangled-ChdPmiv-.mjs +1570 -0
- package/dist/testing/index.d.mts +123 -0
- package/dist/testing/index.mjs +242 -0
- package/dist/webhooks-rn9-8lxT.mjs +70 -0
- package/package.json +115 -3
- package/dist/.gitkeep +0 -0
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
import { d as forgeIterable, j as toWarning, kt as UnknownForgeError } from "./define-D3yEXtKK.mjs";
|
|
2
|
+
//#region src/provider.ts
|
|
3
|
+
/** Items without the sorted timestamp sort last within a page and are yielded as soon as they lead it. */
|
|
4
|
+
function mergeByTime(providers, verb, query, read) {
|
|
5
|
+
const order = {
|
|
6
|
+
sort: query.sort === "created" ? "created" : "updated",
|
|
7
|
+
direction: query.direction === "asc" ? "asc" : "desc"
|
|
8
|
+
};
|
|
9
|
+
const sign = order.direction === "asc" ? 1 : -1;
|
|
10
|
+
const rank = (item, missing) => {
|
|
11
|
+
const time = (order.sort === "created" ? item.createdAt : item.updatedAt)?.getTime();
|
|
12
|
+
return time === void 0 ? missing : sign * time;
|
|
13
|
+
};
|
|
14
|
+
return forgeIterable(async function* (warn) {
|
|
15
|
+
const sources = providers.filter((provider) => provider.can(verb)).map((provider) => ({
|
|
16
|
+
provider,
|
|
17
|
+
items: [],
|
|
18
|
+
done: false
|
|
19
|
+
}));
|
|
20
|
+
async function fill(source) {
|
|
21
|
+
while (!source.items.length && !source.done) try {
|
|
22
|
+
const page = await read(source.provider, {
|
|
23
|
+
...query,
|
|
24
|
+
...order,
|
|
25
|
+
cursor: source.cursor
|
|
26
|
+
});
|
|
27
|
+
page.warnings?.forEach(warn);
|
|
28
|
+
source.items = page.items.toSorted((a, b) => rank(a, Infinity) - rank(b, Infinity) || 0);
|
|
29
|
+
source.cursor = page.cursor;
|
|
30
|
+
source.done = !page.cursor?.nextUrl && !page.cursor?.token;
|
|
31
|
+
} catch (error) {
|
|
32
|
+
warn(toWarning("search_failed", error, `${source.provider.forge}:${source.provider.instance}`));
|
|
33
|
+
source.done = true;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
await Promise.all(sources.map(fill));
|
|
37
|
+
while (true) {
|
|
38
|
+
let next;
|
|
39
|
+
let best = Infinity;
|
|
40
|
+
for (const source of sources) {
|
|
41
|
+
const head = source.items[0];
|
|
42
|
+
if (!head) continue;
|
|
43
|
+
const key = rank(head, -Infinity);
|
|
44
|
+
if (!next || key < best) {
|
|
45
|
+
next = source;
|
|
46
|
+
best = key;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
if (!next) return;
|
|
50
|
+
yield next.items.shift();
|
|
51
|
+
await fill(next);
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Combines providers into a registry. Pass factories such as `github()`, or providers that already exist.
|
|
57
|
+
* @param factories Factories such as `github()`, or providers that already exist.
|
|
58
|
+
* @example
|
|
59
|
+
* ```ts
|
|
60
|
+
* import { createForges, github, gitlab } from 'forges'
|
|
61
|
+
*
|
|
62
|
+
* const forges = createForges([
|
|
63
|
+
* github({ auth: { type: 'token', token: process.env.GITHUB_TOKEN! } }),
|
|
64
|
+
* gitlab(),
|
|
65
|
+
* ])
|
|
66
|
+
*
|
|
67
|
+
* const repo = await forges.repos.get({ forge: 'github', instance: 'github.com', owner: 'danielroe', name: 'forges' })
|
|
68
|
+
* ```
|
|
69
|
+
*/
|
|
70
|
+
function createForges(factories) {
|
|
71
|
+
const providers = factories.map((factory) => "create" in factory ? factory.create() : factory);
|
|
72
|
+
const seen = /* @__PURE__ */ new Set();
|
|
73
|
+
for (const provider of providers) {
|
|
74
|
+
const key = `${provider.forge}:${provider.instance}`;
|
|
75
|
+
if (seen.has(key)) throw new TypeError(`Two providers are registered for ${provider.forge} on ${provider.instance}; pass \`instance\` to tell them apart`);
|
|
76
|
+
seen.add(key);
|
|
77
|
+
}
|
|
78
|
+
const find = (ref) => providers.find((provider) => provider.forge === ref.forge && provider.instance === ref.instance);
|
|
79
|
+
const route = (ref) => {
|
|
80
|
+
const provider = find(ref);
|
|
81
|
+
if (!provider) throw new UnknownForgeError(`No provider registered for ${ref.forge} on ${ref.instance}`, {
|
|
82
|
+
forge: ref.forge,
|
|
83
|
+
instance: ref.instance
|
|
84
|
+
});
|
|
85
|
+
return provider;
|
|
86
|
+
};
|
|
87
|
+
const parseUrl = (url) => {
|
|
88
|
+
for (const provider of providers) {
|
|
89
|
+
const parsed = provider.parseUrl(url);
|
|
90
|
+
if (parsed) return {
|
|
91
|
+
...parsed,
|
|
92
|
+
provider
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
return {
|
|
97
|
+
providers,
|
|
98
|
+
for: find,
|
|
99
|
+
forUrl: (url) => parseUrl(url)?.provider,
|
|
100
|
+
parseUrl,
|
|
101
|
+
repos: { get: (ref, options) => route(ref).repos.get(ref, options) },
|
|
102
|
+
threads: {
|
|
103
|
+
get: (ref, options) => route(ref).threads.get(ref, options),
|
|
104
|
+
async getMany(refs, options) {
|
|
105
|
+
const groups = /* @__PURE__ */ new Map();
|
|
106
|
+
const results = Array.from({ length: refs.length });
|
|
107
|
+
for (const [index, ref] of refs.entries()) {
|
|
108
|
+
const provider = find(ref);
|
|
109
|
+
if (!provider) {
|
|
110
|
+
const error = new UnknownForgeError(`No provider registered for ${ref.forge} on ${ref.instance}`, {
|
|
111
|
+
forge: ref.forge,
|
|
112
|
+
instance: ref.instance
|
|
113
|
+
});
|
|
114
|
+
results[index] = {
|
|
115
|
+
ok: false,
|
|
116
|
+
ref,
|
|
117
|
+
warning: toWarning("thread_unreadable", error, ref.number)
|
|
118
|
+
};
|
|
119
|
+
continue;
|
|
120
|
+
}
|
|
121
|
+
groups.set(provider, [...groups.get(provider) ?? [], index]);
|
|
122
|
+
}
|
|
123
|
+
await Promise.all([...groups].map(async ([provider, indexes]) => {
|
|
124
|
+
const batch = await provider.threads.getMany(indexes.map((index) => refs[index]), options);
|
|
125
|
+
for (const [position, index] of indexes.entries()) results[index] = batch[position];
|
|
126
|
+
}));
|
|
127
|
+
return results;
|
|
128
|
+
}
|
|
129
|
+
},
|
|
130
|
+
releases: {
|
|
131
|
+
list: (repo, options) => route(repo).releases.list(repo, options),
|
|
132
|
+
latest: (repo, options) => route(repo).releases.latest(repo, options)
|
|
133
|
+
},
|
|
134
|
+
securityAlerts: { list: (repo, options) => route(repo).securityAlerts.list(repo, options) },
|
|
135
|
+
search: {
|
|
136
|
+
threads: (query = {}) => mergeByTime(providers, "search.threads", query, (provider, page) => provider.search.threadsPage(page)),
|
|
137
|
+
repos: (query = {}) => mergeByTime(providers, "search.repos", query, (provider, page) => provider.search.reposPage(page))
|
|
138
|
+
},
|
|
139
|
+
get: (forge, instance) => providers.find((provider) => provider.forge === forge && (!instance || provider.instance === instance)),
|
|
140
|
+
all: (forge) => providers.filter((provider) => provider.forge === forge),
|
|
141
|
+
notifications: { list(options) {
|
|
142
|
+
const warnings = [];
|
|
143
|
+
return {
|
|
144
|
+
warnings,
|
|
145
|
+
async *[Symbol.asyncIterator]() {
|
|
146
|
+
for (const provider of providers) {
|
|
147
|
+
if (!provider.can("notifications.list")) continue;
|
|
148
|
+
const iterable = provider.notifications.list({
|
|
149
|
+
...options,
|
|
150
|
+
cursor: void 0
|
|
151
|
+
});
|
|
152
|
+
try {
|
|
153
|
+
yield* iterable;
|
|
154
|
+
} catch (error) {
|
|
155
|
+
warnings.push({
|
|
156
|
+
...toWarning("notifications_failed", error),
|
|
157
|
+
subject: `${provider.forge}:${provider.instance}`
|
|
158
|
+
});
|
|
159
|
+
} finally {
|
|
160
|
+
warnings.push(...iterable.warnings);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
};
|
|
165
|
+
} }
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
//#endregion
|
|
169
|
+
export { createForges as t };
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import { a as toActor, i as pushinScopesFor, n as PushinOptions, o as toComment, r as pushin, s as toRepo, t as PushinAuth } from "../index-DEt25E1w.mjs";
|
|
2
|
+
export { PushinAuth, PushinOptions, pushin, pushinScopesFor, toActor as toPushinActor, toComment as toPushinComment, toRepo as toPushinRepo };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { t as pushin, n as pushinScopesFor, r as toPushinActor, i as toPushinComment, a as toPushinRepo } from "../pushin-CkxgJ2cU.mjs";
|
|
@@ -0,0 +1,343 @@
|
|
|
1
|
+
import { A as toPage, C as requireIssueOrPull, O as toDate, T as resolveToken, f as getManyConcurrently, i as githubShapedWeb, l as createListing, n as perKind, r as verb, t as defineForgeProvider } from "./define-D3yEXtKK.mjs";
|
|
2
|
+
//#region src/pushin/normalise.ts
|
|
3
|
+
const FORGE = "pushin";
|
|
4
|
+
function toActor(origin, user) {
|
|
5
|
+
if (!user) return;
|
|
6
|
+
return {
|
|
7
|
+
...origin,
|
|
8
|
+
login: user.login,
|
|
9
|
+
id: String(user.id ?? user.login),
|
|
10
|
+
name: user.name ?? void 0,
|
|
11
|
+
avatarUrl: user.avatar_url,
|
|
12
|
+
url: user.html_url,
|
|
13
|
+
typeRaw: user.type,
|
|
14
|
+
isBotHint: user.type === "Bot"
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
function toRepoRef(origin, raw) {
|
|
18
|
+
const [owner, name] = raw.full_name.split("/");
|
|
19
|
+
return {
|
|
20
|
+
...origin,
|
|
21
|
+
owner: raw.owner?.login ?? owner ?? "",
|
|
22
|
+
name: raw.name ?? name ?? "",
|
|
23
|
+
externalId: raw.node_id ?? String(raw.id)
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
function toRepo(origin, raw) {
|
|
27
|
+
const visibility = raw.visibility === "private" || raw.private ? "private" : "public";
|
|
28
|
+
return {
|
|
29
|
+
ref: toRepoRef(origin, raw),
|
|
30
|
+
description: raw.description ?? void 0,
|
|
31
|
+
defaultBranch: raw.default_branch,
|
|
32
|
+
visibility,
|
|
33
|
+
visibilityRaw: raw.visibility,
|
|
34
|
+
isFork: false,
|
|
35
|
+
isArchived: false,
|
|
36
|
+
topics: raw.topics ?? [],
|
|
37
|
+
url: raw.html_url,
|
|
38
|
+
cloneUrls: raw.clone_url ? { https: raw.clone_url } : void 0,
|
|
39
|
+
createdAt: toDate(raw.created_at),
|
|
40
|
+
updatedAt: toDate(raw.updated_at),
|
|
41
|
+
owner: toActor(origin, raw.owner),
|
|
42
|
+
homepage: raw.homepage || void 0,
|
|
43
|
+
stars: raw.stargazers_count,
|
|
44
|
+
forks: raw.forks_count,
|
|
45
|
+
permissions: toPermissions(raw.permissions),
|
|
46
|
+
raw
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
function toLabel(raw) {
|
|
50
|
+
return {
|
|
51
|
+
name: raw.name,
|
|
52
|
+
colour: raw.color && /^[0-9a-f]{6}$/i.test(raw.color) ? raw.color : void 0,
|
|
53
|
+
description: raw.description ?? void 0
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
function toPermissions(raw) {
|
|
57
|
+
return raw && {
|
|
58
|
+
admin: raw.admin ?? false,
|
|
59
|
+
maintain: raw.maintain ?? false,
|
|
60
|
+
push: raw.push ?? false,
|
|
61
|
+
triage: raw.triage ?? false,
|
|
62
|
+
pull: raw.pull ?? false
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
function toRole(raw) {
|
|
66
|
+
const permissions = raw.permissions ?? {};
|
|
67
|
+
if (permissions.admin) return "admin";
|
|
68
|
+
if (permissions.maintain) return "maintain";
|
|
69
|
+
if (permissions.push) return "write";
|
|
70
|
+
if (permissions.triage) return "triage";
|
|
71
|
+
return permissions.pull ? "read" : "none";
|
|
72
|
+
}
|
|
73
|
+
function toCollaborator(origin, raw) {
|
|
74
|
+
return {
|
|
75
|
+
actor: toActor(origin, raw),
|
|
76
|
+
role: toRole(raw),
|
|
77
|
+
roleRaw: raw.role_name,
|
|
78
|
+
permissions: toPermissions(raw.permissions),
|
|
79
|
+
raw
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
function toComment(thread, raw) {
|
|
83
|
+
return {
|
|
84
|
+
ref: {
|
|
85
|
+
forge: FORGE,
|
|
86
|
+
instance: thread.instance,
|
|
87
|
+
thread,
|
|
88
|
+
id: String(raw.id)
|
|
89
|
+
},
|
|
90
|
+
body: raw.body,
|
|
91
|
+
author: toActor({
|
|
92
|
+
forge: FORGE,
|
|
93
|
+
instance: thread.instance
|
|
94
|
+
}, raw.user),
|
|
95
|
+
createdAt: toDate(raw.created_at),
|
|
96
|
+
updatedAt: toDate(raw.updated_at),
|
|
97
|
+
raw
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
const REASONS = {
|
|
101
|
+
assign: "assigned",
|
|
102
|
+
review_requested: "review_requested",
|
|
103
|
+
mention: "mention",
|
|
104
|
+
author: "author",
|
|
105
|
+
comment: "comment",
|
|
106
|
+
subscribed: "subscribed",
|
|
107
|
+
state_change: "state_change",
|
|
108
|
+
manual: "manual"
|
|
109
|
+
};
|
|
110
|
+
function toNotification(origin, raw) {
|
|
111
|
+
const repo = toRepoRef(origin, raw.repository);
|
|
112
|
+
const kind = raw.subject.type === "Issue" ? "issue" : raw.subject.type === "PullRequest" ? "pull_request" : void 0;
|
|
113
|
+
const number = /\/(\d+)\/?$/.exec(raw.subject.url)?.[1];
|
|
114
|
+
return {
|
|
115
|
+
ref: {
|
|
116
|
+
...origin,
|
|
117
|
+
id: String(raw.id)
|
|
118
|
+
},
|
|
119
|
+
subject: kind && number ? {
|
|
120
|
+
type: "thread",
|
|
121
|
+
thread: {
|
|
122
|
+
...origin,
|
|
123
|
+
repo,
|
|
124
|
+
kind,
|
|
125
|
+
number
|
|
126
|
+
}
|
|
127
|
+
} : {
|
|
128
|
+
type: "other",
|
|
129
|
+
typeRaw: raw.subject.type,
|
|
130
|
+
repo,
|
|
131
|
+
url: raw.subject.url
|
|
132
|
+
},
|
|
133
|
+
subjectTypeRaw: raw.subject.type,
|
|
134
|
+
reason: REASONS[raw.reason] ?? "unknown",
|
|
135
|
+
reasonRaw: raw.reason,
|
|
136
|
+
unread: raw.unread,
|
|
137
|
+
title: raw.subject.title,
|
|
138
|
+
subjectState: "unknown",
|
|
139
|
+
updatedAt: toDate(raw.updated_at) ?? /* @__PURE__ */ new Date(0),
|
|
140
|
+
lastReadAt: toDate(raw.last_read_at),
|
|
141
|
+
url: raw.html_url ?? raw.subject.url,
|
|
142
|
+
raw
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
function toThread(ref, raw) {
|
|
146
|
+
const origin = {
|
|
147
|
+
forge: FORGE,
|
|
148
|
+
instance: ref.instance
|
|
149
|
+
};
|
|
150
|
+
const kind = raw.pull_request ? "pull_request" : ref.kind;
|
|
151
|
+
const merged = kind === "pull_request" && (raw.merged || raw.merged_at || raw.pull_request?.merged_at || raw.state === "merged");
|
|
152
|
+
const state = merged ? "merged" : raw.state === "closed" ? "closed" : "open";
|
|
153
|
+
return {
|
|
154
|
+
ref: {
|
|
155
|
+
...ref,
|
|
156
|
+
kind,
|
|
157
|
+
externalId: raw.node_id ?? String(raw.id)
|
|
158
|
+
},
|
|
159
|
+
kind,
|
|
160
|
+
title: raw.title,
|
|
161
|
+
body: raw.body ?? void 0,
|
|
162
|
+
state,
|
|
163
|
+
stateRaw: merged ? "merged" : raw.state,
|
|
164
|
+
isDraft: kind === "pull_request" && (raw.draft ?? false),
|
|
165
|
+
author: toActor(origin, raw.user),
|
|
166
|
+
assignees: (raw.assignees ?? []).flatMap((user) => toActor(origin, user) ?? []),
|
|
167
|
+
reviewers: [],
|
|
168
|
+
labels: (raw.labels ?? []).map(toLabel),
|
|
169
|
+
url: raw.html_url,
|
|
170
|
+
createdAt: toDate(raw.created_at),
|
|
171
|
+
updatedAt: toDate(raw.updated_at),
|
|
172
|
+
closedAt: toDate(raw.closed_at),
|
|
173
|
+
lastActivityAt: toDate(raw.updated_at),
|
|
174
|
+
locked: raw.locked,
|
|
175
|
+
commentCount: raw.comments,
|
|
176
|
+
branches: kind === "pull_request" && (raw.head || raw.base) ? {
|
|
177
|
+
head: {
|
|
178
|
+
ref: raw.head?.ref ?? "",
|
|
179
|
+
sha: raw.head?.sha ?? void 0
|
|
180
|
+
},
|
|
181
|
+
base: { ref: raw.base?.ref ?? "" }
|
|
182
|
+
} : void 0,
|
|
183
|
+
raw
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
//#endregion
|
|
187
|
+
//#region src/pushin/index.ts
|
|
188
|
+
function setupPushin({ options, instance, origin: context, fetcher, baseUrl }) {
|
|
189
|
+
const anonymous = options.auth?.type === "anonymous";
|
|
190
|
+
const list = createListing(fetcher, "per_page");
|
|
191
|
+
const repoPath = (repo) => `/repos/${encodeURIComponent(repo.owner)}/${encodeURIComponent(repo.name)}`;
|
|
192
|
+
function threadPath(ref) {
|
|
193
|
+
return `${repoPath(ref.repo)}/${ref.kind === "pull_request" ? "pulls" : "issues"}/${encodeURIComponent(ref.number)}`;
|
|
194
|
+
}
|
|
195
|
+
async function get(thread, options) {
|
|
196
|
+
const ref = requireIssueOrPull(thread, context, "read");
|
|
197
|
+
const { data } = await fetcher.json(threadPath(ref), { signal: options?.signal });
|
|
198
|
+
return toThread(ref, data);
|
|
199
|
+
}
|
|
200
|
+
async function listPage(repo, query = {}) {
|
|
201
|
+
if (query.kind && query.kind !== "issue" && query.kind !== "pull_request") return {
|
|
202
|
+
items: [],
|
|
203
|
+
warnings: [{
|
|
204
|
+
code: "kind_unsupported",
|
|
205
|
+
message: `Pushin.eu has no listing for ${query.kind}s`
|
|
206
|
+
}]
|
|
207
|
+
};
|
|
208
|
+
const kind = query.kind === "pull_request" ? "pull_request" : "issue";
|
|
209
|
+
const result = await fetcher.page(`${repoPath(repo)}/${kind === "pull_request" ? "pulls" : "issues"}`, {
|
|
210
|
+
query: {
|
|
211
|
+
state: query.state === "merged" ? "closed" : query.state,
|
|
212
|
+
per_page: query.perPage
|
|
213
|
+
},
|
|
214
|
+
cursor: query.cursor,
|
|
215
|
+
signal: query.signal
|
|
216
|
+
});
|
|
217
|
+
return toPage(result, (raw) => {
|
|
218
|
+
const thread = toThread({
|
|
219
|
+
forge: FORGE,
|
|
220
|
+
instance,
|
|
221
|
+
repo,
|
|
222
|
+
kind,
|
|
223
|
+
number: String(raw.number)
|
|
224
|
+
}, raw);
|
|
225
|
+
return query.kind === "issue" && raw.pull_request || query.state === "merged" && thread.state !== "merged" ? void 0 : thread;
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
async function commentsPage(thread, listOptions = {}) {
|
|
229
|
+
const ref = requireIssueOrPull(thread, context, "read comments on");
|
|
230
|
+
const result = await fetcher.page(`${threadPath(ref)}/comments`, {
|
|
231
|
+
query: { per_page: listOptions.perPage },
|
|
232
|
+
cursor: listOptions.cursor,
|
|
233
|
+
signal: listOptions.signal
|
|
234
|
+
});
|
|
235
|
+
return toPage(result, (raw) => toComment(ref, raw));
|
|
236
|
+
}
|
|
237
|
+
async function eventsPage(thread, listOptions = {}) {
|
|
238
|
+
const ref = requireIssueOrPull(thread, context, "read events for");
|
|
239
|
+
const page = await commentsPage(ref, listOptions);
|
|
240
|
+
return {
|
|
241
|
+
...page,
|
|
242
|
+
items: page.items.map((comment) => ({
|
|
243
|
+
forge: FORGE,
|
|
244
|
+
instance,
|
|
245
|
+
id: comment.ref.id,
|
|
246
|
+
kind: "comment",
|
|
247
|
+
kindRaw: "comment",
|
|
248
|
+
summary: `${comment.author?.login ?? "someone"} commented`,
|
|
249
|
+
occurredAt: comment.createdAt ?? /* @__PURE__ */ new Date(0),
|
|
250
|
+
actor: comment.author,
|
|
251
|
+
repo: ref.repo,
|
|
252
|
+
thread: ref,
|
|
253
|
+
source: "poll",
|
|
254
|
+
detail: {
|
|
255
|
+
type: "comment",
|
|
256
|
+
comment: comment.ref,
|
|
257
|
+
body: comment.body
|
|
258
|
+
},
|
|
259
|
+
payload: comment.raw
|
|
260
|
+
}))
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
return {
|
|
264
|
+
traits: {
|
|
265
|
+
eventKinds: "native",
|
|
266
|
+
authKinds: ["token", "anonymous"]
|
|
267
|
+
},
|
|
268
|
+
users: { me: verb(true, async (options) => {
|
|
269
|
+
const { data } = await fetcher.json("/user", { signal: options?.signal });
|
|
270
|
+
return {
|
|
271
|
+
...toActor({
|
|
272
|
+
forge: FORGE,
|
|
273
|
+
instance
|
|
274
|
+
}, data),
|
|
275
|
+
company: data.company ?? void 0,
|
|
276
|
+
raw: data
|
|
277
|
+
};
|
|
278
|
+
}) },
|
|
279
|
+
repos: {
|
|
280
|
+
get: verb(true, async (repo, options) => toRepo({
|
|
281
|
+
forge: FORGE,
|
|
282
|
+
instance
|
|
283
|
+
}, (await fetcher.json(repoPath(repo), { signal: options?.signal })).data)),
|
|
284
|
+
listPage: verb(true, (listOptions = {}) => list("/user/repos", listOptions, (raw) => toRepo({
|
|
285
|
+
forge: FORGE,
|
|
286
|
+
instance
|
|
287
|
+
}, raw))),
|
|
288
|
+
labelsPage: verb(true, (repo, listOptions = {}) => list(`${repoPath(repo)}/labels`, listOptions, (raw) => toLabel(raw))),
|
|
289
|
+
collaboratorsPage: verb(!anonymous, (repo, listOptions = {}) => list(`${repoPath(repo)}/collaborators`, listOptions, (raw) => toCollaborator({
|
|
290
|
+
forge: FORGE,
|
|
291
|
+
instance
|
|
292
|
+
}, raw)))
|
|
293
|
+
},
|
|
294
|
+
threads: {
|
|
295
|
+
get: perKind({
|
|
296
|
+
issue: true,
|
|
297
|
+
pull_request: true
|
|
298
|
+
}, get),
|
|
299
|
+
listPage: perKind({
|
|
300
|
+
issue: true,
|
|
301
|
+
pull_request: true
|
|
302
|
+
}, listPage),
|
|
303
|
+
getMany: verb(true, (refs, options) => getManyConcurrently(refs, get, options)),
|
|
304
|
+
eventsPage: verb("emulated", eventsPage),
|
|
305
|
+
commentsPage: perKind({
|
|
306
|
+
issue: true,
|
|
307
|
+
pull_request: true
|
|
308
|
+
}, commentsPage)
|
|
309
|
+
},
|
|
310
|
+
notifications: { listPage: verb(true, (listOptions = {}) => list("/notifications", listOptions, (raw) => toNotification({
|
|
311
|
+
forge: FORGE,
|
|
312
|
+
instance
|
|
313
|
+
}, raw), { query: {
|
|
314
|
+
all: listOptions.all,
|
|
315
|
+
since: listOptions.since?.toISOString()
|
|
316
|
+
} })) },
|
|
317
|
+
web: githubShapedWeb(baseUrl.replace(/\/api\/v1$/, ""), {
|
|
318
|
+
pull: "pulls",
|
|
319
|
+
commentFragment: "comment-",
|
|
320
|
+
file: (at) => `/blob/${encodeURIComponent(at)}`,
|
|
321
|
+
lineFragment: (line) => `L${line}`
|
|
322
|
+
}),
|
|
323
|
+
webhooks: {},
|
|
324
|
+
scopes: pushinScopesFor
|
|
325
|
+
};
|
|
326
|
+
}
|
|
327
|
+
/** Creates a Pushin.eu provider. */
|
|
328
|
+
const pushin = /* @__PURE__ */ defineForgeProvider({
|
|
329
|
+
forge: FORGE,
|
|
330
|
+
experimental: true,
|
|
331
|
+
anonymous: true,
|
|
332
|
+
baseUrl: "https://pushin.eu",
|
|
333
|
+
apiPath: "/api/v1",
|
|
334
|
+
headers: { accept: "application/vnd.github+json" },
|
|
335
|
+
authHeaders: ({ options: { auth } }) => auth?.type === "token" ? async () => ({ authorization: `Bearer ${await resolveToken(auth)}` }) : void 0,
|
|
336
|
+
setup: setupPushin
|
|
337
|
+
});
|
|
338
|
+
/** Scope requirements for a Pushin.eu operation. */
|
|
339
|
+
function pushinScopesFor(_verb) {
|
|
340
|
+
return { note: "A personal access token created in Settings; Pushin.eu does not scope tokens per resource" };
|
|
341
|
+
}
|
|
342
|
+
//#endregion
|
|
343
|
+
export { toRepo as a, toComment as i, pushinScopesFor as n, toActor as r, pushin as t };
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
+
"type": "object",
|
|
4
|
+
"properties": {
|
|
5
|
+
"forge": {
|
|
6
|
+
"$ref": "#/definitions/ForgeKind"
|
|
7
|
+
},
|
|
8
|
+
"instance": {
|
|
9
|
+
"$ref": "#/definitions/ForgeInstance",
|
|
10
|
+
"description": "The host of the instance, such as `github.com` or `git.example.com`."
|
|
11
|
+
},
|
|
12
|
+
"login": {
|
|
13
|
+
"type": "string"
|
|
14
|
+
},
|
|
15
|
+
"id": {
|
|
16
|
+
"type": "string"
|
|
17
|
+
},
|
|
18
|
+
"name": {
|
|
19
|
+
"type": "string"
|
|
20
|
+
},
|
|
21
|
+
"avatarUrl": {
|
|
22
|
+
"type": "string"
|
|
23
|
+
},
|
|
24
|
+
"url": {
|
|
25
|
+
"type": "string"
|
|
26
|
+
},
|
|
27
|
+
"typeRaw": {
|
|
28
|
+
"type": "string",
|
|
29
|
+
"description": "Forge-reported account type, retained verbatim for callers that classify further."
|
|
30
|
+
},
|
|
31
|
+
"isBotHint": {
|
|
32
|
+
"type": "boolean",
|
|
33
|
+
"description": "Hint derived from forge metadata that the actor is automation. Callers own the classification policy."
|
|
34
|
+
},
|
|
35
|
+
"app": {
|
|
36
|
+
"type": "object",
|
|
37
|
+
"properties": {
|
|
38
|
+
"slug": {
|
|
39
|
+
"type": "string"
|
|
40
|
+
},
|
|
41
|
+
"id": {
|
|
42
|
+
"type": "string"
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
"required": [
|
|
46
|
+
"slug"
|
|
47
|
+
],
|
|
48
|
+
"description": "The app behind a bot account, where the forge ties one to it (GitHub Apps' `slug[bot]` users)."
|
|
49
|
+
}
|
|
50
|
+
},
|
|
51
|
+
"required": [
|
|
52
|
+
"forge",
|
|
53
|
+
"id",
|
|
54
|
+
"instance",
|
|
55
|
+
"isBotHint",
|
|
56
|
+
"login"
|
|
57
|
+
],
|
|
58
|
+
"description": "An account: a user, an organisation or a bot.",
|
|
59
|
+
"x-forges-type": "Actor",
|
|
60
|
+
"definitions": {
|
|
61
|
+
"ForgeInstance": {
|
|
62
|
+
"type": "string",
|
|
63
|
+
"description": "Host of a single forge deployment, for example `github.com` or `codeberg.org`.",
|
|
64
|
+
"x-forges-type": "ForgeInstance"
|
|
65
|
+
},
|
|
66
|
+
"ForgeKind": {
|
|
67
|
+
"anyOf": [
|
|
68
|
+
{
|
|
69
|
+
"$ref": "#/definitions/KnownForgeKind"
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
"type": "string"
|
|
73
|
+
}
|
|
74
|
+
],
|
|
75
|
+
"description": "Identifier of a forge implementation. Unknown forges are permitted so that third-party providers can be registered without a core release.",
|
|
76
|
+
"x-forges-type": "ForgeKind"
|
|
77
|
+
},
|
|
78
|
+
"KnownForgeKind": {
|
|
79
|
+
"type": "string",
|
|
80
|
+
"enum": [
|
|
81
|
+
"github",
|
|
82
|
+
"gitlab",
|
|
83
|
+
"bitbucket",
|
|
84
|
+
"forgejo",
|
|
85
|
+
"gitea",
|
|
86
|
+
"tangled",
|
|
87
|
+
"gitee",
|
|
88
|
+
"azure-devops",
|
|
89
|
+
"cursor-origin",
|
|
90
|
+
"pushin"
|
|
91
|
+
],
|
|
92
|
+
"description": "The forges this package ships providers for.",
|
|
93
|
+
"x-forges-type": "KnownForgeKind"
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
+
"type": "object",
|
|
4
|
+
"properties": {
|
|
5
|
+
"dependency": {
|
|
6
|
+
"$ref": "#/definitions/Support"
|
|
7
|
+
},
|
|
8
|
+
"code_scanning": {
|
|
9
|
+
"$ref": "#/definitions/Support"
|
|
10
|
+
},
|
|
11
|
+
"secret": {
|
|
12
|
+
"$ref": "#/definitions/Support"
|
|
13
|
+
},
|
|
14
|
+
"advisory": {
|
|
15
|
+
"$ref": "#/definitions/Support"
|
|
16
|
+
}
|
|
17
|
+
},
|
|
18
|
+
"required": [
|
|
19
|
+
"dependency",
|
|
20
|
+
"code_scanning",
|
|
21
|
+
"secret",
|
|
22
|
+
"advisory"
|
|
23
|
+
],
|
|
24
|
+
"description": "Support per security alert kind.",
|
|
25
|
+
"x-forges-type": "AlertSupport",
|
|
26
|
+
"definitions": {
|
|
27
|
+
"Support": {
|
|
28
|
+
"anyOf": [
|
|
29
|
+
{
|
|
30
|
+
"type": "boolean"
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
"type": "string",
|
|
34
|
+
"const": "emulated"
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
"type": "string",
|
|
38
|
+
"const": "experimental"
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
"type": "string",
|
|
42
|
+
"const": "unverified"
|
|
43
|
+
}
|
|
44
|
+
],
|
|
45
|
+
"description": "Capability flag. Every value other than `false` means the operation is available:\n\n- `true`: native, and verified against the forge's responses.\n- `'experimental'`: native and verified, but built on an API the forge marks unstable, so it can change.\n- `'emulated'`: composed from other calls, so the result is approximate.\n- `'unverified'`: implemented, but not yet verified against the forge's responses, so it may not work.",
|
|
46
|
+
"x-forges-type": "Support"
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
+
"type": "string",
|
|
4
|
+
"enum": [
|
|
5
|
+
"token",
|
|
6
|
+
"basic",
|
|
7
|
+
"app",
|
|
8
|
+
"app_password",
|
|
9
|
+
"oauth",
|
|
10
|
+
"anonymous"
|
|
11
|
+
],
|
|
12
|
+
"description": "The credential types that a provider can accept.",
|
|
13
|
+
"x-forges-type": "AuthKind",
|
|
14
|
+
"definitions": {}
|
|
15
|
+
}
|