@amerged/ohmyhost-runtime 0.1.36 → 0.1.38

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.
@@ -28,6 +28,7 @@ const MAX_HEADER_BYTES = 2048;
28
28
  async function timedRead(
29
29
  reader: ReadableStreamDefaultReader<Uint8Array>,
30
30
  timeoutMilliseconds: number,
31
+ status: number,
31
32
  signal?: AbortSignal,
32
33
  ): Promise<ReadableStreamReadResult<Uint8Array>> {
33
34
  let timer: ReturnType<typeof setTimeout> | undefined;
@@ -35,13 +36,13 @@ async function timedRead(
35
36
  try {
36
37
  if (signal?.aborted) {
37
38
  void reader.cancel().catch(() => undefined);
38
- throw new CustomerRuntimeGatewayError("gateway_timeout", null, true);
39
+ throw new CustomerRuntimeGatewayError("gateway_timeout", status, true);
39
40
  }
40
41
  return await Promise.race([
41
42
  reader.read(),
42
43
  new Promise<never>((_resolve, reject) => {
43
44
  const abort = () => {
44
- reject(new CustomerRuntimeGatewayError("gateway_timeout", null, true));
45
+ reject(new CustomerRuntimeGatewayError("gateway_timeout", status, true));
45
46
  void reader.cancel().catch(() => undefined);
46
47
  };
47
48
  timer = setTimeout(abort, timeoutMilliseconds);
@@ -53,8 +54,8 @@ async function timedRead(
53
54
  ]);
54
55
  } catch (error) {
55
56
  if (error instanceof CustomerRuntimeGatewayError) throw error;
56
- if (signal?.aborted) throw new CustomerRuntimeGatewayError("gateway_timeout", null, true);
57
- throw new CustomerRuntimeGatewayError("gateway_unavailable", null, true);
57
+ if (signal?.aborted) throw new CustomerRuntimeGatewayError("gateway_timeout", status, true);
58
+ throw new CustomerRuntimeGatewayError("gateway_unavailable", status, true);
58
59
  } finally {
59
60
  clearTimeout(timer);
60
61
  if (abortListener !== undefined) signal?.removeEventListener("abort", abortListener);
@@ -72,7 +73,7 @@ export function decodeStorageBatch(
72
73
  response.body === null
73
74
  ) {
74
75
  void response.body?.cancel().catch(() => undefined);
75
- invalidResponse();
76
+ invalidResponse(response.status);
76
77
  }
77
78
  const source = response.body.getReader();
78
79
  const decoder = new TextDecoder("utf-8", { fatal: true });
@@ -101,9 +102,9 @@ export function decodeStorageBatch(
101
102
  async function chunk(maximum: number): Promise<Uint8Array> {
102
103
  if (cancelled) throw cancellationError();
103
104
  while (offset === buffer.byteLength) {
104
- const next = await timedRead(source, timeoutMilliseconds);
105
+ const next = await timedRead(source, timeoutMilliseconds, response.status);
105
106
  if (cancelled) throw cancellationError();
106
- if (next.done || !(next.value instanceof Uint8Array)) invalidResponse();
107
+ if (next.done || !(next.value instanceof Uint8Array)) invalidResponse(response.status);
107
108
  buffer = next.value;
108
109
  offset = 0;
109
110
  }
@@ -126,7 +127,7 @@ export function decodeStorageBatch(
126
127
  const length = new DataView((await exactBytes(4)).buffer).getUint32(0);
127
128
  controlBytes += 4 + length;
128
129
  if (length < 1 || length > MAX_HEADER_BYTES || controlBytes > MAX_CONTROL_BYTES)
129
- invalidResponse();
130
+ invalidResponse(response.status);
130
131
  const bytes = await exactBytes(length);
131
132
  return parseHeader(bytes);
132
133
  }
@@ -135,7 +136,7 @@ export function decodeStorageBatch(
135
136
  if (buffer.byteLength - offset < 4) return undefined;
136
137
  const length = new DataView(buffer.buffer, buffer.byteOffset + offset, 4).getUint32(0);
137
138
  if (length < 1 || length > MAX_HEADER_BYTES || controlBytes + 4 + length > MAX_CONTROL_BYTES)
138
- invalidResponse();
139
+ invalidResponse(response.status);
139
140
  if (buffer.byteLength - offset < 4 + length) return undefined;
140
141
  controlBytes += 4 + length;
141
142
  const bytes = buffer.subarray(offset + 4, offset + 4 + length);
@@ -146,7 +147,7 @@ export function decodeStorageBatch(
146
147
  try {
147
148
  return JSON.parse(decoder.decode(bytes)) as unknown;
148
149
  } catch {
149
- return invalidResponse();
150
+ return invalidResponse(response.status);
150
151
  }
151
152
  }
152
153
  const iterator = iterate();
@@ -173,7 +174,7 @@ export function decodeStorageBatch(
173
174
  try {
174
175
  for (let index = 0; index < keys.length; index++) {
175
176
  const objectKey = keys[index];
176
- if (objectKey === undefined) invalidResponse();
177
+ if (objectKey === undefined) invalidResponse(response.status);
177
178
  if (!currentConsumed)
178
179
  throw new CustomerRuntimeGatewayError(
179
180
  "gateway_request_invalid",
@@ -190,8 +191,10 @@ export function decodeStorageBatch(
190
191
  candidate?.["state"] === "found"
191
192
  ? ["index", "objectKey", "state", "contentType", "contentLength", "etag"]
192
193
  : ["index", "objectKey", "state"],
194
+ response.status,
193
195
  );
194
- if (record["index"] !== index || record["objectKey"] !== keys[index]) invalidResponse();
196
+ if (record["index"] !== index || record["objectKey"] !== keys[index])
197
+ invalidResponse(response.status);
195
198
  if (record["state"] === "not_found") {
196
199
  yield Object.freeze({ state: "not_found", objectKey });
197
200
  continue;
@@ -208,7 +211,7 @@ export function decodeStorageBatch(
208
211
  typeof record["etag"] !== "string" ||
209
212
  !/^[0-9a-f]{32}(?:-[1-9][0-9]{0,4})?$/u.test(record["etag"])
210
213
  )
211
- invalidResponse();
214
+ invalidResponse(response.status);
212
215
  let remaining = Number(record["contentLength"]);
213
216
  currentConsumed = remaining === 0;
214
217
  const body = new ReadableStream<Uint8Array>(
@@ -268,18 +271,19 @@ export function decodeStorageBatch(
268
271
  );
269
272
  if (bodyFailure !== undefined) throw bodyFailure;
270
273
  const buffered = bufferedHeader();
271
- const terminal = exactRecord(buffered === undefined ? await header() : buffered, [
272
- "state",
273
- "count",
274
- ]);
274
+ const terminal = exactRecord(
275
+ buffered === undefined ? await header() : buffered,
276
+ ["state", "count"],
277
+ response.status,
278
+ );
275
279
  if (
276
280
  terminal["state"] !== "completed" ||
277
281
  terminal["count"] !== keys.length ||
278
282
  offset !== buffer.byteLength
279
283
  )
280
- invalidResponse();
281
- const end = await timedRead(source, timeoutMilliseconds);
282
- if (!end.done) invalidResponse();
284
+ invalidResponse(response.status);
285
+ const end = await timedRead(source, timeoutMilliseconds, response.status);
286
+ if (!end.done) invalidResponse(response.status);
283
287
  if (cancelled) throw cancellationError();
284
288
  finished = true;
285
289
  } finally {
@@ -303,7 +307,7 @@ export async function readStorageBatchJson(
303
307
  response.body === null
304
308
  ) {
305
309
  void response.body?.cancel().catch(() => undefined);
306
- invalidResponse();
310
+ invalidResponse(response.status);
307
311
  }
308
312
  const reader = response.body.getReader();
309
313
  const chunks: Uint8Array[] = [];
@@ -311,14 +315,14 @@ export async function readStorageBatchJson(
311
315
  let finished = false;
312
316
  try {
313
317
  for (;;) {
314
- const next = await timedRead(reader, timeoutMilliseconds, signal);
318
+ const next = await timedRead(reader, timeoutMilliseconds, response.status, signal);
315
319
  if (next.done) {
316
320
  finished = true;
317
321
  break;
318
322
  }
319
- if (!(next.value instanceof Uint8Array)) invalidResponse();
323
+ if (!(next.value instanceof Uint8Array)) invalidResponse(response.status);
320
324
  length += next.value.byteLength;
321
- if (length > MAX_CONTROL_BYTES) invalidResponse();
325
+ if (length > MAX_CONTROL_BYTES) invalidResponse(response.status);
322
326
  chunks.push(next.value);
323
327
  }
324
328
  } finally {
@@ -334,6 +338,6 @@ export async function readStorageBatchJson(
334
338
  try {
335
339
  return JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(bytes)) as unknown;
336
340
  } catch {
337
- return invalidResponse();
341
+ return invalidResponse(response.status);
338
342
  }
339
343
  }
@@ -7,6 +7,7 @@ import {
7
7
  postGatewayJson,
8
8
  requireConfiguredUlid,
9
9
  validateGatewayConnection,
10
+ throwGatewayRelayError,
10
11
  type GatewayConnection,
11
12
  } from "./http.js";
12
13
  import {
@@ -33,10 +34,12 @@ export interface StorageUploadInput {
33
34
  readonly bytes: Uint8Array;
34
35
  }
35
36
 
37
+ /** Server-only private files; use global fetch for gateway and signed-object transports. */
36
38
  export function createPrivateStorageClient(
37
39
  configuration: GatewayConnection & {
38
40
  readonly projectId: string;
39
41
  readonly environmentId: string;
42
+ /** Global fetch for short-lived signed object URLs; explicit transports remain test seams. */
40
43
  readonly capabilityFetch: (request: Request) => Promise<Response>;
41
44
  },
42
45
  ) {
@@ -79,18 +82,22 @@ export function createPrivateStorageClient(
79
82
  if (response.status !== 200) throwStorageError(response.status, response.body, environmentId);
80
83
  const record = response.body as Record<string, unknown>;
81
84
  if (record?.["state"] === "completed") {
82
- const completed = exactRecord(response.body, ["state", "transfer_id"]);
85
+ const completed = exactRecord(response.body, ["state", "transfer_id"], response.status);
83
86
  return Object.freeze({
84
87
  state: "completed" as const,
85
- transferId: requireUlid(completed["transfer_id"]),
88
+ transferId: requireUlid(completed["transfer_id"], response.status),
86
89
  });
87
90
  }
88
- const ready = exactRecord(response.body, ["state", "transfer_id", "capability"]);
89
- if (ready["state"] !== "ready") invalidResponse();
91
+ const ready = exactRecord(
92
+ response.body,
93
+ ["state", "transfer_id", "capability"],
94
+ response.status,
95
+ );
96
+ if (ready["state"] !== "ready") invalidResponse(response.status);
90
97
  return Object.freeze({
91
98
  state: "ready" as const,
92
- transferId: requireUlid(ready["transfer_id"]),
93
- capability: parseCapability(ready["capability"], "PUT", input.objectKey),
99
+ transferId: requireUlid(ready["transfer_id"], response.status),
100
+ capability: parseCapability(ready["capability"], "PUT", input.objectKey, response.status),
94
101
  });
95
102
  }
96
103
 
@@ -107,11 +114,12 @@ export function createPrivateStorageClient(
107
114
  if (response.status !== 200 && response.status !== 202) {
108
115
  throwStorageError(response.status, response.body, environmentId);
109
116
  }
110
- const record = exactRecord(response.body, ["state", "transfer_id"]);
111
- if (record["state"] !== "completed" && record["state"] !== "pending") invalidResponse();
117
+ const record = exactRecord(response.body, ["state", "transfer_id"], response.status);
118
+ if (record["state"] !== "completed" && record["state"] !== "pending")
119
+ invalidResponse(response.status);
112
120
  return Object.freeze({
113
121
  state: record["state"],
114
- transferId: requireUlid(record["transfer_id"]),
122
+ transferId: requireUlid(record["transfer_id"], response.status),
115
123
  });
116
124
  }
117
125
 
@@ -127,9 +135,9 @@ export function createPrivateStorageClient(
127
135
  8 * 1024,
128
136
  );
129
137
  if (response.status !== 200) throwStorageError(response.status, response.body, environmentId);
130
- const record = exactRecord(response.body, ["state", "capability"]);
131
- if (record["state"] !== "ready") invalidResponse();
132
- return parseCapability(record["capability"], "GET", objectKey);
138
+ const record = exactRecord(response.body, ["state", "capability"], response.status);
139
+ if (record["state"] !== "ready") invalidResponse(response.status);
140
+ return parseCapability(record["capability"], "GET", objectKey, response.status);
133
141
  }
134
142
 
135
143
  async function deleteObject(input: { idempotencyKey: string; objectKey: string }) {
@@ -142,11 +150,11 @@ export function createPrivateStorageClient(
142
150
  input.idempotencyKey,
143
151
  );
144
152
  if (response.status !== 200) throwStorageError(response.status, response.body, environmentId);
145
- const record = exactRecord(response.body, ["state", "deletion_id"]);
146
- if (record["state"] !== "completed") invalidResponse();
153
+ const record = exactRecord(response.body, ["state", "deletion_id"], response.status);
154
+ if (record["state"] !== "completed") invalidResponse(response.status);
147
155
  return Object.freeze({
148
156
  state: "completed" as const,
149
- deletionId: requireUlid(record["deletion_id"]),
157
+ deletionId: requireUlid(record["deletion_id"], response.status),
150
158
  });
151
159
  }
152
160
 
@@ -257,9 +265,9 @@ export function createPrivateStorageClient(
257
265
  controller.signal,
258
266
  );
259
267
  if (response.status !== 200) throwStorageError(response.status, value, environmentId);
260
- const record = exactRecord(value, ["state", "results"]);
268
+ const record = exactRecord(value, ["state", "results"], response.status);
261
269
  if (!Array.isArray(record["results"]) || record["results"].length !== objects.length)
262
- invalidResponse();
270
+ invalidResponse(response.status);
263
271
  const results = record["results"].map((value, index): StorageDeleteResult => {
264
272
  const candidate = value as Record<string, unknown>;
265
273
  const item = exactRecord(
@@ -269,22 +277,23 @@ export function createPrivateStorageClient(
269
277
  : candidate?.["state"] === "pending"
270
278
  ? ["object_key", "state", "retry_at"]
271
279
  : ["object_key", "state", "code", "retryable"],
280
+ response.status,
272
281
  );
273
282
  const requested = objects[index];
274
283
  if (requested === undefined || item["object_key"] !== requested.objectKey)
275
- invalidResponse();
284
+ invalidResponse(response.status);
276
285
  const objectKey = requested.objectKey;
277
286
  if (item["state"] === "completed")
278
287
  return Object.freeze({
279
288
  state: "completed",
280
289
  objectKey,
281
- deletionId: requireUlid(item["deletion_id"]),
290
+ deletionId: requireUlid(item["deletion_id"], response.status),
282
291
  });
283
292
  if (item["state"] === "pending") {
284
293
  return Object.freeze({
285
294
  state: "pending",
286
295
  objectKey,
287
- retryAt: requireRetryAt(item["retry_at"]),
296
+ retryAt: requireRetryAt(item["retry_at"], response.status),
288
297
  });
289
298
  }
290
299
  if (
@@ -293,7 +302,7 @@ export function createPrivateStorageClient(
293
302
  typeof item["retryable"] !== "boolean" ||
294
303
  (item["code"] === "object_not_found" && item["retryable"] !== false)
295
304
  )
296
- invalidResponse();
305
+ invalidResponse(response.status);
297
306
  return Object.freeze({
298
307
  state: "failed",
299
308
  objectKey,
@@ -305,10 +314,10 @@ export function createPrivateStorageClient(
305
314
  record["state"] !==
306
315
  (results.every((item) => item.state === "completed") ? "completed" : "partial")
307
316
  )
308
- invalidResponse();
317
+ invalidResponse(response.status);
309
318
  if (controller.signal.aborted || Date.now() >= deadline) {
310
319
  controller.abort();
311
- throw new CustomerRuntimeGatewayError("gateway_timeout", null, true);
320
+ throw new CustomerRuntimeGatewayError("gateway_timeout", response.status, true);
312
321
  }
313
322
  return Object.freeze(results);
314
323
  } finally {
@@ -341,16 +350,18 @@ function validateBatchKeys(value: readonly string[]): readonly string[] {
341
350
  return Object.freeze(Array.from(value));
342
351
  }
343
352
 
344
- function parseCapability(value: unknown, operation: "GET" | "PUT", objectKey: string) {
345
- const record = exactRecord(value, [
346
- "operation",
347
- "objectKey",
348
- "url",
349
- "expiresAt",
350
- "expectedContentLength",
351
- "requiredHeaders",
352
- ]);
353
- const headers = exactHeaders(record["requiredHeaders"]);
353
+ function parseCapability(
354
+ value: unknown,
355
+ operation: "GET" | "PUT",
356
+ objectKey: string,
357
+ status: number,
358
+ ) {
359
+ const record = exactRecord(
360
+ value,
361
+ ["operation", "objectKey", "url", "expiresAt", "expectedContentLength", "requiredHeaders"],
362
+ status,
363
+ );
364
+ const headers = exactHeaders(record["requiredHeaders"], status);
354
365
  if (
355
366
  record["operation"] !== operation ||
356
367
  record["objectKey"] !== objectKey ||
@@ -363,7 +374,7 @@ function parseCapability(value: unknown, operation: "GET" | "PUT", objectKey: st
363
374
  Number(record["expectedContentLength"]) < 0 ||
364
375
  Number(record["expectedContentLength"]) > 1_073_741_824))
365
376
  ) {
366
- invalidResponse();
377
+ invalidResponse(status);
367
378
  }
368
379
  return Object.freeze({
369
380
  operation,
@@ -376,8 +387,8 @@ function parseCapability(value: unknown, operation: "GET" | "PUT", objectKey: st
376
387
  });
377
388
  }
378
389
 
379
- function exactHeaders(value: unknown): Readonly<Record<string, string>> {
380
- if (typeof value !== "object" || value === null || Array.isArray(value)) invalidResponse();
390
+ function exactHeaders(value: unknown, status: number): Readonly<Record<string, string>> {
391
+ if (typeof value !== "object" || value === null || Array.isArray(value)) invalidResponse(status);
381
392
  const entries = Object.entries(value as Record<string, unknown>);
382
393
  if (
383
394
  entries.length > 16 ||
@@ -390,12 +401,13 @@ function exactHeaders(value: unknown): Readonly<Record<string, string>> {
390
401
  /[\r\n]/u.test(header),
391
402
  )
392
403
  ) {
393
- invalidResponse();
404
+ invalidResponse(status);
394
405
  }
395
406
  return Object.freeze(Object.fromEntries(entries) as Record<string, string>);
396
407
  }
397
408
 
398
409
  function throwStorageError(status: number, body: unknown, environmentId: string): never {
410
+ throwGatewayRelayError(status, body);
399
411
  const candidate = body as Record<string, unknown>;
400
412
  const record = exactRecord(
401
413
  body,
@@ -407,6 +419,7 @@ function throwStorageError(status: number, body: unknown, environmentId: string)
407
419
  : typeof body === "object" && body !== null && "transfer_id" in body
408
420
  ? ["code", "transfer_id"]
409
421
  : ["code"],
422
+ status,
410
423
  );
411
424
  const code = record["code"];
412
425
  if (status === 401 && code === "unauthorized") {
@@ -416,7 +429,7 @@ function throwStorageError(status: number, body: unknown, environmentId: string)
416
429
  throw new CustomerRuntimeGatewayError("storage_object_not_found", status, false);
417
430
  }
418
431
  if (status === 409 && code === "upload_expired") {
419
- requireUlid(record["transfer_id"]);
432
+ requireUlid(record["transfer_id"], status);
420
433
  throw new CustomerRuntimeGatewayError("storage_upload_expired", status, false);
421
434
  }
422
435
  if (status === 409 && code === "data_change_in_progress") {
@@ -432,11 +445,11 @@ function throwStorageError(status: number, body: unknown, environmentId: string)
432
445
  "storage_binding_data_changed",
433
446
  status,
434
447
  false,
435
- `environment ${environmentId} now uses a different data area; create a new deployment plan and redeploy that environment. Repeating this request will not update this deployment's binding`,
448
+ `the environment's data area changed while this request ran; read the current state for environment ${environmentId} and issue a new request`,
436
449
  );
437
450
  }
438
451
  if (status === 409 && code === "storage_delete_pending") {
439
- const retryAt = requireRetryAt(record["retry_at"]);
452
+ const retryAt = requireRetryAt(record["retry_at"], status);
440
453
  throw new CustomerRuntimeGatewayError(
441
454
  "storage_delete_pending",
442
455
  status,
@@ -450,20 +463,17 @@ function throwStorageError(status: number, body: unknown, environmentId: string)
450
463
  if (status === 413 && typeof code === "string") {
451
464
  throw new CustomerRuntimeGatewayError("storage_quota_exceeded", status, false);
452
465
  }
453
- if (status === 503 && code === "storage_gateway_unavailable") {
454
- throw new CustomerRuntimeGatewayError("gateway_unavailable", status, true);
455
- }
456
- return invalidResponse();
466
+ return invalidResponse(status);
457
467
  }
458
468
 
459
- function requireRetryAt(value: unknown): string {
469
+ function requireRetryAt(value: unknown, status: number): string {
460
470
  if (
461
471
  typeof value !== "string" ||
462
472
  !/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/u.test(value) ||
463
473
  !Number.isFinite(Date.parse(value)) ||
464
474
  new Date(value).toISOString() !== value
465
475
  )
466
- invalidResponse();
476
+ invalidResponse(status);
467
477
  return value;
468
478
  }
469
479
 
@@ -507,8 +517,9 @@ function validSignedUrl(value: string): boolean {
507
517
  }
508
518
  }
509
519
 
510
- function requireUlid(value: unknown): string {
511
- if (typeof value !== "string" || !/^[0-9A-HJKMNP-TV-Z]{26}$/u.test(value)) invalidResponse();
520
+ function requireUlid(value: unknown, status: number | null = null): string {
521
+ if (typeof value !== "string" || !/^[0-9A-HJKMNP-TV-Z]{26}$/u.test(value))
522
+ invalidResponse(status);
512
523
  return value;
513
524
  }
514
525
 
@@ -1,8 +0,0 @@
1
- import { type GatewayConnection } from "./http.js";
2
- /** Stream owned attachments through the private mail binding using the environment's runtime key. */
3
- export declare function createMailAttachmentClient(configuration: GatewayConnection & {
4
- projectId: string;
5
- }): {
6
- download(downloadPath: string): Promise<Response>;
7
- };
8
- //# sourceMappingURL=mail-attachments.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"mail-attachments.d.ts","sourceRoot":"","sources":["../src/mail-attachments.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,KAAK,iBAAiB,EACvB,MAAM,WAAW,CAAC;AAEnB,qGAAqG;AACrG,wBAAgB,0BAA0B,CACxC,aAAa,EAAE,iBAAiB,GAAG;IAAE,SAAS,EAAE,MAAM,CAAA;CAAE;2BAazB,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC;EA8B1D"}
@@ -1,32 +0,0 @@
1
- import { validateGatewayConnection, fetchWithTimeout, requireConfiguredUlid, } from "./http.js";
2
- import { CustomerRuntimeGatewayError } from "./errors.js";
3
- /** Stream owned attachments through the private mail binding using the environment's runtime key. */
4
- export function createMailAttachmentClient(configuration) {
5
- const connection = validateGatewayConnection(configuration);
6
- const projectId = requireConfiguredUlid(configuration.projectId, "projectId", "OHMYHOST_PROJECT_ID");
7
- const pattern = new RegExp(`^/v1/projects/${projectId}/environments/[0-7][0-9A-HJKMNP-TV-Z]{25}/mail/messages/[0-7][0-9A-HJKMNP-TV-Z]{25}/attachments/[A-Za-z0-9_-]{1,128}$`, "u");
8
- return {
9
- async download(downloadPath) {
10
- if (!pattern.test(downloadPath))
11
- throw new CustomerRuntimeGatewayError("gateway_request_invalid", null, false);
12
- const response = await fetchWithTimeout(connection.fetch, new Request(new URL(downloadPath, connection.endpoint), {
13
- redirect: "manual",
14
- headers: { authorization: `Bearer ${connection.key}` },
15
- }), connection.timeoutMilliseconds);
16
- if (response.status !== 200) {
17
- await response.body?.cancel();
18
- throw new CustomerRuntimeGatewayError(response.status === 410
19
- ? "mail_content_expired"
20
- : response.status === 413
21
- ? "mail_attachment_too_large"
22
- : response.status === 404
23
- ? "mail_message_not_found"
24
- : response.status === 401
25
- ? "unauthorized"
26
- : "gateway_unavailable", response.status, response.status >= 500);
27
- }
28
- return response;
29
- },
30
- };
31
- }
32
- //# sourceMappingURL=mail-attachments.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"mail-attachments.js","sourceRoot":"","sources":["../src/mail-attachments.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,yBAAyB,EACzB,gBAAgB,EAChB,qBAAqB,GAEtB,MAAM,WAAW,CAAC;AACnB,OAAO,EAAE,2BAA2B,EAAE,MAAM,aAAa,CAAC;AAC1D,qGAAqG;AACrG,MAAM,UAAU,0BAA0B,CACxC,aAAwD;IAExD,MAAM,UAAU,GAAG,yBAAyB,CAAC,aAAa,CAAC,CAAC;IAC5D,MAAM,SAAS,GAAG,qBAAqB,CACrC,aAAa,CAAC,SAAS,EACvB,WAAW,EACX,qBAAqB,CACtB,CAAC;IACF,MAAM,OAAO,GAAG,IAAI,MAAM,CACxB,iBAAiB,SAAS,uHAAuH,EACjJ,GAAG,CACJ,CAAC;IACF,OAAO;QACL,KAAK,CAAC,QAAQ,CAAC,YAAoB;YACjC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,YAAY,CAAC;gBAC7B,MAAM,IAAI,2BAA2B,CAAC,yBAAyB,EAAE,IAAI,EAAE,KAAK,CAAC,CAAC;YAChF,MAAM,QAAQ,GAAG,MAAM,gBAAgB,CACrC,UAAU,CAAC,KAAK,EAChB,IAAI,OAAO,CAAC,IAAI,GAAG,CAAC,YAAY,EAAE,UAAU,CAAC,QAAQ,CAAC,EAAE;gBACtD,QAAQ,EAAE,QAAQ;gBAClB,OAAO,EAAE,EAAE,aAAa,EAAE,UAAU,UAAU,CAAC,GAAG,EAAE,EAAE;aACvD,CAAC,EACF,UAAU,CAAC,mBAAmB,CAC/B,CAAC;YACF,IAAI,QAAQ,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;gBAC5B,MAAM,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,CAAC;gBAC9B,MAAM,IAAI,2BAA2B,CACnC,QAAQ,CAAC,MAAM,KAAK,GAAG;oBACrB,CAAC,CAAC,sBAAsB;oBACxB,CAAC,CAAC,QAAQ,CAAC,MAAM,KAAK,GAAG;wBACvB,CAAC,CAAC,2BAA2B;wBAC7B,CAAC,CAAC,QAAQ,CAAC,MAAM,KAAK,GAAG;4BACvB,CAAC,CAAC,wBAAwB;4BAC1B,CAAC,CAAC,QAAQ,CAAC,MAAM,KAAK,GAAG;gCACvB,CAAC,CAAC,cAAc;gCAChB,CAAC,CAAC,qBAAqB,EAC/B,QAAQ,CAAC,MAAM,EACf,QAAQ,CAAC,MAAM,IAAI,GAAG,CACvB,CAAC;YACJ,CAAC;YACD,OAAO,QAAQ,CAAC;QAClB,CAAC;KACF,CAAC;AACJ,CAAC"}
@@ -1,53 +0,0 @@
1
- import {
2
- validateGatewayConnection,
3
- fetchWithTimeout,
4
- requireConfiguredUlid,
5
- type GatewayConnection,
6
- } from "./http.js";
7
- import { CustomerRuntimeGatewayError } from "./errors.js";
8
- /** Stream owned attachments through the private mail binding using the environment's runtime key. */
9
- export function createMailAttachmentClient(
10
- configuration: GatewayConnection & { projectId: string },
11
- ) {
12
- const connection = validateGatewayConnection(configuration);
13
- const projectId = requireConfiguredUlid(
14
- configuration.projectId,
15
- "projectId",
16
- "OHMYHOST_PROJECT_ID",
17
- );
18
- const pattern = new RegExp(
19
- `^/v1/projects/${projectId}/environments/[0-7][0-9A-HJKMNP-TV-Z]{25}/mail/messages/[0-7][0-9A-HJKMNP-TV-Z]{25}/attachments/[A-Za-z0-9_-]{1,128}$`,
20
- "u",
21
- );
22
- return {
23
- async download(downloadPath: string): Promise<Response> {
24
- if (!pattern.test(downloadPath))
25
- throw new CustomerRuntimeGatewayError("gateway_request_invalid", null, false);
26
- const response = await fetchWithTimeout(
27
- connection.fetch,
28
- new Request(new URL(downloadPath, connection.endpoint), {
29
- redirect: "manual",
30
- headers: { authorization: `Bearer ${connection.key}` },
31
- }),
32
- connection.timeoutMilliseconds,
33
- );
34
- if (response.status !== 200) {
35
- await response.body?.cancel();
36
- throw new CustomerRuntimeGatewayError(
37
- response.status === 410
38
- ? "mail_content_expired"
39
- : response.status === 413
40
- ? "mail_attachment_too_large"
41
- : response.status === 404
42
- ? "mail_message_not_found"
43
- : response.status === 401
44
- ? "unauthorized"
45
- : "gateway_unavailable",
46
- response.status,
47
- response.status >= 500,
48
- );
49
- }
50
- return response;
51
- },
52
- };
53
- }