plato-sandbox-sdk 1.1.1

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 (203) hide show
  1. package/BaseClient.ts +28 -0
  2. package/Client.ts +1849 -0
  3. package/README.md +339 -0
  4. package/api/client/index.ts +1 -0
  5. package/api/client/requests/CreateSandboxRequest.ts +37 -0
  6. package/api/client/requests/CreateSnapshotRequest.ts +11 -0
  7. package/api/client/requests/EvaluateRequest.ts +9 -0
  8. package/api/client/requests/GetEnvironmentStateRequest.ts +11 -0
  9. package/api/client/requests/GetTestCasesRequest.ts +15 -0
  10. package/api/client/requests/LogRequest.ts +19 -0
  11. package/api/client/requests/MakeEnvironmentRequest.ts +27 -0
  12. package/api/client/requests/PostEvaluationResultRequest.ts +14 -0
  13. package/api/client/requests/ResetEnvironmentRequest.ts +11 -0
  14. package/api/client/requests/SetupRootAccessRequest.ts +14 -0
  15. package/api/client/requests/SetupSandboxRequest.ts +29 -0
  16. package/api/client/requests/StartWorkerRequest.ts +28 -0
  17. package/api/client/requests/index.ts +12 -0
  18. package/api/errors/BadRequestError.ts +17 -0
  19. package/api/errors/index.ts +1 -0
  20. package/api/index.ts +3 -0
  21. package/api/types/ActiveSessionResponse.ts +6 -0
  22. package/api/types/BackupEnvironmentResponse.ts +7 -0
  23. package/api/types/CdpUrlResponse.ts +12 -0
  24. package/api/types/CloseEnvironmentResponse.ts +6 -0
  25. package/api/types/CreateSandboxResponse.ts +14 -0
  26. package/api/types/CreateSnapshotResponse.ts +10 -0
  27. package/api/types/DeleteSandboxResponse.ts +6 -0
  28. package/api/types/Environment.ts +7 -0
  29. package/api/types/EnvironmentStateResponse.ts +11 -0
  30. package/api/types/ErrorResponse.ts +6 -0
  31. package/api/types/EvaluateResponse.ts +21 -0
  32. package/api/types/EvaluationResult.ts +9 -0
  33. package/api/types/HeartbeatResponse.ts +6 -0
  34. package/api/types/JobStatusResponse.ts +7 -0
  35. package/api/types/LogResponse.ts +5 -0
  36. package/api/types/MakeEnvironmentResponse.ts +6 -0
  37. package/api/types/OperationEvent.ts +42 -0
  38. package/api/types/PlatoConfig.ts +8 -0
  39. package/api/types/PlatoTask.ts +21 -0
  40. package/api/types/PlatoTaskMetadata.ts +20 -0
  41. package/api/types/PostEvaluationResultResponse.ts +6 -0
  42. package/api/types/ProxyUrlResponse.ts +12 -0
  43. package/api/types/ResetEnvironmentResponse.ts +13 -0
  44. package/api/types/RunningSessionsCountResponse.ts +6 -0
  45. package/api/types/Sandbox.ts +10 -0
  46. package/api/types/ScoringType.ts +7 -0
  47. package/api/types/SetupRootAccessResponse.ts +7 -0
  48. package/api/types/SetupSandboxResponse.ts +6 -0
  49. package/api/types/SimConfigCompute.ts +9 -0
  50. package/api/types/SimConfigDataset.ts +10 -0
  51. package/api/types/SimConfigListener.ts +27 -0
  52. package/api/types/SimConfigMetadata.ts +14 -0
  53. package/api/types/SimConfigService.ts +8 -0
  54. package/api/types/Simulator.ts +10 -0
  55. package/api/types/SimulatorListItem.ts +7 -0
  56. package/api/types/SshInfo.ts +18 -0
  57. package/api/types/StartWorkerResponse.ts +7 -0
  58. package/api/types/TaskMetadata.ts +20 -0
  59. package/api/types/TestCase.ts +40 -0
  60. package/api/types/TestCasesResponse.ts +10 -0
  61. package/api/types/Variable.ts +6 -0
  62. package/api/types/WorkerReadyResponse.ts +9 -0
  63. package/api/types/index.ts +42 -0
  64. package/core/fetcher/APIResponse.ts +23 -0
  65. package/core/fetcher/BinaryResponse.ts +36 -0
  66. package/core/fetcher/EndpointMetadata.ts +13 -0
  67. package/core/fetcher/EndpointSupplier.ts +14 -0
  68. package/core/fetcher/Fetcher.ts +165 -0
  69. package/core/fetcher/Headers.ts +93 -0
  70. package/core/fetcher/HttpResponsePromise.ts +116 -0
  71. package/core/fetcher/RawResponse.ts +61 -0
  72. package/core/fetcher/ResponseWithBody.ts +7 -0
  73. package/core/fetcher/Supplier.ts +11 -0
  74. package/core/fetcher/createRequestUrl.ts +6 -0
  75. package/core/fetcher/getErrorResponseBody.ts +33 -0
  76. package/core/fetcher/getFetchFn.ts +3 -0
  77. package/core/fetcher/getHeader.ts +8 -0
  78. package/core/fetcher/getRequestBody.ts +16 -0
  79. package/core/fetcher/getResponseBody.ts +43 -0
  80. package/core/fetcher/index.ts +11 -0
  81. package/core/fetcher/makeRequest.ts +44 -0
  82. package/core/fetcher/requestWithRetries.ts +73 -0
  83. package/core/fetcher/signals.ts +38 -0
  84. package/core/headers.ts +33 -0
  85. package/core/index.ts +4 -0
  86. package/core/json.ts +27 -0
  87. package/core/runtime/index.ts +1 -0
  88. package/core/runtime/runtime.ts +133 -0
  89. package/core/stream/Stream.ts +155 -0
  90. package/core/stream/index.ts +1 -0
  91. package/core/url/encodePathParam.ts +18 -0
  92. package/core/url/index.ts +3 -0
  93. package/core/url/join.ts +80 -0
  94. package/core/url/qs.ts +74 -0
  95. package/dist/BaseClient.d.ts +25 -0
  96. package/dist/Client.d.ts +376 -0
  97. package/dist/api/client/index.d.ts +2 -0
  98. package/dist/api/client/requests/CreateSandboxRequest.d.ts +35 -0
  99. package/dist/api/client/requests/CreateSnapshotRequest.d.ts +10 -0
  100. package/dist/api/client/requests/EvaluateRequest.d.ts +8 -0
  101. package/dist/api/client/requests/GetEnvironmentStateRequest.d.ts +10 -0
  102. package/dist/api/client/requests/GetTestCasesRequest.d.ts +14 -0
  103. package/dist/api/client/requests/LogRequest.d.ts +18 -0
  104. package/dist/api/client/requests/MakeEnvironmentRequest.d.ts +26 -0
  105. package/dist/api/client/requests/PostEvaluationResultRequest.d.ts +13 -0
  106. package/dist/api/client/requests/ResetEnvironmentRequest.d.ts +10 -0
  107. package/dist/api/client/requests/SetupRootAccessRequest.d.ts +13 -0
  108. package/dist/api/client/requests/SetupSandboxRequest.d.ts +27 -0
  109. package/dist/api/client/requests/StartWorkerRequest.d.ts +26 -0
  110. package/dist/api/client/requests/index.d.ts +13 -0
  111. package/dist/api/errors/BadRequestError.d.ts +7 -0
  112. package/dist/api/errors/index.d.ts +2 -0
  113. package/dist/api/index.d.ts +4 -0
  114. package/dist/api/types/ActiveSessionResponse.d.ts +5 -0
  115. package/dist/api/types/BackupEnvironmentResponse.d.ts +6 -0
  116. package/dist/api/types/CdpUrlResponse.d.ts +10 -0
  117. package/dist/api/types/CloseEnvironmentResponse.d.ts +5 -0
  118. package/dist/api/types/CreateSandboxResponse.d.ts +13 -0
  119. package/dist/api/types/CreateSnapshotResponse.d.ts +9 -0
  120. package/dist/api/types/DeleteSandboxResponse.d.ts +5 -0
  121. package/dist/api/types/Environment.d.ts +6 -0
  122. package/dist/api/types/EnvironmentStateResponse.d.ts +9 -0
  123. package/dist/api/types/ErrorResponse.d.ts +5 -0
  124. package/dist/api/types/EvaluateResponse.d.ts +18 -0
  125. package/dist/api/types/EvaluationResult.d.ts +8 -0
  126. package/dist/api/types/HeartbeatResponse.d.ts +5 -0
  127. package/dist/api/types/JobStatusResponse.d.ts +6 -0
  128. package/dist/api/types/LogResponse.d.ts +4 -0
  129. package/dist/api/types/MakeEnvironmentResponse.d.ts +5 -0
  130. package/dist/api/types/OperationEvent.d.ts +40 -0
  131. package/dist/api/types/PlatoConfig.d.ts +6 -0
  132. package/dist/api/types/PlatoTask.d.ts +19 -0
  133. package/dist/api/types/PlatoTaskMetadata.d.ts +18 -0
  134. package/dist/api/types/PostEvaluationResultResponse.d.ts +5 -0
  135. package/dist/api/types/ProxyUrlResponse.d.ts +10 -0
  136. package/dist/api/types/ResetEnvironmentResponse.d.ts +11 -0
  137. package/dist/api/types/RunningSessionsCountResponse.d.ts +5 -0
  138. package/dist/api/types/Sandbox.d.ts +9 -0
  139. package/dist/api/types/ScoringType.d.ts +6 -0
  140. package/dist/api/types/SetupRootAccessResponse.d.ts +6 -0
  141. package/dist/api/types/SetupSandboxResponse.d.ts +5 -0
  142. package/dist/api/types/SimConfigCompute.d.ts +8 -0
  143. package/dist/api/types/SimConfigDataset.d.ts +8 -0
  144. package/dist/api/types/SimConfigListener.d.ts +25 -0
  145. package/dist/api/types/SimConfigMetadata.d.ts +12 -0
  146. package/dist/api/types/SimConfigService.d.ts +7 -0
  147. package/dist/api/types/Simulator.d.ts +9 -0
  148. package/dist/api/types/SimulatorListItem.d.ts +6 -0
  149. package/dist/api/types/SshInfo.d.ts +17 -0
  150. package/dist/api/types/StartWorkerResponse.d.ts +6 -0
  151. package/dist/api/types/TaskMetadata.d.ts +18 -0
  152. package/dist/api/types/TestCase.d.ts +34 -0
  153. package/dist/api/types/TestCasesResponse.d.ts +8 -0
  154. package/dist/api/types/Variable.d.ts +5 -0
  155. package/dist/api/types/WorkerReadyResponse.d.ts +8 -0
  156. package/dist/api/types/index.d.ts +43 -0
  157. package/dist/core/fetcher/APIResponse.d.ts +21 -0
  158. package/dist/core/fetcher/BinaryResponse.d.ts +21 -0
  159. package/dist/core/fetcher/EndpointMetadata.d.ts +14 -0
  160. package/dist/core/fetcher/EndpointSupplier.d.ts +13 -0
  161. package/dist/core/fetcher/Fetcher.d.ts +43 -0
  162. package/dist/core/fetcher/Headers.d.ts +3 -0
  163. package/dist/core/fetcher/HttpResponsePromise.d.ts +59 -0
  164. package/dist/core/fetcher/RawResponse.d.ts +30 -0
  165. package/dist/core/fetcher/ResponseWithBody.d.ts +5 -0
  166. package/dist/core/fetcher/Supplier.d.ts +5 -0
  167. package/dist/core/fetcher/createRequestUrl.d.ts +2 -0
  168. package/dist/core/fetcher/getErrorResponseBody.d.ts +2 -0
  169. package/dist/core/fetcher/getFetchFn.d.ts +2 -0
  170. package/dist/core/fetcher/getHeader.d.ts +2 -0
  171. package/dist/core/fetcher/getRequestBody.d.ts +8 -0
  172. package/dist/core/fetcher/getResponseBody.d.ts +2 -0
  173. package/dist/core/fetcher/index.d.ts +12 -0
  174. package/dist/core/fetcher/makeRequest.d.ts +2 -0
  175. package/dist/core/fetcher/requestWithRetries.d.ts +2 -0
  176. package/dist/core/fetcher/signals.d.ts +12 -0
  177. package/dist/core/headers.d.ts +3 -0
  178. package/dist/core/index.d.ts +5 -0
  179. package/dist/core/json.d.ts +16 -0
  180. package/dist/core/runtime/index.d.ts +2 -0
  181. package/dist/core/runtime/runtime.d.ts +10 -0
  182. package/dist/core/stream/Stream.d.ts +48 -0
  183. package/dist/core/stream/index.d.ts +2 -0
  184. package/dist/core/url/encodePathParam.d.ts +2 -0
  185. package/dist/core/url/index.d.ts +4 -0
  186. package/dist/core/url/join.d.ts +2 -0
  187. package/dist/core/url/qs.d.ts +7 -0
  188. package/dist/errors/ApiError.d.ts +13 -0
  189. package/dist/errors/ApiTimeoutError.d.ts +4 -0
  190. package/dist/errors/index.d.ts +3 -0
  191. package/dist/helpers/SandboxHelpers.d.ts +127 -0
  192. package/dist/helpers/SandboxMonitor.d.ts +89 -0
  193. package/dist/helpers/index.d.ts +9 -0
  194. package/dist/index.d.ts +6 -0
  195. package/errors/ApiError.ts +53 -0
  196. package/errors/ApiTimeoutError.ts +8 -0
  197. package/errors/index.ts +2 -0
  198. package/helpers/README.md +229 -0
  199. package/helpers/SandboxHelpers.ts +213 -0
  200. package/helpers/SandboxMonitor.ts +252 -0
  201. package/helpers/index.ts +10 -0
  202. package/index.ts +7 -0
  203. package/package.json +54 -0
@@ -0,0 +1,165 @@
1
+ import { toJson } from "../json.js";
2
+ import type { APIResponse } from "./APIResponse.js";
3
+ import { createRequestUrl } from "./createRequestUrl.js";
4
+ import type { EndpointMetadata } from "./EndpointMetadata.js";
5
+ import { EndpointSupplier } from "./EndpointSupplier.js";
6
+ import { getErrorResponseBody } from "./getErrorResponseBody.js";
7
+ import { getFetchFn } from "./getFetchFn.js";
8
+ import { getRequestBody } from "./getRequestBody.js";
9
+ import { getResponseBody } from "./getResponseBody.js";
10
+ import { makeRequest } from "./makeRequest.js";
11
+ import { abortRawResponse, toRawResponse, unknownRawResponse } from "./RawResponse.js";
12
+ import { requestWithRetries } from "./requestWithRetries.js";
13
+
14
+ export type FetchFunction = <R = unknown>(args: Fetcher.Args) => Promise<APIResponse<R, Fetcher.Error>>;
15
+
16
+ export declare namespace Fetcher {
17
+ export interface Args {
18
+ url: string;
19
+ method: string;
20
+ contentType?: string;
21
+ headers?: Record<string, string | EndpointSupplier<string | null | undefined> | null | undefined>;
22
+ queryParameters?: Record<string, unknown>;
23
+ body?: unknown;
24
+ timeoutMs?: number;
25
+ maxRetries?: number;
26
+ withCredentials?: boolean;
27
+ abortSignal?: AbortSignal;
28
+ requestType?: "json" | "file" | "bytes";
29
+ responseType?: "json" | "blob" | "sse" | "streaming" | "text" | "arrayBuffer" | "binary-response";
30
+ duplex?: "half";
31
+ endpointMetadata?: EndpointMetadata;
32
+ }
33
+
34
+ export type Error = FailedStatusCodeError | NonJsonError | TimeoutError | UnknownError;
35
+
36
+ export interface FailedStatusCodeError {
37
+ reason: "status-code";
38
+ statusCode: number;
39
+ body: unknown;
40
+ }
41
+
42
+ export interface NonJsonError {
43
+ reason: "non-json";
44
+ statusCode: number;
45
+ rawBody: string;
46
+ }
47
+
48
+ export interface TimeoutError {
49
+ reason: "timeout";
50
+ }
51
+
52
+ export interface UnknownError {
53
+ reason: "unknown";
54
+ errorMessage: string;
55
+ }
56
+ }
57
+
58
+ async function getHeaders(args: Fetcher.Args): Promise<Record<string, string>> {
59
+ const newHeaders: Record<string, string> = {};
60
+ if (args.body !== undefined && args.contentType != null) {
61
+ newHeaders["Content-Type"] = args.contentType;
62
+ }
63
+
64
+ if (args.headers == null) {
65
+ return newHeaders;
66
+ }
67
+
68
+ for (const [key, value] of Object.entries(args.headers)) {
69
+ const result = await EndpointSupplier.get(value, { endpointMetadata: args.endpointMetadata ?? {} });
70
+ if (typeof result === "string") {
71
+ newHeaders[key] = result;
72
+ continue;
73
+ }
74
+ if (result == null) {
75
+ continue;
76
+ }
77
+ newHeaders[key] = `${result}`;
78
+ }
79
+ return newHeaders;
80
+ }
81
+
82
+ export async function fetcherImpl<R = unknown>(args: Fetcher.Args): Promise<APIResponse<R, Fetcher.Error>> {
83
+ const url = createRequestUrl(args.url, args.queryParameters);
84
+ const requestBody: BodyInit | undefined = await getRequestBody({
85
+ body: args.body,
86
+ type: args.requestType === "json" ? "json" : "other",
87
+ });
88
+ const fetchFn = await getFetchFn();
89
+
90
+ try {
91
+ const response = await requestWithRetries(
92
+ async () =>
93
+ makeRequest(
94
+ fetchFn,
95
+ url,
96
+ args.method,
97
+ await getHeaders(args),
98
+ requestBody,
99
+ args.timeoutMs,
100
+ args.abortSignal,
101
+ args.withCredentials,
102
+ args.duplex,
103
+ ),
104
+ args.maxRetries,
105
+ );
106
+
107
+ if (response.status >= 200 && response.status < 400) {
108
+ return {
109
+ ok: true,
110
+ body: (await getResponseBody(response, args.responseType)) as R,
111
+ headers: response.headers,
112
+ rawResponse: toRawResponse(response),
113
+ };
114
+ } else {
115
+ return {
116
+ ok: false,
117
+ error: {
118
+ reason: "status-code",
119
+ statusCode: response.status,
120
+ body: await getErrorResponseBody(response),
121
+ },
122
+ rawResponse: toRawResponse(response),
123
+ };
124
+ }
125
+ } catch (error) {
126
+ if (args.abortSignal?.aborted) {
127
+ return {
128
+ ok: false,
129
+ error: {
130
+ reason: "unknown",
131
+ errorMessage: "The user aborted a request",
132
+ },
133
+ rawResponse: abortRawResponse,
134
+ };
135
+ } else if (error instanceof Error && error.name === "AbortError") {
136
+ return {
137
+ ok: false,
138
+ error: {
139
+ reason: "timeout",
140
+ },
141
+ rawResponse: abortRawResponse,
142
+ };
143
+ } else if (error instanceof Error) {
144
+ return {
145
+ ok: false,
146
+ error: {
147
+ reason: "unknown",
148
+ errorMessage: error.message,
149
+ },
150
+ rawResponse: unknownRawResponse,
151
+ };
152
+ }
153
+
154
+ return {
155
+ ok: false,
156
+ error: {
157
+ reason: "unknown",
158
+ errorMessage: toJson(error),
159
+ },
160
+ rawResponse: unknownRawResponse,
161
+ };
162
+ }
163
+ }
164
+
165
+ export const fetcher: FetchFunction = fetcherImpl;
@@ -0,0 +1,93 @@
1
+ let Headers: typeof globalThis.Headers;
2
+
3
+ if (typeof globalThis.Headers !== "undefined") {
4
+ Headers = globalThis.Headers;
5
+ } else {
6
+ Headers = class Headers implements Headers {
7
+ private headers: Map<string, string[]>;
8
+
9
+ constructor(init?: HeadersInit) {
10
+ this.headers = new Map();
11
+
12
+ if (init) {
13
+ if (init instanceof Headers) {
14
+ init.forEach((value, key) => this.append(key, value));
15
+ } else if (Array.isArray(init)) {
16
+ for (const [key, value] of init) {
17
+ if (typeof key === "string" && typeof value === "string") {
18
+ this.append(key, value);
19
+ } else {
20
+ throw new TypeError("Each header entry must be a [string, string] tuple");
21
+ }
22
+ }
23
+ } else {
24
+ for (const [key, value] of Object.entries(init)) {
25
+ if (typeof value === "string") {
26
+ this.append(key, value);
27
+ } else {
28
+ throw new TypeError("Header values must be strings");
29
+ }
30
+ }
31
+ }
32
+ }
33
+ }
34
+
35
+ append(name: string, value: string): void {
36
+ const key = name.toLowerCase();
37
+ const existing = this.headers.get(key) || [];
38
+ this.headers.set(key, [...existing, value]);
39
+ }
40
+
41
+ delete(name: string): void {
42
+ const key = name.toLowerCase();
43
+ this.headers.delete(key);
44
+ }
45
+
46
+ get(name: string): string | null {
47
+ const key = name.toLowerCase();
48
+ const values = this.headers.get(key);
49
+ return values ? values.join(", ") : null;
50
+ }
51
+
52
+ has(name: string): boolean {
53
+ const key = name.toLowerCase();
54
+ return this.headers.has(key);
55
+ }
56
+
57
+ set(name: string, value: string): void {
58
+ const key = name.toLowerCase();
59
+ this.headers.set(key, [value]);
60
+ }
61
+
62
+ forEach(callbackfn: (value: string, key: string, parent: Headers) => void, thisArg?: unknown): void {
63
+ const boundCallback = thisArg ? callbackfn.bind(thisArg) : callbackfn;
64
+ this.headers.forEach((values, key) => boundCallback(values.join(", "), key, this));
65
+ }
66
+
67
+ getSetCookie(): string[] {
68
+ return this.headers.get("set-cookie") || [];
69
+ }
70
+
71
+ *entries(): HeadersIterator<[string, string]> {
72
+ for (const [key, values] of this.headers.entries()) {
73
+ yield [key, values.join(", ")];
74
+ }
75
+ }
76
+
77
+ *keys(): HeadersIterator<string> {
78
+ yield* this.headers.keys();
79
+ }
80
+
81
+ *values(): HeadersIterator<string> {
82
+ for (const values of this.headers.values()) {
83
+ yield values.join(", ");
84
+ }
85
+ }
86
+
87
+ [Symbol.iterator](): HeadersIterator<[string, string]> {
88
+ return this.entries();
89
+ }
90
+ };
91
+ }
92
+
93
+ export { Headers };
@@ -0,0 +1,116 @@
1
+ import type { WithRawResponse } from "./RawResponse.js";
2
+
3
+ /**
4
+ * A promise that returns the parsed response and lets you retrieve the raw response too.
5
+ */
6
+ export class HttpResponsePromise<T> extends Promise<T> {
7
+ private innerPromise: Promise<WithRawResponse<T>>;
8
+ private unwrappedPromise: Promise<T> | undefined;
9
+
10
+ private constructor(promise: Promise<WithRawResponse<T>>) {
11
+ // Initialize with a no-op to avoid premature parsing
12
+ super((resolve) => {
13
+ resolve(undefined as unknown as T);
14
+ });
15
+ this.innerPromise = promise;
16
+ }
17
+
18
+ /**
19
+ * Creates an `HttpResponsePromise` from a function that returns a promise.
20
+ *
21
+ * @param fn - A function that returns a promise resolving to a `WithRawResponse` object.
22
+ * @param args - Arguments to pass to the function.
23
+ * @returns An `HttpResponsePromise` instance.
24
+ */
25
+ public static fromFunction<F extends (...args: never[]) => Promise<WithRawResponse<T>>, T>(
26
+ fn: F,
27
+ ...args: Parameters<F>
28
+ ): HttpResponsePromise<T> {
29
+ return new HttpResponsePromise<T>(fn(...args));
30
+ }
31
+
32
+ /**
33
+ * Creates a function that returns an `HttpResponsePromise` from a function that returns a promise.
34
+ *
35
+ * @param fn - A function that returns a promise resolving to a `WithRawResponse` object.
36
+ * @returns A function that returns an `HttpResponsePromise` instance.
37
+ */
38
+ public static interceptFunction<
39
+ F extends (...args: never[]) => Promise<WithRawResponse<T>>,
40
+ T = Awaited<ReturnType<F>>["data"],
41
+ >(fn: F): (...args: Parameters<F>) => HttpResponsePromise<T> {
42
+ return (...args: Parameters<F>): HttpResponsePromise<T> => {
43
+ return HttpResponsePromise.fromPromise<T>(fn(...args));
44
+ };
45
+ }
46
+
47
+ /**
48
+ * Creates an `HttpResponsePromise` from an existing promise.
49
+ *
50
+ * @param promise - A promise resolving to a `WithRawResponse` object.
51
+ * @returns An `HttpResponsePromise` instance.
52
+ */
53
+ public static fromPromise<T>(promise: Promise<WithRawResponse<T>>): HttpResponsePromise<T> {
54
+ return new HttpResponsePromise<T>(promise);
55
+ }
56
+
57
+ /**
58
+ * Creates an `HttpResponsePromise` from an executor function.
59
+ *
60
+ * @param executor - A function that takes resolve and reject callbacks to create a promise.
61
+ * @returns An `HttpResponsePromise` instance.
62
+ */
63
+ public static fromExecutor<T>(
64
+ executor: (resolve: (value: WithRawResponse<T>) => void, reject: (reason?: unknown) => void) => void,
65
+ ): HttpResponsePromise<T> {
66
+ const promise = new Promise<WithRawResponse<T>>(executor);
67
+ return new HttpResponsePromise<T>(promise);
68
+ }
69
+
70
+ /**
71
+ * Creates an `HttpResponsePromise` from a resolved result.
72
+ *
73
+ * @param result - A `WithRawResponse` object to resolve immediately.
74
+ * @returns An `HttpResponsePromise` instance.
75
+ */
76
+ public static fromResult<T>(result: WithRawResponse<T>): HttpResponsePromise<T> {
77
+ const promise = Promise.resolve(result);
78
+ return new HttpResponsePromise<T>(promise);
79
+ }
80
+
81
+ private unwrap(): Promise<T> {
82
+ if (!this.unwrappedPromise) {
83
+ this.unwrappedPromise = this.innerPromise.then(({ data }) => data);
84
+ }
85
+ return this.unwrappedPromise;
86
+ }
87
+
88
+ /** @inheritdoc */
89
+ public override then<TResult1 = T, TResult2 = never>(
90
+ onfulfilled?: ((value: T) => TResult1 | PromiseLike<TResult1>) | null,
91
+ onrejected?: ((reason: unknown) => TResult2 | PromiseLike<TResult2>) | null,
92
+ ): Promise<TResult1 | TResult2> {
93
+ return this.unwrap().then(onfulfilled, onrejected);
94
+ }
95
+
96
+ /** @inheritdoc */
97
+ public override catch<TResult = never>(
98
+ onrejected?: ((reason: unknown) => TResult | PromiseLike<TResult>) | null,
99
+ ): Promise<T | TResult> {
100
+ return this.unwrap().catch(onrejected);
101
+ }
102
+
103
+ /** @inheritdoc */
104
+ public override finally(onfinally?: (() => void) | null): Promise<T> {
105
+ return this.unwrap().finally(onfinally);
106
+ }
107
+
108
+ /**
109
+ * Retrieves the data and raw response.
110
+ *
111
+ * @returns A promise resolving to a `WithRawResponse` object.
112
+ */
113
+ public async withRawResponse(): Promise<WithRawResponse<T>> {
114
+ return await this.innerPromise;
115
+ }
116
+ }
@@ -0,0 +1,61 @@
1
+ import { Headers } from "./Headers.js";
2
+
3
+ /**
4
+ * The raw response from the fetch call excluding the body.
5
+ */
6
+ export type RawResponse = Omit<
7
+ {
8
+ [K in keyof Response as Response[K] extends Function ? never : K]: Response[K]; // strips out functions
9
+ },
10
+ "ok" | "body" | "bodyUsed"
11
+ >; // strips out body and bodyUsed
12
+
13
+ /**
14
+ * A raw response indicating that the request was aborted.
15
+ */
16
+ export const abortRawResponse: RawResponse = {
17
+ headers: new Headers(),
18
+ redirected: false,
19
+ status: 499,
20
+ statusText: "Client Closed Request",
21
+ type: "error",
22
+ url: "",
23
+ } as const;
24
+
25
+ /**
26
+ * A raw response indicating an unknown error.
27
+ */
28
+ export const unknownRawResponse: RawResponse = {
29
+ headers: new Headers(),
30
+ redirected: false,
31
+ status: 0,
32
+ statusText: "Unknown Error",
33
+ type: "error",
34
+ url: "",
35
+ } as const;
36
+
37
+ /**
38
+ * Converts a `RawResponse` object into a `RawResponse` by extracting its properties,
39
+ * excluding the `body` and `bodyUsed` fields.
40
+ *
41
+ * @param response - The `RawResponse` object to convert.
42
+ * @returns A `RawResponse` object containing the extracted properties of the input response.
43
+ */
44
+ export function toRawResponse(response: Response): RawResponse {
45
+ return {
46
+ headers: response.headers,
47
+ redirected: response.redirected,
48
+ status: response.status,
49
+ statusText: response.statusText,
50
+ type: response.type,
51
+ url: response.url,
52
+ };
53
+ }
54
+
55
+ /**
56
+ * Creates a `RawResponse` from a standard `Response` object.
57
+ */
58
+ export interface WithRawResponse<T> {
59
+ readonly data: T;
60
+ readonly rawResponse: RawResponse;
61
+ }
@@ -0,0 +1,7 @@
1
+ export type ResponseWithBody = Response & {
2
+ body: ReadableStream<Uint8Array>;
3
+ };
4
+
5
+ export function isResponseWithBody(response: Response): response is ResponseWithBody {
6
+ return (response as ResponseWithBody).body != null;
7
+ }
@@ -0,0 +1,11 @@
1
+ export type Supplier<T> = T | Promise<T> | (() => T | Promise<T>);
2
+
3
+ export const Supplier = {
4
+ get: async <T>(supplier: Supplier<T>): Promise<T> => {
5
+ if (typeof supplier === "function") {
6
+ return (supplier as () => T)();
7
+ } else {
8
+ return supplier;
9
+ }
10
+ },
11
+ };
@@ -0,0 +1,6 @@
1
+ import { toQueryString } from "../url/qs.js";
2
+
3
+ export function createRequestUrl(baseUrl: string, queryParameters?: Record<string, unknown>): string {
4
+ const queryString = toQueryString(queryParameters, { arrayFormat: "repeat" });
5
+ return queryString ? `${baseUrl}?${queryString}` : baseUrl;
6
+ }
@@ -0,0 +1,33 @@
1
+ import { fromJson } from "../json.js";
2
+ import { getResponseBody } from "./getResponseBody.js";
3
+
4
+ export async function getErrorResponseBody(response: Response): Promise<unknown> {
5
+ let contentType = response.headers.get("Content-Type")?.toLowerCase();
6
+ if (contentType == null || contentType.length === 0) {
7
+ return getResponseBody(response);
8
+ }
9
+
10
+ if (contentType.indexOf(";") !== -1) {
11
+ contentType = contentType.split(";")[0]?.trim() ?? "";
12
+ }
13
+ switch (contentType) {
14
+ case "application/hal+json":
15
+ case "application/json":
16
+ case "application/ld+json":
17
+ case "application/problem+json":
18
+ case "application/vnd.api+json":
19
+ case "text/json": {
20
+ const text = await response.text();
21
+ return text.length > 0 ? fromJson(text) : undefined;
22
+ }
23
+ default:
24
+ if (contentType.startsWith("application/vnd.") && contentType.endsWith("+json")) {
25
+ const text = await response.text();
26
+ return text.length > 0 ? fromJson(text) : undefined;
27
+ }
28
+
29
+ // Fallback to plain text if content type is not recognized
30
+ // Even if no body is present, the response will be an empty string
31
+ return await response.text();
32
+ }
33
+ }
@@ -0,0 +1,3 @@
1
+ export async function getFetchFn(): Promise<typeof fetch> {
2
+ return fetch;
3
+ }
@@ -0,0 +1,8 @@
1
+ export function getHeader(headers: Record<string, any>, header: string): string | undefined {
2
+ for (const [headerKey, headerValue] of Object.entries(headers)) {
3
+ if (headerKey.toLowerCase() === header.toLowerCase()) {
4
+ return headerValue;
5
+ }
6
+ }
7
+ return undefined;
8
+ }
@@ -0,0 +1,16 @@
1
+ import { toJson } from "../json.js";
2
+
3
+ export declare namespace GetRequestBody {
4
+ interface Args {
5
+ body: unknown;
6
+ type: "json" | "file" | "bytes" | "other";
7
+ }
8
+ }
9
+
10
+ export async function getRequestBody({ body, type }: GetRequestBody.Args): Promise<BodyInit | undefined> {
11
+ if (type.includes("json")) {
12
+ return toJson(body);
13
+ } else {
14
+ return body as BodyInit;
15
+ }
16
+ }
@@ -0,0 +1,43 @@
1
+ import { fromJson } from "../json.js";
2
+ import { getBinaryResponse } from "./BinaryResponse.js";
3
+ import { isResponseWithBody } from "./ResponseWithBody.js";
4
+
5
+ export async function getResponseBody(response: Response, responseType?: string): Promise<unknown> {
6
+ if (!isResponseWithBody(response)) {
7
+ return undefined;
8
+ }
9
+ switch (responseType) {
10
+ case "binary-response":
11
+ return getBinaryResponse(response);
12
+ case "blob":
13
+ return await response.blob();
14
+ case "arrayBuffer":
15
+ return await response.arrayBuffer();
16
+ case "sse":
17
+ return response.body;
18
+ case "streaming":
19
+ return response.body;
20
+
21
+ case "text":
22
+ return await response.text();
23
+ }
24
+
25
+ // if responseType is "json" or not specified, try to parse as JSON
26
+ const text = await response.text();
27
+ if (text.length > 0) {
28
+ try {
29
+ const responseBody = fromJson(text);
30
+ return responseBody;
31
+ } catch (_err) {
32
+ return {
33
+ ok: false,
34
+ error: {
35
+ reason: "non-json",
36
+ statusCode: response.status,
37
+ rawBody: text,
38
+ },
39
+ };
40
+ }
41
+ }
42
+ return undefined;
43
+ }
@@ -0,0 +1,11 @@
1
+ export type { APIResponse } from "./APIResponse.js";
2
+ export type { BinaryResponse } from "./BinaryResponse.js";
3
+ export type { EndpointMetadata } from "./EndpointMetadata.js";
4
+ export { EndpointSupplier } from "./EndpointSupplier.js";
5
+ export type { Fetcher, FetchFunction } from "./Fetcher.js";
6
+ export { fetcher } from "./Fetcher.js";
7
+ export { getHeader } from "./getHeader.js";
8
+ export { HttpResponsePromise } from "./HttpResponsePromise.js";
9
+ export type { RawResponse, WithRawResponse } from "./RawResponse.js";
10
+ export { abortRawResponse, toRawResponse, unknownRawResponse } from "./RawResponse.js";
11
+ export { Supplier } from "./Supplier.js";
@@ -0,0 +1,44 @@
1
+ import { anySignal, getTimeoutSignal } from "./signals.js";
2
+
3
+ export const makeRequest = async (
4
+ fetchFn: (url: string, init: RequestInit) => Promise<Response>,
5
+ url: string,
6
+ method: string,
7
+ headers: Record<string, string>,
8
+ requestBody: BodyInit | undefined,
9
+ timeoutMs?: number,
10
+ abortSignal?: AbortSignal,
11
+ withCredentials?: boolean,
12
+ duplex?: "half",
13
+ ): Promise<Response> => {
14
+ const signals: AbortSignal[] = [];
15
+
16
+ // Add timeout signal
17
+ let timeoutAbortId: NodeJS.Timeout | undefined;
18
+ if (timeoutMs != null) {
19
+ const { signal, abortId } = getTimeoutSignal(timeoutMs);
20
+ timeoutAbortId = abortId;
21
+ signals.push(signal);
22
+ }
23
+
24
+ // Add arbitrary signal
25
+ if (abortSignal != null) {
26
+ signals.push(abortSignal);
27
+ }
28
+ const newSignals = anySignal(signals);
29
+ const response = await fetchFn(url, {
30
+ method: method,
31
+ headers,
32
+ body: requestBody,
33
+ signal: newSignals,
34
+ credentials: withCredentials ? "include" : undefined,
35
+ // @ts-ignore
36
+ duplex,
37
+ });
38
+
39
+ if (timeoutAbortId != null) {
40
+ clearTimeout(timeoutAbortId);
41
+ }
42
+
43
+ return response;
44
+ };