@node-core/utils 4.0.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.
Files changed (98) hide show
  1. package/LICENSE +7 -0
  2. package/README.md +158 -0
  3. package/bin/get-metadata.js +11 -0
  4. package/bin/git-node.js +30 -0
  5. package/bin/ncu-ci.js +600 -0
  6. package/bin/ncu-config.js +101 -0
  7. package/bin/ncu-team.js +76 -0
  8. package/components/git/backport.js +70 -0
  9. package/components/git/epilogue.js +18 -0
  10. package/components/git/land.js +223 -0
  11. package/components/git/metadata.js +94 -0
  12. package/components/git/release.js +99 -0
  13. package/components/git/security.js +35 -0
  14. package/components/git/status.js +32 -0
  15. package/components/git/sync.js +24 -0
  16. package/components/git/v8.js +121 -0
  17. package/components/git/vote.js +84 -0
  18. package/components/git/wpt.js +87 -0
  19. package/components/metadata.js +49 -0
  20. package/lib/auth.js +133 -0
  21. package/lib/backport_session.js +302 -0
  22. package/lib/cache.js +107 -0
  23. package/lib/cherry_pick.js +304 -0
  24. package/lib/ci/build-types/benchmark_run.js +72 -0
  25. package/lib/ci/build-types/citgm_build.js +194 -0
  26. package/lib/ci/build-types/citgm_comparison_build.js +174 -0
  27. package/lib/ci/build-types/commit_build.js +112 -0
  28. package/lib/ci/build-types/daily_build.js +24 -0
  29. package/lib/ci/build-types/fanned_build.js +87 -0
  30. package/lib/ci/build-types/health_build.js +63 -0
  31. package/lib/ci/build-types/job.js +114 -0
  32. package/lib/ci/build-types/linter_build.js +35 -0
  33. package/lib/ci/build-types/normal_build.js +89 -0
  34. package/lib/ci/build-types/pr_build.js +101 -0
  35. package/lib/ci/build-types/test_build.js +186 -0
  36. package/lib/ci/build-types/test_run.js +41 -0
  37. package/lib/ci/ci_failure_parser.js +325 -0
  38. package/lib/ci/ci_type_parser.js +203 -0
  39. package/lib/ci/ci_utils.js +106 -0
  40. package/lib/ci/failure_aggregator.js +152 -0
  41. package/lib/ci/jenkins_constants.js +28 -0
  42. package/lib/ci/run_ci.js +120 -0
  43. package/lib/cli.js +192 -0
  44. package/lib/collaborators.js +140 -0
  45. package/lib/config.js +72 -0
  46. package/lib/figures.js +7 -0
  47. package/lib/file.js +43 -0
  48. package/lib/github/templates/next-security-release.md +97 -0
  49. package/lib/github/tree.js +162 -0
  50. package/lib/landing_session.js +506 -0
  51. package/lib/links.js +123 -0
  52. package/lib/mergeable_state.js +3 -0
  53. package/lib/metadata_gen.js +61 -0
  54. package/lib/pr_checker.js +605 -0
  55. package/lib/pr_data.js +115 -0
  56. package/lib/pr_summary.js +62 -0
  57. package/lib/prepare_release.js +772 -0
  58. package/lib/prepare_security.js +117 -0
  59. package/lib/proxy.js +21 -0
  60. package/lib/queries/DefaultBranchRef.gql +8 -0
  61. package/lib/queries/LastCommit.gql +16 -0
  62. package/lib/queries/PR.gql +37 -0
  63. package/lib/queries/PRComments.gql +27 -0
  64. package/lib/queries/PRCommits.gql +45 -0
  65. package/lib/queries/PRs.gql +25 -0
  66. package/lib/queries/Reviews.gql +23 -0
  67. package/lib/queries/SearchIssue.gql +51 -0
  68. package/lib/queries/Team.gql +22 -0
  69. package/lib/queries/TreeEntries.gql +12 -0
  70. package/lib/queries/VotePRInfo.gql +28 -0
  71. package/lib/release/utils.js +53 -0
  72. package/lib/request.js +185 -0
  73. package/lib/review_state.js +5 -0
  74. package/lib/reviews.js +178 -0
  75. package/lib/run.js +106 -0
  76. package/lib/session.js +415 -0
  77. package/lib/sync_session.js +15 -0
  78. package/lib/team_info.js +95 -0
  79. package/lib/update-v8/applyNodeChanges.js +49 -0
  80. package/lib/update-v8/backport.js +258 -0
  81. package/lib/update-v8/commitUpdate.js +26 -0
  82. package/lib/update-v8/common.js +35 -0
  83. package/lib/update-v8/constants.js +86 -0
  84. package/lib/update-v8/index.js +56 -0
  85. package/lib/update-v8/majorUpdate.js +171 -0
  86. package/lib/update-v8/minorUpdate.js +105 -0
  87. package/lib/update-v8/updateMaintainingDependencies.js +34 -0
  88. package/lib/update-v8/updateV8Clone.js +53 -0
  89. package/lib/update-v8/updateVersionNumbers.js +122 -0
  90. package/lib/update-v8/util.js +62 -0
  91. package/lib/user.js +4 -0
  92. package/lib/user_status.js +5 -0
  93. package/lib/utils.js +66 -0
  94. package/lib/verbosity.js +26 -0
  95. package/lib/voting_session.js +136 -0
  96. package/lib/wpt/index.js +243 -0
  97. package/lib/wpt/templates/README.md +16 -0
  98. package/package.json +69 -0
@@ -0,0 +1,117 @@
1
+ import nv from '@pkgjs/nv';
2
+ import auth from './auth.js';
3
+ import Request from './request.js';
4
+ import fs from 'node:fs';
5
+
6
+ export default class SecurityReleaseSteward {
7
+ constructor(cli) {
8
+ this.cli = cli;
9
+ }
10
+
11
+ async start() {
12
+ const { cli } = this;
13
+ const credentials = await auth({
14
+ github: true,
15
+ h1: true
16
+ });
17
+
18
+ const req = new Request(credentials);
19
+ const create = await cli.prompt(
20
+ 'Create the Next Security Release issue?',
21
+ { defaultAnswer: true });
22
+ if (create) {
23
+ const issue = new SecurityReleaseIssue(req);
24
+ const content = await issue.buildIssue(cli);
25
+ const data = await req.createIssue('Next Security Release', content, {
26
+ owner: 'nodejs-private',
27
+ repo: 'node-private'
28
+ });
29
+ if (data.html_url) {
30
+ cli.ok('Created: ' + data.html_url);
31
+ } else {
32
+ cli.error(data);
33
+ }
34
+ }
35
+ }
36
+ }
37
+
38
+ class SecurityReleaseIssue {
39
+ constructor(req) {
40
+ this.req = req;
41
+ this.content = '';
42
+ this.title = 'Next Security Release';
43
+ this.affectedLines = {};
44
+ }
45
+
46
+ getSecurityIssueTemplate() {
47
+ return fs.readFileSync(
48
+ new URL(
49
+ './github/templates/next-security-release.md',
50
+ import.meta.url
51
+ ),
52
+ 'utf-8'
53
+ );
54
+ }
55
+
56
+ async buildIssue(cli) {
57
+ this.content = this.getSecurityIssueTemplate();
58
+ cli.info('Getting triaged H1 reports...');
59
+ const reports = await this.req.getTriagedReports();
60
+ await this.fillReports(cli, reports);
61
+
62
+ this.fillAffectedLines(Object.keys(this.affectedLines));
63
+
64
+ const target = await cli.prompt('Enter target date in YYYY-MM-DD format:', {
65
+ questionType: 'input',
66
+ defaultAnswer: 'TBD'
67
+ });
68
+ this.fillTargetDate(target);
69
+
70
+ return this.content;
71
+ }
72
+
73
+ async fillReports(cli, reports) {
74
+ const supportedVersions = (await nv('supported'))
75
+ .map((v) => v.versionName + '.x')
76
+ .join(',');
77
+
78
+ let reportsContent = '';
79
+ for (const report of reports.data) {
80
+ const { id, attributes: { title }, relationships: { severity } } = report;
81
+ const reportLevel = severity.data.attributes.rating;
82
+ cli.separator();
83
+ cli.info(`Report: ${id} - ${title} (${reportLevel})`);
84
+ const include = await cli.prompt(
85
+ 'Would you like to include this report to the next security release?',
86
+ { defaultAnswer: true });
87
+ if (!include) {
88
+ continue;
89
+ }
90
+
91
+ reportsContent +=
92
+ ` * **[${id}](https://hackerone.com/bugs?subject=nodejs&report_id=${id}) - ${title} (TBD) - (${reportLevel})**\n`;
93
+ const versions = await cli.prompt('Which active release lines this report affects?', {
94
+ questionType: 'input',
95
+ defaultAnswer: supportedVersions
96
+ });
97
+ for (const v of versions.split(',')) {
98
+ if (!this.affectedLines[v]) this.affectedLines[v] = true;
99
+ reportsContent += ` * ${v} - TBD\n`;
100
+ }
101
+ }
102
+ this.content = this.content.replace('%REPORTS%', reportsContent);
103
+ }
104
+
105
+ fillAffectedLines(affectedLines) {
106
+ let affected = '';
107
+ for (const line of affectedLines) {
108
+ affected += ` * ${line} - TBD\n`;
109
+ }
110
+ this.content =
111
+ this.content.replace('%AFFECTED_LINES%', affected);
112
+ }
113
+
114
+ fillTargetDate(date) {
115
+ this.content = this.content.replace('%RELEASE_DATE%', date);
116
+ }
117
+ }
package/lib/proxy.js ADDED
@@ -0,0 +1,21 @@
1
+ import { globalAgent } from 'node:https';
2
+ import { spawnSync } from 'child_process';
3
+
4
+ import { ProxyAgent } from 'undici';
5
+
6
+ import { getMergedConfig } from './config.js';
7
+
8
+ export default function proxy() {
9
+ let proxyUrl = getMergedConfig().proxy;
10
+ if (proxyUrl == null || proxyUrl === '') {
11
+ proxyUrl = spawnSync(
12
+ 'git',
13
+ ['config', '--get', '--path', 'https.proxy']
14
+ ).stdout.toString();
15
+ }
16
+ if (proxyUrl == null || proxyUrl === '') {
17
+ return globalAgent;
18
+ } else {
19
+ return new ProxyAgent(proxyUrl);
20
+ }
21
+ }
@@ -0,0 +1,8 @@
1
+ query DefaultBranchRef($owner: String!, $repo: String!) {
2
+ repository(owner: $owner, name: $repo) {
3
+ defaultBranchRef {
4
+ name
5
+ }
6
+ }
7
+ }
8
+
@@ -0,0 +1,16 @@
1
+ query LastCommit($owner: String!, $repo: String!, $branch: String!, $path: String) {
2
+ repository(owner: $owner, name: $repo) {
3
+ ref(qualifiedName: $branch) {
4
+ target {
5
+ ... on Commit {
6
+ history(first: 1, path: $path) {
7
+ nodes {
8
+ oid
9
+ messageHeadline
10
+ }
11
+ }
12
+ }
13
+ }
14
+ }
15
+ }
16
+ }
@@ -0,0 +1,37 @@
1
+ query PR($prid: Int!, $owner: String!, $repo: String!) {
2
+ repository(owner: $owner, name: $repo) {
3
+ pullRequest(number: $prid) {
4
+ createdAt,
5
+ authorAssociation,
6
+ author {
7
+ ... on User {
8
+ login,
9
+ email,
10
+ name
11
+ }
12
+ },
13
+ url,
14
+ bodyHTML,
15
+ bodyText,
16
+ labels(first: 100) {
17
+ nodes {
18
+ name
19
+ }
20
+ },
21
+ files(first: 100) {
22
+ nodes {
23
+ path
24
+ }
25
+ },
26
+ title,
27
+ baseRefName,
28
+ headRefName,
29
+ changedFiles,
30
+ mergeable,
31
+ closed,
32
+ closedAt,
33
+ merged,
34
+ mergedAt
35
+ }
36
+ }
37
+ }
@@ -0,0 +1,27 @@
1
+ query Comments($prid: Int!, $owner: String!, $repo: String!, $after: String) {
2
+ repository(owner: $owner, name: $repo) {
3
+ pullRequest(number: $prid) {
4
+ comments(first: 100, after: $after) {
5
+ totalCount
6
+ pageInfo {
7
+ hasNextPage
8
+ endCursor
9
+ }
10
+ nodes {
11
+ publishedAt
12
+ bodyText
13
+ author {
14
+ login
15
+ }
16
+ reactions(content: THUMBS_UP, first: 100) {
17
+ nodes {
18
+ user {
19
+ login
20
+ }
21
+ }
22
+ }
23
+ }
24
+ }
25
+ }
26
+ }
27
+ }
@@ -0,0 +1,45 @@
1
+ query Commits($prid: Int!, $owner: String!, $repo: String!, $after: String) {
2
+ repository(owner: $owner, name: $repo) {
3
+ pullRequest(number: $prid) {
4
+ commits(last: 100, after: $after) {
5
+ totalCount
6
+ pageInfo {
7
+ hasNextPage
8
+ endCursor
9
+ }
10
+ nodes {
11
+ commit {
12
+ committedDate
13
+ author {
14
+ user {
15
+ login
16
+ }
17
+ email
18
+ name
19
+ }
20
+ committer {
21
+ email
22
+ name
23
+ }
24
+ oid
25
+ message
26
+ messageHeadline
27
+ authoredByCommitter
28
+ checkSuites(first: 100) {
29
+ nodes {
30
+ app {
31
+ slug
32
+ }
33
+ conclusion,
34
+ status
35
+ }
36
+ }
37
+ status {
38
+ state
39
+ }
40
+ }
41
+ }
42
+ }
43
+ }
44
+ }
45
+ }
@@ -0,0 +1,25 @@
1
+ query PRs($owner: String!, $repo: String!, $labels: [String!]) {
2
+ repository(owner: $owner, name: $repo) {
3
+ pullRequests(states: [OPEN], first: 20, labels: $labels) {
4
+ nodes {
5
+ title,
6
+ url,
7
+ number,
8
+ author {
9
+ ... on User {
10
+ login,
11
+ email,
12
+ name
13
+ }
14
+ },
15
+ labels(first: 100) {
16
+ nodes {
17
+ name
18
+ }
19
+ },
20
+ baseRefName,
21
+ mergeable,
22
+ }
23
+ }
24
+ }
25
+ }
@@ -0,0 +1,23 @@
1
+ query Reviews($prid: Int!, $owner: String!, $repo: String!, $after: String) {
2
+ repository(owner: $owner, name: $repo) {
3
+ pullRequest(number: $prid) {
4
+ reviews(first: 100, after: $after) {
5
+ totalCount
6
+ pageInfo {
7
+ hasNextPage
8
+ endCursor
9
+ }
10
+ nodes {
11
+ bodyText
12
+ state
13
+ author {
14
+ login
15
+ }
16
+ authorCanPushToRepository
17
+ url
18
+ publishedAt
19
+ }
20
+ }
21
+ }
22
+ }
23
+ }
@@ -0,0 +1,51 @@
1
+ query SearchIssueByUser($queryString: String!, $isCommenter: Boolean!, $after: String) {
2
+ search(query: $queryString, type: ISSUE, first: 100, after: $after) {
3
+ nodes {
4
+ ... on PullRequest {
5
+ url
6
+ publishedAt
7
+ author {
8
+ login
9
+ }
10
+ title
11
+ reviews(last: 100) @include(if: $isCommenter) {
12
+ nodes {
13
+ publishedAt
14
+ author {
15
+ login
16
+ }
17
+ }
18
+ }
19
+ labels(first: 100) {
20
+ nodes {
21
+ name
22
+ }
23
+ }
24
+ comments(last: 100) @include(if: $isCommenter) {
25
+ nodes {
26
+ publishedAt
27
+ author {
28
+ login
29
+ }
30
+ }
31
+ }
32
+ }
33
+ ... on Issue {
34
+ publishedAt
35
+ url
36
+ author {
37
+ login
38
+ }
39
+ title
40
+ comments(last: 100) @include(if: $isCommenter) {
41
+ nodes {
42
+ publishedAt
43
+ author {
44
+ login
45
+ }
46
+ }
47
+ }
48
+ }
49
+ }
50
+ }
51
+ }
@@ -0,0 +1,22 @@
1
+ query team($org: String!, $team: String!, $after: String) {
2
+ organization(login: $org) {
3
+ login
4
+ team(slug: $team) {
5
+ name,
6
+ description,
7
+ members(first: 100, after: $after) {
8
+ totalCount
9
+ pageInfo {
10
+ hasNextPage
11
+ endCursor
12
+ }
13
+ nodes {
14
+ login,
15
+ name,
16
+ email,
17
+ url
18
+ }
19
+ }
20
+ }
21
+ }
22
+ }
@@ -0,0 +1,12 @@
1
+ query TreeEntries($owner: String!, $repo: String!, $expression: String!) {
2
+ repository(owner: $owner, name: $repo) {
3
+ object(expression: $expression) {
4
+ ... on Tree {
5
+ entries {
6
+ name
7
+ type
8
+ }
9
+ }
10
+ }
11
+ }
12
+ }
@@ -0,0 +1,28 @@
1
+ query PR($prid: Int!, $owner: String!, $repo: String!) {
2
+ repository(owner: $owner, name: $repo) {
3
+ pullRequest(number: $prid) {
4
+ commits(first: 1) {
5
+ nodes {
6
+ commit {
7
+ oid
8
+ }
9
+ }
10
+ }
11
+ headRef {
12
+ name
13
+ repository {
14
+ sshUrl
15
+ url
16
+ }
17
+ }
18
+ closed
19
+ merged
20
+ }
21
+ }
22
+ viewer {
23
+ login
24
+ publicKeys(first: 1) {
25
+ totalCount
26
+ }
27
+ }
28
+ }
@@ -0,0 +1,53 @@
1
+ export function getEOLDate(ltsStartDate) {
2
+ // Maintenance LTS lasts for 18 months.
3
+ const result = getLTSMaintenanceStartDate(ltsStartDate);
4
+ result.setMonth(result.getMonth() + 18);
5
+ return result;
6
+ }
7
+
8
+ export function getLTSMaintenanceStartDate(ltsStartDate) {
9
+ // Active LTS lasts for one year.
10
+ const result = new Date(ltsStartDate);
11
+ result.setMonth(result.getMonth() + 12);
12
+ return result;
13
+ }
14
+
15
+ export function getStartLTSBlurb({ date, ltsCodename, versionComponents }) {
16
+ const dateFormat = { month: 'long', year: 'numeric' };
17
+ // TODO pull these from the schedule.json in the Release repo?
18
+ const mainDate = getLTSMaintenanceStartDate(date);
19
+ const mainStart = mainDate.toLocaleString('en-US', dateFormat);
20
+ const eolDate = getEOLDate(date);
21
+ const eol = eolDate.toLocaleString('en-US', dateFormat);
22
+ const { major } = versionComponents;
23
+ return [
24
+ /* eslint-disable max-len */
25
+ `This release marks the transition of Node.js ${major}.x into Long Term Support (LTS)`,
26
+ `with the codename '${ltsCodename}'. The ${major}.x release line now moves into "Active LTS"`,
27
+ `and will remain so until ${mainStart}. After that time, it will move into`,
28
+ `"Maintenance" until end of life in ${eol}.`
29
+ /* eslint-enable */
30
+ ].join('\n');
31
+ }
32
+
33
+ export function updateTestProcessRelease(test, options) {
34
+ const { versionComponents, ltsCodename } = options;
35
+ if (test.includes(ltsCodename)) {
36
+ return test;
37
+ }
38
+ const inLines = test.split('\n');
39
+ const outLines = [];
40
+ const { major, minor } = versionComponents;
41
+ for (const line of inLines) {
42
+ if (line === '} else {') {
43
+ outLines.push(`} else if (versionParts[0] === '${major}' ` +
44
+ `&& versionParts[1] >= ${minor}) {`
45
+ );
46
+ outLines.push(
47
+ ` assert.strictEqual(process.release.lts, '${ltsCodename}');`
48
+ );
49
+ }
50
+ outLines.push(line);
51
+ }
52
+ return outLines.join('\n');
53
+ }
package/lib/request.js ADDED
@@ -0,0 +1,185 @@
1
+ import fs from 'node:fs';
2
+
3
+ import { fetch } from 'undici';
4
+
5
+ import { CI_DOMAIN } from './ci/ci_type_parser.js';
6
+ import proxy from './proxy.js';
7
+ import {
8
+ isDebugVerbosity,
9
+ debuglog
10
+ } from './verbosity.js';
11
+
12
+ function wrappedFetch(url, options, ...args) {
13
+ if (isDebugVerbosity()) {
14
+ debuglog('[fetch]', url);
15
+ }
16
+ return fetch(url, options, ...args);
17
+ }
18
+
19
+ export default class Request {
20
+ constructor(credentials) {
21
+ this.credentials = credentials;
22
+ this.proxyAgent = proxy();
23
+ }
24
+
25
+ loadQuery(file) {
26
+ const filePath = new URL(`./queries/${file}.gql`, import.meta.url);
27
+ return fs.readFileSync(filePath, 'utf8');
28
+ }
29
+
30
+ async fetch(url, options) {
31
+ options.agent = this.proxyAgent;
32
+ if (url.startsWith(`https://${CI_DOMAIN}`)) {
33
+ options.headers = options.headers || {};
34
+ Object.assign(options.headers, this.getJenkinsHeaders());
35
+ }
36
+ return wrappedFetch(url, options);
37
+ }
38
+
39
+ async buffer(url, options = {}) {
40
+ const res = await this.fetch(url, options);
41
+ const buffer = await res.arrayBuffer();
42
+ return Buffer.from(buffer);
43
+ }
44
+
45
+ async text(url, options = {}) {
46
+ return this.fetch(url, options).then(res => res.text());
47
+ }
48
+
49
+ async json(url, options = {}) {
50
+ options.headers = options.headers || {};
51
+ const text = await this.text(url, options);
52
+ try {
53
+ return JSON.parse(text);
54
+ } catch (e) {
55
+ if (isDebugVerbosity()) {
56
+ debuglog('[Request] Cannot parse JSON response from',
57
+ url, ':\n', text);
58
+ }
59
+ throw e;
60
+ }
61
+ }
62
+
63
+ async createIssue(title, body, { owner, repo }) {
64
+ const url = `https://api.github.com/repos/${owner}/${repo}/issues`;
65
+ const options = {
66
+ method: 'POST',
67
+ headers: {
68
+ Authorization: `Basic ${this.credentials.github}`,
69
+ 'User-Agent': 'node-core-utils',
70
+ Accept: 'application/vnd.github+json'
71
+ },
72
+ body: JSON.stringify({
73
+ title,
74
+ body
75
+ })
76
+ };
77
+ return this.json(url, options);
78
+ }
79
+
80
+ async gql(name, variables, path) {
81
+ const query = this.loadQuery(name);
82
+ if (path) {
83
+ const result = await this.queryAll(query, variables, path);
84
+ return result;
85
+ } else {
86
+ const result = await this.query(query, variables);
87
+ return result;
88
+ }
89
+ }
90
+
91
+ getJenkinsHeaders() {
92
+ const jenkinsCredentials = this.credentials.jenkins;
93
+ if (!jenkinsCredentials) {
94
+ throw new Error('The request has not been ' +
95
+ 'authenticated with a Jenkins token');
96
+ }
97
+ return {
98
+ Authorization: `Basic ${jenkinsCredentials}`,
99
+ 'User-Agent': 'node-core-utils'
100
+ };
101
+ }
102
+
103
+ async getTriagedReports() {
104
+ const url = 'https://api.hackerone.com/v1/reports?filter[program][]=nodejs&filter[state][]=triaged';
105
+ const options = {
106
+ method: 'GET',
107
+ headers: {
108
+ Authorization: `Basic ${this.credentials.h1}`,
109
+ 'User-Agent': 'node-core-utils',
110
+ Accept: 'application/json'
111
+ }
112
+ };
113
+ return this.json(url, options);
114
+ }
115
+
116
+ // This is for github v4 API queries, for other types of queries
117
+ // use .text or .json
118
+ async query(query, variables) {
119
+ const githubCredentials = this.credentials.github;
120
+ if (!githubCredentials) {
121
+ throw new Error('The request has not been ' +
122
+ 'authenticated with a GitHub token');
123
+ }
124
+ const url = 'https://api.github.com/graphql';
125
+ const options = {
126
+ agent: this.proxyAgent,
127
+ method: 'POST',
128
+ headers: {
129
+ Authorization: `Basic ${githubCredentials}`,
130
+ 'User-Agent': 'node-core-utils',
131
+ Accept: 'application/vnd.github.antiope-preview+json'
132
+ },
133
+ body: JSON.stringify({
134
+ query,
135
+ variables
136
+ })
137
+ };
138
+
139
+ const result = await this.json(url, options);
140
+ if (result.errors) {
141
+ const { type, message } = result.errors[0];
142
+ const err = new Error(`[${type}] GraphQL request Error: ${message}`);
143
+ err.data = {
144
+ variables
145
+ };
146
+ throw err;
147
+ }
148
+ if (result.message) {
149
+ const err = new Error(`GraphQL request Error: ${result.message}`);
150
+ err.data = {
151
+ variables
152
+ };
153
+ throw err;
154
+ }
155
+ return result.data;
156
+ }
157
+
158
+ async queryAll(query, variables, path) {
159
+ let after = null;
160
+ let all = [];
161
+ // first page
162
+ do {
163
+ const varWithPage = Object.assign({
164
+ after
165
+ }, variables);
166
+ const data = await this.query(query, varWithPage);
167
+ let current = data;
168
+ for (const step of path) {
169
+ current = current[step];
170
+ }
171
+ // current should have:
172
+ // totalCount
173
+ // pageInfo { hasNextPage, endCursor }
174
+ // nodes
175
+ all = all.concat(current.nodes);
176
+ if (current.pageInfo.hasNextPage) {
177
+ after = current.pageInfo.endCursor;
178
+ } else {
179
+ after = null;
180
+ }
181
+ } while (after !== null);
182
+
183
+ return all;
184
+ }
185
+ }
@@ -0,0 +1,5 @@
1
+ export const PENDING = 'PENDING';
2
+ export const COMMENTED = 'COMMENTED';
3
+ export const APPROVED = 'APPROVED';
4
+ export const CHANGES_REQUESTED = 'CHANGES_REQUESTED';
5
+ export const DISMISSED = 'DISMISSED';