@shivaedev/github 0.8.5 → 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/CHANGELOG.md +37 -0
- package/README.md +47 -3
- package/dist/GitHub.d.ts +100 -17
- package/dist/GitHub.d.ts.map +1 -1
- package/dist/GitHub.js +4 -1
- package/dist/GitHub.js.map +1 -1
- package/dist/GitHubToken.d.ts +2 -2
- package/dist/GitHubToken.d.ts.map +1 -1
- package/dist/GitHubToken.js +1 -1
- package/dist/GitHubToken.js.map +1 -1
- package/dist/appTokens.d.ts +6 -0
- package/dist/appTokens.d.ts.map +1 -0
- package/dist/appTokens.js +64 -0
- package/dist/appTokens.js.map +1 -0
- package/dist/artifacts.d.ts +1 -1
- package/dist/artifacts.d.ts.map +1 -1
- package/dist/artifacts.js +8 -5
- package/dist/artifacts.js.map +1 -1
- package/dist/authenticatedRequest.d.ts +1 -1
- package/dist/authenticatedRequest.d.ts.map +1 -1
- package/dist/authenticatedRequest.js +3 -2
- package/dist/authenticatedRequest.js.map +1 -1
- package/dist/cache.d.ts +2 -0
- package/dist/cache.d.ts.map +1 -1
- package/dist/cache.js +3 -0
- package/dist/cache.js.map +1 -1
- package/dist/checkRuns.d.ts +116 -11
- package/dist/checkRuns.d.ts.map +1 -1
- package/dist/checkRuns.js +39 -18
- package/dist/checkRuns.js.map +1 -1
- package/dist/githubApp.d.ts +2 -4
- package/dist/githubApp.d.ts.map +1 -1
- package/dist/githubApp.js +5 -53
- package/dist/githubApp.js.map +1 -1
- package/dist/installationClients.d.ts +185 -0
- package/dist/installationClients.d.ts.map +1 -0
- package/dist/installationClients.js +28 -0
- package/dist/installationClients.js.map +1 -0
- package/dist/json.d.ts.map +1 -1
- package/dist/json.js +4 -0
- package/dist/json.js.map +1 -1
- package/dist/representations.d.ts +4 -4
- package/dist/representations.d.ts.map +1 -1
- package/dist/representations.js +1 -1
- package/dist/representations.js.map +1 -1
- package/dist/requests.d.ts +2 -2
- package/dist/requests.d.ts.map +1 -1
- package/dist/requests.js +11 -9
- package/dist/requests.js.map +1 -1
- package/dist/status.d.ts +1 -1
- package/dist/status.d.ts.map +1 -1
- package/dist/stickyComments.d.ts +1 -1
- package/package.json +7 -7
- package/src/GitHub.ts +15 -1
- package/src/GitHubToken.ts +4 -2
- package/src/appTokens.ts +92 -0
- package/src/artifacts.ts +11 -6
- package/src/authenticatedRequest.ts +3 -1
- package/src/cache.ts +5 -0
- package/src/checkRuns.ts +58 -18
- package/src/githubApp.ts +6 -74
- package/src/installationClients.ts +44 -0
- package/src/json.ts +4 -0
- package/src/representations.ts +2 -2
- package/src/requests.ts +11 -9
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
import { Context, Effect, Layer } from "effect";
|
|
2
|
+
import { HttpClient } from "effect/http";
|
|
3
|
+
import type { AppCredentials } from "#appJwt.ts";
|
|
4
|
+
export declare const GitHubAppCredentials: Context.Service<{
|
|
5
|
+
readonly credentials: AppCredentials;
|
|
6
|
+
}, {
|
|
7
|
+
readonly credentials: AppCredentials;
|
|
8
|
+
}>;
|
|
9
|
+
export declare const GitHubApp: Context.Service<"@shivaedev/github/GitHubApp", Readonly<{
|
|
10
|
+
readonly installation: (installationId: number) => Effect.Effect<Readonly<{
|
|
11
|
+
readonly downloadArtifactArchive: (options: import("./artifacts.ts").ArtifactArchiveOptions) => Effect.Effect<Uint8Array<ArrayBuffer>, import("./errors.ts").GitHubAppError | import("./errors.ts").GitHubArtifactError | import("./errors.ts").GitHubArtifactTooLarge | import("./errors.ts").GitHubConversationLocked | import("./errors.ts").GitHubPermissionDenied | import("./errors.ts").GitHubRateLimited | import("./errors.ts").GitHubTransportError | import("@shivaedev/http/errors.ts").InvalidBodyLimit | import("@shivaedev/http/errors.ts").RemoteHttpError | import("@shivaedev/http/errors.ts").RemoteRateLimited | import("@shivaedev/http/errors.ts").RemoteUnauthorized | import("@shivaedev/http/errors.ts").ResponseBodyTooLarge, never>;
|
|
12
|
+
readonly list: (path: string, beforeRequest?: ((resource: string) => Effect.Effect<void, import("@shivaedev/errors/taxonomy.ts").ServiceUnavailable>) | undefined, byteLimit?: number | undefined) => Effect.Effect<import("effect/Stream").Stream<{
|
|
13
|
+
decode: <TSchema extends import("effect/Schema").Constraint>(schema: TSchema) => Effect.Effect<TSchema["Type"], import("effect/Schema").SchemaError, TSchema["DecodingServices"]>;
|
|
14
|
+
hasNext: boolean;
|
|
15
|
+
}, import("./errors.ts").GitHubAppError | import("./errors.ts").GitHubConversationLocked | import("./errors.ts").GitHubPermissionDenied | import("./errors.ts").GitHubRateLimited | import("./errors.ts").GitHubTransportError | import("@shivaedev/http/errors.ts").InvalidBodyLimit | import("effect/Cause").NoSuchElementError | import("@shivaedev/http/errors.ts").PaginationLinkError | import("@shivaedev/http/errors.ts").ResponseBodyTooLarge | import("@shivaedev/errors/taxonomy.ts").ServiceUnavailable | import("@shivaedev/http/errors.ts").RemoteStatusError, never>, never, never>;
|
|
16
|
+
readonly read: (path: string, byteLimit?: number | undefined) => Effect.Effect<{
|
|
17
|
+
decode: <TSchema extends import("effect/Schema").Constraint>(schema: TSchema) => Effect.Effect<TSchema["Type"], import("effect/Schema").SchemaError, TSchema["DecodingServices"]>;
|
|
18
|
+
}, import("./errors.ts").GitHubAppError | import("./errors.ts").GitHubConversationLocked | import("./errors.ts").GitHubPermissionDenied | import("./errors.ts").GitHubRateLimited | import("./errors.ts").GitHubTransportError | import("@shivaedev/http/errors.ts").InvalidBodyLimit | import("@shivaedev/http/errors.ts").ResponseBodyTooLarge | import("@shivaedev/http/errors.ts").RemoteStatusError, never>;
|
|
19
|
+
readonly write: (method: "DELETE" | "PATCH" | "POST" | "PUT", path: string, body?: unknown, byteLimit?: number | undefined) => Effect.Effect<{
|
|
20
|
+
decode: <TSchema extends import("effect/Schema").Constraint>(schema: TSchema) => Effect.Effect<TSchema["Type"], import("effect/Schema").SchemaError, TSchema["DecodingServices"]>;
|
|
21
|
+
}, import("./errors.ts").GitHubAppError | import("./errors.ts").GitHubConversationLocked | import("./errors.ts").GitHubPermissionDenied | import("./errors.ts").GitHubRateLimited | import("./errors.ts").GitHubTransportError | import("effect/http/HttpBody").HttpBodyError | import("@shivaedev/http/errors.ts").InvalidBodyLimit | import("@shivaedev/http/errors.ts").ResponseBodyTooLarge | import("@shivaedev/http/errors.ts").RemoteStatusError, never>;
|
|
22
|
+
readonly createCheckRun: (owner: string, repo: string, input: {
|
|
23
|
+
readonly detailsUrl?: string | undefined;
|
|
24
|
+
readonly headSha: string;
|
|
25
|
+
readonly name: string;
|
|
26
|
+
readonly output?: {
|
|
27
|
+
readonly summary: string;
|
|
28
|
+
readonly text?: string | undefined;
|
|
29
|
+
readonly title: string;
|
|
30
|
+
} | undefined;
|
|
31
|
+
readonly conclusion?: "action_required" | "cancelled" | "failure" | "neutral" | "skipped" | "stale" | "success" | "timed_out" | undefined;
|
|
32
|
+
readonly status: "in_progress" | "queued";
|
|
33
|
+
} | {
|
|
34
|
+
readonly detailsUrl?: string | undefined;
|
|
35
|
+
readonly headSha: string;
|
|
36
|
+
readonly name: string;
|
|
37
|
+
readonly output?: {
|
|
38
|
+
readonly summary: string;
|
|
39
|
+
readonly text?: string | undefined;
|
|
40
|
+
readonly title: string;
|
|
41
|
+
} | undefined;
|
|
42
|
+
readonly conclusion: "action_required" | "cancelled" | "failure" | "neutral" | "skipped" | "stale" | "success" | "timed_out";
|
|
43
|
+
readonly status: "completed";
|
|
44
|
+
}) => Effect.Effect<{
|
|
45
|
+
readonly app?: {
|
|
46
|
+
readonly id: number;
|
|
47
|
+
} | null | undefined;
|
|
48
|
+
readonly conclusion: "action_required" | "cancelled" | "failure" | "neutral" | "skipped" | "stale" | "success" | "timed_out" | null;
|
|
49
|
+
readonly detailsUrl: string | null;
|
|
50
|
+
readonly headSha: string;
|
|
51
|
+
readonly id: number;
|
|
52
|
+
readonly name: string;
|
|
53
|
+
readonly output: {
|
|
54
|
+
readonly summary: string | null;
|
|
55
|
+
readonly text: string | null;
|
|
56
|
+
readonly title: string | null;
|
|
57
|
+
};
|
|
58
|
+
readonly status: "completed" | "in_progress" | "pending" | "queued" | "requested" | "waiting";
|
|
59
|
+
}, import("./errors.ts").GitHubAppError | import("./errors.ts").GitHubConversationLocked | import("./errors.ts").GitHubPermissionDenied | import("./errors.ts").GitHubRateLimited | import("./errors.ts").GitHubTransportError | import("effect/http/HttpBody").HttpBodyError | import("@shivaedev/http/errors.ts").InvalidBodyLimit | import("@shivaedev/http/errors.ts").ResponseBodyTooLarge | import("effect/Schema").SchemaError | import("@shivaedev/http/errors.ts").RemoteStatusError, never>;
|
|
60
|
+
readonly findLatestCheckRun: (owner: string, repo: string, input: {
|
|
61
|
+
readonly appId?: number | undefined;
|
|
62
|
+
readonly headSha: string;
|
|
63
|
+
readonly name: string;
|
|
64
|
+
} | {
|
|
65
|
+
readonly appId: number;
|
|
66
|
+
readonly headSha: string;
|
|
67
|
+
readonly name?: string | undefined;
|
|
68
|
+
}) => Effect.Effect<import("effect/Option").Option<{
|
|
69
|
+
readonly app?: {
|
|
70
|
+
readonly id: number;
|
|
71
|
+
} | null | undefined;
|
|
72
|
+
readonly conclusion: "action_required" | "cancelled" | "failure" | "neutral" | "skipped" | "stale" | "success" | "timed_out" | null;
|
|
73
|
+
readonly detailsUrl: string | null;
|
|
74
|
+
readonly headSha: string;
|
|
75
|
+
readonly id: number;
|
|
76
|
+
readonly name: string;
|
|
77
|
+
readonly output: {
|
|
78
|
+
readonly summary: string | null;
|
|
79
|
+
readonly text: string | null;
|
|
80
|
+
readonly title: string | null;
|
|
81
|
+
};
|
|
82
|
+
readonly status: "completed" | "in_progress" | "pending" | "queued" | "requested" | "waiting";
|
|
83
|
+
}>, import("./errors.ts").GitHubAppError | import("./errors.ts").GitHubConversationLocked | import("./errors.ts").GitHubPermissionDenied | import("./errors.ts").GitHubRateLimited | import("./errors.ts").GitHubTransportError | import("@shivaedev/http/errors.ts").InvalidBodyLimit | import("effect/Cause").NoSuchElementError | import("@shivaedev/http/errors.ts").PaginationLinkError | import("@shivaedev/http/errors.ts").ResponseBodyTooLarge | import("effect/Schema").SchemaError | import("@shivaedev/errors/taxonomy.ts").ServiceUnavailable | import("@shivaedev/http/errors.ts").RemoteStatusError, never>;
|
|
84
|
+
readonly updateCheckRun: (owner: string, repo: string, id: number, input: {
|
|
85
|
+
readonly detailsUrl?: string | undefined;
|
|
86
|
+
readonly output?: {
|
|
87
|
+
readonly summary: string;
|
|
88
|
+
readonly text?: string | undefined;
|
|
89
|
+
readonly title: string;
|
|
90
|
+
} | undefined;
|
|
91
|
+
readonly status: "in_progress" | "queued";
|
|
92
|
+
} | {
|
|
93
|
+
readonly conclusion: "action_required" | "cancelled" | "failure" | "neutral" | "skipped" | "stale" | "success" | "timed_out";
|
|
94
|
+
readonly detailsUrl?: string | undefined;
|
|
95
|
+
readonly output?: {
|
|
96
|
+
readonly summary: string;
|
|
97
|
+
readonly text?: string | undefined;
|
|
98
|
+
readonly title: string;
|
|
99
|
+
} | undefined;
|
|
100
|
+
readonly status: "completed";
|
|
101
|
+
}) => Effect.Effect<{
|
|
102
|
+
readonly app?: {
|
|
103
|
+
readonly id: number;
|
|
104
|
+
} | null | undefined;
|
|
105
|
+
readonly conclusion: "action_required" | "cancelled" | "failure" | "neutral" | "skipped" | "stale" | "success" | "timed_out" | null;
|
|
106
|
+
readonly detailsUrl: string | null;
|
|
107
|
+
readonly headSha: string;
|
|
108
|
+
readonly id: number;
|
|
109
|
+
readonly name: string;
|
|
110
|
+
readonly output: {
|
|
111
|
+
readonly summary: string | null;
|
|
112
|
+
readonly text: string | null;
|
|
113
|
+
readonly title: string | null;
|
|
114
|
+
};
|
|
115
|
+
readonly status: "completed" | "in_progress" | "pending" | "queued" | "requested" | "waiting";
|
|
116
|
+
}, import("./errors.ts").GitHubAppError | import("./errors.ts").GitHubConversationLocked | import("./errors.ts").GitHubPermissionDenied | import("./errors.ts").GitHubRateLimited | import("./errors.ts").GitHubTransportError | import("effect/http/HttpBody").HttpBodyError | import("@shivaedev/http/errors.ts").InvalidBodyLimit | import("@shivaedev/http/errors.ts").ResponseBodyTooLarge | import("effect/Schema").SchemaError | import("@shivaedev/http/errors.ts").RemoteStatusError, never>;
|
|
117
|
+
readonly upsertCheckRun: (owner: string, repo: string, input: {
|
|
118
|
+
readonly detailsUrl?: string | undefined;
|
|
119
|
+
readonly headSha: string;
|
|
120
|
+
readonly name: string;
|
|
121
|
+
readonly output?: {
|
|
122
|
+
readonly summary: string;
|
|
123
|
+
readonly text?: string | undefined;
|
|
124
|
+
readonly title: string;
|
|
125
|
+
} | undefined;
|
|
126
|
+
readonly conclusion?: "action_required" | "cancelled" | "failure" | "neutral" | "skipped" | "stale" | "success" | "timed_out" | undefined;
|
|
127
|
+
readonly status: "in_progress" | "queued";
|
|
128
|
+
} | {
|
|
129
|
+
readonly detailsUrl?: string | undefined;
|
|
130
|
+
readonly headSha: string;
|
|
131
|
+
readonly name: string;
|
|
132
|
+
readonly output?: {
|
|
133
|
+
readonly summary: string;
|
|
134
|
+
readonly text?: string | undefined;
|
|
135
|
+
readonly title: string;
|
|
136
|
+
} | undefined;
|
|
137
|
+
readonly conclusion: "action_required" | "cancelled" | "failure" | "neutral" | "skipped" | "stale" | "success" | "timed_out";
|
|
138
|
+
readonly status: "completed";
|
|
139
|
+
}, appId?: number | undefined) => Effect.Effect<{
|
|
140
|
+
readonly app?: {
|
|
141
|
+
readonly id: number;
|
|
142
|
+
} | null | undefined;
|
|
143
|
+
readonly conclusion: "action_required" | "cancelled" | "failure" | "neutral" | "skipped" | "stale" | "success" | "timed_out" | null;
|
|
144
|
+
readonly detailsUrl: string | null;
|
|
145
|
+
readonly headSha: string;
|
|
146
|
+
readonly id: number;
|
|
147
|
+
readonly name: string;
|
|
148
|
+
readonly output: {
|
|
149
|
+
readonly summary: string | null;
|
|
150
|
+
readonly text: string | null;
|
|
151
|
+
readonly title: string | null;
|
|
152
|
+
};
|
|
153
|
+
readonly status: "completed" | "in_progress" | "pending" | "queued" | "requested" | "waiting";
|
|
154
|
+
}, import("./errors.ts").GitHubAppError | import("./errors.ts").GitHubConversationLocked | import("./errors.ts").GitHubPermissionDenied | import("./errors.ts").GitHubRateLimited | import("./errors.ts").GitHubTransportError | import("effect/http/HttpBody").HttpBodyError | import("@shivaedev/http/errors.ts").InvalidBodyLimit | import("effect/Cause").NoSuchElementError | import("@shivaedev/http/errors.ts").PaginationLinkError | import("@shivaedev/http/errors.ts").ResponseBodyTooLarge | import("effect/Schema").SchemaError | import("@shivaedev/errors/taxonomy.ts").ServiceUnavailable | import("@shivaedev/http/errors.ts").RemoteStatusError, never>;
|
|
155
|
+
readonly upsertStickyComment: (input: {
|
|
156
|
+
readonly appId?: number | undefined;
|
|
157
|
+
readonly author?: string | undefined;
|
|
158
|
+
readonly body: string;
|
|
159
|
+
readonly marker: string;
|
|
160
|
+
readonly number: number;
|
|
161
|
+
readonly owner: string;
|
|
162
|
+
readonly repo: string;
|
|
163
|
+
}) => Effect.Effect<{
|
|
164
|
+
readonly body: string;
|
|
165
|
+
readonly id: number;
|
|
166
|
+
readonly performedViaGithubApp?: {
|
|
167
|
+
readonly id: number;
|
|
168
|
+
} | null | undefined;
|
|
169
|
+
readonly user: {
|
|
170
|
+
readonly login: string;
|
|
171
|
+
readonly type?: string | undefined;
|
|
172
|
+
} | null;
|
|
173
|
+
}, import("./errors.ts").GitHubAppError | import("./errors.ts").GitHubConversationLocked | import("./errors.ts").GitHubPermissionDenied | import("./errors.ts").GitHubRateLimited | import("./errors.ts").GitHubTransportError | import("effect/http/HttpBody").HttpBodyError | import("@shivaedev/http/errors.ts").InvalidBodyLimit | import("effect/Cause").NoSuchElementError | import("@shivaedev/http/errors.ts").PaginationLinkError | import("@shivaedev/http/errors.ts").ResponseBodyTooLarge | import("effect/Schema").SchemaError | import("@shivaedev/errors/taxonomy.ts").ServiceUnavailable | import("@shivaedev/http/errors.ts").RemoteStatusError, never>;
|
|
174
|
+
readonly bytes: (path: string, accept: string, limit: number) => Effect.Effect<Uint8Array<ArrayBufferLike>, import("./errors.ts").GitHubAppError | import("./errors.ts").GitHubConversationLocked | import("./errors.ts").GitHubPermissionDenied | import("./errors.ts").GitHubRateLimited | import("./errors.ts").GitHubTransportError | import("@shivaedev/http/errors.ts").InvalidBodyLimit | import("@shivaedev/http/errors.ts").ResponseBodyTooLarge | import("@shivaedev/http/errors.ts").RemoteStatusError, never>;
|
|
175
|
+
readonly observations: () => Effect.Effect<import("./rateLimits.ts").RateLimitObservation[], never, never>;
|
|
176
|
+
readonly resource: (path: string) => Effect.Effect<string, never, never>;
|
|
177
|
+
readonly text: (path: string, accept: string, limit: number) => Effect.Effect<string, import("./errors.ts").GitHubAppError | import("./errors.ts").GitHubConversationLocked | import("./errors.ts").GitHubPermissionDenied | import("./errors.ts").GitHubRateLimited | import("./errors.ts").GitHubTransportError | import("@shivaedev/http/errors.ts").InvalidBodyLimit | import("@shivaedev/http/errors.ts").ResponseBodyTooLarge | import("@shivaedev/http/errors.ts").RemoteStatusError, never>;
|
|
178
|
+
}>, never, never>;
|
|
179
|
+
}>> & {
|
|
180
|
+
readonly layer: Layer.Layer<"@shivaedev/github/GitHubApp", never, HttpClient.HttpClient | {
|
|
181
|
+
readonly credentials: AppCredentials;
|
|
182
|
+
}>;
|
|
183
|
+
};
|
|
184
|
+
export declare function withInstallation<A, E, R>(installationId: number, effect: Effect.Effect<A, E, R>): Effect.Effect<A, E, "@shivaedev/github/GitHubApp" | Exclude<R, "@shivaedev/github/GitHub">>;
|
|
185
|
+
//# sourceMappingURL=installationClients.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"installationClients.d.ts","sourceRoot":"","sources":["../src/installationClients.ts"],"names":[],"mappings":"AAAA,OAAO,EAAS,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,QAAQ,CAAC;AACvD,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEzC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAKjD,eAAO,MAAM,oBAAoB;0BAA2C,cAAc;;;EAAuC,CAAC;AAElI,eAAO,MAAM,SAAS;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;8BAFsD,cAAc;;CA6BxF,CAAC;AAEH,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,cAAc,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,+FAI/F"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { Cache, Context, Effect, Layer } from "effect";
|
|
2
|
+
import { HttpClient } from "effect/http";
|
|
3
|
+
import { defineService } from "@shivaedev/service/defineService.ts";
|
|
4
|
+
import { appTokens } from "#appTokens.ts";
|
|
5
|
+
import { GitHub } from "#GitHub.ts";
|
|
6
|
+
import { GitHubConfig } from "#GitHubConfig.ts";
|
|
7
|
+
export const GitHubAppCredentials = Context.Service("@shivaedev/github/AppCredentials");
|
|
8
|
+
export const GitHubApp = defineService({
|
|
9
|
+
id: "@shivaedev/github/GitHubApp",
|
|
10
|
+
initialize: Effect.gen(function* () {
|
|
11
|
+
const { credentials } = yield* GitHubAppCredentials;
|
|
12
|
+
const http = yield* HttpClient.HttpClient;
|
|
13
|
+
const config = yield* GitHubConfig;
|
|
14
|
+
return yield* Cache.make({
|
|
15
|
+
capacity: 100,
|
|
16
|
+
lookup: (installationId) => Layer.build(GitHub.layer.pipe(Layer.provide(appTokens({ ...credentials, installationId })), Layer.provide(Layer.succeed(HttpClient.HttpClient)(http)), Layer.provide(Layer.succeed(GitHubConfig)(config)))).pipe(Effect.scoped, Effect.map((context) => Context.get(context, GitHub))),
|
|
17
|
+
timeToLive: "30 minutes",
|
|
18
|
+
});
|
|
19
|
+
}),
|
|
20
|
+
methods: (clients) => ({
|
|
21
|
+
installation: (installationId) => Cache.get(clients, installationId).pipe(Effect.tap((client) => Cache.set(clients, installationId, client))),
|
|
22
|
+
}),
|
|
23
|
+
requires: [GitHubAppCredentials, HttpClient.HttpClient, GitHubConfig],
|
|
24
|
+
});
|
|
25
|
+
export function withInstallation(installationId, effect) {
|
|
26
|
+
return Effect.flatMap(GitHubApp, (app) => Effect.flatMap(app.installation(installationId), (client) => Effect.provideService(effect, GitHub, client)));
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=installationClients.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"installationClients.js","sourceRoot":"","sources":["../src/installationClients.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,QAAQ,CAAC;AACvD,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,aAAa,EAAE,MAAM,qCAAqC,CAAC;AAEpE,OAAO,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAC1C,OAAO,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AACpC,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAEhD,MAAM,CAAC,MAAM,oBAAoB,GAAG,OAAO,CAAC,OAAO,CAA2C,kCAAkC,CAAC,CAAC;AAElI,MAAM,CAAC,MAAM,SAAS,GAAG,aAAa,CAAC;IACtC,EAAE,EAAE,6BAA6B;IACjC,UAAU,EAAE,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;QAC/B,MAAM,EAAE,WAAW,EAAE,GAAG,KAAK,CAAC,CAAC,oBAAoB,CAAC;QACpD,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,UAAU,CAAC,UAAU,CAAC;QAC1C,MAAM,MAAM,GAAG,KAAK,CAAC,CAAC,YAAY,CAAC;QACnC,OAAO,KAAK,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC;YACxB,QAAQ,EAAE,GAAG;YACb,MAAM,EAAE,CAAC,cAAsB,EAAE,EAAE,CAClC,KAAK,CAAC,KAAK,CACV,MAAM,CAAC,KAAK,CAAC,IAAI,CAChB,KAAK,CAAC,OAAO,CAAC,SAAS,CAAC,EAAE,GAAG,WAAW,EAAE,cAAc,EAAE,CAAC,CAAC,EAC5D,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,CAAC,EACzD,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC,MAAM,CAAC,CAAC,CAClD,CACD,CAAC,IAAI,CACL,MAAM,CAAC,MAAM,EACb,MAAM,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC,CACrD;YACF,UAAU,EAAE,YAAY;SACxB,CAAC,CAAC;IACJ,CAAC,CAAC;IACF,OAAO,EAAE,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;QACtB,YAAY,EAAE,CAAC,cAAsB,EAAE,EAAE,CACxC,KAAK,CAAC,GAAG,CAAC,OAAO,EAAE,cAAc,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,OAAO,EAAE,cAAc,EAAE,MAAM,CAAC,CAAC,CAAC;KAC5G,CAAC;IACF,QAAQ,EAAE,CAAC,oBAAoB,EAAE,UAAU,CAAC,UAAU,EAAE,YAAY,CAAC;CACrE,CAAC,CAAC;AAEH,MAAM,UAAU,gBAAgB,CAAU,cAAsB,EAAE,MAA8B;IAC/F,OAAO,MAAM,CAAC,OAAO,CAAC,SAAS,EAAE,CAAC,GAAG,EAAE,EAAE,CACxC,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC,cAAc,CAAC,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC,CAC3G,CAAC;AACH,CAAC"}
|
package/dist/json.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"json.d.ts","sourceRoot":"","sources":["../src/json.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;
|
|
1
|
+
{"version":3,"file":"json.d.ts","sourceRoot":"","sources":["../src/json.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAExC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAEjD,eAAO,MAAM,UAAU,GAAgC,OAAO,SAAS,MAAM,CAAC,UAAU,4HAWtF,CAAC;AAEH,wBAAgB,YAAY,CAAC,KAAK,EAAE,cAAc;IACxC,MAAM,GAAG,OAAO,SAAS,MAAM,CAAC,UAAU,UAAU,OAAO;EACpE"}
|
package/dist/json.js
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
import { Effect, Schema } from "effect";
|
|
2
|
+
import * as HttpStatus from "effect/http/HttpStatus";
|
|
2
3
|
export const decodeJson = Effect.fnUntraced(function* (entry, schema) {
|
|
4
|
+
if (entry.response.status === HttpStatus.fromLiteral("NoContent")) {
|
|
5
|
+
return yield* Schema.decodeUnknownEffect(schema)(undefined);
|
|
6
|
+
}
|
|
3
7
|
const cached = entry.decoded.get(schema);
|
|
4
8
|
if (entry.decoded.has(schema) && Schema.is(schema)(cached)) {
|
|
5
9
|
return cached;
|
package/dist/json.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"json.js","sourceRoot":"","sources":["../src/json.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;
|
|
1
|
+
{"version":3,"file":"json.js","sourceRoot":"","sources":["../src/json.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AACxC,OAAO,KAAK,UAAU,MAAM,wBAAwB,CAAC;AAGrD,MAAM,CAAC,MAAM,UAAU,GAAG,MAAM,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAqC,KAAqB,EAAE,MAAe;IAC/H,IAAI,KAAK,CAAC,QAAQ,CAAC,MAAM,KAAK,UAAU,CAAC,WAAW,CAAC,WAAW,CAAC,EAAE,CAAC;QACnE,OAAO,KAAK,CAAC,CAAC,MAAM,CAAC,mBAAmB,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,CAAC;IAC7D,CAAC;IACD,MAAM,MAAM,GAAG,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IACzC,IAAI,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,MAAM,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC;QAC5D,OAAO,MAAM,CAAC;IACf,CAAC;IACD,MAAM,KAAK,GAAG,KAAK,CAAC,CAAC,MAAM,CAAC,mBAAmB,CAAC,MAAM,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC;IACtH,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;IACjC,OAAO,KAAK,CAAC;AACd,CAAC,CAAC,CAAC;AAEH,MAAM,UAAU,YAAY,CAAC,KAAqB;IACjD,OAAO,EAAE,MAAM,EAAE,CAAoC,MAAe,EAAE,EAAE,CAAC,UAAU,CAAC,KAAK,EAAE,MAAM,CAAC,EAAE,CAAC;AACtG,CAAC"}
|
|
@@ -7,13 +7,13 @@ export declare function representations(apiUrl: string, request: ReturnType<type
|
|
|
7
7
|
list: (path: string, beforeRequest?: (resource: string) => Effect.Effect<void, ServiceUnavailable>, byteLimit?: number) => Effect.Effect<Stream.Stream<{
|
|
8
8
|
decode: <TSchema extends import("effect/Schema").Constraint>(schema: TSchema) => Effect.Effect<TSchema["Type"], import("effect/Schema").SchemaError, TSchema["DecodingServices"]>;
|
|
9
9
|
hasNext: boolean;
|
|
10
|
-
}, import("./errors.ts").GitHubAppError | import("./errors.ts").GitHubConversationLocked | import("./errors.ts").GitHubPermissionDenied | import("./errors.ts").GitHubRateLimited | import("./errors.ts").GitHubTransportError | import("effect/Cause").NoSuchElementError | import("@shivaedev/http/errors.ts").PaginationLinkError | import("@shivaedev/http/errors.ts").ResponseBodyTooLarge | ServiceUnavailable | import("@shivaedev/http/errors.ts").RemoteStatusError, never>, never, never>;
|
|
10
|
+
}, import("./errors.ts").GitHubAppError | import("./errors.ts").GitHubConversationLocked | import("./errors.ts").GitHubPermissionDenied | import("./errors.ts").GitHubRateLimited | import("./errors.ts").GitHubTransportError | import("@shivaedev/http/errors.ts").InvalidBodyLimit | import("effect/Cause").NoSuchElementError | import("@shivaedev/http/errors.ts").PaginationLinkError | import("@shivaedev/http/errors.ts").ResponseBodyTooLarge | ServiceUnavailable | import("@shivaedev/http/errors.ts").RemoteStatusError, never>, never, never>;
|
|
11
11
|
read: (path: string, byteLimit?: number) => Effect.Effect<{
|
|
12
12
|
decode: <TSchema extends import("effect/Schema").Constraint>(schema: TSchema) => Effect.Effect<TSchema["Type"], import("effect/Schema").SchemaError, TSchema["DecodingServices"]>;
|
|
13
|
-
}, import("./errors.ts").GitHubAppError | import("./errors.ts").GitHubConversationLocked | import("./errors.ts").GitHubPermissionDenied | import("./errors.ts").GitHubRateLimited | import("./errors.ts").GitHubTransportError | import("@shivaedev/http/errors.ts").ResponseBodyTooLarge | import("@shivaedev/http/errors.ts").RemoteStatusError, never>;
|
|
14
|
-
write: (method: "POST" | "PATCH", path: string, body
|
|
13
|
+
}, import("./errors.ts").GitHubAppError | import("./errors.ts").GitHubConversationLocked | import("./errors.ts").GitHubPermissionDenied | import("./errors.ts").GitHubRateLimited | import("./errors.ts").GitHubTransportError | import("@shivaedev/http/errors.ts").InvalidBodyLimit | import("@shivaedev/http/errors.ts").ResponseBodyTooLarge | import("@shivaedev/http/errors.ts").RemoteStatusError, never>;
|
|
14
|
+
write: (method: "POST" | "PATCH" | "PUT" | "DELETE", path: string, body?: unknown, byteLimit?: number) => Effect.Effect<{
|
|
15
15
|
decode: <TSchema extends import("effect/Schema").Constraint>(schema: TSchema) => Effect.Effect<TSchema["Type"], import("effect/Schema").SchemaError, TSchema["DecodingServices"]>;
|
|
16
|
-
}, import("./errors.ts").GitHubAppError | import("./errors.ts").GitHubConversationLocked | import("./errors.ts").GitHubPermissionDenied | import("./errors.ts").GitHubRateLimited | import("./errors.ts").GitHubTransportError | import("effect/http/HttpBody").HttpBodyError | import("@shivaedev/http/errors.ts").ResponseBodyTooLarge | import("@shivaedev/http/errors.ts").RemoteStatusError, never>;
|
|
16
|
+
}, import("./errors.ts").GitHubAppError | import("./errors.ts").GitHubConversationLocked | import("./errors.ts").GitHubPermissionDenied | import("./errors.ts").GitHubRateLimited | import("./errors.ts").GitHubTransportError | import("effect/http/HttpBody").HttpBodyError | import("@shivaedev/http/errors.ts").InvalidBodyLimit | import("@shivaedev/http/errors.ts").ResponseBodyTooLarge | import("@shivaedev/http/errors.ts").RemoteStatusError, never>;
|
|
17
17
|
};
|
|
18
18
|
export type Representations = Omit<ReturnType<typeof representations>, "get">;
|
|
19
19
|
//# sourceMappingURL=representations.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"representations.d.ts","sourceRoot":"","sources":["../src/representations.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AACxC,OAAO,EAAc,iBAAiB,EAA2B,MAAM,aAAa,CAAC;AACrF,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,+BAA+B,CAAC;AAKxE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAE7C,wBAAgB,eAAe,CAC9B,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,UAAU,CAAC,OAAO,QAAQ,CAAC,EACpC,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,EAClC,iBAAiB,SAAc;gBAEZ,MAAM;iBAOlB,MAAM,kBACI,CAAC,QAAQ,EAAE,MAAM,KAAK,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,kBAAkB,CAAC;;;;iBAL5C,MAAM;;;oBA6BH,MAAM,GAAG,OAAO,QAAQ,MAAM,
|
|
1
|
+
{"version":3,"file":"representations.d.ts","sourceRoot":"","sources":["../src/representations.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AACxC,OAAO,EAAc,iBAAiB,EAA2B,MAAM,aAAa,CAAC;AACrF,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,+BAA+B,CAAC;AAKxE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAE7C,wBAAgB,eAAe,CAC9B,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,UAAU,CAAC,OAAO,QAAQ,CAAC,EACpC,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,EAClC,iBAAiB,SAAc;gBAEZ,MAAM;iBAOlB,MAAM,kBACI,CAAC,QAAQ,EAAE,MAAM,KAAK,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,kBAAkB,CAAC;;;;iBAL5C,MAAM;;;oBA6BH,MAAM,GAAG,OAAO,GAAG,KAAK,GAAG,QAAQ,QAAQ,MAAM,SAAS,OAAO;;;EAQtG;AAED,MAAM,MAAM,eAAe,GAAG,IAAI,CAAC,UAAU,CAAC,OAAO,eAAe,CAAC,EAAE,KAAK,CAAC,CAAC"}
|
package/dist/representations.js
CHANGED
|
@@ -22,7 +22,7 @@ export function representations(apiUrl, request, resource, responseByteLimit = 1
|
|
|
22
22
|
return Effect.succeed(paginate(client, get(path)).pipe(Stream.mapEffect((response) => Effect.fromNullishOr(entries.get(response))), Stream.map((entry) => ({ ...jsonResponse(entry), hasNext: nextLink(entry.response.headers.link) !== undefined }))));
|
|
23
23
|
}
|
|
24
24
|
function writeRepresentation(method, path, body, byteLimit = responseByteLimit) {
|
|
25
|
-
return HttpClientRequest.make(method)(new URL(path, apiUrl).href, { headers: { Accept: "application/vnd.github+json" } }).pipe(HttpClientRequest.bodyJson(body), Effect.flatMap((input) => request(input, byteLimit)), Effect.map(jsonResponse));
|
|
25
|
+
return HttpClientRequest.make(method)(new URL(path, apiUrl).href, { headers: { Accept: "application/vnd.github+json" } }).pipe((input) => (body === undefined ? Effect.succeed(input) : HttpClientRequest.bodyJson(input, body)), Effect.flatMap((input) => request(input, byteLimit)), Effect.map(jsonResponse));
|
|
26
26
|
}
|
|
27
27
|
return { get, list: listRepresentations, read: readRepresentation, write: writeRepresentation };
|
|
28
28
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"representations.js","sourceRoot":"","sources":["../src/representations.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AACxC,OAAO,EAAE,UAAU,EAAE,iBAAiB,EAA2B,MAAM,aAAa,CAAC;AAErF,OAAO,EAAE,QAAQ,EAAE,MAAM,6BAA6B,CAAC;AACvD,OAAO,EAAE,QAAQ,EAAE,MAAM,6BAA6B,CAAC;AAEvD,OAAO,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAGxC,MAAM,UAAU,eAAe,CAC9B,MAAc,EACd,OAAoC,EACpC,QAAkC,EAClC,iBAAiB,GAAG,IAAI,GAAG,IAAI;IAE/B,SAAS,GAAG,CAAC,IAAY,EAAE,MAAM,GAAG,6BAA6B;QAChE,OAAO,iBAAiB,CAAC,GAAG,CAAC,IAAI,GAAG,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,OAAO,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE,CAAC,CAAC;IAC3F,CAAC;IACD,SAAS,kBAAkB,CAAC,IAAY,EAAE,SAAS,GAAG,iBAAiB;QACtE,OAAO,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,SAAS,CAAC,EAAE,YAAY,CAAC,CAAC;IAChE,CAAC;IACD,SAAS,mBAAmB,CAC3B,IAAY,EACZ,aAA6E,EAC7E,SAAS,GAAG,iBAAiB;QAE7B,MAAM,OAAO,GAAG,IAAI,OAAO,EAAyD,CAAC;QACrF,SAAS,SAAS,CAAC,KAA0C;YAC5D,OAAO,CAAC,aAAa,EAAE,CAAC,QAAQ,CAAC,IAAI,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAC,CAAC,IAAI,MAAM,CAAC,IAAI,CAAC,CAAC,IAAI,CAClF,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,KAAK,EAAE,SAAS,CAAC,CAAC,EACzC,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE;gBACpB,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;gBACnC,OAAO,KAAK,CAAC,QAAQ,CAAC;YACvB,CAAC,CAAC,CACF,CAAC;QACH,CAAC;QACD,MAAM,MAAM,GAAG,UAAU,CAAC,QAAQ,CACjC,CAAC,MAA0D,EAAE,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,MAAM,EAAE,SAAS,CAAC,EACjG,MAAM,CAAC,OAAO,CACd,CAAC;QACF,OAAO,MAAM,CAAC,OAAO,CACpB,QAAQ,CAAC,MAAM,EAAE,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAC/B,MAAM,CAAC,SAAS,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,MAAM,CAAC,aAAa,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,EAC3E,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,EAAE,GAAG,YAAY,CAAC,KAAK,CAAC,EAAE,OAAO,EAAE,QAAQ,CAAC,KAAK,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,SAAS,EAAE,CAAC,CAAC,CACjH,CACD,CAAC;IACH,CAAC;IACD,SAAS,mBAAmB,CAAC,
|
|
1
|
+
{"version":3,"file":"representations.js","sourceRoot":"","sources":["../src/representations.ts"],"names":[],"mappings":"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"}
|
package/dist/requests.d.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { Effect } from "effect";
|
|
2
|
-
import {
|
|
2
|
+
import { HttpClient, HttpClientRequest } from "effect/http";
|
|
3
3
|
import { ResponseBodyTooLarge } from "@shivaedev/http/errors.ts";
|
|
4
4
|
import { type CachedResponse } from "./cache.ts";
|
|
5
5
|
import { GitHubTransportError } from "./errors.ts";
|
|
6
6
|
import type { GitHubToken } from "./GitHubToken.ts";
|
|
7
7
|
import type { RateLimits } from "./rateLimits.ts";
|
|
8
8
|
import type { TokenRefresh } from "./TokenRefresh.ts";
|
|
9
|
-
export declare function requests(client: HttpClient.HttpClient, token: GitHubToken["Service"], rates: RateLimits, refresh?: typeof TokenRefresh.Service): (input: HttpClientRequest.HttpClientRequest, limit: number) => Effect.Effect<CachedResponse, import("./errors.ts").GitHubAppError | import("./errors.ts").GitHubConversationLocked | import("./errors.ts").GitHubPermissionDenied | import("./errors.ts").GitHubRateLimited | GitHubTransportError | ResponseBodyTooLarge | import("@shivaedev/http/errors.ts").RemoteStatusError, never>;
|
|
9
|
+
export declare function requests(client: HttpClient.HttpClient, token: GitHubToken["Service"], rates: RateLimits, refresh?: typeof TokenRefresh.Service): (input: HttpClientRequest.HttpClientRequest, limit: number) => Effect.Effect<CachedResponse, import("./errors.ts").GitHubAppError | import("./errors.ts").GitHubConversationLocked | import("./errors.ts").GitHubPermissionDenied | import("./errors.ts").GitHubRateLimited | GitHubTransportError | import("@shivaedev/http/errors.ts").InvalidBodyLimit | ResponseBodyTooLarge | import("@shivaedev/http/errors.ts").RemoteStatusError, never>;
|
|
10
10
|
//# sourceMappingURL=requests.d.ts.map
|
package/dist/requests.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"requests.d.ts","sourceRoot":"","sources":["../src/requests.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAChC,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"requests.d.ts","sourceRoot":"","sources":["../src/requests.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAChC,OAAO,EAAE,UAAU,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAE5D,OAAO,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAC;AAEjE,OAAO,EAAE,KAAK,cAAc,EAAsC,MAAM,YAAY,CAAC;AACrF,OAAO,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AACnD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAElD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAEtD,wBAAgB,QAAQ,CACvB,MAAM,EAAE,UAAU,CAAC,UAAU,EAC7B,KAAK,EAAE,WAAW,CAAC,SAAS,CAAC,EAC7B,KAAK,EAAE,UAAU,EACjB,OAAO,GAAE,OAAO,YAAY,CAAC,OAAmB,obA6BhD"}
|
package/dist/requests.js
CHANGED
|
@@ -1,34 +1,36 @@
|
|
|
1
1
|
import { Effect } from "effect";
|
|
2
|
-
import { HttpClientRequest } from "effect/http";
|
|
2
|
+
import { HttpClient, HttpClientRequest } from "effect/http";
|
|
3
3
|
import { readBoundedBody } from "@shivaedev/http/body.ts";
|
|
4
4
|
import { ResponseBodyTooLarge } from "@shivaedev/http/errors.ts";
|
|
5
5
|
import { authenticatedRequest } from "./authenticatedRequest.js";
|
|
6
|
-
import { responseCache } from "./cache.js";
|
|
6
|
+
import { responseCache, revalidatedResponse } from "./cache.js";
|
|
7
7
|
import { GitHubTransportError } from "./errors.js";
|
|
8
8
|
import { isUnchanged } from "./status.js";
|
|
9
9
|
export function requests(client, token, rates, refresh = undefined) {
|
|
10
10
|
const cache = responseCache();
|
|
11
|
-
const send = authenticatedRequest(client, token, rates, refresh);
|
|
11
|
+
const send = authenticatedRequest(HttpClient.withScope(client), token, rates, refresh);
|
|
12
12
|
return Effect.fn("GitHub.request")(function* (input, limit) {
|
|
13
|
-
const key =
|
|
13
|
+
const key = JSON.stringify([input.url, input.urlParams, input.headers.accept]);
|
|
14
14
|
const cached = input.method === "GET" ? cache.get(key) : undefined;
|
|
15
15
|
let request = HttpClientRequest.removeHeader(input, "if-none-match");
|
|
16
|
-
if (cached?.
|
|
17
|
-
request = HttpClientRequest.setHeader(request, "If-None-Match", cached.
|
|
16
|
+
if (cached?.etag !== undefined) {
|
|
17
|
+
request = HttpClientRequest.setHeader(request, "If-None-Match", cached.etag);
|
|
18
18
|
}
|
|
19
19
|
const response = yield* send(request, cached === undefined ? undefined : "revalidate");
|
|
20
20
|
if (isUnchanged(response) && cached !== undefined) {
|
|
21
21
|
if (cached.bytes.byteLength > limit) {
|
|
22
22
|
return yield* new ResponseBodyTooLarge({ limit });
|
|
23
23
|
}
|
|
24
|
-
|
|
24
|
+
const entry = revalidatedResponse(cached, response);
|
|
25
|
+
cache.set(key, entry);
|
|
26
|
+
return entry;
|
|
25
27
|
}
|
|
26
28
|
const bytes = yield* readBoundedBody(response, limit).pipe(Effect.catchTag("HttpClientError", (cause) => Effect.fail(new GitHubTransportError({ path: new URL(input.url).pathname }, { cause }))));
|
|
27
|
-
const entry = { bytes, decoded: new Map(), response };
|
|
29
|
+
const entry = { bytes, decoded: new Map(), etag: response.headers.etag, response };
|
|
28
30
|
if (input.method === "GET" && response.headers.etag !== undefined) {
|
|
29
31
|
cache.set(key, entry);
|
|
30
32
|
}
|
|
31
33
|
return entry;
|
|
32
|
-
});
|
|
34
|
+
}, Effect.scoped);
|
|
33
35
|
}
|
|
34
36
|
//# sourceMappingURL=requests.js.map
|
package/dist/requests.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"requests.js","sourceRoot":"","sources":["../src/requests.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAChC,OAAO,
|
|
1
|
+
{"version":3,"file":"requests.js","sourceRoot":"","sources":["../src/requests.ts"],"names":[],"mappings":"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"}
|
package/dist/status.d.ts
CHANGED
|
@@ -3,5 +3,5 @@ import { type HttpClientResponse } from "effect/http";
|
|
|
3
3
|
import { GitHubConversationLocked, GitHubPermissionDenied, GitHubRateLimited } from "#errors.ts";
|
|
4
4
|
export declare function isUnauthorized(response: HttpClientResponse.HttpClientResponse): boolean;
|
|
5
5
|
export declare function isUnchanged(response: HttpClientResponse.HttpClientResponse): boolean;
|
|
6
|
-
export declare const checkStatus: (response: HttpClientResponse.HttpClientResponse, resource: string, failures?: any) => Effect.Effect<HttpClientResponse.HttpClientResponse, GitHubConversationLocked | GitHubPermissionDenied | GitHubRateLimited | import("effect/http/HttpClientError").HttpClientError | import("@shivaedev/http/errors.ts").ResponseBodyTooLarge | import("@shivaedev/http/errors.ts").RemoteStatusError, never>;
|
|
6
|
+
export declare const checkStatus: (response: HttpClientResponse.HttpClientResponse, resource: string, failures?: any) => Effect.Effect<HttpClientResponse.HttpClientResponse, GitHubConversationLocked | GitHubPermissionDenied | GitHubRateLimited | import("effect/http/HttpClientError").HttpClientError | import("@shivaedev/http/errors.ts").InvalidBodyLimit | import("@shivaedev/http/errors.ts").ResponseBodyTooLarge | import("@shivaedev/http/errors.ts").RemoteStatusError, never>;
|
|
7
7
|
//# sourceMappingURL=status.d.ts.map
|
package/dist/status.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"status.d.ts","sourceRoot":"","sources":["../src/status.ts"],"names":[],"mappings":"AAAA,OAAO,EAAY,MAAM,EAAkB,MAAM,QAAQ,CAAC;AAC1D,OAAO,EAAc,KAAK,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAKlE,OAAO,EAAE,wBAAwB,EAAE,sBAAsB,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAEjG,wBAAgB,cAAc,CAAC,QAAQ,EAAE,kBAAkB,CAAC,kBAAkB,WAE7E;AAED,wBAAgB,WAAW,CAAC,QAAQ,EAAE,kBAAkB,CAAC,kBAAkB,WAE1E;AAED,eAAO,MAAM,WAAW,
|
|
1
|
+
{"version":3,"file":"status.d.ts","sourceRoot":"","sources":["../src/status.ts"],"names":[],"mappings":"AAAA,OAAO,EAAY,MAAM,EAAkB,MAAM,QAAQ,CAAC;AAC1D,OAAO,EAAc,KAAK,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAKlE,OAAO,EAAE,wBAAwB,EAAE,sBAAsB,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAEjG,wBAAgB,cAAc,CAAC,QAAQ,EAAE,kBAAkB,CAAC,kBAAkB,WAE7E;AAED,wBAAgB,WAAW,CAAC,QAAQ,EAAE,kBAAkB,CAAC,kBAAkB,WAE1E;AAED,eAAO,MAAM,WAAW,6bAgBtB,CAAC"}
|
package/dist/stickyComments.d.ts
CHANGED
|
@@ -41,6 +41,6 @@ export declare function stickyComments(client: Representations): {
|
|
|
41
41
|
readonly login: string;
|
|
42
42
|
readonly type?: string | undefined;
|
|
43
43
|
} | null;
|
|
44
|
-
}, import("./errors.ts").GitHubAppError | import("./errors.ts").GitHubConversationLocked | import("./errors.ts").GitHubPermissionDenied | import("./errors.ts").GitHubRateLimited | import("./errors.ts").GitHubTransportError | import("effect/http/HttpBody").HttpBodyError | import("effect/Cause").NoSuchElementError | import("@shivaedev/http/errors.ts").PaginationLinkError | import("@shivaedev/http/errors.ts").ResponseBodyTooLarge | Schema.SchemaError | import("@shivaedev/errors/taxonomy.ts").ServiceUnavailable | import("@shivaedev/http/errors.ts").RemoteStatusError, never>;
|
|
44
|
+
}, import("./errors.ts").GitHubAppError | import("./errors.ts").GitHubConversationLocked | import("./errors.ts").GitHubPermissionDenied | import("./errors.ts").GitHubRateLimited | import("./errors.ts").GitHubTransportError | import("effect/http/HttpBody").HttpBodyError | import("@shivaedev/http/errors.ts").InvalidBodyLimit | import("effect/Cause").NoSuchElementError | import("@shivaedev/http/errors.ts").PaginationLinkError | import("@shivaedev/http/errors.ts").ResponseBodyTooLarge | Schema.SchemaError | import("@shivaedev/errors/taxonomy.ts").ServiceUnavailable | import("@shivaedev/http/errors.ts").RemoteStatusError, never>;
|
|
45
45
|
};
|
|
46
46
|
//# sourceMappingURL=stickyComments.d.ts.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@shivaedev/github",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0",
|
|
4
4
|
"description": "Typed GitHub REST requests, conditional reads and resource budgets for Effect applications",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"effect"
|
|
@@ -43,17 +43,17 @@
|
|
|
43
43
|
"README.md"
|
|
44
44
|
],
|
|
45
45
|
"dependencies": {
|
|
46
|
-
"@shivaedev/errors": "0.
|
|
47
|
-
"@shivaedev/http": "0.
|
|
48
|
-
"@shivaedev/service": "0.6.
|
|
46
|
+
"@shivaedev/errors": "0.5.0",
|
|
47
|
+
"@shivaedev/http": "0.4.0",
|
|
48
|
+
"@shivaedev/service": "0.6.1"
|
|
49
49
|
},
|
|
50
50
|
"devDependencies": {
|
|
51
51
|
"@effect/platform-node": "4.0.6",
|
|
52
52
|
"@effect/platform-node-shared": "4.0.6",
|
|
53
53
|
"@effect/vitest": "4.0.6",
|
|
54
|
-
"@shivaedev/quality": "0.
|
|
55
|
-
"@shivaedev/test": "0.12.
|
|
56
|
-
"@shivaedev/test-story": "0.
|
|
54
|
+
"@shivaedev/quality": "0.55.0",
|
|
55
|
+
"@shivaedev/test": "0.12.1",
|
|
56
|
+
"@shivaedev/test-story": "0.33.0",
|
|
57
57
|
"@types/node": "24.19.2",
|
|
58
58
|
"effect": "4.0.6",
|
|
59
59
|
"typescript": "7.0.2",
|
package/src/GitHub.ts
CHANGED
|
@@ -64,8 +64,22 @@ export function listBounded<TSchema extends Schema.Constraint>(path: string, sch
|
|
|
64
64
|
);
|
|
65
65
|
}
|
|
66
66
|
|
|
67
|
+
type WriteMethod = "POST" | "PATCH" | "PUT" | "DELETE";
|
|
68
|
+
|
|
67
69
|
export function write<TSchema extends Schema.Constraint>(
|
|
68
|
-
method:
|
|
70
|
+
method: WriteMethod,
|
|
71
|
+
path: string,
|
|
72
|
+
body: unknown,
|
|
73
|
+
schema: TSchema,
|
|
74
|
+
responseByteLimit?: number,
|
|
75
|
+
): ReturnType<typeof writeDecoded<TSchema>>;
|
|
76
|
+
export function write(method: WriteMethod, path: string, body?: unknown): ReturnType<typeof writeDecoded<typeof Schema.Void>>;
|
|
77
|
+
export function write(method: WriteMethod, path: string, body?: unknown, schema: Schema.Constraint = Schema.Void, responseByteLimit?: number) {
|
|
78
|
+
return writeDecoded(method, path, body, schema, responseByteLimit);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function writeDecoded<TSchema extends Schema.Constraint>(
|
|
82
|
+
method: WriteMethod,
|
|
69
83
|
path: string,
|
|
70
84
|
body: unknown,
|
|
71
85
|
schema: TSchema,
|
package/src/GitHubToken.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Config, Context, Effect, Layer, Option, Redacted, Stream } from "effect";
|
|
2
2
|
import { ChildProcess, ChildProcessSpawner } from "effect/process";
|
|
3
|
-
import { type GitHubAppError, GitHubTokenError } from "
|
|
3
|
+
import { type GitHubAppError, type GitHubRateLimited, GitHubTokenError } from "#errors.ts";
|
|
4
4
|
|
|
5
5
|
type Token = Option.Option<Redacted.Redacted<string>>;
|
|
6
6
|
const environmentToken = Config.Redacted("GH_TOKEN").pipe(
|
|
@@ -8,7 +8,9 @@ const environmentToken = Config.Redacted("GH_TOKEN").pipe(
|
|
|
8
8
|
Config.option,
|
|
9
9
|
);
|
|
10
10
|
|
|
11
|
-
export class GitHubToken extends Context.Service<GitHubToken, Token | Effect.Effect<Token, GitHubAppError>>()(
|
|
11
|
+
export class GitHubToken extends Context.Service<GitHubToken, Token | Effect.Effect<Token, GitHubAppError | GitHubRateLimited>>()(
|
|
12
|
+
"@shivaedev/github/GitHubToken",
|
|
13
|
+
) {
|
|
12
14
|
static readonly fromEnv = Layer.effect(GitHubToken)(environmentToken);
|
|
13
15
|
|
|
14
16
|
static readonly fromEnvOrCli = Layer.effect(GitHubToken)(
|
package/src/appTokens.ts
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { Cache, Clock, Context, Effect, Exit, Layer, Option, type Redacted, Schema } from "effect";
|
|
2
|
+
import { HttpClient, HttpClientRequest } from "effect/http";
|
|
3
|
+
import { readBoundedJson } from "@shivaedev/http/body.ts";
|
|
4
|
+
import { appJwt } from "#appJwt.ts";
|
|
5
|
+
import { authenticatedRequest } from "#authenticatedRequest.ts";
|
|
6
|
+
import { GitHubAppError } from "#errors.ts";
|
|
7
|
+
import { GitHubConfig } from "#GitHubConfig.ts";
|
|
8
|
+
import { GitHubToken } from "#GitHubToken.ts";
|
|
9
|
+
import type { GitHubAppOptions } from "#githubApp.ts";
|
|
10
|
+
import { rateLimits } from "#rateLimits.ts";
|
|
11
|
+
import { TokenRefresh } from "#TokenRefresh.ts";
|
|
12
|
+
|
|
13
|
+
const Installation = Schema.Struct({ id: Schema.Number });
|
|
14
|
+
const AccessToken = Schema.Struct({ expiresAt: Schema.DateFromString, token: Schema.RedactedFromValue(Schema.String) }).pipe(
|
|
15
|
+
Schema.encodeKeys({ expiresAt: "expires_at" }),
|
|
16
|
+
);
|
|
17
|
+
|
|
18
|
+
export function appTokens(options: GitHubAppOptions) {
|
|
19
|
+
return Layer.effectContext(
|
|
20
|
+
Effect.gen(function* () {
|
|
21
|
+
if (!("installationId" in options || "owner" in options)) {
|
|
22
|
+
return Context.make(GitHubToken, Effect.map(appJwt(options), Option.some)).pipe(Context.add(TokenRefresh, undefined));
|
|
23
|
+
}
|
|
24
|
+
const client = yield* HttpClient.HttpClient;
|
|
25
|
+
const { apiUrl } = yield* GitHubConfig;
|
|
26
|
+
let installationId = "installationId" in options ? options.installationId : undefined;
|
|
27
|
+
const request = appRequests(client, apiUrl);
|
|
28
|
+
const exchange = Effect.gen(function* () {
|
|
29
|
+
const jwt = yield* appJwt(options);
|
|
30
|
+
if (installationId === undefined && "owner" in options && options.owner !== undefined) {
|
|
31
|
+
const path = `/repos/${encodeURIComponent(options.owner)}/${encodeURIComponent(options.repository)}/installation`;
|
|
32
|
+
installationId = (yield* request("GET", path, jwt, Installation)).id;
|
|
33
|
+
}
|
|
34
|
+
const access = yield* request("POST", `/app/installations/${installationId}/access_tokens`, jwt, AccessToken, {
|
|
35
|
+
permissions: options.scope?.permissions,
|
|
36
|
+
repositories: options.scope?.repositories,
|
|
37
|
+
"repository_ids": options.scope?.repositoryIds,
|
|
38
|
+
});
|
|
39
|
+
const now = yield* Clock.currentTimeMillis;
|
|
40
|
+
return { ...access, timeToLive: Math.max(0, access.expiresAt.getTime() - now - 300_000) };
|
|
41
|
+
});
|
|
42
|
+
const cache = yield* Cache.makeWith((_: undefined) => exchange, {
|
|
43
|
+
capacity: 1,
|
|
44
|
+
timeToLive: (exit) => (Exit.isSuccess(exit) ? exit.value.timeToLive : 0),
|
|
45
|
+
});
|
|
46
|
+
return Context.make(
|
|
47
|
+
GitHubToken,
|
|
48
|
+
Effect.map(Cache.get(cache, undefined), ({ token }) => Option.some(token)),
|
|
49
|
+
).pipe(
|
|
50
|
+
Context.add(TokenRefresh, (rejected) =>
|
|
51
|
+
Cache.invalidateWhen(cache, undefined, ({ token }) => Option.isSome(rejected) && token === rejected.value),
|
|
52
|
+
),
|
|
53
|
+
);
|
|
54
|
+
}),
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function appRequests(client: HttpClient.HttpClient, apiUrl: string) {
|
|
59
|
+
const scoped = HttpClient.withScope(client);
|
|
60
|
+
const rates = rateLimits();
|
|
61
|
+
return Effect.fnUntraced(function* <TSchema extends Schema.Constraint>(
|
|
62
|
+
method: "GET" | "POST",
|
|
63
|
+
path: string,
|
|
64
|
+
jwt: Redacted.Redacted<string>,
|
|
65
|
+
schema: TSchema,
|
|
66
|
+
body?: unknown,
|
|
67
|
+
) {
|
|
68
|
+
let input = HttpClientRequest.make(method)(new URL(path, apiUrl).href, {
|
|
69
|
+
headers: { Accept: "application/vnd.github+json" },
|
|
70
|
+
});
|
|
71
|
+
if (body !== undefined) {
|
|
72
|
+
input = yield* HttpClientRequest.bodyJson(input, body).pipe(Effect.mapError((cause) => new GitHubAppError({ reason: "encode" }, { cause })));
|
|
73
|
+
}
|
|
74
|
+
const response = yield* authenticatedRequest(
|
|
75
|
+
scoped,
|
|
76
|
+
Option.some(jwt),
|
|
77
|
+
rates,
|
|
78
|
+
)(input).pipe(
|
|
79
|
+
Effect.mapError((cause) => {
|
|
80
|
+
if (cause._tag === "GitHubRateLimited") {
|
|
81
|
+
return cause;
|
|
82
|
+
}
|
|
83
|
+
return cause._tag === "GitHubTransportError"
|
|
84
|
+
? new GitHubAppError({ reason: "transport" }, { cause: cause.cause })
|
|
85
|
+
: new GitHubAppError({ reason: "status", status: "status" in cause ? cause.status : 403 });
|
|
86
|
+
}),
|
|
87
|
+
);
|
|
88
|
+
return yield* readBoundedJson(response, 64 * 1024, schema).pipe(
|
|
89
|
+
Effect.mapError((cause) => new GitHubAppError({ reason: cause._tag === "HttpClientError" ? "transport" : "response" }, { cause })),
|
|
90
|
+
);
|
|
91
|
+
}, Effect.scoped);
|
|
92
|
+
}
|