@effected/github 0.25.0 → 0.26.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.
@@ -0,0 +1,84 @@
1
+ // src/internal/requestOptions.ts
2
+ /**
3
+ * The options `@octokit/endpoint`'s `parse` reads off the flat parameter
4
+ * object, plus `data`, which replaces the body.
5
+ *
6
+ * @internal
7
+ */
8
+ const OCTOKIT_REQUEST_OPTION_KEYS = /* @__PURE__ */ new Set([
9
+ "method",
10
+ "baseUrl",
11
+ "url",
12
+ "headers",
13
+ "request",
14
+ "mediaType",
15
+ "data"
16
+ ]);
17
+ /**
18
+ * The template parameters of `PATCH /repos/{owner}/{repo}`, which octokit
19
+ * consumes as the path and never sends.
20
+ *
21
+ * @internal
22
+ */
23
+ const REPOSITORY_PATCH_ROUTE_PARAMS = /* @__PURE__ */ new Set(["owner", "repo"]);
24
+ /**
25
+ * The template parameters of
26
+ * `PUT /repos/{owner}/{repo}/environments/{environment_name}`, which octokit
27
+ * consumes as the path and never sends.
28
+ *
29
+ * @internal
30
+ */
31
+ const ENVIRONMENT_PUT_ROUTE_PARAMS = /* @__PURE__ */ new Set([
32
+ "owner",
33
+ "repo",
34
+ "environment_name"
35
+ ]);
36
+ /**
37
+ * Why a key of an open body map may not be sent on a route whose template
38
+ * names `routeParams`, or `undefined` when it may.
39
+ *
40
+ * @remarks
41
+ * `noun` names what the map holds, with its article ("a repository setting",
42
+ * "an environment setting"), in the reason. `__proto__` is refused for a different reason:
43
+ * assigning it to a body object sets the object's prototype instead of a
44
+ * field, so the key would vanish from the wire while being reported sent.
45
+ *
46
+ * @internal
47
+ */
48
+ const reservedBodyKey = (key, routeParams, noun) => OCTOKIT_REQUEST_OPTION_KEYS.has(key) ? `is an octokit request option, not ${noun}` : routeParams.has(key) ? `is the route's own path parameter, not ${noun}` : key === "__proto__" ? "would set the body's prototype instead of a field" : void 0;
49
+ /**
50
+ * Why a repository-settings key may not be sent, or `undefined` when it may.
51
+ *
52
+ * @internal
53
+ */
54
+ const reservedSettingKey = (key) => reservedBodyKey(key, REPOSITORY_PATCH_ROUTE_PARAMS, "a repository setting");
55
+ /**
56
+ * The request options the raw surface (`request`, `requestDecoded`,
57
+ * `paginate`, `paginateStream`) lets a caller pass: the two typed
58
+ * `Rest.RequestExtras`, and `data`, which replaces the body but cannot move
59
+ * the request.
60
+ *
61
+ * @internal
62
+ */
63
+ const RAW_PASSTHROUGH_OPTION_KEYS = /* @__PURE__ */ new Set([
64
+ "headers",
65
+ "mediaType",
66
+ "data"
67
+ ]);
68
+ /**
69
+ * Why a parameter of a raw-surface call may not be sent, or `undefined` when
70
+ * it may.
71
+ *
72
+ * @remarks
73
+ * The rest of {@link OCTOKIT_REQUEST_OPTION_KEYS}: `baseUrl` and `url` pick
74
+ * the host the token goes to, `request` swaps the fetch that receives it, and
75
+ * `method` overrides the route's verb. None is in the raw surface's type, so
76
+ * one arriving here came through a cast or an untyped object. A route whose
77
+ * own body has a field called `url` (a webhook's configuration) sends it
78
+ * inside `data`, where octokit never reads it as an option.
79
+ *
80
+ * @internal
81
+ */
82
+ const reservedRawParam = (key) => OCTOKIT_REQUEST_OPTION_KEYS.has(key) && !RAW_PASSTHROUGH_OPTION_KEYS.has(key) ? key === "url" ? "is read by octokit as the request URL; send a body field called url inside data" : "is an octokit request option the raw surface does not accept" : void 0;
83
+
84
+ export { ENVIRONMENT_PUT_ROUTE_PARAMS, OCTOKIT_REQUEST_OPTION_KEYS, RAW_PASSTHROUGH_OPTION_KEYS, REPOSITORY_PATCH_ROUTE_PARAMS, reservedBodyKey, reservedRawParam, reservedSettingKey };
@@ -0,0 +1,113 @@
1
+ import { HttpRedaction } from "@effected/crypto";
2
+
3
+ // src/internal/scrub.ts
4
+ const REDACTED = "REDACTED";
5
+ const MAX_DEPTH = 8;
6
+ const asRecord = (value) => typeof value === "object" && value !== null ? value : void 0;
7
+ const define = (target, key, value) => {
8
+ Object.defineProperty(target, key, {
9
+ value,
10
+ enumerable: true,
11
+ writable: true,
12
+ configurable: true
13
+ });
14
+ };
15
+ /** Strings through `HttpRedaction.text`, numbers and booleans as they are, everything else dropped. */
16
+ const primitive = (value) => typeof value === "string" ? HttpRedaction.text(value) : typeof value === "number" || typeof value === "boolean" ? value : void 0;
17
+ /** A header record minus cookies, every sensitive value `REDACTED`. */
18
+ const headers = (input) => {
19
+ const record = asRecord(input);
20
+ if (record === void 0) return void 0;
21
+ const out = {};
22
+ for (const [name, value] of Object.entries(record)) {
23
+ const lower = name.toLowerCase();
24
+ if (lower === "set-cookie" || lower === "cookie") continue;
25
+ const kept = primitive(value);
26
+ if (kept === void 0) continue;
27
+ out[name] = HttpRedaction.isSensitiveHeader(lower) ? REDACTED : kept;
28
+ }
29
+ return out;
30
+ };
31
+ /** The top-level primitive fields of a record (`message`, `documentation_url`, `status`); nested values dropped. */
32
+ const primitives = (input) => {
33
+ const record = asRecord(input);
34
+ if (record === void 0) return void 0;
35
+ const out = {};
36
+ for (const [key, value] of Object.entries(record)) {
37
+ const kept = primitive(value);
38
+ if (kept !== void 0) out[key] = kept;
39
+ }
40
+ return out;
41
+ };
42
+ const optional = (key, value) => value === void 0 ? {} : { [key]: value };
43
+ const copy = (value, depth) => {
44
+ if (typeof value === "string") return HttpRedaction.text(value);
45
+ if (value === null || typeof value !== "object") return value;
46
+ if (depth >= MAX_DEPTH) return void 0;
47
+ const source = value;
48
+ const out = value instanceof Error ? Object.create(Object.getPrototypeOf(value)) : new Error(HttpRedaction.text(typeof source.message === "string" ? source.message : Object.prototype.toString.call(value)));
49
+ if (value instanceof Error) {
50
+ Object.defineProperty(out, "message", {
51
+ value: HttpRedaction.text(value.message),
52
+ writable: true,
53
+ configurable: true
54
+ });
55
+ if (value.stack !== void 0) Object.defineProperty(out, "stack", {
56
+ value: HttpRedaction.text(value.stack),
57
+ writable: true,
58
+ configurable: true
59
+ });
60
+ }
61
+ for (const [key, field] of Object.entries(source)) {
62
+ if (key === "message" || key === "stack" || key === "cause") continue;
63
+ const kept = primitive(field);
64
+ if (kept !== void 0) define(out, key, kept);
65
+ }
66
+ const request = asRecord(source.request);
67
+ if (request !== void 0) define(out, "request", {
68
+ ...optional("method", typeof request.method === "string" ? request.method : void 0),
69
+ ...optional("url", typeof request.url === "string" ? HttpRedaction.url(request.url) : void 0),
70
+ ...optional("headers", headers(request.headers))
71
+ });
72
+ const response = asRecord(source.response);
73
+ if (response !== void 0) define(out, "response", {
74
+ ...optional("url", typeof response.url === "string" ? HttpRedaction.url(response.url) : void 0),
75
+ ...optional("status", typeof response.status === "number" ? response.status : void 0),
76
+ ...optional("headers", headers(response.headers)),
77
+ ...optional("data", primitives(response.data))
78
+ });
79
+ if (source.headers !== void 0) {
80
+ const kept = headers(source.headers);
81
+ if (kept !== void 0) define(out, "headers", kept);
82
+ }
83
+ if (source.cause !== void 0) {
84
+ const nested = copy(source.cause, depth + 1);
85
+ if (nested !== void 0) define(out, "cause", nested);
86
+ }
87
+ return out;
88
+ };
89
+ /**
90
+ * Sanitize what octokit threw before keeping it as a cause.
91
+ *
92
+ * @remarks
93
+ * A value carrying a `request`, `response` or `headers` field — octokit's
94
+ * `RequestError`, a `GraphqlResponseError`, the transport's own GraphQL throw
95
+ * — becomes a copy on the same prototype that keeps `name`, `message`,
96
+ * `status` and the other primitive fields, the request's method, redacted url
97
+ * and headers, and the response's redacted url, status, headers minus
98
+ * `set-cookie` and the top-level primitive fields of its body (`message`,
99
+ * `documentation_url`). Every sensitive header value reads `REDACTED`. The
100
+ * request body, the per-request options, the GraphQL variables and partial
101
+ * `data`, and the nested `errors` arrays are dropped: the facts a caller
102
+ * routes on (`validation`, `errors`) were already read off the original.
103
+ *
104
+ * Anything else — an `AbortError`, a kit error, a plain object a test throws
105
+ * — is returned unchanged, by identity.
106
+ */
107
+ const scrubThrowable = (error) => {
108
+ const record = asRecord(error);
109
+ if (record === void 0) return error;
110
+ return record.request !== void 0 || record.response !== void 0 || record.headers !== void 0 ? copy(error, 0) : error;
111
+ };
112
+
113
+ export { scrubThrowable };
@@ -0,0 +1,70 @@
1
+ // src/internal/uploads.ts
2
+ /** The upload host on github.com. @internal */
3
+ const DOTCOM_UPLOADS_BASE_URL = "https://uploads.github.com";
4
+ /** The REST root on github.com, and the base of a client configured with none. @internal */
5
+ const DOTCOM_API_BASE_URL = "https://api.github.com";
6
+ const GHES_REST_SUFFIX = "/api/v3";
7
+ /**
8
+ * The upload channel, carried on the client shape under a key only this
9
+ * package can name.
10
+ *
11
+ * @remarks
12
+ * A symbol rather than a `WeakMap` keyed on the shape: a property survives a
13
+ * consumer's `{ ...client, request: wrapped }` spread, a map entry does not.
14
+ * It is not on `GitHubClientShape`, so the public surface does not grow, and a
15
+ * module-private `Symbol` (not `Symbol.for`) cannot be forged from outside.
16
+ */
17
+ const UPLOAD_CHANNEL = Symbol("@effected/github/uploadChannel");
18
+ /**
19
+ * Mark a client shape built by this package with its upload channel.
20
+ *
21
+ * @internal
22
+ */
23
+ const withUploadChannel = (shape, channel) => Object.assign(shape, { [UPLOAD_CHANNEL]: channel });
24
+ /**
25
+ * The upload channel of a client this package built, or `undefined` for a
26
+ * shape built elsewhere (a hand-written double, `GitHubClient.makeTest`, a
27
+ * fixture). A channel whose `baseUrl` is `undefined` means "built here, for
28
+ * github.com", which is not the same as "not built here".
29
+ *
30
+ * @internal
31
+ */
32
+ const uploadChannelOf = (client) => client[UPLOAD_CHANNEL];
33
+ /** `url` with every trailing slash removed — a loop, not a regex. */
34
+ const trimTrailingSlashes = (url) => {
35
+ let end = url.length;
36
+ while (end > 0 && url.charCodeAt(end - 1) === 47) end--;
37
+ return url.slice(0, end);
38
+ };
39
+ /**
40
+ * The release-asset upload base for a configured REST base URL.
41
+ *
42
+ * @remarks
43
+ * - absent, or `https://api.github.com` → `https://uploads.github.com`;
44
+ * - a GHES root `https://<host>/api/v3` → `https://<host>/api/uploads`;
45
+ * - anything else (a proxy, a test server) → the configured base itself, so
46
+ * the token still only goes to the host the caller configured.
47
+ *
48
+ * @internal
49
+ */
50
+ const uploadsBaseUrlFor = (baseUrl) => {
51
+ if (baseUrl === void 0) return DOTCOM_UPLOADS_BASE_URL;
52
+ const trimmed = trimTrailingSlashes(baseUrl);
53
+ if (trimmed === "https://api.github.com") return DOTCOM_UPLOADS_BASE_URL;
54
+ if (trimmed.endsWith(GHES_REST_SUFFIX)) return `${trimmed.slice(0, -7)}/api/uploads`;
55
+ return trimmed;
56
+ };
57
+ /**
58
+ * The origin of `url`, or `undefined` when it does not parse as an absolute URL.
59
+ *
60
+ * @internal
61
+ */
62
+ const originOf = (url) => {
63
+ try {
64
+ return new URL(url).origin;
65
+ } catch {
66
+ return;
67
+ }
68
+ };
69
+
70
+ export { DOTCOM_API_BASE_URL, DOTCOM_UPLOADS_BASE_URL, originOf, uploadChannelOf, uploadsBaseUrlFor, withUploadChannel };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@effected/github",
3
- "version": "0.25.0",
3
+ "version": "0.26.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.11.0",
42
- "@effected/jwt": "^0.6.0",
43
- "@effected/oidc": "^0.1.2",
44
- "@effected/semver": "^0.18.0",
41
+ "@effected/crypto": "^0.3.0",
42
+ "@effected/github-references": "^0.12.0",
43
+ "@effected/jwt": "^0.7.0",
44
+ "@effected/oidc": "^0.2.0",
45
+ "@effected/semver": "^0.19.0",
45
46
  "@octokit/core": "^7.0.6",
46
47
  "@octokit/plugin-paginate-rest": "^16.0.0",
47
48
  "@octokit/types": "^18.0.0",
@@ -5,7 +5,7 @@
5
5
  "toolPackages": [
6
6
  {
7
7
  "packageName": "@microsoft/api-extractor",
8
- "packageVersion": "7.59.1"
8
+ "packageVersion": "7.59.4"
9
9
  }
10
10
  ]
11
11
  }