@effected/github 0.6.0 → 0.7.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/CheckRun.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import { GitHubClient } from "./GitHubClient.js";
2
2
  import { Repo } from "./Repo.js";
3
+ import { numericId } from "./internal/ids.js";
3
4
  import { Cause, Context, Effect, Exit, Layer, Ref, Schema } from "effect";
4
5
 
5
6
  //#region src/CheckRun.ts
@@ -197,7 +198,7 @@ const concludeFor = (name, id, exit, recorded, complete) => {
197
198
  return Exit.isSuccess(exit) ? write : Effect.ignore(write);
198
199
  };
199
200
  const refOf = (raw) => CheckRunRef.make({
200
- id: raw.id,
201
+ id: numericId(raw.id),
201
202
  name: raw.name,
202
203
  url: raw.html_url ?? "",
203
204
  status: raw.status
package/GitHubApp.js CHANGED
@@ -1,6 +1,7 @@
1
1
  import { GitHubError } from "./GitHubError.js";
2
2
  import { GitHubGraphQLError } from "./GraphQL.js";
3
3
  import { GitHubClient, makeClientShape } from "./GitHubClient.js";
4
+ import { numericId } from "./internal/ids.js";
4
5
  import { Clock, Context, DateTime, Duration, Effect, Layer, Option, Redacted, Ref, Schema, Stream } from "effect";
5
6
  import githubAppJwt from "universal-github-app-jwt";
6
7
 
@@ -261,7 +262,7 @@ function makeApp(options) {
261
262
  return Effect.sync(() => {
262
263
  const installations = Effect.fn("GitHubApp.installations")(function* (credentials) {
263
264
  return (yield* (yield* asApp(credentials, options)).paginate("GET /app/installations", {}).pipe(Effect.catch(appFailure("installation")))).map((entry) => Installation.make({
264
- id: entry.id,
265
+ id: numericId(entry.id),
265
266
  ...entry.account !== null && entry.account !== void 0 && "login" in entry.account ? { account: entry.account.login } : {}
266
267
  }));
267
268
  });
@@ -304,7 +305,7 @@ function makeApp(options) {
304
305
  return AppIdentity.make({
305
306
  slug,
306
307
  name,
307
- ...Option.isSome(user) ? { userId: user.value.id } : {}
308
+ ...Option.isSome(user) ? { userId: numericId(user.value.id) } : {}
308
309
  });
309
310
  }),
310
311
  installations
package/GitHubIssue.js CHANGED
@@ -1,6 +1,7 @@
1
1
  import { GraphQLDocument } from "./GraphQL.js";
2
2
  import { GitHubClient } from "./GitHubClient.js";
3
3
  import { Repo } from "./Repo.js";
4
+ import { numericId } from "./internal/ids.js";
4
5
  import { CommentRecord } from "./PullRequestComment.js";
5
6
  import { Context, Effect, Layer, Schema } from "effect";
6
7
 
@@ -212,13 +213,14 @@ const make = (client) => ({
212
213
  repo,
213
214
  number
214
215
  });
215
- return (yield* client.request("POST /repos/{owner}/{repo}/issues/{issue_number}/comments", {
216
+ const created = yield* client.request("POST /repos/{owner}/{repo}/issues/{issue_number}/comments", {
216
217
  owner,
217
218
  repo,
218
219
  issue_number: number,
219
220
  body,
220
221
  headers: API_VERSION_HEADERS
221
- })).id;
222
+ });
223
+ return numericId(created.id);
222
224
  }),
223
225
  commentOnce: Effect.fn("GitHubIssue.commentOnce")(function* (issueNumber, marker, body) {
224
226
  const { owner, repo } = yield* Repo;
@@ -237,7 +239,7 @@ const make = (client) => ({
237
239
  if (existing !== void 0) return CommentOnceResult.make({
238
240
  wrote: false,
239
241
  comment: CommentRecord.make({
240
- id: existing.id,
242
+ id: numericId(existing.id),
241
243
  body: existing.body ?? "",
242
244
  url: existing.html_url
243
245
  })
@@ -252,7 +254,7 @@ const make = (client) => ({
252
254
  return CommentOnceResult.make({
253
255
  wrote: true,
254
256
  comment: CommentRecord.make({
255
- id: created.id,
257
+ id: numericId(created.id),
256
258
  body: created.body ?? "",
257
259
  url: created.html_url
258
260
  })
@@ -1,5 +1,6 @@
1
1
  import { GitHubClient } from "./GitHubClient.js";
2
2
  import { Repo } from "./Repo.js";
3
+ import { numericId } from "./internal/ids.js";
3
4
  import { Context, Effect, Layer, Option, Schema } from "effect";
4
5
 
5
6
  //#region src/PullRequestComment.ts
@@ -61,7 +62,7 @@ const unstubbed = (member) => {
61
62
  throw new Error(`PullRequestComment.makeTest: ${member}() was called but not stubbed — pass an override.`);
62
63
  };
63
64
  const recordOf = (raw) => CommentRecord.make({
64
- id: raw.id,
65
+ id: numericId(raw.id),
65
66
  body: raw.body ?? "",
66
67
  url: raw.html_url
67
68
  });
package/README.md CHANGED
@@ -122,7 +122,7 @@ const program = Effect.gen(function* () {
122
122
 
123
123
  The existence check pages the issue's comments to the end and matches the marker in `upsert`'s exact spelling, so a comment either member writes stays findable by the other. It is a look-before-write: two runs racing on the same issue can both see no marker and both post, so treat the marker as the record rather than the check as a lock.
124
124
 
125
- Whether a run should comment at all usually turns on which issues a pull request closes, and that is a grammar rather than an API call. `IssueReferences` is that grammar as plain functions no service, no layer, nothing but strings in in the two dialects consumers actually write:
125
+ Whether a run should comment at all usually turns on which issues a pull request closes, and that is a grammar rather than an API call. That grammar now lives in [`@effected/github-references`](https://www.npmjs.com/package/@effected/github-references), a pure package with no octokit behind it; this package re-exports the two dialects it used to own so existing code keeps compiling:
126
126
 
127
127
  ```ts
128
128
  import { harvestIssueReferences, parseBareLineReference } from "@effected/github";
@@ -140,6 +140,8 @@ console.log(parseBareLineReference("closes #12 for real"));
140
140
 
141
141
  `harvestIssueReferences` reads the **inline-in-prose** dialect: one of the nine closing keywords (`CLOSING_KEYWORDS`) followed by whitespace and `#<number>`, anywhere in the text, no colon — the spelling GitHub itself scans a pull request body for. Duplicates come back as written, because whether `fixes #1, fixes #1` means one intent or two is the caller's question. `parseBareLineReference` reads the **bare-line** dialect, where the whole trimmed line is the reference and the colon is optional — the shape a generated references block writes, one per line. Cross-repo (`owner/repo#12`) and full-URL references are out of scope.
142
142
 
143
+ The re-export covers exactly six names — `CLOSING_KEYWORDS`, `ClosingKeyword`, `IssueReference`, `harvestIssueReferences`, `BareLineReference`, `parseBareLineReference` — and may be dropped at a later release. Import from `@effected/github-references` directly for new code; it also carries a third dialect this package does not re-export, the closing list (`Closes #247, #248 and #251`).
144
+
143
145
  ## Repository configuration
144
146
 
145
147
  Six services cover the half of a repository that is policy rather than content: `RepositorySecret`, `RepositoryVariable`, `Ruleset`, `DeploymentEnvironment`, `RepositorySecurity` and `CodeScanning`. They are shaped for a program applying the same configuration across a fleet, so every list read paginates to the end and a truncated page never becomes a wrong decision.
@@ -273,7 +275,7 @@ const TestClient = GitHubClient.layerFixture(fixtures);
273
275
  - `CheckRun` — `withCheckRun` concludes on every exit path; `CheckRunOutput.truncated()` cuts rendered output to GitHub's byte limits.
274
276
  - `PullRequest` / `PullRequestComment` — upserts for both, `listFiles` answering with the same full `CommitFile` records a commit read returns, `headSha`/`baseSha` on `PullRequestInfo`, plus `CommentMarker` for finding a sticky comment again.
275
277
  - `GitHubIssue` — issues and their comments, including `commentOnce` for a marked comment that is posted exactly once and never edited, and `linkedIssues` for what a pull request closes.
276
- - `IssueReferences` — GitHub's nine closing keywords as pure functions: `harvestIssueReferences` for references inline in prose, `parseBareLineReference` for one-per-line generated ones.
278
+ - `harvestIssueReferences` / `parseBareLineReference` a compatibility re-export of the two closing-reference dialects that moved to `@effected/github-references`, with `CLOSING_KEYWORDS` and their result types.
277
279
  - `GitHubRelease` — releases and asset uploads, including the one route (`uploadAsset`, with the endpoint's optional display label) outside GitHub's generated endpoint map.
278
280
  - `RepositorySecret` / `RepositoryVariable` — repository and environment secrets and variables, with the sealed-box encryption GitHub's secrets API demands kept in one module nothing else imports.
279
281
  - `Ruleset` / `DeploymentEnvironment` / `RepositorySecurity` / `CodeScanning` — the rest of the configuration tier: rulesets matched by name and scope, idempotent environment writes, the three security toggles GitHub keeps off the repository endpoint, and CodeQL default setup with the language detection that gates it.
package/index.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import { BareLineReference, CLOSING_KEYWORDS, ClosingKeyword, IssueReference, harvestIssueReferences, parseBareLineReference } from "@effected/github-references";
1
2
  import { Config, Context, Duration, Effect, Layer, Option, Redacted, Result, Schedule, Schema, Scope, Stream } from "effect";
2
3
  import { PaginatingEndpoints } from "@octokit/plugin-paginate-rest";
3
4
  import { Endpoints, RequestHeaders } from "@octokit/types";
@@ -2549,108 +2550,6 @@ declare class GitTag extends GitTag_base {
2549
2550
  static readonly layerTest: (overrides?: Partial<GitTagShape>) => Layer.Layer<GitTag>;
2550
2551
  }
2551
2552
  //#endregion
2552
- //#region src/IssueReferences.d.ts
2553
- /**
2554
- * GitHub's closing-keyword issue-reference grammar, as pure functions.
2555
- *
2556
- * @remarks
2557
- * GitHub links an issue to a pull request when the PR's description carries
2558
- * `<keyword> #<number>` for one of nine documented keywords. Consumers speak
2559
- * that grammar in two distinct dialects, and this module models exactly those
2560
- * two — no service, no layer, nothing but strings in and values out:
2561
- *
2562
- * - **Inline-in-prose** ({@link harvestIssueReferences}): a reference may appear anywhere in
2563
- * running text — `"fixes #12 and closes #13"` — with mandatory whitespace
2564
- * and **no colon**, because that is the spelling GitHub itself scans PR
2565
- * bodies for. This is the dialect a release pipeline harvests from commit
2566
- * subjects and PR descriptions.
2567
- * - **Bare-line** ({@link parseBareLineReference}): the whole line, after trimming, *is*
2568
- * the reference — `"Closes: #12"` — with an **optional colon**, because a
2569
- * generated references region writes one reference per line and a colon
2570
- * reads better there. GitHub does not require the colon; the region format
2571
- * allows it, so the parser must too. The dialects differ because their
2572
- * producers do: prose is written by humans for GitHub's scanner, the region
2573
- * is written by tooling for humans.
2574
- *
2575
- * Deliberately out of scope this round: cross-repo references
2576
- * (`owner/repo#N`) and full-URL references
2577
- * (`https://github.com/owner/repo/issues/N`). Both are real GitHub spellings;
2578
- * neither dialect's consumers emit them yet, and guessing at their shape here
2579
- * would freeze an API nobody has driven.
2580
- */
2581
- /**
2582
- * The nine closing keywords GitHub documents, lowercased.
2583
- *
2584
- * @public
2585
- */
2586
- declare const CLOSING_KEYWORDS: readonly ["close", "closes", "closed", "fix", "fixes", "fixed", "resolve", "resolves", "resolved"];
2587
- /**
2588
- * One of the nine documented closing keywords, in canonical lowercase form.
2589
- *
2590
- * @public
2591
- */
2592
- type ClosingKeyword = (typeof CLOSING_KEYWORDS)[number];
2593
- /**
2594
- * One closing reference found in prose by {@link harvestIssueReferences}.
2595
- *
2596
- * @public
2597
- */
2598
- interface IssueReference {
2599
- /** The referenced issue number. */
2600
- readonly issueNumber: number;
2601
- /** The matched keyword, lowercased to its canonical form. */
2602
- readonly keyword: ClosingKeyword;
2603
- /** Offset of the first character of the whole match (`keyword` through digits). */
2604
- readonly start: number;
2605
- /** Offset one past the last character of the whole match. */
2606
- readonly end: number;
2607
- }
2608
- /**
2609
- * The closing reference a bare line carries, per {@link parseBareLineReference}.
2610
- *
2611
- * @remarks
2612
- * No offsets: in the bare-line dialect the whole line is the reference, so
2613
- * positions within it locate nothing a caller acts on.
2614
- *
2615
- * @public
2616
- */
2617
- interface BareLineReference {
2618
- /** The referenced issue number. */
2619
- readonly issueNumber: number;
2620
- /** The matched keyword, lowercased to its canonical form. */
2621
- readonly keyword: ClosingKeyword;
2622
- }
2623
- /**
2624
- * Every inline closing reference in `text`, in document order.
2625
- *
2626
- * @remarks
2627
- * The **inline-in-prose** dialect: case-insensitive `<keyword> #<number>`
2628
- * anywhere in the text, whitespace mandatory, colon not accepted — a colon
2629
- * spelling belongs to the bare-line dialect and {@link parseBareLineReference}.
2630
- * Duplicates are preserved: whether `fixes #1, fixes #1` means one reference
2631
- * or two is the caller's business, not a parser's.
2632
- *
2633
- * A match whose digits exceed `Number.MAX_SAFE_INTEGER` is skipped, not
2634
- * misparsed — see the module remarks for the scope boundary.
2635
- *
2636
- * @public
2637
- */
2638
- declare const harvestIssueReferences: (text: string) => ReadonlyArray<IssueReference>;
2639
- /**
2640
- * The reference a whole line carries, or `Option.none()`.
2641
- *
2642
- * @remarks
2643
- * The **bare-line** dialect: after trimming, the entire line must be
2644
- * `<keyword>[:] #<number>` — keyword case-insensitive, colon optional,
2645
- * whitespace before the `#` mandatory. Trailing prose, a missing keyword, or
2646
- * anything else at all is a rejection, never a partial parse; a line carries
2647
- * one reference or none. Digits past `Number.MAX_SAFE_INTEGER` reject too,
2648
- * for the same reason {@link harvestIssueReferences} skips them.
2649
- *
2650
- * @public
2651
- */
2652
- declare const parseBareLineReference: (line: string) => Option.Option<BareLineReference>;
2653
- //#endregion
2654
2553
  //#region src/PullRequest.d.ts
2655
2554
  /** How a pull request is merged. @public */
2656
2555
  declare const MergeMethod: Schema.Literals<readonly ["merge", "squash", "rebase"]>;
package/index.js CHANGED
@@ -18,7 +18,6 @@ import { CommentOnceResult, GitHubIssue, IssueInfo, LinkedIssue } from "./GitHub
18
18
  import { GitHubRelease, ReleaseAsset, ReleaseInfo } from "./GitHubRelease.js";
19
19
  import { GRAPHQL_ONLY_SETTINGS, GitHubRepository, SECURITY_ANALYSIS_STATUS_FIELDS, transformSecurityAndAnalysis } from "./GitHubRepository.js";
20
20
  import { GitTag, SemverTag, TagRef, versionFromTag } from "./GitTag.js";
21
- import { CLOSING_KEYWORDS, harvestIssueReferences, parseBareLineReference } from "./IssueReferences.js";
22
21
  import { PageOptions } from "./Rest.js";
23
22
  import { MergeMethod, PullRequest, PullRequestInfo } from "./PullRequest.js";
24
23
  import { RepositorySecret } from "./RepositorySecret.js";
@@ -27,5 +26,6 @@ import { RepositoryVariable } from "./RepositoryVariable.js";
27
26
  import { Ruleset } from "./Ruleset.js";
28
27
  import { ExtraPermission, PermissionGap, PermissionLevel, PermissionResult, TokenPermissionError, TokenPermissions } from "./TokenPermissions.js";
29
28
  import { WorkflowDispatch, WorkflowRunStatus } from "./WorkflowDispatch.js";
29
+ import { CLOSING_KEYWORDS, harvestIssueReferences, parseBareLineReference } from "@effected/github-references";
30
30
 
31
31
  export { Annotation, AnnotationLevel, AppIdentity, ArtifactMetadata, Attestation, AttestationListEntry, AttestationRecord, BotIdentity, CLOSING_KEYWORDS, CheckConclusion, CheckRun, CheckRunOutput, CheckRunRef, CodeScanning, CommentMarker, CommentOnceResult, CommentRecord, CommitComparison, CommitFile, CommitRef, CommitSummary, DeploymentEnvironment, ExtraPermission, FileChange, FileContent, FileDeletion, FileMode, FileStatus, GRAPHQL_ONLY_SETTINGS, GitBranch, GitCommit, GitHubApp, GitHubAppError, GitHubClient, GitHubCommit, GitHubContent, GitHubError, GitHubErrorKind, GitHubGraphQLError, GitHubIssue, GitHubRelease, GitHubRepository, GitTag, GraphQLDocument, GraphQLErrorEntry, Installation, InstallationToken, InvalidRepoRefError, IssueInfo, LinkedIssue, MergeMethod, PageOptions, PermissionGap, PermissionLevel, PermissionResult, PullRequest, PullRequestComment, PullRequestInfo, RateLimitSnapshot, ReleaseAsset, ReleaseInfo, Repo, RepoRef, RepositorySecret, RepositorySecurity, RepositoryVariable, RetryPolicy, Ruleset, SECURITY_ANALYSIS_STATUS_FIELDS, SemverTag, StorageRecordInput, TagRef, TokenPermissionError, TokenPermissions, WorkflowDispatch, WorkflowRunStatus, harvestIssueReferences, parseBareLineReference, transformSecurityAndAnalysis, versionFromTag };
@@ -0,0 +1,17 @@
1
+ //#region src/internal/ids.ts
2
+ /**
3
+ * Narrows the `number | bigint` resource ids `@octokit/types` v17 declares
4
+ * back to `number`.
5
+ *
6
+ * GitHub's REST payloads arrive through `JSON.parse`, which never produces a
7
+ * bigint, so at runtime the value is always a number today — the union is
8
+ * upstream future-proofing for ids beyond 2^53. If GitHub ever crosses that
9
+ * line, the `id: number` fields on this package's public records have to be
10
+ * redesigned; a runtime coercion here could not paper over it.
11
+ *
12
+ * Leaf module: imports nothing.
13
+ */
14
+ const numericId = (id) => Number(id);
15
+
16
+ //#endregion
17
+ export { numericId };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@effected/github",
3
- "version": "0.6.0",
3
+ "version": "0.7.0",
4
4
  "private": false,
5
5
  "description": "Typed GitHub REST and GraphQL services over the octokit core request surface, with app auth and resource helpers.",
6
6
  "keywords": [
@@ -38,10 +38,11 @@
38
38
  "./package.json": "./package.json"
39
39
  },
40
40
  "dependencies": {
41
+ "@effected/github-references": "^0.1.0",
41
42
  "@effected/semver": "^0.5.0",
42
43
  "@octokit/core": "^7.0.6",
43
- "@octokit/plugin-paginate-rest": "^14.0.0",
44
- "@octokit/types": "^16.0.0",
44
+ "@octokit/plugin-paginate-rest": "^15.0.0",
45
+ "@octokit/types": "^17.0.0",
45
46
  "blakejs": "1.2.1",
46
47
  "tweetnacl": "1.0.3",
47
48
  "universal-github-app-jwt": "^2.2.2"
@@ -1,126 +0,0 @@
1
- import { Option } from "effect";
2
-
3
- //#region src/IssueReferences.ts
4
- /**
5
- * GitHub's closing-keyword issue-reference grammar, as pure functions.
6
- *
7
- * @remarks
8
- * GitHub links an issue to a pull request when the PR's description carries
9
- * `<keyword> #<number>` for one of nine documented keywords. Consumers speak
10
- * that grammar in two distinct dialects, and this module models exactly those
11
- * two — no service, no layer, nothing but strings in and values out:
12
- *
13
- * - **Inline-in-prose** ({@link harvestIssueReferences}): a reference may appear anywhere in
14
- * running text — `"fixes #12 and closes #13"` — with mandatory whitespace
15
- * and **no colon**, because that is the spelling GitHub itself scans PR
16
- * bodies for. This is the dialect a release pipeline harvests from commit
17
- * subjects and PR descriptions.
18
- * - **Bare-line** ({@link parseBareLineReference}): the whole line, after trimming, *is*
19
- * the reference — `"Closes: #12"` — with an **optional colon**, because a
20
- * generated references region writes one reference per line and a colon
21
- * reads better there. GitHub does not require the colon; the region format
22
- * allows it, so the parser must too. The dialects differ because their
23
- * producers do: prose is written by humans for GitHub's scanner, the region
24
- * is written by tooling for humans.
25
- *
26
- * Deliberately out of scope this round: cross-repo references
27
- * (`owner/repo#N`) and full-URL references
28
- * (`https://github.com/owner/repo/issues/N`). Both are real GitHub spellings;
29
- * neither dialect's consumers emit them yet, and guessing at their shape here
30
- * would freeze an API nobody has driven.
31
- */
32
- /**
33
- * The nine closing keywords GitHub documents, lowercased.
34
- *
35
- * @public
36
- */
37
- const CLOSING_KEYWORDS = [
38
- "close",
39
- "closes",
40
- "closed",
41
- "fix",
42
- "fixes",
43
- "fixed",
44
- "resolve",
45
- "resolves",
46
- "resolved"
47
- ];
48
- /**
49
- * Both patterns derive from {@link CLOSING_KEYWORDS} so the constant and the
50
- * grammar cannot drift. Alternation order is safe: the regex engine backtracks
51
- * through alternatives, so `close` matching inside `closes` fails at the
52
- * mandatory `#`-introducer and retries the longer keyword.
53
- */
54
- const KEYWORDS = CLOSING_KEYWORDS.join("|");
55
- /** Inline-in-prose: mandatory whitespace, no colon. */
56
- const INLINE_PATTERN = new RegExp(`\\b(${KEYWORDS})\\s+#(\\d+)`, "gi");
57
- /**
58
- * Bare-line: optional colon, then mandatory same-line whitespace. `[ \t]`
59
- * rather than `\s`, so an embedded newline cannot smuggle two lines past a
60
- * parser whose contract is one.
61
- */
62
- const BARE_LINE_PATTERN = new RegExp(`^(${KEYWORDS}):?[ \\t]+#(\\d+)$`, "i");
63
- /**
64
- * `#<digits>` parsed to a number — or `undefined` when the digits exceed
65
- * `Number.MAX_SAFE_INTEGER`, because a silently rounded issue number is worse
66
- * than a skipped match. Callers skip such matches; they do not fail.
67
- */
68
- const safeIssueNumber = (digits) => {
69
- const value = Number(digits);
70
- return Number.isSafeInteger(value) ? value : void 0;
71
- };
72
- /**
73
- * Every inline closing reference in `text`, in document order.
74
- *
75
- * @remarks
76
- * The **inline-in-prose** dialect: case-insensitive `<keyword> #<number>`
77
- * anywhere in the text, whitespace mandatory, colon not accepted — a colon
78
- * spelling belongs to the bare-line dialect and {@link parseBareLineReference}.
79
- * Duplicates are preserved: whether `fixes #1, fixes #1` means one reference
80
- * or two is the caller's business, not a parser's.
81
- *
82
- * A match whose digits exceed `Number.MAX_SAFE_INTEGER` is skipped, not
83
- * misparsed — see the module remarks for the scope boundary.
84
- *
85
- * @public
86
- */
87
- const harvestIssueReferences = (text) => {
88
- const references = [];
89
- for (const match of text.matchAll(INLINE_PATTERN)) {
90
- const issueNumber = safeIssueNumber(match[2] ?? "");
91
- if (issueNumber === void 0) continue;
92
- references.push({
93
- issueNumber,
94
- keyword: (match[1] ?? "").toLowerCase(),
95
- start: match.index,
96
- end: match.index + match[0].length
97
- });
98
- }
99
- return references;
100
- };
101
- /**
102
- * The reference a whole line carries, or `Option.none()`.
103
- *
104
- * @remarks
105
- * The **bare-line** dialect: after trimming, the entire line must be
106
- * `<keyword>[:] #<number>` — keyword case-insensitive, colon optional,
107
- * whitespace before the `#` mandatory. Trailing prose, a missing keyword, or
108
- * anything else at all is a rejection, never a partial parse; a line carries
109
- * one reference or none. Digits past `Number.MAX_SAFE_INTEGER` reject too,
110
- * for the same reason {@link harvestIssueReferences} skips them.
111
- *
112
- * @public
113
- */
114
- const parseBareLineReference = (line) => {
115
- const match = BARE_LINE_PATTERN.exec(line.trim());
116
- if (match === null) return Option.none();
117
- const issueNumber = safeIssueNumber(match[2] ?? "");
118
- if (issueNumber === void 0) return Option.none();
119
- return Option.some({
120
- issueNumber,
121
- keyword: (match[1] ?? "").toLowerCase()
122
- });
123
- };
124
-
125
- //#endregion
126
- export { CLOSING_KEYWORDS, harvestIssueReferences, parseBareLineReference };