@milaboratories/pframes-rs-serv 1.1.3 → 1.1.4
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/bin/parquet-server.mjs +2 -2
- package/dist/export.cjs +1 -1
- package/dist/export.cjs.map +1 -1
- package/dist/export.d.ts +1 -1
- package/dist/export.d.ts.map +1 -1
- package/dist/export.js +1 -1
- package/dist/export.js.map +1 -1
- package/dist/fs-store.cjs +18 -20
- package/dist/fs-store.cjs.map +1 -1
- package/dist/fs-store.d.ts +1 -1
- package/dist/fs-store.d.ts.map +1 -1
- package/dist/fs-store.js +18 -20
- package/dist/fs-store.js.map +1 -1
- package/dist/handler.cjs +5 -5
- package/dist/handler.cjs.map +1 -1
- package/dist/handler.d.ts +2 -2
- package/dist/handler.d.ts.map +1 -1
- package/dist/handler.js +5 -5
- package/dist/handler.js.map +1 -1
- package/dist/index.d.ts +5 -5
- package/dist/parquet-server.cjs +19 -19
- package/dist/parquet-server.cjs.map +1 -1
- package/dist/parquet-server.d.ts +1 -1
- package/dist/parquet-server.d.ts.map +1 -1
- package/dist/parquet-server.js +19 -19
- package/dist/parquet-server.js.map +1 -1
- package/dist/serve.cjs +20 -20
- package/dist/serve.cjs.map +1 -1
- package/dist/serve.d.ts +2 -2
- package/dist/serve.d.ts.map +1 -1
- package/dist/serve.js +20 -20
- package/dist/serve.js.map +1 -1
- package/dist/utils/etag.cjs +1 -1
- package/dist/utils/etag.cjs.map +1 -1
- package/dist/utils/etag.d.ts +3 -3
- package/dist/utils/etag.js +1 -1
- package/dist/utils/etag.js.map +1 -1
- package/dist/utils/filename.cjs.map +1 -1
- package/dist/utils/filename.d.ts +2 -2
- package/dist/utils/filename.js.map +1 -1
- package/dist/utils/headers.cjs +17 -17
- package/dist/utils/headers.cjs.map +1 -1
- package/dist/utils/headers.js +17 -17
- package/dist/utils/headers.js.map +1 -1
- package/dist/utils/index.d.ts +7 -7
- package/dist/utils/method.cjs +3 -3
- package/dist/utils/method.cjs.map +1 -1
- package/dist/utils/method.d.ts +5 -5
- package/dist/utils/method.js +3 -3
- package/dist/utils/method.js.map +1 -1
- package/dist/utils/options.cjs +10 -10
- package/dist/utils/options.cjs.map +1 -1
- package/dist/utils/options.d.ts +2 -2
- package/dist/utils/options.d.ts.map +1 -1
- package/dist/utils/options.js +10 -10
- package/dist/utils/options.js.map +1 -1
- package/dist/utils/range.cjs +4 -4
- package/dist/utils/range.cjs.map +1 -1
- package/dist/utils/range.d.ts +2 -2
- package/dist/utils/range.d.ts.map +1 -1
- package/dist/utils/range.js +4 -4
- package/dist/utils/range.js.map +1 -1
- package/dist/utils/status.cjs +1 -1
- package/dist/utils/status.cjs.map +1 -1
- package/dist/utils/status.js +1 -1
- package/dist/utils/status.js.map +1 -1
- package/package.json +23 -24
- package/src/export.ts +9 -11
- package/src/fs-store.ts +32 -43
- package/src/handler.ts +20 -28
- package/src/index.ts +5 -5
- package/src/parquet-server.ts +33 -37
- package/src/serve.ts +33 -49
- package/src/utils/etag.ts +4 -4
- package/src/utils/filename.ts +3 -3
- package/src/utils/headers.ts +24 -24
- package/src/utils/index.ts +7 -7
- package/src/utils/method.ts +10 -10
- package/src/utils/options.ts +14 -18
- package/src/utils/range.ts +7 -9
- package/src/utils/status.ts +1 -1
package/src/serve.ts
CHANGED
|
@@ -2,52 +2,42 @@ import {
|
|
|
2
2
|
createServer as createHttpServer,
|
|
3
3
|
type RequestListener,
|
|
4
4
|
type Server as HttpServer,
|
|
5
|
-
type ServerOptions
|
|
6
|
-
} from
|
|
7
|
-
import {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
} from
|
|
11
|
-
import
|
|
12
|
-
import {
|
|
13
|
-
import
|
|
14
|
-
import {
|
|
15
|
-
base64Encode,
|
|
16
|
-
Base64Encoded,
|
|
17
|
-
ensureError
|
|
18
|
-
} from '@milaboratories/pl-model-common';
|
|
19
|
-
import { generate, type GenerateResult } from 'selfsigned';
|
|
20
|
-
import { randomUUID } from 'node:crypto';
|
|
21
|
-
import { authorizeRequestHandler } from './handler';
|
|
5
|
+
type ServerOptions,
|
|
6
|
+
} from "node:http";
|
|
7
|
+
import { createServer as createHttpsServer, type Server as HttpsServer } from "node:https";
|
|
8
|
+
import type { AddressInfo } from "node:net";
|
|
9
|
+
import { Deferred } from "@milaboratories/helpers";
|
|
10
|
+
import type { PFrameInternal } from "@milaboratories/pl-model-middle-layer";
|
|
11
|
+
import { base64Encode, Base64Encoded, ensureError } from "@milaboratories/pl-model-common";
|
|
12
|
+
import { generate, type GenerateResult } from "selfsigned";
|
|
13
|
+
import { randomUUID } from "node:crypto";
|
|
14
|
+
import { authorizeRequestHandler } from "./handler";
|
|
22
15
|
|
|
23
16
|
/** Generate a self-signed certificate for localhost */
|
|
24
17
|
async function generateCertificate(): Promise<GenerateResult> {
|
|
25
|
-
return await generate([{ name:
|
|
18
|
+
return await generate([{ name: "commonName", value: "localhost" }], {
|
|
26
19
|
keySize: 2048,
|
|
27
|
-
algorithm:
|
|
20
|
+
algorithm: "sha256",
|
|
28
21
|
extensions: [
|
|
29
22
|
{
|
|
30
|
-
name:
|
|
23
|
+
name: "subjectAltName",
|
|
31
24
|
altNames: [
|
|
32
|
-
{ type: 2, value:
|
|
33
|
-
{ type: 7, ip:
|
|
34
|
-
{ type: 7, ip:
|
|
35
|
-
]
|
|
36
|
-
}
|
|
37
|
-
]
|
|
25
|
+
{ type: 2, value: "localhost" }, // DNS
|
|
26
|
+
{ type: 7, ip: "127.0.0.1" }, // IPv4
|
|
27
|
+
{ type: 7, ip: "::1" }, // IPv6
|
|
28
|
+
],
|
|
29
|
+
},
|
|
30
|
+
],
|
|
38
31
|
});
|
|
39
32
|
}
|
|
40
33
|
|
|
41
34
|
/** Create an object store URL from the server address info. */
|
|
42
|
-
function createObjectStoreUrl(
|
|
43
|
-
|
|
44
|
-
noHttps?: true
|
|
45
|
-
): PFrameInternal.ObjectStoreUrl {
|
|
46
|
-
const protocol = noHttps ? 'http' : 'https';
|
|
35
|
+
function createObjectStoreUrl(info: AddressInfo, noHttps?: true): PFrameInternal.ObjectStoreUrl {
|
|
36
|
+
const protocol = noHttps ? "http" : "https";
|
|
47
37
|
switch (info.family) {
|
|
48
|
-
case
|
|
38
|
+
case "IPv4":
|
|
49
39
|
return `${protocol}://${info.address}:${info.port}/` as PFrameInternal.ObjectStoreUrl;
|
|
50
|
-
case
|
|
40
|
+
case "IPv6":
|
|
51
41
|
return `${protocol}://[${info.address}]:${info.port}/` as PFrameInternal.ObjectStoreUrl;
|
|
52
42
|
default:
|
|
53
43
|
return `${protocol}://localhost:${info.port}/` as PFrameInternal.ObjectStoreUrl;
|
|
@@ -62,7 +52,7 @@ export async function serve({
|
|
|
62
52
|
handler,
|
|
63
53
|
port = 0,
|
|
64
54
|
noHttps,
|
|
65
|
-
noAuth
|
|
55
|
+
noAuth,
|
|
66
56
|
}: PFrameInternal.HttpServerOptions): Promise<PFrameInternal.HttpServer> {
|
|
67
57
|
const started = new Deferred<PFrameInternal.HttpServer>();
|
|
68
58
|
try {
|
|
@@ -78,7 +68,7 @@ export async function serve({
|
|
|
78
68
|
// Create HTTP server
|
|
79
69
|
let encodedCaCert: Base64Encoded<PFrameInternal.PemCertificate> | undefined;
|
|
80
70
|
const defaultOptions: ServerOptions = {
|
|
81
|
-
keepAlive: true
|
|
71
|
+
keepAlive: true,
|
|
82
72
|
};
|
|
83
73
|
let server: HttpServer | HttpsServer;
|
|
84
74
|
|
|
@@ -87,19 +77,13 @@ export async function serve({
|
|
|
87
77
|
} else {
|
|
88
78
|
const { cert, private: key, public: ca } = await generateCertificate();
|
|
89
79
|
encodedCaCert = base64Encode(cert as PFrameInternal.PemCertificate);
|
|
90
|
-
server = createHttpsServer(
|
|
91
|
-
{ ...defaultOptions, cert, key, ca },
|
|
92
|
-
effectiveHandler
|
|
93
|
-
);
|
|
80
|
+
server = createHttpsServer({ ...defaultOptions, cert, key, ca }, effectiveHandler);
|
|
94
81
|
}
|
|
95
82
|
|
|
96
83
|
server
|
|
97
|
-
.on(
|
|
84
|
+
.on("listening", () => {
|
|
98
85
|
// Cast is safe by specification <https://nodejs.org/api/net.html#serveraddress>
|
|
99
|
-
const url = createObjectStoreUrl(
|
|
100
|
-
server.address() as AddressInfo,
|
|
101
|
-
noHttps
|
|
102
|
-
);
|
|
86
|
+
const url = createObjectStoreUrl(server.address() as AddressInfo, noHttps);
|
|
103
87
|
stopped = new Deferred<void>();
|
|
104
88
|
|
|
105
89
|
started.resolve({
|
|
@@ -112,17 +96,17 @@ export async function serve({
|
|
|
112
96
|
stop(): Promise<void> {
|
|
113
97
|
server.close();
|
|
114
98
|
return stopped!.promise;
|
|
115
|
-
}
|
|
99
|
+
},
|
|
116
100
|
});
|
|
117
101
|
})
|
|
118
|
-
.on(
|
|
102
|
+
.on("error", (err) => {
|
|
119
103
|
started.reject(err);
|
|
120
104
|
stopped?.reject(err);
|
|
121
105
|
})
|
|
122
|
-
.on(
|
|
106
|
+
.on("close", () => stopped?.resolve())
|
|
123
107
|
.listen({
|
|
124
|
-
host:
|
|
125
|
-
port
|
|
108
|
+
host: "localhost",
|
|
109
|
+
port,
|
|
126
110
|
});
|
|
127
111
|
} catch (error: unknown) {
|
|
128
112
|
started.reject(ensureError(error));
|
package/src/utils/etag.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { Branded } from
|
|
2
|
-
import type { PFrameInternal } from
|
|
1
|
+
import type { Branded } from "@milaboratories/pl-model-common";
|
|
2
|
+
import type { PFrameInternal } from "@milaboratories/pl-model-middle-layer";
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* See <https://datatracker.ietf.org/doc/html/rfc9110#section-8.8.3>
|
|
@@ -11,10 +11,10 @@ import type { PFrameInternal } from '@milaboratories/pl-model-middle-layer';
|
|
|
11
11
|
* ETag: W/"xyzzy"
|
|
12
12
|
* ```
|
|
13
13
|
*/
|
|
14
|
-
export type Etag = Branded<string,
|
|
14
|
+
export type Etag = Branded<string, "Etag">;
|
|
15
15
|
|
|
16
16
|
export function createETag(filename: PFrameInternal.ParquetFileName): Etag {
|
|
17
17
|
// For immutable files, use URL-safe base64 encoded filename as ETag
|
|
18
|
-
const filenameETag = Buffer.from(filename,
|
|
18
|
+
const filenameETag = Buffer.from(filename, "utf8").toString("base64url");
|
|
19
19
|
return `"${filenameETag}"` as Etag;
|
|
20
20
|
}
|
package/src/utils/filename.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import type { PFrameInternal } from
|
|
2
|
-
import type { IncomingMessage } from
|
|
1
|
+
import type { PFrameInternal } from "@milaboratories/pl-model-middle-layer";
|
|
2
|
+
import type { IncomingMessage } from "node:http";
|
|
3
3
|
|
|
4
4
|
const PARQUET_FILENAME_REGEX = /^\/([\w\-.]+.parquet)$/;
|
|
5
5
|
|
|
6
6
|
export function getFilenameFromUrl(
|
|
7
|
-
request: IncomingMessage
|
|
7
|
+
request: IncomingMessage,
|
|
8
8
|
): PFrameInternal.ParquetFileName | null {
|
|
9
9
|
const url = request.url;
|
|
10
10
|
if (url === undefined) return null;
|
package/src/utils/headers.ts
CHANGED
|
@@ -1,31 +1,31 @@
|
|
|
1
1
|
/** HTTP header names used in the parquet server handler */
|
|
2
2
|
export const HeaderName = {
|
|
3
|
-
Accept:
|
|
4
|
-
AcceptRanges:
|
|
5
|
-
Allow:
|
|
6
|
-
Authorization:
|
|
7
|
-
CacheControl:
|
|
8
|
-
Connection:
|
|
9
|
-
ContentLength:
|
|
10
|
-
ContentRange:
|
|
11
|
-
ContentType:
|
|
12
|
-
Date:
|
|
13
|
-
ETag:
|
|
14
|
-
IfMatch:
|
|
15
|
-
IfModifiedSince:
|
|
16
|
-
IfNoneMatch:
|
|
17
|
-
IfUnmodifiedSince:
|
|
18
|
-
LastModified:
|
|
19
|
-
Range:
|
|
20
|
-
WWWAuthenticate:
|
|
3
|
+
Accept: "accept",
|
|
4
|
+
AcceptRanges: "accept-ranges",
|
|
5
|
+
Allow: "allow",
|
|
6
|
+
Authorization: "authorization",
|
|
7
|
+
CacheControl: "cache-control",
|
|
8
|
+
Connection: "connection",
|
|
9
|
+
ContentLength: "content-length",
|
|
10
|
+
ContentRange: "content-range",
|
|
11
|
+
ContentType: "content-type",
|
|
12
|
+
Date: "date",
|
|
13
|
+
ETag: "etag",
|
|
14
|
+
IfMatch: "if-match",
|
|
15
|
+
IfModifiedSince: "if-modified-since",
|
|
16
|
+
IfNoneMatch: "if-none-match",
|
|
17
|
+
IfUnmodifiedSince: "if-unmodified-since",
|
|
18
|
+
LastModified: "last-modified",
|
|
19
|
+
Range: "range",
|
|
20
|
+
WWWAuthenticate: "www-authenticate",
|
|
21
21
|
} as const;
|
|
22
22
|
|
|
23
23
|
/** HTTP header values used in the parquet server handler */
|
|
24
24
|
export const HeaderValue = {
|
|
25
|
-
AcceptRanges:
|
|
26
|
-
Allow:
|
|
27
|
-
CacheControl:
|
|
28
|
-
Connection:
|
|
29
|
-
ContentType:
|
|
30
|
-
WWWAuthenticate: 'Bearer realm="parquet-server"'
|
|
25
|
+
AcceptRanges: "bytes",
|
|
26
|
+
Allow: "GET, HEAD",
|
|
27
|
+
CacheControl: "public, immutable, max-age=31536000",
|
|
28
|
+
Connection: "close",
|
|
29
|
+
ContentType: "application/octet-stream",
|
|
30
|
+
WWWAuthenticate: 'Bearer realm="parquet-server"',
|
|
31
31
|
} as const;
|
package/src/utils/index.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
export * from
|
|
2
|
-
export * from
|
|
3
|
-
export * from
|
|
4
|
-
export * from
|
|
5
|
-
export * from
|
|
6
|
-
export * from
|
|
7
|
-
export * from
|
|
1
|
+
export * from "./filename";
|
|
2
|
+
export * from "./etag";
|
|
3
|
+
export * from "./headers";
|
|
4
|
+
export * from "./options";
|
|
5
|
+
export * from "./range";
|
|
6
|
+
export * from "./method";
|
|
7
|
+
export * from "./status";
|
package/src/utils/method.ts
CHANGED
|
@@ -1,20 +1,20 @@
|
|
|
1
|
-
import type { PFrameInternal } from
|
|
2
|
-
import type { IncomingHttpHeaders } from
|
|
1
|
+
import type { PFrameInternal } from "@milaboratories/pl-model-middle-layer";
|
|
2
|
+
import type { IncomingHttpHeaders } from "node:http";
|
|
3
3
|
|
|
4
4
|
export function isGetOrHead(
|
|
5
|
-
method: IncomingHttpHeaders[
|
|
5
|
+
method: IncomingHttpHeaders["method"],
|
|
6
6
|
): method is PFrameInternal.HttpMethod {
|
|
7
|
-
return method ===
|
|
7
|
+
return method === "GET" || method === "HEAD";
|
|
8
8
|
}
|
|
9
9
|
|
|
10
10
|
export function isGet(
|
|
11
|
-
method: PFrameInternal.HttpMethod
|
|
12
|
-
): method is Extract<PFrameInternal.HttpMethod,
|
|
13
|
-
return method ===
|
|
11
|
+
method: PFrameInternal.HttpMethod,
|
|
12
|
+
): method is Extract<PFrameInternal.HttpMethod, "GET"> {
|
|
13
|
+
return method === "GET";
|
|
14
14
|
}
|
|
15
15
|
|
|
16
16
|
export function isHead(
|
|
17
|
-
method: PFrameInternal.HttpMethod
|
|
18
|
-
): method is Extract<PFrameInternal.HttpMethod,
|
|
19
|
-
return method ===
|
|
17
|
+
method: PFrameInternal.HttpMethod,
|
|
18
|
+
): method is Extract<PFrameInternal.HttpMethod, "HEAD"> {
|
|
19
|
+
return method === "HEAD";
|
|
20
20
|
}
|
package/src/utils/options.ts
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
import type { IncomingMessage } from
|
|
2
|
-
import type { Etag } from
|
|
1
|
+
import type { IncomingMessage } from "node:http";
|
|
2
|
+
import type { Etag } from "./etag";
|
|
3
3
|
|
|
4
4
|
type EtagMatchType =
|
|
5
5
|
| {
|
|
6
|
-
type:
|
|
6
|
+
type: "match";
|
|
7
7
|
value: Etag[];
|
|
8
8
|
}
|
|
9
9
|
| {
|
|
10
|
-
type:
|
|
10
|
+
type: "wildcard";
|
|
11
11
|
};
|
|
12
12
|
|
|
13
13
|
class EtagMatch {
|
|
@@ -15,21 +15,21 @@ class EtagMatch {
|
|
|
15
15
|
|
|
16
16
|
static parse(etagMatch: string | undefined): EtagMatch | null {
|
|
17
17
|
if (etagMatch === undefined) return null;
|
|
18
|
-
if (etagMatch ===
|
|
19
|
-
return new EtagMatch({ type:
|
|
18
|
+
if (etagMatch === "*") {
|
|
19
|
+
return new EtagMatch({ type: "wildcard" });
|
|
20
20
|
} else {
|
|
21
21
|
return new EtagMatch({
|
|
22
|
-
type:
|
|
23
|
-
value: etagMatch.split(
|
|
22
|
+
type: "match",
|
|
23
|
+
value: etagMatch.split(",").map((etag) => etag.trim() as Etag),
|
|
24
24
|
});
|
|
25
25
|
}
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
matches(etag: Etag): boolean {
|
|
29
29
|
switch (this.etagMatch.type) {
|
|
30
|
-
case
|
|
30
|
+
case "match":
|
|
31
31
|
return this.etagMatch.value.includes(etag);
|
|
32
|
-
case
|
|
32
|
+
case "wildcard":
|
|
33
33
|
return true;
|
|
34
34
|
}
|
|
35
35
|
}
|
|
@@ -100,14 +100,10 @@ export class Options {
|
|
|
100
100
|
private ifUnmodifiedSince: DateMatch | null;
|
|
101
101
|
|
|
102
102
|
constructor(request: IncomingMessage) {
|
|
103
|
-
this.ifMatch = EtagMatch.parse(request.headers[
|
|
104
|
-
this.ifNoneMatch = EtagMatch.parse(request.headers[
|
|
105
|
-
this.ifModifiedSince = DateMatch.parse(
|
|
106
|
-
|
|
107
|
-
);
|
|
108
|
-
this.ifUnmodifiedSince = DateMatch.parse(
|
|
109
|
-
request.headers['if-unmodified-since']
|
|
110
|
-
);
|
|
103
|
+
this.ifMatch = EtagMatch.parse(request.headers["if-match"]);
|
|
104
|
+
this.ifNoneMatch = EtagMatch.parse(request.headers["if-none-match"]);
|
|
105
|
+
this.ifModifiedSince = DateMatch.parse(request.headers["if-modified-since"]);
|
|
106
|
+
this.ifUnmodifiedSince = DateMatch.parse(request.headers["if-unmodified-since"]);
|
|
111
107
|
}
|
|
112
108
|
|
|
113
109
|
/**
|
package/src/utils/range.ts
CHANGED
|
@@ -1,10 +1,8 @@
|
|
|
1
|
-
import type { PFrameInternal } from
|
|
2
|
-
import type { IncomingMessage } from
|
|
1
|
+
import type { PFrameInternal } from "@milaboratories/pl-model-middle-layer";
|
|
2
|
+
import type { IncomingMessage } from "node:http";
|
|
3
3
|
|
|
4
|
-
export function parseRange(
|
|
5
|
-
request
|
|
6
|
-
): PFrameInternal.HttpRange | null | undefined {
|
|
7
|
-
const range = request.headers['range'];
|
|
4
|
+
export function parseRange(request: IncomingMessage): PFrameInternal.HttpRange | null | undefined {
|
|
5
|
+
const range = request.headers["range"];
|
|
8
6
|
if (range === undefined) return undefined;
|
|
9
7
|
|
|
10
8
|
const match = range.match(/^bytes=(\d*)-(\d*)$/);
|
|
@@ -22,17 +20,17 @@ export function parseRange(
|
|
|
22
20
|
|
|
23
21
|
// Both start and end are specified - bounded range
|
|
24
22
|
if (start !== null && end !== null) {
|
|
25
|
-
return { type:
|
|
23
|
+
return { type: "bounded", start, end };
|
|
26
24
|
}
|
|
27
25
|
|
|
28
26
|
// Only start is specified - offset range (e.g., bytes=500-)
|
|
29
27
|
if (start !== null && end === null) {
|
|
30
|
-
return { type:
|
|
28
|
+
return { type: "offset", offset: start };
|
|
31
29
|
}
|
|
32
30
|
|
|
33
31
|
// Only end is specified - suffix range (e.g., bytes=-500)
|
|
34
32
|
if (start === null && end !== null) {
|
|
35
|
-
return { type:
|
|
33
|
+
return { type: "suffix", suffix: end };
|
|
36
34
|
}
|
|
37
35
|
|
|
38
36
|
// Neither start nor end specified (bytes=-) - invalid
|
package/src/utils/status.ts
CHANGED
|
@@ -12,5 +12,5 @@ export const StatusCode = {
|
|
|
12
12
|
PreconditionFailed: 412, // <https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/412>
|
|
13
13
|
RangeNotSatisfiable: 416, // <https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/416>
|
|
14
14
|
InternalServerError: 500, // <https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/500>
|
|
15
|
-
GatewayTimeout: 504 // <https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/504>
|
|
15
|
+
GatewayTimeout: 504, // <https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/504>
|
|
16
16
|
} as const;
|