@theholocron/github-client 1.50.0 → 1.51.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/dist/index.d.mts CHANGED
@@ -391,6 +391,29 @@ interface TeamMember {
391
391
  type?: string;
392
392
  }
393
393
  //#endregion
394
+ //#region ../../../../../src/variables/variables.d.ts
395
+ type VariableScope = {
396
+ kind: "repo";
397
+ } | {
398
+ kind: "environment";
399
+ name: string;
400
+ } | {
401
+ kind: "organization";
402
+ org: string;
403
+ };
404
+ /**
405
+ * Which repositories of an organization can read a variable. Only organization variables have one. This client does
406
+ * not read or set the repositories behind `"selected"`; use `"all"` or `"private"` where the access has to be reconciled.
407
+ */
408
+ type VariableVisibility = "all" | "private" | "selected";
409
+ /** A GitHub Actions variable. Unlike a secret its value is readable, so a reconcile can tell whether it differs. */
410
+ interface GitHubVariable {
411
+ name: string;
412
+ value: string;
413
+ /** Present on organization variables only. */
414
+ visibility?: VariableVisibility;
415
+ }
416
+ //#endregion
394
417
  //#region ../../../../../src/user/user.d.ts
395
418
  interface GitHubUser {
396
419
  login: string;
@@ -617,6 +640,13 @@ declare function createGitHubClient(opts: GitHubClientOptions): {
617
640
  topics: {
618
641
  setTopics: (repo: string, names: string[]) => Promise<void>;
619
642
  };
643
+ variables: {
644
+ listVariables: (repo: string, scope: VariableScope) => Promise<GitHubVariable[]>;
645
+ getVariable: (repo: string, scope: VariableScope, name: string) => Promise<GitHubVariable | null>;
646
+ createVariable: (repo: string, scope: VariableScope, body: Record<string, unknown>) => Promise<void>;
647
+ updateVariable: (repo: string, scope: VariableScope, name: string, body: Record<string, unknown>) => Promise<void>;
648
+ deleteVariable: (repo: string, scope: VariableScope, name: string) => Promise<void>;
649
+ };
620
650
  user: {
621
651
  getCurrentUser: () => Promise<GitHubUser>;
622
652
  };
@@ -628,4 +658,4 @@ declare function createGitHubClient(opts: GitHubClientOptions): {
628
658
  };
629
659
  type GitHubClient = ReturnType<typeof createGitHubClient>;
630
660
  //#endregion
631
- export { type CheckRunAnnotation, type CheckRunAnnotationLevel, type CheckRunConclusion, type CheckRunOutput, type CheckRunStatus, type CodeScanningSetupResult, type CreateCheckRunInput, type CreatePagesPayload, type CreatePullInput, type CreateReviewComment, type CreateReviewInput, type GitBlob, type GitCommit, type GitContents, type GitHubAppCredentials, type GitHubCheckRun, GitHubClient, type GitHubClientOptions, type GitHubContents, type GitHubEnvironment, type GitHubInstallationWebhookPayload, type GitHubIssue, type GitHubLabel, type GitHubMilestone, type GitHubPages, type GitHubPublicKey, type GitHubPullRequest, type GitHubPullRequestCommit, type GitHubPullRequestFile, type GitHubPullRequestReview, type GitHubPullRequestWebhookPayload, type GitHubPushWebhookPayload, type GitHubRepo, type GitHubRuleset, type GitHubUser, type GitHubWebhookHeaders, type GitHubWorkflowRun, type GitPull, type GitRef, type GitTree, type GitTreeItem, type InstallationAccessToken, type IssueSearchParams, MAX_CHECK_RUN_ANNOTATIONS, type PagesBuildStatus, type PagesBuildType, type PutContentsInput, type PutContentsResult, type ReviewThread, type SecretScope, type TeamMember, type TeamPermission, type UpdateCheckRunInput, type UpdatePagesPayload, type WorkflowRunFilter, createAppJWT, createGitHubClient, createInstallationClient, getInstallationAccessToken, parseGitHubWebhookHeaders, verifyGitHubWebhookSignature };
661
+ export { type CheckRunAnnotation, type CheckRunAnnotationLevel, type CheckRunConclusion, type CheckRunOutput, type CheckRunStatus, type CodeScanningSetupResult, type CreateCheckRunInput, type CreatePagesPayload, type CreatePullInput, type CreateReviewComment, type CreateReviewInput, type GitBlob, type GitCommit, type GitContents, type GitHubAppCredentials, type GitHubCheckRun, GitHubClient, type GitHubClientOptions, type GitHubContents, type GitHubEnvironment, type GitHubInstallationWebhookPayload, type GitHubIssue, type GitHubLabel, type GitHubMilestone, type GitHubPages, type GitHubPublicKey, type GitHubPullRequest, type GitHubPullRequestCommit, type GitHubPullRequestFile, type GitHubPullRequestReview, type GitHubPullRequestWebhookPayload, type GitHubPushWebhookPayload, type GitHubRepo, type GitHubRuleset, type GitHubUser, type GitHubVariable, type GitHubWebhookHeaders, type GitHubWorkflowRun, type GitPull, type GitRef, type GitTree, type GitTreeItem, type InstallationAccessToken, type IssueSearchParams, MAX_CHECK_RUN_ANNOTATIONS, type PagesBuildStatus, type PagesBuildType, type PutContentsInput, type PutContentsResult, type ReviewThread, type SecretScope, type TeamMember, type TeamPermission, type UpdateCheckRunInput, type UpdatePagesPayload, type VariableScope, type VariableVisibility, type WorkflowRunFilter, createAppJWT, createGitHubClient, createInstallationClient, getInstallationAccessToken, parseGitHubWebhookHeaders, verifyGitHubWebhookSignature };
package/dist/index.mjs CHANGED
@@ -417,7 +417,7 @@ function rulesets(rest) {
417
417
  }
418
418
  //#endregion
419
419
  //#region ../../../../../src/secrets/secrets.ts
420
- function scopeBase(repo, scope) {
420
+ function scopeBase$1(repo, scope) {
421
421
  switch (scope.kind) {
422
422
  case "repo": return `${repoBase(repo)}/actions`;
423
423
  case "environment": return `${repoBase(repo)}/environments/${scope.name}`;
@@ -426,14 +426,14 @@ function scopeBase(repo, scope) {
426
426
  }
427
427
  function secrets(rest) {
428
428
  return {
429
- listSecrets: (repo, scope) => rest.request(`${scopeBase(repo, scope)}/secrets`).then((r) => r.secrets.map((s) => s.name)),
430
- getPublicKey: (repo, scope) => rest.request(`${scopeBase(repo, scope)}/secrets/public-key`),
431
- putSecret: (repo, scope, name, body) => rest.request(`${scopeBase(repo, scope)}/secrets/${name}`, {
429
+ listSecrets: (repo, scope) => rest.request(`${scopeBase$1(repo, scope)}/secrets`).then((r) => r.secrets.map((s) => s.name)),
430
+ getPublicKey: (repo, scope) => rest.request(`${scopeBase$1(repo, scope)}/secrets/public-key`),
431
+ putSecret: (repo, scope, name, body) => rest.request(`${scopeBase$1(repo, scope)}/secrets/${name}`, {
432
432
  method: "PUT",
433
433
  body,
434
434
  expectNoContent: true
435
435
  }),
436
- deleteSecret: (repo, scope, name) => rest.request(`${scopeBase(repo, scope)}/secrets/${name}`, {
436
+ deleteSecret: (repo, scope, name) => rest.request(`${scopeBase$1(repo, scope)}/secrets/${name}`, {
437
437
  method: "DELETE",
438
438
  expectNoContent: true
439
439
  })
@@ -487,9 +487,9 @@ function security(rest) {
487
487
  }
488
488
  //#endregion
489
489
  //#region ../../../../../src/teams/teams.ts
490
- const PAGE = 100;
490
+ const PAGE$1 = 100;
491
491
  /** A guard so a runaway listing cannot loop forever: 50 pages of 100 is far more than any team. */
492
- const MAX_PAGES = 50;
492
+ const MAX_PAGES$1 = 50;
493
493
  function teams(rest) {
494
494
  return {
495
495
  /**
@@ -499,14 +499,14 @@ function teams(rest) {
499
499
  members: async (org, teamSlug) => {
500
500
  const path = `/orgs/${segment(org)}/teams/${segment(teamSlug)}/members`;
501
501
  const read = (page) => rest.request(path, { query: {
502
- per_page: String(PAGE),
502
+ per_page: String(PAGE$1),
503
503
  page: String(page)
504
504
  } });
505
505
  const all = [];
506
- for (let page = 1; page <= MAX_PAGES; page++) {
506
+ for (let page = 1; page <= MAX_PAGES$1; page++) {
507
507
  const batch = await read(page);
508
508
  all.push(...batch);
509
- if (batch.length < PAGE) return all;
509
+ if (batch.length < PAGE$1) return all;
510
510
  }
511
511
  if ((await read(51)).length > 0) throw new Error(`team ${org}/${teamSlug} has more than 5000 members; refusing to read them all`);
512
512
  return all;
@@ -531,6 +531,69 @@ function user(rest) {
531
531
  return { getCurrentUser: () => rest.request("/user") };
532
532
  }
533
533
  //#endregion
534
+ //#region ../../../../../src/variables/variables.ts
535
+ /** GitHub's maximum page size for this listing is 30, not the usual 100. */
536
+ const PAGE = 30;
537
+ /** A guard so a runaway listing cannot loop forever: 100 pages of 30 is more than GitHub allows. */
538
+ const MAX_PAGES = 100;
539
+ /** `/repos/owner/name`, refusing anything but exactly two safe segments, so a caller's `repo` cannot address another resource. */
540
+ function repoPath(repo) {
541
+ const parts = repo.split("/");
542
+ if (parts.length !== 2) throw new Error(`invalid repository: ${JSON.stringify(repo)} (expected "owner/name")`);
543
+ return `/repos/${parts.map(segment).join("/")}`;
544
+ }
545
+ function scopeBase(repo, scope) {
546
+ switch (scope.kind) {
547
+ case "repo": return `${repoPath(repo)}/actions/variables`;
548
+ case "environment": return `${repoPath(repo)}/environments/${segment(scope.name)}/variables`;
549
+ case "organization": return `/orgs/${segment(scope.org)}/actions/variables`;
550
+ }
551
+ }
552
+ function variables(rest) {
553
+ return {
554
+ /** Every variable in the scope, read page by page, with its value. */
555
+ listVariables: async (repo, scope) => {
556
+ const path = scopeBase(repo, scope);
557
+ const all = [];
558
+ for (let page = 1; page <= MAX_PAGES; page++) {
559
+ const batch = await rest.request(path, { query: {
560
+ per_page: String(PAGE),
561
+ page: String(page)
562
+ } });
563
+ all.push(...batch.variables);
564
+ if (batch.variables.length < PAGE || all.length >= batch.total_count) return all;
565
+ }
566
+ throw new Error(`more than 3000 variables in ${path}; refusing to read them all`);
567
+ },
568
+ /** One variable, or `null` when the scope has none of that name. */
569
+ getVariable: async (repo, scope, name) => {
570
+ try {
571
+ return await rest.request(`${scopeBase(repo, scope)}/${segment(name)}`);
572
+ } catch (err) {
573
+ if (err instanceof ProviderApiError && err.status === 404) return null;
574
+ throw err;
575
+ }
576
+ },
577
+ createVariable: (repo, scope, body) => rest.request(scopeBase(repo, scope), {
578
+ method: "POST",
579
+ body,
580
+ expectNoContent: true
581
+ }),
582
+ updateVariable: async (repo, scope, name, body) => rest.request(`${scopeBase(repo, scope)}/${segment(name)}`, {
583
+ method: "PATCH",
584
+ body: {
585
+ name,
586
+ ...body
587
+ },
588
+ expectNoContent: true
589
+ }),
590
+ deleteVariable: async (repo, scope, name) => rest.request(`${scopeBase(repo, scope)}/${segment(name)}`, {
591
+ method: "DELETE",
592
+ expectNoContent: true
593
+ })
594
+ };
595
+ }
596
+ //#endregion
534
597
  //#region ../../../../../src/workflows/workflows.ts
535
598
  function workflows(rest) {
536
599
  return {
@@ -787,6 +850,7 @@ function createGitHubClient(opts) {
787
850
  security: security(rest),
788
851
  teams: teams(rest),
789
852
  topics: topics(rest),
853
+ variables: variables(rest),
790
854
  user: user(rest),
791
855
  workflows: workflows(rest)
792
856
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@theholocron/github-client",
3
- "version": "1.50.0",
3
+ "version": "1.51.0",
4
4
  "description": "A TypeScript client for the GitHub REST API",
5
5
  "keywords": [
6
6
  "github",
@@ -33,7 +33,7 @@
33
33
  ],
34
34
  "dependencies": {
35
35
  "@theholocron/observability": "^0.3.0",
36
- "@theholocron/http-client": "^1.50.0"
36
+ "@theholocron/http-client": "^1.51.0"
37
37
  },
38
38
  "devDependencies": {
39
39
  "@theholocron/cli": "5.0.0-alpha.79",