@framers/agentos-ext-github 0.8.0 → 0.10.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 +19 -13
- package/dist/documents/GitHubDocumentsConnector.d.ts +123 -26
- package/dist/documents/GitHubDocumentsConnector.d.ts.map +1 -1
- package/dist/documents/GitHubDocumentsConnector.js +229 -46
- package/dist/documents/GitHubDocumentsConnector.js.map +1 -1
- package/dist/documents/index.d.ts +3 -3
- package/dist/documents/index.d.ts.map +1 -1
- package/dist/documents/index.js +2 -2
- package/dist/documents/index.js.map +1 -1
- package/dist/documents/links.d.ts +28 -14
- package/dist/documents/links.d.ts.map +1 -1
- package/dist/documents/links.js +113 -39
- package/dist/documents/links.js.map +1 -1
- package/dist/documents/tree.d.ts +47 -13
- package/dist/documents/tree.d.ts.map +1 -1
- package/dist/documents/tree.js +98 -15
- package/dist/documents/tree.js.map +1 -1
- package/manifest.json +1 -1
- package/package.json +5 -4
- package/dist/__tests__/app.test.d.ts +0 -7
- package/dist/__tests__/app.test.d.ts.map +0 -1
- package/dist/__tests__/app.test.js +0 -1219
- package/dist/__tests__/app.test.js.map +0 -1
- package/dist/__tests__/documents.test.d.ts +0 -9
- package/dist/__tests__/documents.test.d.ts.map +0 -1
- package/dist/__tests__/documents.test.js +0 -644
- package/dist/__tests__/documents.test.js.map +0 -1
- package/dist/__tests__/entry.test.d.ts +0 -7
- package/dist/__tests__/entry.test.d.ts.map +0 -1
- package/dist/__tests__/entry.test.js +0 -34
- package/dist/__tests__/entry.test.js.map +0 -1
- package/dist/__tests__/events.test.d.ts +0 -8
- package/dist/__tests__/events.test.d.ts.map +0 -1
- package/dist/__tests__/events.test.js +0 -321
- package/dist/__tests__/events.test.js.map +0 -1
- package/dist/__tests__/fileAndBranchTools.test.d.ts +0 -6
- package/dist/__tests__/fileAndBranchTools.test.d.ts.map +0 -1
- package/dist/__tests__/fileAndBranchTools.test.js +0 -310
- package/dist/__tests__/fileAndBranchTools.test.js.map +0 -1
- package/dist/__tests__/indexer.test.d.ts +0 -6
- package/dist/__tests__/indexer.test.d.ts.map +0 -1
- package/dist/__tests__/indexer.test.js +0 -253
- package/dist/__tests__/indexer.test.js.map +0 -1
- package/dist/__tests__/issueTools.test.d.ts +0 -5
- package/dist/__tests__/issueTools.test.d.ts.map +0 -1
- package/dist/__tests__/issueTools.test.js +0 -183
- package/dist/__tests__/issueTools.test.js.map +0 -1
- package/dist/__tests__/links.test.d.ts +0 -8
- package/dist/__tests__/links.test.d.ts.map +0 -1
- package/dist/__tests__/links.test.js +0 -182
- package/dist/__tests__/links.test.js.map +0 -1
- package/dist/__tests__/prTools.test.d.ts +0 -5
- package/dist/__tests__/prTools.test.d.ts.map +0 -1
- package/dist/__tests__/prTools.test.js +0 -318
- package/dist/__tests__/prTools.test.js.map +0 -1
- package/dist/__tests__/refs.test.d.ts +0 -7
- package/dist/__tests__/refs.test.d.ts.map +0 -1
- package/dist/__tests__/refs.test.js +0 -420
- package/dist/__tests__/refs.test.js.map +0 -1
- package/dist/__tests__/releaseAndActionsTools.test.d.ts +0 -6
- package/dist/__tests__/releaseAndActionsTools.test.d.ts.map +0 -1
- package/dist/__tests__/releaseAndActionsTools.test.js +0 -315
- package/dist/__tests__/releaseAndActionsTools.test.js.map +0 -1
- package/dist/__tests__/repoTools.test.d.ts +0 -6
- package/dist/__tests__/repoTools.test.d.ts.map +0 -1
- package/dist/__tests__/repoTools.test.js +0 -308
- package/dist/__tests__/repoTools.test.js.map +0 -1
- package/dist/__tests__/service.test.d.ts +0 -6
- package/dist/__tests__/service.test.d.ts.map +0 -1
- package/dist/__tests__/service.test.js +0 -173
- package/dist/__tests__/service.test.js.map +0 -1
package/dist/documents/links.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @fileoverview The GitHub addresses a person pastes, read without a request: a repository, a folder or a file on
|
|
3
3
|
* github.com, and a file on raw.githubusercontent.com. Any other address on GitHub's hosts is refused, so no form of a
|
|
4
|
-
* GitHub address is read as another site's page; an address on any other host is not GitHub's.
|
|
4
|
+
* GitHub address is read as another site's page; an address on any other host is not GitHub's. A text is read as the
|
|
5
|
+
* URL standard reads an address, so every form a browser opens as a GitHub address is answered as one.
|
|
5
6
|
* @module agentos-ext-github/documents/links
|
|
6
7
|
*/
|
|
7
8
|
/** The hosts of GitHub's site, where a repository, a folder and a file are read. */
|
|
@@ -12,6 +13,13 @@ const RAW_HOST = 'raw.githubusercontent.com';
|
|
|
12
13
|
const OTHER_HOSTS = new Set(['gist.github.com', 'api.github.com', 'codeload.github.com', 'uploads.github.com']);
|
|
13
14
|
/** The domain under which GitHub serves its users' content: raw files, gists, release assets, avatars. */
|
|
14
15
|
const CONTENT_DOMAIN = 'githubusercontent.com';
|
|
16
|
+
/**
|
|
17
|
+
* The domain of GitHub Enterprise Cloud with data residency: each enterprise on a subdomain of its own, its API on
|
|
18
|
+
* `api.<subdomain>.ghe.com`
|
|
19
|
+
* (https://docs.github.com/en/enterprise-cloud@latest/admin/data-residency/about-github-enterprise-cloud-with-data-residency).
|
|
20
|
+
* Its repositories are not read here.
|
|
21
|
+
*/
|
|
22
|
+
const ENTERPRISE_DOMAIN = 'ghe.com';
|
|
15
23
|
/** First path segments of github.com that name GitHub's own pages, never an account. */
|
|
16
24
|
const SITE_PAGES = new Set([
|
|
17
25
|
'about', 'account', 'apps', 'codespaces', 'collections', 'contact', 'copilot', 'customer-stories', 'dashboard',
|
|
@@ -23,8 +31,19 @@ const SITE_PAGES = new Set([
|
|
|
23
31
|
const OWNER = /^[A-Za-z0-9][A-Za-z0-9_-]{0,99}$/;
|
|
24
32
|
/** A repository's name: letters, digits, dots, dashes and underscores. */
|
|
25
33
|
const REPO = /^[A-Za-z0-9._-]{1,100}$/;
|
|
26
|
-
/**
|
|
27
|
-
|
|
34
|
+
/**
|
|
35
|
+
* A scheme and the slashes before an address's host, as the URL standard reads them
|
|
36
|
+
* (https://url.spec.whatwg.org/#scheme-state), in any case: after `http`, `https`, `ws`, `wss` and `ftp`, special
|
|
37
|
+
* schemes, any run of `/` and `\`, none included (https://url.spec.whatwg.org/#special-authority-slashes-state); after
|
|
38
|
+
* `file`, two of them (https://url.spec.whatwg.org/#file-slash-state), and none when a third follows, since the host is
|
|
39
|
+
* then empty (https://url.spec.whatwg.org/#file-host-state) and the address names no host; after any other scheme
|
|
40
|
+
* (`git://`, `ssh://`), `//`.
|
|
41
|
+
*/
|
|
42
|
+
const AUTHORITY = /^(?:(?:https?|wss?|ftp):[/\\]*|file:[/\\]{2}(?![/\\])|(?!file:)[a-z][a-z0-9+.-]*:\/\/)/i;
|
|
43
|
+
/** The characters the URL standard removes wherever they stand in an address: tab, line feed and carriage return. */
|
|
44
|
+
const TAB_OR_NEWLINE = /[\t\n\r]/g;
|
|
45
|
+
/** A white space character, as `String.prototype.trim` drops it. */
|
|
46
|
+
const WHITE_SPACE = /\s/;
|
|
28
47
|
/** An opaque address, such as `mailto:` or `javascript:`, which names no host. */
|
|
29
48
|
const OPAQUE = /^[A-Za-z][A-Za-z0-9+-]*:/;
|
|
30
49
|
/**
|
|
@@ -34,6 +53,12 @@ const OPAQUE = /^[A-Za-z][A-Za-z0-9+-]*:/;
|
|
|
34
53
|
* no path and stays opaque.
|
|
35
54
|
*/
|
|
36
55
|
const CREDENTIALS = /^[^/?#\s@]*@[^/?#\s]*\//;
|
|
56
|
+
/**
|
|
57
|
+
* GitHub's namespaces of a branch's and a tag's full name, which its own Raw links write
|
|
58
|
+
* (`https://github.com/cli/cli/raw/refs/tags/v2.0.0/README.md` for a file at the tag `v2.0.0`): the one form that tells
|
|
59
|
+
* a tag from a branch of the same name.
|
|
60
|
+
*/
|
|
61
|
+
const FULL_NAME = /^refs\/(?:heads|tags)\//;
|
|
37
62
|
/** Git's address of a repository over SSH without a scheme: `git@github.com:<owner>/<repo>.git`. */
|
|
38
63
|
const SCP = /^([A-Za-z0-9._-]+)@([^/:\s]+):(?!\/)(.*)$/;
|
|
39
64
|
/** A refusal: the address is GitHub's, and names nothing read here. */
|
|
@@ -41,16 +66,38 @@ function refused() {
|
|
|
41
66
|
return { kind: 'refused' };
|
|
42
67
|
}
|
|
43
68
|
/**
|
|
44
|
-
*
|
|
45
|
-
* (
|
|
46
|
-
|
|
69
|
+
* True for a character dropped around a pasted address: a C0 control or a space, as the URL standard drops them
|
|
70
|
+
* (https://url.spec.whatwg.org/#concept-basic-url-parser), or any other white space, as `String.prototype.trim` does.
|
|
71
|
+
*/
|
|
72
|
+
function isAround(code) {
|
|
73
|
+
return code <= 0x20 || WHITE_SPACE.test(String.fromCharCode(code));
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* A pasted text as the URL standard reads an address: without the C0 controls, spaces and other white space around it,
|
|
77
|
+
* and without any tab or newline within it. Read by a loop at each end, so a text of many such characters costs one
|
|
78
|
+
* pass.
|
|
79
|
+
*/
|
|
80
|
+
function cleaned(text) {
|
|
81
|
+
let start = 0;
|
|
82
|
+
let end = text.length;
|
|
83
|
+
while (start < end && isAround(text.charCodeAt(start)))
|
|
84
|
+
start += 1;
|
|
85
|
+
while (end > start && isAround(text.charCodeAt(end - 1)))
|
|
86
|
+
end -= 1;
|
|
87
|
+
return text.slice(start, end).replace(TAB_OR_NEWLINE, '');
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* The address a text holds, read as its `https://` form: the scheme and the slashes before the host are dropped
|
|
91
|
+
* whatever the scheme, and a text without one (`github.com/<owner>/<repo>`,
|
|
92
|
+
* `<user>:<password>@github.com/<owner>/<repo>`) is read as if it had it. Null for an opaque address (a scheme the URL
|
|
93
|
+
* standard does not treat as special, without `//`) and for text that is no address.
|
|
47
94
|
*/
|
|
48
95
|
function addressOf(text) {
|
|
49
|
-
const
|
|
50
|
-
if (
|
|
96
|
+
const authority = AUTHORITY.exec(text);
|
|
97
|
+
if (authority === null && OPAQUE.test(text) && !CREDENTIALS.test(text))
|
|
51
98
|
return null;
|
|
52
99
|
try {
|
|
53
|
-
return new URL(`https://${
|
|
100
|
+
return new URL(`https://${authority === null ? text : text.slice(authority[0].length)}`);
|
|
54
101
|
}
|
|
55
102
|
catch {
|
|
56
103
|
// Text that is no address; the parser's message would quote it.
|
|
@@ -68,9 +115,16 @@ function hostOf(host) {
|
|
|
68
115
|
end -= 1;
|
|
69
116
|
return host.slice(0, end).toLowerCase();
|
|
70
117
|
}
|
|
71
|
-
/** True for
|
|
118
|
+
/** True for `domain` itself or a host under it. */
|
|
119
|
+
function isUnder(host, domain) {
|
|
120
|
+
return host === domain || host.endsWith(`.${domain}`);
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* True for a host of GitHub's: its site, its raw files, its other hosts, the hosts of its users' content, and the
|
|
124
|
+
* hosts of GitHub Enterprise Cloud with data residency.
|
|
125
|
+
*/
|
|
72
126
|
function isGitHubHost(host) {
|
|
73
|
-
return SITE_HOSTS.has(host) || host === RAW_HOST || OTHER_HOSTS.has(host) || host
|
|
127
|
+
return SITE_HOSTS.has(host) || host === RAW_HOST || OTHER_HOSTS.has(host) || isUnder(host, CONTENT_DOMAIN) || isUnder(host, ENTERPRISE_DOMAIN);
|
|
74
128
|
}
|
|
75
129
|
/**
|
|
76
130
|
* An address's path as its segments, each decoded, without a trailing slash; null for a path with an empty segment
|
|
@@ -103,21 +157,29 @@ function hasControl(text) {
|
|
|
103
157
|
return false;
|
|
104
158
|
}
|
|
105
159
|
/**
|
|
106
|
-
* A folder's or a file's `<ref>/<path>` from the segments after `/tree/`, `/blob/` or `/raw/`,
|
|
107
|
-
*
|
|
108
|
-
* (`refs/
|
|
160
|
+
* A folder's or a file's `<ref>/<path>` from the segments after `/tree/`, `/blob/` or `/raw/`, a branch's or a tag's
|
|
161
|
+
* full name (`refs/heads/<branch>`, `refs/tags/<tag>`) kept whole. Null for none, for another kind of reference
|
|
162
|
+
* (`refs/pull/`, `refs/remotes/`) or a namespace with no name, and for a text with an empty, `.` or `..` segment or a
|
|
163
|
+
* control character once decoded.
|
|
109
164
|
*/
|
|
110
165
|
function refAndPathOf(segments) {
|
|
111
|
-
|
|
112
|
-
if (parts[0] === 'refs') {
|
|
113
|
-
if (parts[1] !== 'heads' || parts.length < 3)
|
|
114
|
-
return null;
|
|
115
|
-
parts = parts.slice(2);
|
|
116
|
-
}
|
|
117
|
-
const text = parts.join('/');
|
|
166
|
+
const text = segments.join('/');
|
|
118
167
|
if (text === '' || hasControl(text))
|
|
119
168
|
return null;
|
|
120
|
-
|
|
169
|
+
const parts = text.split('/');
|
|
170
|
+
if (!parts.every((part) => part !== '' && part !== '.' && part !== '..'))
|
|
171
|
+
return null;
|
|
172
|
+
if (parts[0] === 'refs' && (parts.length < 3 || (parts[1] !== 'heads' && parts[1] !== 'tags')))
|
|
173
|
+
return null;
|
|
174
|
+
return text;
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* True for a ref and a path whose ref is followed by more of the text: a slash after the ref's name, the namespace of a
|
|
178
|
+
* full name aside. Where the ref ends is `resolveRef`'s to settle; a text without such a slash names a ref alone.
|
|
179
|
+
*/
|
|
180
|
+
function namesMore(refAndPath) {
|
|
181
|
+
const name = FULL_NAME.test(refAndPath) ? refAndPath.slice(refAndPath.indexOf('/', 'refs/'.length) + 1) : refAndPath;
|
|
182
|
+
return name.includes('/');
|
|
121
183
|
}
|
|
122
184
|
/** The owner and the repository of an address's first two segments, `.git` dropped; null when either is not one. */
|
|
123
185
|
function repositoryOf(segments) {
|
|
@@ -143,9 +205,10 @@ function siteLink(segments) {
|
|
|
143
205
|
const refAndPath = refAndPathOf(segments.slice(3));
|
|
144
206
|
if (refAndPath === null)
|
|
145
207
|
return refused();
|
|
146
|
-
const named = refAndPath
|
|
147
|
-
// GitHub
|
|
148
|
-
|
|
208
|
+
const named = namesMore(refAndPath);
|
|
209
|
+
// GitHub redirects a blob address that names a ref alone by its short name to the folder, and answers 404 for one that
|
|
210
|
+
// names it by its full name and for a raw one.
|
|
211
|
+
if (view === 'tree' || (view === 'blob' && !named && !FULL_NAME.test(refAndPath)))
|
|
149
212
|
return { kind: 'github_folder', ...repository, refAndPath };
|
|
150
213
|
if (!named)
|
|
151
214
|
return refused();
|
|
@@ -157,34 +220,45 @@ function rawLink(segments) {
|
|
|
157
220
|
return refused();
|
|
158
221
|
const repository = repositoryOf(segments);
|
|
159
222
|
const refAndPath = refAndPathOf(segments.slice(2));
|
|
160
|
-
if (repository === null || refAndPath === null || !refAndPath
|
|
223
|
+
if (repository === null || refAndPath === null || !namesMore(refAndPath))
|
|
161
224
|
return refused();
|
|
162
225
|
return { kind: 'github_file', ...repository, refAndPath, raw: true };
|
|
163
226
|
}
|
|
164
227
|
/**
|
|
165
228
|
* What a GitHub address names, read without a request. Null for an address whose host is not GitHub's, and for text
|
|
166
229
|
* that is no address; `refused` for an address on GitHub's hosts that names no repository, folder or file read here: an
|
|
167
|
-
* account, an issue, a pull request, a wiki, a release, a commit, a
|
|
168
|
-
* pages, an address that carries credentials (with a
|
|
169
|
-
*
|
|
170
|
-
*
|
|
171
|
-
*
|
|
230
|
+
* account, an issue, a pull request, a wiki, a release, a commit, a reference that is neither a branch nor a tag
|
|
231
|
+
* (`refs/pull/<n>/head`), a gist, the API, an archive, GitHub's own pages, an address that carries credentials (with a
|
|
232
|
+
* scheme or without one: `<user>:<password>@github.com/...`), a host of GitHub Enterprise Cloud with data residency
|
|
233
|
+
* (`<subdomain>.ghe.com`), and a host other than github.com, www.github.com and raw.githubusercontent.com. The scheme
|
|
234
|
+
* (or its absence), a port, the dots that end a host, `.git`, a trailing slash, a query and a fragment are ignored, and
|
|
235
|
+
* so is the `git` user of an SSH address (`git@github.com:<owner>/<repo>.git`).
|
|
236
|
+
*
|
|
237
|
+
* The text is read as the URL standard reads an address: the C0 controls and spaces around it, and any other white
|
|
238
|
+
* space there, are dropped, and every tab and newline in it; after `http`, `https`, `ws`, `wss` and `ftp` any run of
|
|
239
|
+
* `/` and `\` stands before the host, none included (`https:github.com/<owner>/<repo>`,
|
|
240
|
+
* `https:\\github.com\<owner>\<repo>`), as a browser opens such an address; after `file`, two of them; after any
|
|
241
|
+
* other scheme, `//`. A backslash in the path is a slash.
|
|
172
242
|
*
|
|
173
243
|
* A folder's and a file's ref and path are answered as one text, each segment decoded, since a branch name may hold a
|
|
174
|
-
* slash
|
|
175
|
-
*
|
|
176
|
-
*
|
|
244
|
+
* slash. A branch's or a tag's full name, `refs/heads/<branch>` or `refs/tags/<tag>`, the form GitHub's own Raw links
|
|
245
|
+
* write, stays whole in it, so `resolveRef` reads the name among the branches or the tags alone: the full name is the
|
|
246
|
+
* one form that tells a tag from a branch of the same name, or a branch from a longer tag beside it (the branch
|
|
247
|
+
* `release` and the tag `release/2.0`). A `/blob/` address whose text has no slash names a ref alone and is a folder,
|
|
248
|
+
* as GitHub shows it (its `/blob/<ref>` redirects to `/tree/<ref>`); one whose text has a slash is a file, which
|
|
249
|
+
* `resolveRef` may settle as a ref alone (an empty path). A full name with nothing after it under `/blob/` or `/raw/`
|
|
250
|
+
* names no file, as GitHub answers it 404, and is refused.
|
|
177
251
|
*
|
|
178
|
-
* @param text The address as pasted; spaces around it are dropped.
|
|
252
|
+
* @param text The address as pasted; the controls and spaces around it are dropped.
|
|
179
253
|
* @throws TypeError when `text` is not a string.
|
|
180
254
|
*/
|
|
181
255
|
export function parseGitHubLink(text) {
|
|
182
256
|
if (typeof text !== 'string')
|
|
183
257
|
throw new TypeError('a link is a string');
|
|
184
|
-
const
|
|
185
|
-
if (
|
|
258
|
+
const pasted = cleaned(text);
|
|
259
|
+
if (pasted === '')
|
|
186
260
|
return null;
|
|
187
|
-
const scp = SCP.exec(
|
|
261
|
+
const scp = SCP.exec(pasted);
|
|
188
262
|
if (scp !== null) {
|
|
189
263
|
const host = hostOf(scp[2]);
|
|
190
264
|
if (!isGitHubHost(host))
|
|
@@ -195,7 +269,7 @@ export function parseGitHubLink(text) {
|
|
|
195
269
|
const segments = address === null ? null : segmentsOf(address.pathname);
|
|
196
270
|
return segments === null ? refused() : siteLink(segments);
|
|
197
271
|
}
|
|
198
|
-
const address = addressOf(
|
|
272
|
+
const address = addressOf(pasted);
|
|
199
273
|
if (address === null)
|
|
200
274
|
return null;
|
|
201
275
|
const host = hostOf(address.hostname);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"links.js","sourceRoot":"","sources":["../../src/documents/links.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"links.js","sourceRoot":"","sources":["../../src/documents/links.ts"],"names":[],"mappings":"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"}
|
package/dist/documents/tree.d.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @fileoverview A repository's tree at a commit, read within a request budget, and the filter that keeps its
|
|
3
3
|
* documentation. GitHub cuts a recursive tree past 100,000 entries or 7 MB and marks it `truncated`; such a tree is
|
|
4
|
-
* read again one subtree at a time,
|
|
4
|
+
* read again one subtree at a time, a folder's files by the folders on its path alone, and a level GitHub cuts short
|
|
5
|
+
* even when asked for alone is refused whole.
|
|
5
6
|
* @module agentos-ext-github/documents/tree
|
|
6
7
|
*/
|
|
7
8
|
/** One file of a tree: its path from the repository's root, its blob and its size in bytes. */
|
|
@@ -77,23 +78,40 @@ export declare class TreeTooLarge extends Error {
|
|
|
77
78
|
*/
|
|
78
79
|
constructor(maxRequests: number, reason?: TreeTooLargeReason);
|
|
79
80
|
}
|
|
80
|
-
/** The budget of a tree read, the folders it never walks into, and its abort signal. */
|
|
81
|
+
/** The budget of a tree read, the folders it never walks into, the folder or the file it reads, and its abort signal. */
|
|
81
82
|
export interface ReadTreeOptions {
|
|
82
83
|
/** At most this many requests ({@link DEFAULT_MAX_TREE_REQUESTS} when absent). */
|
|
83
84
|
maxRequests?: number;
|
|
84
85
|
/** Folders, by name, whose files are left out and which a read by subtree never requests. */
|
|
85
86
|
skipFolders?: readonly string[];
|
|
87
|
+
/**
|
|
88
|
+
* A folder of the tree, or a file, by its path with no leading or trailing slash: the files at it or under it are
|
|
89
|
+
* read, and no others. When GitHub truncates the recursive tree, the folders on the path are asked for one at a time
|
|
90
|
+
* and the folder it names is read as a whole tree is, so the read costs the path's depth and the folder's own
|
|
91
|
+
* subtree, whatever lies beside it. The whole tree when absent or `''`.
|
|
92
|
+
*/
|
|
93
|
+
path?: string;
|
|
86
94
|
signal?: AbortSignal;
|
|
87
95
|
}
|
|
88
96
|
/** A file's extension with its dot, in lower case, or `''` when its name has none. */
|
|
89
97
|
export declare function extensionOf(path: string): string;
|
|
90
98
|
/**
|
|
91
|
-
* Every file of a repository's tree at a commit (symbolic links aside)
|
|
92
|
-
* answers it truncated, its top level is read and each subtree is
|
|
93
|
-
* for one answer cost a request per folder. Each request counts
|
|
94
|
-
* {@link TreeTooLarge} (`budget`) rather than make one more. A level GitHub
|
|
95
|
-
* throws {@link TreeTooLarge} (`truncated`) at that answer, before any
|
|
96
|
-
* `truncated` field can be set on any answer, and a part of the tree
|
|
99
|
+
* Every file of a repository's tree at a commit (symbolic links aside), or of the folder or the file `options.path`
|
|
100
|
+
* names. The tree is asked for recursively; when GitHub answers it truncated, its top level is read and each subtree is
|
|
101
|
+
* asked for the same way, so only the parts too large for one answer cost a request per folder. Each request counts
|
|
102
|
+
* against `maxRequests`, and the read throws {@link TreeTooLarge} (`budget`) rather than make one more. A level GitHub
|
|
103
|
+
* answers truncated without recursion as well throws {@link TreeTooLarge} (`truncated`) at that answer, before any
|
|
104
|
+
* folder under it is asked for: the tree answer's `truncated` field can be set on any answer, and a part of the tree
|
|
105
|
+
* would leave out files that then read as removed.
|
|
106
|
+
*
|
|
107
|
+
* With a `path`, the files at it or under it are answered. A truncated tree is then read by the folders on the path
|
|
108
|
+
* alone, each asked for without recursion, down to the folder the path names, which is read as above, or to the file:
|
|
109
|
+
* a small folder of a large repository costs the path's depth and its own subtree, never a request for a folder beside
|
|
110
|
+
* it. A level that does not name the path's next folder or file answers no file, unless GitHub cut that level short,
|
|
111
|
+
* which throws {@link TreeTooLarge} (`truncated`), since the name may stand past the cut; a level cut short that names
|
|
112
|
+
* it is read on, since the folder's own subtree is whole. A path through a skipped folder (one of its segments before
|
|
113
|
+
* the last, which may name a file) holds no file kept, so it answers no file before any request, whatever the tree; a
|
|
114
|
+
* read whose signal is already aborted throws {@link GitHubNoAnswer} (`cancelled`) there, as a request would.
|
|
97
115
|
*
|
|
98
116
|
* Name the repository by its id. A full name reads whatever repository holds that name when the tree is asked for: a
|
|
99
117
|
* renamed repository's old name can be taken again, and an account's old login claimed by another account, which ends
|
|
@@ -105,10 +123,19 @@ export declare function extensionOf(path: string): string;
|
|
|
105
123
|
* @param repository The repository's id, read at `/repositories/<id>`, or its full name, `owner/name`.
|
|
106
124
|
* @param commit The commit whose tree is read.
|
|
107
125
|
* @param apiBase GitHub's REST API, or a stand-in's address in tests.
|
|
108
|
-
* @param options The request budget, the folders skipped and an abort signal.
|
|
109
|
-
* @throws TypeError before any request when `repository` is a number that is not a positive integer.
|
|
126
|
+
* @param options The request budget, the folders skipped, the folder or the file read and an abort signal.
|
|
127
|
+
* @throws TypeError before any request when `repository` is a number that is not a positive integer, or `options.path`
|
|
128
|
+
* has an empty, `.` or `..` segment or is not text.
|
|
110
129
|
*/
|
|
111
130
|
export declare function readTree(fetchImpl: typeof fetch, token: string, repository: number | string, commit: string, apiBase?: string, options?: ReadTreeOptions): Promise<TreeBlob[]>;
|
|
131
|
+
/**
|
|
132
|
+
* The path of a folder or a file of a repository a caller names, checked: absent and `''` name the whole repository
|
|
133
|
+
* and answer `''`. A path with an empty, `.` or `..` segment (a leading or a trailing slash among them), and a value
|
|
134
|
+
* that is not text, throw a `TypeError` that quotes none of it.
|
|
135
|
+
*/
|
|
136
|
+
export declare function pathOf(path: unknown): string;
|
|
137
|
+
/** The blobs at `path` or under it: a folder's files, or the one file it names; every blob for `''`. */
|
|
138
|
+
export declare function withinPath<T extends TreeBlob>(blobs: readonly T[], path: string): T[];
|
|
112
139
|
/**
|
|
113
140
|
* The filter that keeps a tree's documentation: the extensions kept, and with `includeCode` the code extensions beside
|
|
114
141
|
* them; nothing under a skipped folder, nothing past the size cap, at most `maxFiles`, in path order. With code asked
|
|
@@ -140,6 +167,12 @@ export interface PlanTreeOptions extends TreeFilter {
|
|
|
140
167
|
maxCodeBytes?: number;
|
|
141
168
|
/** The extensions, with their dot, of the files skipped as images ({@link IMAGE_EXTENSIONS} when absent). */
|
|
142
169
|
imageExtensions?: readonly string[];
|
|
170
|
+
/**
|
|
171
|
+
* A folder of the tree, or a file, by its path with no leading or trailing slash: only the files at it or under it
|
|
172
|
+
* are planned, and `maxFiles` counts them alone, as a listing keeps them under the same `path`. The whole tree when
|
|
173
|
+
* absent or `''`; a path with an empty, `.` or `..` segment, or one that is not text, is a `TypeError`.
|
|
174
|
+
*/
|
|
175
|
+
path?: string;
|
|
143
176
|
}
|
|
144
177
|
/**
|
|
145
178
|
* `pathFilter`'s sibling: every file of a tree, in path order, with its outcome under the same filter, so a caller can
|
|
@@ -149,11 +182,12 @@ export interface PlanTreeOptions extends TreeFilter {
|
|
|
149
182
|
* (`count`, the documentation keeping its places first and the code taking the room left, as in `pathFilter`). A file
|
|
150
183
|
* whose extension the filter's `extensions` names is documentation, an image's included; while code is asked for, one
|
|
151
184
|
* of a code extension keeps `maxCodeBytes` as its bound, as a code file a tool wrote is left out whatever `extensions`
|
|
152
|
-
* names. Each answer is a new object holding the file's `path`, `sha` and `size` and its `outcome`.
|
|
185
|
+
* names. Each answer is a new object holding the file's `path`, `sha` and `size` and its `outcome`. With a `path`, only
|
|
186
|
+
* the files of that folder, or the one file it names, are planned, and counted alone.
|
|
153
187
|
*
|
|
154
188
|
* @param blobs The files of a tree, as `readTree` answers them.
|
|
155
|
-
* @param options The filter, the code bound
|
|
156
|
-
* @throws TypeError when `maxCodeBytes` is not a non-negative integer.
|
|
189
|
+
* @param options The filter, the code bound, the image extensions and the folder.
|
|
190
|
+
* @throws TypeError when `maxCodeBytes` is not a non-negative integer, or `path` is not a path.
|
|
157
191
|
*/
|
|
158
192
|
export declare function planTree(blobs: readonly TreeBlob[], options?: PlanTreeOptions): PlannedBlob[];
|
|
159
193
|
//# sourceMappingURL=tree.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tree.d.ts","sourceRoot":"","sources":["../../src/documents/tree.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"tree.d.ts","sourceRoot":"","sources":["../../src/documents/tree.ts"],"names":[],"mappings":"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"}
|
package/dist/documents/tree.js
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @fileoverview A repository's tree at a commit, read within a request budget, and the filter that keeps its
|
|
3
3
|
* documentation. GitHub cuts a recursive tree past 100,000 entries or 7 MB and marks it `truncated`; such a tree is
|
|
4
|
-
* read again one subtree at a time,
|
|
4
|
+
* read again one subtree at a time, a folder's files by the folders on its path alone, and a level GitHub cuts short
|
|
5
|
+
* even when asked for alone is refused whole.
|
|
5
6
|
* @module agentos-ext-github/documents/tree
|
|
6
7
|
*/
|
|
7
|
-
import { arrayOf, GITHUB_API_BASE, githubFetch, objectOf, repositoryPath, stringIn } from '../app/http.js';
|
|
8
|
+
import { arrayOf, GITHUB_API_BASE, githubFetch, GitHubNoAnswer, objectOf, repositoryPath, stringIn } from '../app/http.js';
|
|
8
9
|
/** The documentation kept by default: Markdown, text, reStructuredText, HTML, PDF and Word files. */
|
|
9
10
|
export const DOCUMENT_EXTENSIONS = ['.md', '.markdown', '.txt', '.rst', '.html', '.htm', '.pdf', '.docx'];
|
|
10
11
|
/** The folders skipped by default: dependencies, build output and vendored code. */
|
|
@@ -89,12 +90,22 @@ function inSkippedFolder(path, skip) {
|
|
|
89
90
|
return path.split('/').slice(0, -1).some((folder) => skip.has(folder));
|
|
90
91
|
}
|
|
91
92
|
/**
|
|
92
|
-
* Every file of a repository's tree at a commit (symbolic links aside)
|
|
93
|
-
* answers it truncated, its top level is read and each subtree is
|
|
94
|
-
* for one answer cost a request per folder. Each request counts
|
|
95
|
-
* {@link TreeTooLarge} (`budget`) rather than make one more. A level GitHub
|
|
96
|
-
* throws {@link TreeTooLarge} (`truncated`) at that answer, before any
|
|
97
|
-
* `truncated` field can be set on any answer, and a part of the tree
|
|
93
|
+
* Every file of a repository's tree at a commit (symbolic links aside), or of the folder or the file `options.path`
|
|
94
|
+
* names. The tree is asked for recursively; when GitHub answers it truncated, its top level is read and each subtree is
|
|
95
|
+
* asked for the same way, so only the parts too large for one answer cost a request per folder. Each request counts
|
|
96
|
+
* against `maxRequests`, and the read throws {@link TreeTooLarge} (`budget`) rather than make one more. A level GitHub
|
|
97
|
+
* answers truncated without recursion as well throws {@link TreeTooLarge} (`truncated`) at that answer, before any
|
|
98
|
+
* folder under it is asked for: the tree answer's `truncated` field can be set on any answer, and a part of the tree
|
|
99
|
+
* would leave out files that then read as removed.
|
|
100
|
+
*
|
|
101
|
+
* With a `path`, the files at it or under it are answered. A truncated tree is then read by the folders on the path
|
|
102
|
+
* alone, each asked for without recursion, down to the folder the path names, which is read as above, or to the file:
|
|
103
|
+
* a small folder of a large repository costs the path's depth and its own subtree, never a request for a folder beside
|
|
104
|
+
* it. A level that does not name the path's next folder or file answers no file, unless GitHub cut that level short,
|
|
105
|
+
* which throws {@link TreeTooLarge} (`truncated`), since the name may stand past the cut; a level cut short that names
|
|
106
|
+
* it is read on, since the folder's own subtree is whole. A path through a skipped folder (one of its segments before
|
|
107
|
+
* the last, which may name a file) holds no file kept, so it answers no file before any request, whatever the tree; a
|
|
108
|
+
* read whose signal is already aborted throws {@link GitHubNoAnswer} (`cancelled`) there, as a request would.
|
|
98
109
|
*
|
|
99
110
|
* Name the repository by its id. A full name reads whatever repository holds that name when the tree is asked for: a
|
|
100
111
|
* renamed repository's old name can be taken again, and an account's old login claimed by another account, which ends
|
|
@@ -106,15 +117,23 @@ function inSkippedFolder(path, skip) {
|
|
|
106
117
|
* @param repository The repository's id, read at `/repositories/<id>`, or its full name, `owner/name`.
|
|
107
118
|
* @param commit The commit whose tree is read.
|
|
108
119
|
* @param apiBase GitHub's REST API, or a stand-in's address in tests.
|
|
109
|
-
* @param options The request budget, the folders skipped and an abort signal.
|
|
110
|
-
* @throws TypeError before any request when `repository` is a number that is not a positive integer.
|
|
120
|
+
* @param options The request budget, the folders skipped, the folder or the file read and an abort signal.
|
|
121
|
+
* @throws TypeError before any request when `repository` is a number that is not a positive integer, or `options.path`
|
|
122
|
+
* has an empty, `.` or `..` segment or is not text.
|
|
111
123
|
*/
|
|
112
124
|
export async function readTree(fetchImpl, token, repository, commit, apiBase = GITHUB_API_BASE, options = {}) {
|
|
113
125
|
const address = `${apiBase}/${repositoryPath(repository)}`;
|
|
126
|
+
const target = pathOf(options.path);
|
|
114
127
|
const maxRequests = options.maxRequests ?? DEFAULT_MAX_TREE_REQUESTS;
|
|
115
128
|
const skip = new Set(options.skipFolders ?? []);
|
|
116
129
|
const blobs = [];
|
|
117
130
|
let requests = 0;
|
|
131
|
+
// A path through a skipped folder holds no file kept, so no level, whole or cut short, is asked for.
|
|
132
|
+
if (inSkippedFolder(target, skip)) {
|
|
133
|
+
if (options.signal?.aborted === true)
|
|
134
|
+
throw new GitHubNoAnswer('cancelled');
|
|
135
|
+
return blobs;
|
|
136
|
+
}
|
|
118
137
|
const read = async (sha, recursive) => {
|
|
119
138
|
if (requests >= maxRequests)
|
|
120
139
|
throw new TreeTooLarge(maxRequests);
|
|
@@ -149,13 +168,73 @@ export async function readTree(fetchImpl, token, repository, commit, apiBase = G
|
|
|
149
168
|
}
|
|
150
169
|
}
|
|
151
170
|
};
|
|
152
|
-
|
|
171
|
+
/**
|
|
172
|
+
* The files at `target` or under it: the tree asked for recursively once, and, when GitHub cuts it short, each folder
|
|
173
|
+
* on the path asked for alone down to the folder the path names, which `walk` reads, or to the file.
|
|
174
|
+
*/
|
|
175
|
+
const within = async () => {
|
|
176
|
+
const whole = await read(commit, true);
|
|
177
|
+
if (!whole.truncated) {
|
|
178
|
+
const folder = `${target}/`;
|
|
179
|
+
for (const entry of whole.entries)
|
|
180
|
+
if (entry.path === target || entry.path.startsWith(folder))
|
|
181
|
+
keep(entry, entry.path);
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
const names = target.split('/');
|
|
185
|
+
let sha = commit;
|
|
186
|
+
let prefix = '';
|
|
187
|
+
for (const [index, name] of names.entries()) {
|
|
188
|
+
const level = await read(sha, false);
|
|
189
|
+
const entry = level.entries.find((candidate) => candidate.path === name);
|
|
190
|
+
// A level cut short may hold the name past its cut; one read whole holds every name it has.
|
|
191
|
+
if (entry === undefined) {
|
|
192
|
+
if (level.truncated)
|
|
193
|
+
throw new TreeTooLarge(maxRequests, 'truncated');
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
if (entry.type !== 'tree') {
|
|
197
|
+
if (index === names.length - 1)
|
|
198
|
+
keep(entry, `${prefix}${name}`);
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
if (skip.has(name))
|
|
202
|
+
return;
|
|
203
|
+
sha = entry.sha;
|
|
204
|
+
prefix = `${prefix}${name}/`;
|
|
205
|
+
}
|
|
206
|
+
await walk(sha, prefix);
|
|
207
|
+
};
|
|
208
|
+
if (target === '')
|
|
209
|
+
await walk(commit, '');
|
|
210
|
+
else
|
|
211
|
+
await within();
|
|
153
212
|
return blobs;
|
|
154
213
|
}
|
|
155
214
|
/** Blobs in path order. */
|
|
156
215
|
function byPath(a, b) {
|
|
157
216
|
return a.path < b.path ? -1 : a.path > b.path ? 1 : 0;
|
|
158
217
|
}
|
|
218
|
+
/**
|
|
219
|
+
* The path of a folder or a file of a repository a caller names, checked: absent and `''` name the whole repository
|
|
220
|
+
* and answer `''`. A path with an empty, `.` or `..` segment (a leading or a trailing slash among them), and a value
|
|
221
|
+
* that is not text, throw a `TypeError` that quotes none of it.
|
|
222
|
+
*/
|
|
223
|
+
export function pathOf(path) {
|
|
224
|
+
if (path === undefined || path === '')
|
|
225
|
+
return '';
|
|
226
|
+
if (typeof path !== 'string' || path.split('/').some((segment) => segment === '' || segment === '.' || segment === '..')) {
|
|
227
|
+
throw new TypeError('a path is text with no empty, . or .. segment');
|
|
228
|
+
}
|
|
229
|
+
return path;
|
|
230
|
+
}
|
|
231
|
+
/** The blobs at `path` or under it: a folder's files, or the one file it names; every blob for `''`. */
|
|
232
|
+
export function withinPath(blobs, path) {
|
|
233
|
+
if (path === '')
|
|
234
|
+
return [...blobs];
|
|
235
|
+
const folder = `${path}/`;
|
|
236
|
+
return blobs.filter((blob) => blob.path === path || blob.path.startsWith(folder));
|
|
237
|
+
}
|
|
159
238
|
/**
|
|
160
239
|
* The filter that keeps a tree's documentation: the extensions kept, and with `includeCode` the code extensions beside
|
|
161
240
|
* them; nothing under a skipped folder, nothing past the size cap, at most `maxFiles`, in path order. With code asked
|
|
@@ -187,16 +266,18 @@ export function pathFilter(filter = {}) {
|
|
|
187
266
|
* (`count`, the documentation keeping its places first and the code taking the room left, as in `pathFilter`). A file
|
|
188
267
|
* whose extension the filter's `extensions` names is documentation, an image's included; while code is asked for, one
|
|
189
268
|
* of a code extension keeps `maxCodeBytes` as its bound, as a code file a tool wrote is left out whatever `extensions`
|
|
190
|
-
* names. Each answer is a new object holding the file's `path`, `sha` and `size` and its `outcome`.
|
|
269
|
+
* names. Each answer is a new object holding the file's `path`, `sha` and `size` and its `outcome`. With a `path`, only
|
|
270
|
+
* the files of that folder, or the one file it names, are planned, and counted alone.
|
|
191
271
|
*
|
|
192
272
|
* @param blobs The files of a tree, as `readTree` answers them.
|
|
193
|
-
* @param options The filter, the code bound
|
|
194
|
-
* @throws TypeError when `maxCodeBytes` is not a non-negative integer.
|
|
273
|
+
* @param options The filter, the code bound, the image extensions and the folder.
|
|
274
|
+
* @throws TypeError when `maxCodeBytes` is not a non-negative integer, or `path` is not a path.
|
|
195
275
|
*/
|
|
196
276
|
export function planTree(blobs, options = {}) {
|
|
197
277
|
if (options.maxCodeBytes !== undefined && !(Number.isSafeInteger(options.maxCodeBytes) && options.maxCodeBytes >= 0)) {
|
|
198
278
|
throw new TypeError('maxCodeBytes is a number of bytes: a non-negative integer');
|
|
199
279
|
}
|
|
280
|
+
const path = pathOf(options.path);
|
|
200
281
|
const documents = new Set((options.extensions ?? DOCUMENT_EXTENSIONS).map((extension) => extension.toLowerCase()));
|
|
201
282
|
const codeAsked = options.includeCode === true;
|
|
202
283
|
const code = new Set(CODE_EXTENSIONS);
|
|
@@ -205,7 +286,9 @@ export function planTree(blobs, options = {}) {
|
|
|
205
286
|
const maxFiles = options.maxFiles ?? DEFAULT_MAX_FILES;
|
|
206
287
|
const maxFileBytes = options.maxFileBytes ?? DEFAULT_MAX_FILE_BYTES;
|
|
207
288
|
const maxCodeBytes = Math.min(options.maxCodeBytes ?? maxFileBytes, maxFileBytes);
|
|
208
|
-
const planned =
|
|
289
|
+
const planned = withinPath(blobs, path)
|
|
290
|
+
.sort(byPath)
|
|
291
|
+
.map((blob) => ({ path: blob.path, sha: blob.sha, size: blob.size, outcome: 'note' }));
|
|
209
292
|
const documentation = [];
|
|
210
293
|
const sources = [];
|
|
211
294
|
for (const blob of planned) {
|