@framers/agentos-ext-github 0.7.0 → 0.9.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 CHANGED
@@ -351,7 +351,7 @@ Top-level pull request comments use the issues API. `github_pr_comment_list` ret
351
351
 
352
352
  The `./documents` entry reads repositories through a GitHub App instead of one personal token. Every call goes to GitHub's REST API over the `fetch` you pass (the global `fetch` by default) with `X-GitHub-Api-Version: 2026-03-10`, so tests point it at a stand-in with `{ fetchImpl, apiBase }`.
353
353
 
354
- A repository is read by its id, at `/repositories/<id>/branches/<branch>`, `/repositories/<id>/git/trees/<sha>` and `/repositories/<id>/contents/<path>`, the address GitHub's own redirects for a renamed or moved repository lead to, and never by its name. A name can come to mean another repository: a renamed repository's old name can be taken again, and an account's old login claimed by another account, which ends GitHub's redirects to it; and GitHub answers a public repository's branch, tree and files to any token, an installation token narrowed to other repositories included. The full name builds a read's attribution link and nothing else.
354
+ A repository is read by its id, at `/repositories/<id>/branches/<branch>`, `/repositories/<id>/git/trees/<sha>`, `/repositories/<id>/contents/<path>` and `/repositories/<id>/git/matching-refs/heads/<name>`, the address GitHub's own redirects for a renamed or moved repository lead to, and never by its name. A name can come to mean another repository: a renamed repository's old name can be taken again, and an account's old login claimed by another account, which ends GitHub's redirects to it; and GitHub answers a public repository's branch, tree and files to any token, an installation token narrowed to other repositories included. The full name builds a read's attribution link and nothing else.
355
355
 
356
356
  ```ts
357
357
  import { readFileSync } from 'node:fs';
@@ -450,9 +450,11 @@ Every call runs under a deadline, 30 seconds by default (`GITHUB_TIMEOUT_MS`), i
450
450
  | `verifyWebhookSignature(body, header, secret)` | Checks `X-Hub-Signature-256` against the HMAC SHA-256 of the raw body in constant time; an empty or missing secret throws a `TypeError`, since anyone can sign a delivery under an empty key |
451
451
  | `readWebhookRequest(request, secret, { maxBytes })`, `readWebhookDelivery({ headers, body }, secret, { maxBytes })` | Read a delivery under a cap, check its signature and id, and answer it as a typed event, or throw `WebhookDeliveryRefused` with its reason |
452
452
  | `reconcileInstallations(credential, links, { since })` | Answers the linked installations GitHub no longer has (`gone`: missing from the listing and answered 404 at their own address, since an installation removed while the listing is read can move another past a page's end), those GitHub has suspended (`suspended`, whose repositories it does not read), the repositories whose default branch moved past the head last read, the repositories it could not read, and asks again for each event since `since` that no delivery attempt delivered, reporting GitHub's refusals of that step in `deliveries`; a rate limit ends it, with `retryAt` |
453
- | `GitHubDocumentsConnector` | An `AccountSourceConnector` of `@framers/agentos-ext-account-sources` over one installation's linked repositories |
453
+ | `GitHubDocumentsConnector` | An `AccountSourceConnector` of `@framers/agentos-ext-account-sources` over one installation's linked repositories, at each one's default branch or at the branch or commit a selection's `at` names, whole or within the folder its `path` names; `resolveRef(repositoryId, refAndPath)` settles a link's ref as a branch, a tag or a commit |
454
+ | `parseGitHubLink(text)` | The repository, folder or file a GitHub address names, read without a request; `refused` for any other address on GitHub's hosts, `null` for another host's |
454
455
  | `readTree`, `pathFilter`, `TreeTooLarge` | A tree at a commit within a request budget, `readTree(fetchImpl, token, repositoryId, commit)` reading the repository by its id (a full name is taken too, and reads whatever repository holds that name; a number that is not a positive integer is a `TypeError` before any request), and the filter that keeps documentation, and code when asked (`includeCode`); `TreeTooLarge` carries `reason`, `budget` or `truncated` |
455
456
  | `CODE_EXTENSIONS`, `GENERATED_CODE_ENDINGS`, `isGeneratedCode(path)` | The source files a filter that includes code keeps, and the endings of the code files a tool writes (minified and bundled files, generated Protocol Buffers and Dart files), which it leaves out |
457
+ | `planTree(blobs, options)`, `IMAGE_EXTENSIONS` | Every file of a tree with its outcome under `pathFilter`'s filter: a note, or skipped with the reason it is left out |
456
458
  | `GitHubApiError` | An answer outside 2xx; carries the status, `rateLimited` and `retryAt`, and nothing of the body |
457
459
  | `GitHubListingChanged` | An installation's repositories changed during each of three reads of their listing (its pages disagreed on `total_count`), so no read is whole; its message names nothing of the installation |
458
460
  | `GitHubNoAnswer` | A call that got no answer, by its `reason`: `timeout`, `cancelled` or `network` |
@@ -470,13 +472,47 @@ The connector's filter, `new GitHubDocumentsConnector(credential, installationId
470
472
  | `extensions` | `.md`, `.markdown`, `.txt`, `.rst`, `.html`, `.htm`, `.pdf`, `.docx` |
471
473
  | `includeCode` | `false`: when true, the source files of `CODE_EXTENSIONS` are kept beside `extensions` under the same bounds and skipped folders, each read as UTF-8 text, and a code file a tool wrote is left out (`isGeneratedCode`: a name ending in `.min.js`, `.min.css`, `.bundle.js`, `.bundle.css`, `.pb.go`, `_pb2.py` and the like), under an extension `extensions` names too. Data and configuration files (JSON, YAML, XML, CSV) are not code here; name their extensions in `extensions` to keep them |
472
474
  | `skipFolders` | `node_modules`, `vendor`, `dist`, `build`, `.git`, `third_party`, at any depth |
473
- | `maxFiles` | `2000`, in path order: a listing keeps the first `maxFiles` documentation paths, so when a new path sorts before them, the last one kept leaves the listing and `changesSince` answers it gone. With `includeCode`, the documentation keeps its places first and the code paths, in path order, take the room left, so turning code on drops no document |
475
+ | `maxFiles` | `2000`, in path order: a listing keeps the first `maxFiles` documentation paths, so when a new path sorts before them, the last one kept leaves the listing and `changesSince` answers it gone. A selection's `path` narrows a listing to a folder, or a file, before the count, so the folder's files are counted alone; a `listing` handed to `changesSince` with a `path` that holds `maxFiles` files may have been cut before the folder's files, and the folder is read afresh instead of it. With `includeCode`, the documentation keeps its places first and the code paths, in path order, take the room left, so turning code on drops no document |
474
476
  | `maxFileBytes` | `10485760` (10 MiB): larger files are left out of a listing, and a read past it is answered gone |
475
477
  | `maxTreeRequests` | `50` tree requests to list one commit |
476
478
 
477
- An item's id is `<repositoryId>:<path>` and its version `<blob>@<commit>`. A tree is asked for recursively; when GitHub answers it truncated (past 100,000 entries or 7 MB), its top level is read and each subtree is asked for the same way, never walking a skipped folder. `TreeTooLarge` is thrown rather than pass `maxTreeRequests` (`reason: 'budget'`), and at a level GitHub answers truncated when it is asked for alone as well (`reason: 'truncated'`), before any folder under it is asked for: no part of a tree is answered, since the files it left out would read as removed. Files are read through the contents endpoint's raw media type as a stream cut at `maxFileBytes`: PDF, Word and HTML as bytes with their media type, the rest as UTF-8 text, each with a `github` source reference whose `url` is the file at the commit, the commit or branch encoded segment by segment as the path is, so a branch named `docs#1` links as `docs%231`. An item whose version names no commit (none, or no `@<commit>` part) is read at the default branch, and its reference names the branch. `changesSince` takes the cursor it answered last (each documentation path's blob), never reads a push's commit list, so a force push is read like any other, and answers a push to another branch than the default with no change. `listing(repositoryId, commit)` lets a caller keep a commit's listing and pass it to `changesSince` as `listing`, with that commit as `after`; it then reads no tree. A cursor `changesSince` did not answer throws a `TypeError` that quotes none of it, whatever the push's branch. A file read GitHub refuses for a rate limit throws `GitHubApiError` with its `retryAt`, never an answer that the file is gone, and so does a refused installation token (404 for an installation GitHub no longer has): only the contents read's own 404 answers a file `removed`, so a lost installation never reads as every file gone.
479
+ An item's id is `<repositoryId>:<path>` and its version `<blob>@<commit>`. A tree is asked for recursively; when GitHub answers it truncated (past 100,000 entries or 7 MB), its top level is read and each subtree is asked for the same way, never walking a skipped folder. `TreeTooLarge` is thrown rather than pass `maxTreeRequests` (`reason: 'budget'`), and at a level GitHub answers truncated when it is asked for alone as well (`reason: 'truncated'`), before any folder under it is asked for: no part of a tree is answered, since the files it left out would read as removed. Files are read through the contents endpoint's raw media type as a stream cut at `maxFileBytes`: PDF, Word and HTML as bytes with their media type, the rest as UTF-8 text, each with a `github` source reference whose `url` is the file at the commit, the commit or branch encoded segment by segment as the path is, so a branch named `docs#1` links as `docs%231`. An item whose version names no commit (none, or no `@<commit>` part) is read at the default branch, and its reference names the branch. `changesSince` takes the cursor it answered last (each documentation path's blob), never reads a push's commit list, so a force push is read like any other, and answers a push to another branch than the one it follows with no change. `listing(repositoryId, commit)` lets a caller keep a commit's listing and pass it to `changesSince` as `listing`, with that commit as `after`; it then reads no tree. `listing(repositoryId, commit, { path, signal })` reads a folder's listing, or a file's, the same way. A cursor `changesSince` did not answer throws a `TypeError` that quotes none of it, whatever the push's branch. A file read GitHub refuses for a rate limit throws `GitHubApiError` with its `retryAt`, never an answer that the file is gone, and so does a refused installation token (404 for an installation GitHub no longer has): only the contents read's own 404 answers a file `removed`, so a lost installation never reads as every file gone.
478
480
 
479
- The connector refuses, before any request, what it cannot answer whole, and no refusal quotes a cursor, an item id or a path. A repository id, in a selection or in an item id, is a positive integer in decimal (`77`; never `077`, `0x4d` or ` 77`), else a `TypeError`. `list` checks every id of its selection before it reads any repository, so a selection is listed whole or not at all: a repository named twice is a `TypeError`, and one outside the link an `Error` naming its number. `read` throws a `TypeError` for an item id that names no file: one without a path, or whose path has an empty, `.` or `..` segment. `changesSince` compares one repository a call: a selection of none or several, an empty `after`, or a `listing` without the `after` commit it was read at is a `TypeError`, so call it once per repository, each with that repository's own cursor. A call whose signal is already aborted throws `GitHubNoAnswer` (`cancelled`) before it checks anything else, so every aborted call answers alike, whatever its inputs.
481
+ The connector refuses, before any request, what it cannot answer whole, and no refusal quotes a cursor, an item id or a path. A repository id, in a selection or in an item id, is a positive integer in decimal (`77`; never `077`, `0x4d` or ` 77`), else a `TypeError`. `list` checks every id of its selection before it reads any repository, so a selection is listed whole or not at all: a repository named twice is a `TypeError`, and one outside the link an `Error` naming its number. `read` throws a `TypeError` for an item id that names no file: one without a path, or whose path has an empty, `.` or `..` segment. `changesSince` compares one repository a call: a selection of none or several, an empty `after`, or a `listing` without the `after` commit it was read at is a `TypeError`, so call it once per repository, each with that repository's own cursor. A `path` with an empty, `.` or `..` segment (a leading or a trailing slash among them), or one that is not text, is a `TypeError` in `list`, `listing`, `changesSince` and `planTree`, and so is a `path` beside other than one repository in `list`. A call whose signal is already aborted throws `GitHubNoAnswer` (`cancelled`) before it checks anything else, so every aborted call answers alike, whatever its inputs.
482
+
483
+ `list`, `changesSince` and `head` follow the default branch, or the branch or commit a selection's `at` names (`head(repositoryId, at, signal)`; `head(repositoryId, signal)` reads the default branch as before). A branch's head is read when the call is made; a commit, 40 hexadecimal characters in either case, is read as it is with no branch read, in lower case, and never changes. `list({ ids: [String(repositoryId)], at: 'release/1.0' })` lists one repository at that branch's head, each item's version `<blob>@<commit>` at it. `changesSince` compares at the followed branch's head when the call names no `after`, answers a push whose `ref` is not `refs/heads/<at>` with no change (the default branch's push included, while `at` names another branch), and answers any push with no change when `at` is a commit; without a push, an `after` naming another commit than `at`'s is a `TypeError`. An `at` that is neither a commit nor a branch name by [git's rules](https://git-scm.com/docs/git-check-ref-format) (no `..`, `@{`, space, control character, `~`, `^`, `:`, `?`, `*`, `[` or `\`; no part empty, starting with a dot or ending in `.lock`; not ending with a dot, not starting with a dash, neither `@` nor `HEAD`), and an `at` beside other than one repository in `list`, throw a `TypeError` before any request. A tag or a short commit is followed by the commit `resolveRef` settles it to.
484
+
485
+ A selection's `path` narrows `list` and `changesSince` to a folder of one repository, or to the one file it names, by its path with no leading or trailing slash (`resolveRef`'s `path`; `''` or none for the whole repository). The files at the path or under it are kept before `maxFiles` counts them, so a folder's files are listed and compared whatever sorts before them: over the whole repository, 2,000 documents whose paths sort before `docs/` fill the count, and the folder's files are left out of the listing and answered gone by `changesSince`. A `/tree/` address that names a file lists that file, as github.com shows it. `changesSince` narrows a `listing` handed in to the path, and compares a cursor under the path it was answered under: a path in it outside the one given is answered gone.
486
+
487
+ `parseGitHubLink(text)` reads the GitHub address a person pastes, without a request:
488
+
489
+ | Address | Answered as |
490
+ | --- | --- |
491
+ | `github.com/<owner>/<repo>`, with or without `.git`; `git@github.com:<owner>/<repo>.git` | `{ kind: 'github_repository', owner, repo }` |
492
+ | `github.com/<owner>/<repo>/tree/<ref>/<path>`, and `/blob/<ref>` with no path, which GitHub shows as the folder | `{ kind: 'github_folder', owner, repo, refAndPath }` |
493
+ | `github.com/<owner>/<repo>/blob/<ref>/<path>` and `/raw/<ref>/<path>`; `raw.githubusercontent.com/<owner>/<repo>/<ref>/<path>` | `{ kind: 'github_file', owner, repo, refAndPath, raw }`, `raw` true for the two raw forms |
494
+ | Any other address on `github.com`, `www.github.com`, `gist.github.com`, `api.github.com`, `codeload.github.com`, `uploads.github.com` or a host under `githubusercontent.com`: an account, an issue, a pull request, a wiki, a release, a commit, a tag by its full name (`refs/tags/<tag>`), a gist, GitHub's own pages, an address that carries credentials, with a scheme or without one (`<user>:<password>@github.com/...`, which a browser opens as an `https://` address); and any address on `ghe.com` or a host under it, where GitHub Enterprise Cloud with data residency serves an enterprise ([GitHub's docs](https://docs.github.com/en/enterprise-cloud@latest/admin/data-residency/about-github-enterprise-cloud-with-data-residency)) | `{ kind: 'refused' }` |
495
+ | An address on any other host, an opaque address such as `mailto:someone@github.com`, and text that is no address | `null` |
496
+
497
+ The scheme, or its absence, a port, the dots that end a host (`github.com.`, `github.com..`), a trailing slash, a query and a fragment are ignored; `www.github.com` is read as `github.com`, and GitHub's form of a branch by its full name, `refs/heads/<branch>`, as the branch. The text is read as the [URL standard](https://url.spec.whatwg.org/#concept-basic-url-parser) reads an address, as a browser opens it: the C0 controls and spaces around it (and any other white space there) are dropped, and every tab and newline in it; after `http`, `https`, `ws`, `wss` and `ftp` any run of `/` and `\` stands before the host, none included ([special authority slashes](https://url.spec.whatwg.org/#special-authority-slashes-state)), so `https:github.com/acme-team/handbook`, `https:/github.com/...` and `https:\\github.com\acme-team\handbook` read as `https://github.com/acme-team/handbook`; after `file` two of them, and a third leaves the host [empty](https://url.spec.whatwg.org/#file-host-state), so `file:///github.com/acme-team/handbook` names no host (`null`); after any other scheme (`git://`, `ssh://`) `//`, and without it such a scheme names no host (`null`). A backslash in the path is a slash. Each segment is decoded, and an address whose path has an empty segment, an escape that does not decode, or a `.` or `..` segment or a control character once decoded is refused.
498
+
499
+ A folder's and a file's ref and path are one text, `refAndPath`, since a branch or a tag name may hold a slash. `connector.resolveRef(repositoryId, refAndPath)` settles it as `{ ref, path, commit, kind, name }`: `kind` is `branch`, `tag` or `commit`, `name` the ref as the link writes it, `ref` what `list`, `head` and `changesSince` take as `at`, and `path` the rest (`''` for the ref's root). A first segment of 40 hexadecimal characters is a commit (`commit: true`, in lower case) and `HEAD` the default branch, with no request. Otherwise GitHub's [matching references](https://docs.github.com/en/rest/git/refs#list-matching-references), which answer every reference whose name starts with that segment, are read in turn: the branches and the tags, where the longest name of either that is the text or starts it at a slash is the ref, a tag answered at the commit it names (`commit: true`; an annotated tag's object is [read](https://docs.github.com/en/rest/git/tags#get-a-tag) for it), as github.com serves `/tree/<tag>/`, and the tags not read when a branch's name is the whole text; then, for a first segment of 4 to 39 hexadecimal characters, the short commit through GitHub's [commits](https://docs.github.com/en/rest/commits/commits#get-a-commit) under the `application/vnd.github.sha` media type, which answers the whole commit, or 422 for a prefix no commit has or several have. A branch and a tag of one name settle as the branch. Git keeps no branch beside one under its name (`release` beside `release/1.0`), so at most one such branch exists, but it keeps a tag under a branch's name beside the branch, and the tag `release/2.0` beside the branch `release` settles `release/2.0/docs` as the tag. A first segment none of them settles is answered as a branch all the same, and a listing at it throws GitHub's 404 as `GitHubApiError`; so does 409 for a repository with no commit. A first segment no ref name can begin (a control character, a space, `~`, `^`, `:`, `?`, `*`, `[`, `\`, `..` or `@{`, a leading dot, a trailing `.lock`) is a `TypeError` before any request, and one that is no branch name and none of them settles (`-x`, `@`, `end.`) a `TypeError` after the reads. A `/blob/` address whose text has no slash (`/blob/main`) names its ref alone and is answered as the folder, since GitHub redirects it to `/tree/main`; one whose text has a slash is a file, and when `resolveRef` settles the whole text as the ref (`/blob/release/1.0` in a repository with a `release/1.0` branch), the path is `''`: the address names a ref and no file, which GitHub answers 404.
500
+
501
+ ```ts
502
+ import { parseGitHubLink } from '@framers/agentos-ext-github/documents';
503
+
504
+ const link = parseGitHubLink('https://github.com/acme-team/handbook/tree/release/1.0/docs');
505
+ if (link?.kind === 'github_folder') {
506
+ // repositoryId: the linked repository whose full name is `${link.owner}/${link.repo}`.
507
+ const { ref, path, kind } = await connector.resolveRef(repositoryId, link.refAndPath); // 'release/1.0', 'docs', 'branch'
508
+ // The folder's files, kept before maxFiles counts them; a path that names a file lists that file.
509
+ const { items } = await connector.list({ ids: [String(repositoryId)], at: ref, path });
510
+ // A branch's push is compared within the same folder, with the cursor answered under that path.
511
+ const changes = await connector.changesSince(cursor, { ids: [String(repositoryId)], at: ref, path, ref: push.ref, after: push.after });
512
+ }
513
+ ```
514
+
515
+ `planTree(blobs, options)` answers every file of a tree (`readTree`'s blobs) in path order, each a new object with its `path`, `sha`, `size` and `outcome`: `'note'`, or `{ skipped }` with the first reason the file is left out. `folder` under a skipped folder; `kind` for a file that is neither documentation, code nor an image (data, configuration, a binary, a file with no extension); `image` for `IMAGE_EXTENSIONS`, or the `imageExtensions` given; `code` for code while `includeCode` is off; `generated` for code a tool wrote while `includeCode` is on; `size` past `maxFileBytes`, or past `maxCodeBytes` for a file of a code extension while `includeCode` is on, whatever `extensions` names (`maxFileBytes` when absent, a non-negative integer, else a `TypeError`); `count` past `maxFiles`, the documentation keeping its places first and the code taking the room left. The notes are exactly what `pathFilter` keeps under the same filter, a code file past `maxCodeBytes` aside, so a caller can show what is left out and why from one listing, and turn code on or off without reading the tree again. An extension the filter's `extensions` names is documentation, an image's included. With `path`, only the files at that folder or under it, or the one file it names, are planned and counted, as `list` keeps them under the same `path`: a folder link's plan counts the folder's files alone.
480
516
 
481
517
  ## License
482
518
 
@@ -0,0 +1,10 @@
1
+ /**
2
+ * @fileoverview `parseGitHubLink` over the addresses a person pastes: a repository, a folder and a file on github.com and
3
+ * a file on raw.githubusercontent.com, each with and without `.git`, a trailing slash, a fragment and a query, under
4
+ * http, with a port, without a scheme, and as the URL standard reads a special scheme without its two slashes, with
5
+ * backslashes or with controls around it; the GitHub addresses the pack does not read, GitHub Enterprise Cloud's
6
+ * ghe.com hosts among them, each refused and never answered as another site's; and the addresses of other hosts,
7
+ * which are not GitHub's.
8
+ */
9
+ export {};
10
+ //# sourceMappingURL=links.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"links.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/links.test.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG"}
@@ -0,0 +1,255 @@
1
+ /**
2
+ * @fileoverview `parseGitHubLink` over the addresses a person pastes: a repository, a folder and a file on github.com and
3
+ * a file on raw.githubusercontent.com, each with and without `.git`, a trailing slash, a fragment and a query, under
4
+ * http, with a port, without a scheme, and as the URL standard reads a special scheme without its two slashes, with
5
+ * backslashes or with controls around it; the GitHub addresses the pack does not read, GitHub Enterprise Cloud's
6
+ * ghe.com hosts among them, each refused and never answered as another site's; and the addresses of other hosts,
7
+ * which are not GitHub's.
8
+ */
9
+ import { describe, expect, it } from 'vitest';
10
+ import { parseGitHubLink } from '../documents/index.js';
11
+ const COMMIT = '9f2c4e6a'.repeat(5);
12
+ const REPOSITORY = { kind: 'github_repository', owner: 'acme-team', repo: 'handbook' };
13
+ const REFUSED = { kind: 'refused' };
14
+ /** A folder of the handbook at `refAndPath`. */
15
+ function folder(refAndPath) {
16
+ return { kind: 'github_folder', owner: 'acme-team', repo: 'handbook', refAndPath };
17
+ }
18
+ /** A file of the handbook at `refAndPath`, raw when the address served its bytes alone. */
19
+ function file(refAndPath, raw = false) {
20
+ return { kind: 'github_file', owner: 'acme-team', repo: 'handbook', refAndPath, raw };
21
+ }
22
+ describe('parseGitHubLink', () => {
23
+ it('reads a repository whatever its scheme, port, .git, trailing slash, query or fragment', () => {
24
+ const forms = [
25
+ 'https://github.com/acme-team/handbook',
26
+ 'https://github.com/acme-team/handbook/',
27
+ 'https://github.com/acme-team/handbook.git',
28
+ 'https://github.com/acme-team/handbook.git/',
29
+ 'https://github.com/acme-team/handbook#readme',
30
+ 'https://github.com/acme-team/handbook?tab=readme-ov-file',
31
+ 'http://github.com/acme-team/handbook',
32
+ 'https://github.com:8443/acme-team/handbook',
33
+ 'https://www.github.com/acme-team/handbook',
34
+ 'HTTPS://GitHub.COM/acme-team/handbook',
35
+ ' https://github.com/acme-team/handbook\n',
36
+ 'github.com/acme-team/handbook',
37
+ 'github.com:8443/acme-team/handbook/',
38
+ '//github.com/acme-team/handbook',
39
+ 'git://github.com/acme-team/handbook.git',
40
+ 'ssh://git@github.com/acme-team/handbook.git',
41
+ 'git@github.com:acme-team/handbook.git',
42
+ 'https://github.com./acme-team/handbook',
43
+ // An address's host may end in more than one dot (https://url.spec.whatwg.org/#host-parsing keeps them all).
44
+ 'https://github.com../acme-team/handbook',
45
+ 'github.com...:8443/acme-team/handbook',
46
+ ];
47
+ for (const form of forms)
48
+ expect(parseGitHubLink(form), form).toEqual(REPOSITORY);
49
+ });
50
+ it("reads a folder's ref and path as one text, a branch's own slashes included, for the connector to settle", () => {
51
+ const forms = [
52
+ ['https://github.com/acme-team/handbook/tree/release/1.0/docs', folder('release/1.0/docs')],
53
+ ['https://github.com/acme-team/handbook/tree/release/1.0/docs/', folder('release/1.0/docs')],
54
+ ['https://github.com/acme-team/handbook/tree/release/1.0/docs#top', folder('release/1.0/docs')],
55
+ ['https://github.com/acme-team/handbook/tree/release/1.0/docs?plain=1', folder('release/1.0/docs')],
56
+ ['http://github.com:80/acme-team/handbook/tree/main/docs', folder('main/docs')],
57
+ ['github.com/acme-team/handbook/tree/main/docs', folder('main/docs')],
58
+ ['https://github.com/acme-team/handbook.git/tree/main/docs', folder('main/docs')],
59
+ ['https://github.com/acme-team/handbook/tree/main', folder('main')],
60
+ // GitHub's own form of a branch by its full name, read as the branch.
61
+ ['https://github.com/acme-team/handbook/tree/refs/heads/release/1.0/docs', folder('release/1.0/docs')],
62
+ // Each segment decoded: a branch holding a "#", a folder holding a space.
63
+ ['https://github.com/acme-team/handbook/tree/docs%231/guide%20one', folder('docs#1/guide one')],
64
+ [`https://github.com/acme-team/handbook/tree/${COMMIT}/docs`, folder(`${COMMIT}/docs`)],
65
+ // GitHub shows the folder for a blob address that names no path.
66
+ ['https://github.com/acme-team/handbook/blob/main', folder('main')],
67
+ ];
68
+ for (const [form, link] of forms)
69
+ expect(parseGitHubLink(form), form).toEqual(link);
70
+ });
71
+ it("reads a file on github.com and on raw.githubusercontent.com, GitHub's raw addresses marked raw", () => {
72
+ const forms = [
73
+ ['https://github.com/acme-team/handbook/blob/main/docs/agenda.md', file('main/docs/agenda.md')],
74
+ ['https://github.com/acme-team/handbook/blob/main/docs/agenda.md#L3-L4', file('main/docs/agenda.md')],
75
+ ['https://github.com/acme-team/handbook/blob/main/docs/agenda.md?plain=1', file('main/docs/agenda.md')],
76
+ ['http://www.github.com/acme-team/handbook/blob/main/docs/agenda.md', file('main/docs/agenda.md')],
77
+ [`https://github.com/acme-team/handbook/blob/${COMMIT}/docs/agenda.md`, file(`${COMMIT}/docs/agenda.md`)],
78
+ ['https://github.com/acme-team/handbook/blob/refs/heads/main/docs/agenda.md', file('main/docs/agenda.md')],
79
+ ['https://github.com/acme-team/handbook/raw/main/docs/agenda.md', file('main/docs/agenda.md', true)],
80
+ ['https://github.com/acme-team/handbook/raw/refs/heads/main/docs/agenda.md', file('main/docs/agenda.md', true)],
81
+ ['https://raw.githubusercontent.com/acme-team/handbook/main/docs/agenda.md', file('main/docs/agenda.md', true)],
82
+ ['https://raw.githubusercontent.com/acme-team/handbook/refs/heads/main/docs/agenda.md', file('main/docs/agenda.md', true)],
83
+ ['http://raw.githubusercontent.com:8443/acme-team/handbook/main/README.md', file('main/README.md', true)],
84
+ ['raw.githubusercontent.com/acme-team/handbook/main/README.md', file('main/README.md', true)],
85
+ // The text may be a branch alone (release/1.0): resolveRef settles it, answering an empty path for a ref alone.
86
+ ['https://github.com/acme-team/handbook/blob/release/1.0', file('release/1.0')],
87
+ ];
88
+ for (const [form, link] of forms)
89
+ expect(parseGitHubLink(form), form).toEqual(link);
90
+ });
91
+ it('reads an address as the URL standard does: a special scheme without its two slashes or with backslashes, the controls and spaces around it, and tabs and newlines in it', () => {
92
+ // After http, https, ws, wss and ftp the standard takes any run of / and \, none included, before the host
93
+ // (https://url.spec.whatwg.org/#special-authority-slashes-state), and after file two of them; it drops the C0
94
+ // controls and spaces around an address and every tab and newline in it
95
+ // (https://url.spec.whatwg.org/#concept-basic-url-parser). A browser reads each of these as the GitHub address.
96
+ const repositories = [
97
+ 'https:github.com/acme-team/handbook',
98
+ 'https:/github.com/acme-team/handbook',
99
+ 'http:github.com/acme-team/handbook',
100
+ 'HTTPS:///github.com/acme-team/handbook',
101
+ 'https:\\\\github.com\\acme-team\\handbook',
102
+ 'https:\\/github.com/acme-team/handbook',
103
+ 'https://github.com\\acme-team\\handbook',
104
+ 'wss:github.com/acme-team/handbook',
105
+ 'ftp:/github.com:8443/acme-team/handbook',
106
+ 'file:\\\\github.com\\acme-team\\handbook',
107
+ '\u0001https://github.com/acme-team/handbook',
108
+ 'https://github.com/acme-team/handbook\u0000',
109
+ '\u001f\u0000 https:github.com/acme-team/handbook \u0007',
110
+ 'ht\ttps://github.com/acme-team/handbook',
111
+ 'ht\ntps:git\r\nhub.com/acme-team/hand\tbook',
112
+ ];
113
+ for (const form of repositories)
114
+ expect(parseGitHubLink(form), JSON.stringify(form)).toEqual(REPOSITORY);
115
+ const forms = [
116
+ ['https:github.com/acme-team/handbook/tree/main/docs', folder('main/docs')],
117
+ ['https:\\\\github.com\\acme-team\\handbook\\blob\\main\\docs\\agenda.md', file('main/docs/agenda.md')],
118
+ ['\u0002http:/raw.githubusercontent.com/acme-team/handbook/main/README.md', file('main/README.md', true)],
119
+ ];
120
+ for (const [form, link] of forms)
121
+ expect(parseGitHubLink(form), JSON.stringify(form)).toEqual(link);
122
+ const credentials = [
123
+ 'https:someone:secret@github.com/acme-team/handbook',
124
+ 'http:ghp_secret@github.com/acme-team/handbook',
125
+ 'https:\\\\x-access-token:ghs_secret@github.com\\acme-team\\handbook',
126
+ ];
127
+ for (const form of credentials)
128
+ expect(parseGitHubLink(form), form).toEqual(REFUSED);
129
+ // A scheme the standard does not treat as special names a host only after "//"; another host is not GitHub's.
130
+ for (const form of ['git:github.com/acme-team/handbook', 'https:example.com/acme-team/handbook', '\u0001mailto:someone@github.com']) {
131
+ expect(parseGitHubLink(form), JSON.stringify(form)).toBeNull();
132
+ }
133
+ // After file and its two slashes, a third slash or backslash leaves the host empty
134
+ // (https://url.spec.whatwg.org/#file-host-state): the address is a path on no host, and GitHub's name its first segment.
135
+ for (const form of ['file:///github.com/acme-team/handbook', 'file:\\\\\\github.com\\acme-team\\handbook', 'FILE:\\//github.com/acme-team/handbook']) {
136
+ expect(parseGitHubLink(form), JSON.stringify(form)).toBeNull();
137
+ }
138
+ });
139
+ it('refuses a GitHub address that names no repository, folder or file the pack reads', () => {
140
+ const forms = [
141
+ 'https://github.com/acme-team/handbook/issues/12',
142
+ 'https://github.com/acme-team/handbook/pull/3',
143
+ 'https://github.com/acme-team/handbook/pulls',
144
+ 'https://github.com/acme-team/handbook/wiki',
145
+ 'https://github.com/acme-team/handbook/wiki/Home',
146
+ 'https://github.com/acme-team/handbook/releases',
147
+ 'https://github.com/acme-team/handbook/releases/tag/v1.0',
148
+ 'https://github.com/acme-team/handbook/actions',
149
+ `https://github.com/acme-team/handbook/commit/${COMMIT}`,
150
+ 'https://github.com/acme-team/handbook/compare/main...release',
151
+ 'https://github.com/acme-team/handbook/discussions/4',
152
+ 'https://github.com/acme-team/handbook/tree',
153
+ 'https://github.com/acme-team/handbook/blob',
154
+ 'https://github.com/acme-team/handbook/raw/main',
155
+ 'https://github.com/acme-team/handbook/tree/refs/tags/v1.0/docs',
156
+ 'https://github.com/acme-team/handbook/tree/refs/pull/3/head',
157
+ 'https://github.com/acme-team/handbook/tree/main//docs',
158
+ 'https://github.com/acme-team/handbook/tree/main/%2E%2E%2Fsecret',
159
+ 'https://github.com/acme-team/handbook/tree/main/a%00b',
160
+ 'https://github.com/acme-team/hand%ZZbook',
161
+ 'https://github.com/acme-team/hand%20book',
162
+ 'https://github.com/acme-team/.git',
163
+ 'https://github.com/-acme/handbook',
164
+ 'https://github.com/acme-team',
165
+ 'https://github.com',
166
+ 'https://github.com/',
167
+ 'https://github.com/settings/profile',
168
+ 'https://github.com/orgs/acme-team',
169
+ 'https://github.com/topics/python',
170
+ 'https://github.com/marketplace/actions',
171
+ 'https://github.com/sponsors/someone',
172
+ 'https://gist.github.com/someone/0123abcd',
173
+ 'https://api.github.com/repos/acme-team/handbook',
174
+ 'https://codeload.github.com/acme-team/handbook/zip/refs/heads/main',
175
+ 'https://gist.githubusercontent.com/someone/0123abcd/raw/notes.md',
176
+ 'https://objects.githubusercontent.com/github-production-release-asset/1',
177
+ 'https://raw.githubusercontent.com/acme-team/handbook',
178
+ 'https://raw.githubusercontent.com/acme-team/handbook/main',
179
+ 'https://ghp_secret@github.com/acme-team/handbook',
180
+ 'https://someone:secret@github.com/acme-team/handbook',
181
+ 'https://git:secret@github.com/acme-team/handbook',
182
+ 'someone@github.com:acme-team/handbook.git',
183
+ 'git@gist.github.com:0123abcd.git',
184
+ // Credentials before a GitHub host with no scheme, which a browser opens as an https address.
185
+ 'x-access-token:ghs_secret@github.com/acme-team/handbook',
186
+ 'someone:secret@github.com/acme-team/handbook/tree/main/docs',
187
+ 'someone:secret@www.github.com:443/acme-team/handbook',
188
+ 'someone:secret@raw.githubusercontent.com/acme-team/handbook/main/README.md',
189
+ // GitHub Enterprise Cloud with data residency: an enterprise on a subdomain of ghe.com, its API on
190
+ // api.<subdomain>.ghe.com (https://docs.github.com/en/enterprise-cloud@latest/admin/data-residency/about-github-enterprise-cloud-with-data-residency).
191
+ 'https://octocorp.ghe.com/acme-team/handbook',
192
+ 'https://octocorp.ghe.com/acme-team/handbook/tree/main/docs',
193
+ 'https://api.octocorp.ghe.com/repos/acme-team/handbook',
194
+ 'octocorp.ghe.com./acme-team/handbook',
195
+ 'https://ghe.com',
196
+ ];
197
+ for (const form of forms)
198
+ expect(parseGitHubLink(form), form).toEqual(REFUSED);
199
+ });
200
+ it('answers a GitHub host as a GitHub form or refused, never as another site, whatever the scheme, its slashes, port, path, trailing dots or controls around it', () => {
201
+ const hosts = [
202
+ 'github.com',
203
+ 'www.github.com',
204
+ 'gist.github.com',
205
+ 'api.github.com',
206
+ 'codeload.github.com',
207
+ 'raw.githubusercontent.com',
208
+ 'gist.githubusercontent.com',
209
+ 'ghe.com',
210
+ 'octocorp.ghe.com',
211
+ 'api.octocorp.ghe.com',
212
+ ];
213
+ const schemes = ['https://', 'http://', '', 'https:', 'http:/', 'https:\\\\', 'WSS:\\/', '\u0001https://', ' \u0000'];
214
+ for (const host of hosts.flatMap((name) => [name, `${name}.`, `${name}..`])) {
215
+ for (const scheme of schemes) {
216
+ for (const port of ['', ':8080']) {
217
+ for (const path of ['/', '/x', '/a/b/c/d/e/f', '/a/b/unknown/x', '/a/b?x=1#y']) {
218
+ for (const tail of ['', '\u0000', ' \u001f']) {
219
+ const form = `${scheme}${host}${port}${path}${tail}`;
220
+ expect(parseGitHubLink(form), JSON.stringify(form)).not.toBeNull();
221
+ }
222
+ }
223
+ }
224
+ }
225
+ }
226
+ });
227
+ it("answers null for an address that is not GitHub's, and for text that is no address", () => {
228
+ const forms = [
229
+ 'https://example.com/acme-team/handbook',
230
+ 'https://docs.github.com/en/rest/git/refs',
231
+ 'https://acme-team.github.io/handbook/',
232
+ 'https://github.blog/changelog/',
233
+ 'https://notgithub.com/acme-team/handbook',
234
+ 'https://evilgithub.com/acme-team/handbook',
235
+ 'https://github.com.example.org/acme-team/handbook',
236
+ 'https://githubusercontent.com.example.org/acme-team/handbook',
237
+ 'https://ghe.com.example.org/acme-team/handbook',
238
+ 'https://notghe.com/acme-team/handbook',
239
+ 'https://drive.google.com/file/d/1AbC/view',
240
+ 'git@gitlab.com:acme-team/handbook.git',
241
+ 'mailto:someone@github.com',
242
+ 'javascript:alert(1)',
243
+ 'not a link',
244
+ '',
245
+ ' ',
246
+ ];
247
+ for (const form of forms)
248
+ expect(parseGitHubLink(form), form).toBeNull();
249
+ });
250
+ it('refuses a value that is not text with a TypeError', () => {
251
+ expect(() => parseGitHubLink(42)).toThrow(TypeError);
252
+ expect(() => parseGitHubLink(null)).toThrow(TypeError);
253
+ });
254
+ });
255
+ //# sourceMappingURL=links.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"links.test.js","sourceRoot":"","sources":["../../src/__tests__/links.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * @fileoverview A link's ref settled through GitHub's matching references (a branch, then a tag, then a short commit
3
+ * through GitHub's commits), a tree planned with what it leaves out and why, and a repository's documentation listed
4
+ * and compared at a named branch or commit and within a folder, over a fetch stand-in that answers a repository's
5
+ * branches, references, tags, commits and trees and records each request.
6
+ */
7
+ export {};
8
+ //# sourceMappingURL=refs.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"refs.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/refs.test.ts"],"names":[],"mappings":"AAAA;;;;;GAKG"}