@milaboratories/pframes-rs-serv 1.1.0 → 1.1.2

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 (81) hide show
  1. package/dist/export.cjs +20 -0
  2. package/dist/export.cjs.map +1 -0
  3. package/dist/export.d.ts +3 -0
  4. package/dist/export.d.ts.map +1 -0
  5. package/dist/export.js +18 -0
  6. package/dist/export.js.map +1 -0
  7. package/dist/fs-store.cjs +91 -0
  8. package/dist/fs-store.cjs.map +1 -0
  9. package/dist/fs-store.d.ts +14 -0
  10. package/dist/fs-store.d.ts.map +1 -0
  11. package/dist/fs-store.js +89 -0
  12. package/dist/fs-store.js.map +1 -0
  13. package/dist/handler.cjs +171 -0
  14. package/dist/handler.cjs.map +1 -0
  15. package/dist/handler.d.ts +15 -0
  16. package/dist/handler.d.ts.map +1 -0
  17. package/dist/handler.js +168 -0
  18. package/dist/handler.js.map +1 -0
  19. package/dist/index.cjs +18 -0
  20. package/dist/index.cjs.map +1 -0
  21. package/dist/index.d.ts +6 -0
  22. package/dist/index.d.ts.map +1 -0
  23. package/dist/index.js +6 -0
  24. package/dist/index.js.map +1 -0
  25. package/dist/parquet-server.cjs +109 -0
  26. package/dist/parquet-server.cjs.map +1 -0
  27. package/dist/parquet-server.d.ts +24 -0
  28. package/dist/parquet-server.d.ts.map +1 -0
  29. package/dist/parquet-server.js +105 -0
  30. package/dist/parquet-server.js.map +1 -0
  31. package/dist/serve.cjs +103 -0
  32. package/dist/serve.cjs.map +1 -0
  33. package/dist/serve.d.ts +7 -0
  34. package/dist/serve.d.ts.map +1 -0
  35. package/dist/serve.js +101 -0
  36. package/dist/serve.js.map +1 -0
  37. package/dist/utils/etag.cjs +10 -0
  38. package/dist/utils/etag.cjs.map +1 -0
  39. package/dist/utils/etag.d.ts +15 -0
  40. package/dist/utils/etag.d.ts.map +1 -0
  41. package/dist/utils/etag.js +8 -0
  42. package/dist/utils/etag.js.map +1 -0
  43. package/dist/utils/filename.cjs +15 -0
  44. package/dist/utils/filename.cjs.map +1 -0
  45. package/dist/utils/filename.d.ts +4 -0
  46. package/dist/utils/filename.d.ts.map +1 -0
  47. package/dist/utils/filename.js +13 -0
  48. package/dist/utils/filename.js.map +1 -0
  49. package/dist/utils/headers.cjs +29 -0
  50. package/dist/utils/headers.cjs.map +1 -0
  51. package/dist/utils/headers.d.ts +31 -0
  52. package/dist/utils/headers.d.ts.map +1 -0
  53. package/dist/utils/headers.js +26 -0
  54. package/dist/utils/headers.js.map +1 -0
  55. package/dist/utils/index.d.ts +8 -0
  56. package/dist/utils/index.d.ts.map +1 -0
  57. package/dist/utils/method.cjs +16 -0
  58. package/dist/utils/method.cjs.map +1 -0
  59. package/dist/utils/method.d.ts +6 -0
  60. package/dist/utils/method.d.ts.map +1 -0
  61. package/dist/utils/method.js +12 -0
  62. package/dist/utils/method.js.map +1 -0
  63. package/dist/utils/options.cjs +135 -0
  64. package/dist/utils/options.cjs.map +1 -0
  65. package/dist/utils/options.d.ts +64 -0
  66. package/dist/utils/options.d.ts.map +1 -0
  67. package/dist/utils/options.js +133 -0
  68. package/dist/utils/options.js.map +1 -0
  69. package/dist/utils/range.cjs +34 -0
  70. package/dist/utils/range.cjs.map +1 -0
  71. package/dist/utils/range.d.ts +4 -0
  72. package/dist/utils/range.d.ts.map +1 -0
  73. package/dist/utils/range.js +32 -0
  74. package/dist/utils/range.js.map +1 -0
  75. package/dist/utils/status.cjs +21 -0
  76. package/dist/utils/status.cjs.map +1 -0
  77. package/dist/utils/status.d.ts +17 -0
  78. package/dist/utils/status.d.ts.map +1 -0
  79. package/dist/utils/status.js +19 -0
  80. package/dist/utils/status.js.map +1 -0
  81. package/package.json +8 -8
@@ -0,0 +1,7 @@
1
+ import type { PFrameInternal } from '@milaboratories/pl-model-middle-layer';
2
+ /**
3
+ * Serve HTTP requests using the provided handler.
4
+ * Returns a promise that resolves when the server is stopped.
5
+ */
6
+ export declare function serve({ handler, port, noHttps, noAuth }: PFrameInternal.HttpServerOptions): Promise<PFrameInternal.HttpServer>;
7
+ //# sourceMappingURL=serve.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"serve.d.ts","sourceRoot":"","sources":["../src/serve.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uCAAuC,CAAC;AA4C5E;;;GAGG;AACH,wBAAsB,KAAK,CAAC,EAC1B,OAAO,EACP,IAAQ,EACR,OAAO,EACP,MAAM,EACP,EAAE,cAAc,CAAC,iBAAiB,GAAG,OAAO,CAAC,cAAc,CAAC,UAAU,CAAC,CAkEvE"}
package/dist/serve.js ADDED
@@ -0,0 +1,101 @@
1
+ import { createServer } from 'node:http';
2
+ import { createServer as createServer$1 } from 'node:https';
3
+ import { Deferred } from '@milaboratories/helpers';
4
+ import { base64Encode, ensureError } from '@milaboratories/pl-model-common';
5
+ import { generate } from 'selfsigned';
6
+ import { randomUUID } from 'node:crypto';
7
+ import { authorizeRequestHandler } from './handler.js';
8
+
9
+ /** Generate a self-signed certificate for localhost */
10
+ async function generateCertificate() {
11
+ return await generate([{ name: 'commonName', value: 'localhost' }], {
12
+ keySize: 2048,
13
+ algorithm: 'sha256',
14
+ extensions: [
15
+ {
16
+ name: 'subjectAltName',
17
+ altNames: [
18
+ { type: 2, value: 'localhost' }, // DNS
19
+ { type: 7, ip: '127.0.0.1' }, // IPv4
20
+ { type: 7, ip: '::1' } // IPv6
21
+ ]
22
+ }
23
+ ]
24
+ });
25
+ }
26
+ /** Create an object store URL from the server address info. */
27
+ function createObjectStoreUrl(info, noHttps) {
28
+ const protocol = noHttps ? 'http' : 'https';
29
+ switch (info.family) {
30
+ case 'IPv4':
31
+ return `${protocol}://${info.address}:${info.port}/`;
32
+ case 'IPv6':
33
+ return `${protocol}://[${info.address}]:${info.port}/`;
34
+ default:
35
+ return `${protocol}://localhost:${info.port}/`;
36
+ }
37
+ }
38
+ /**
39
+ * Serve HTTP requests using the provided handler.
40
+ * Returns a promise that resolves when the server is stopped.
41
+ */
42
+ async function serve({ handler, port = 0, noHttps, noAuth }) {
43
+ const started = new Deferred();
44
+ try {
45
+ let stopped = null;
46
+ let authToken;
47
+ let effectiveHandler = handler;
48
+ if (!noAuth) {
49
+ authToken = randomUUID();
50
+ effectiveHandler = authorizeRequestHandler(effectiveHandler, authToken);
51
+ }
52
+ // Create HTTP server
53
+ let encodedCaCert;
54
+ const defaultOptions = {
55
+ keepAlive: true
56
+ };
57
+ let server;
58
+ if (noHttps) {
59
+ server = createServer(defaultOptions, effectiveHandler);
60
+ }
61
+ else {
62
+ const { cert, private: key, public: ca } = await generateCertificate();
63
+ encodedCaCert = base64Encode(cert);
64
+ server = createServer$1({ ...defaultOptions, cert, key, ca }, effectiveHandler);
65
+ }
66
+ server
67
+ .on('listening', () => {
68
+ // Cast is safe by specification <https://nodejs.org/api/net.html#serveraddress>
69
+ const url = createObjectStoreUrl(server.address(), noHttps);
70
+ stopped = new Deferred();
71
+ started.resolve({
72
+ get info() {
73
+ return { url, authToken, encodedCaCert };
74
+ },
75
+ get stopped() {
76
+ return stopped.promise;
77
+ },
78
+ stop() {
79
+ server.close();
80
+ return stopped.promise;
81
+ }
82
+ });
83
+ })
84
+ .on('error', (err) => {
85
+ started.reject(err);
86
+ stopped?.reject(err);
87
+ })
88
+ .on('close', () => stopped?.resolve())
89
+ .listen({
90
+ host: 'localhost',
91
+ port
92
+ });
93
+ }
94
+ catch (error) {
95
+ started.reject(ensureError(error));
96
+ }
97
+ return started.promise;
98
+ }
99
+
100
+ export { serve };
101
+ //# sourceMappingURL=serve.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"serve.js","sources":["../src/serve.ts"],"sourcesContent":["import {\n createServer as createHttpServer,\n type RequestListener,\n type Server as HttpServer,\n type ServerOptions\n} from 'node:http';\nimport {\n createServer as createHttpsServer,\n type Server as HttpsServer\n} from 'node:https';\nimport type { AddressInfo } from 'node:net';\nimport { Deferred } from '@milaboratories/helpers';\nimport type { PFrameInternal } from '@milaboratories/pl-model-middle-layer';\nimport {\n base64Encode,\n Base64Encoded,\n ensureError\n} from '@milaboratories/pl-model-common';\nimport { generate, type GenerateResult } from 'selfsigned';\nimport { randomUUID } from 'node:crypto';\nimport { authorizeRequestHandler } from './handler';\n\n/** Generate a self-signed certificate for localhost */\nasync function generateCertificate(): Promise<GenerateResult> {\n return await generate([{ name: 'commonName', value: 'localhost' }], {\n keySize: 2048,\n algorithm: 'sha256',\n extensions: [\n {\n name: 'subjectAltName',\n altNames: [\n { type: 2, value: 'localhost' }, // DNS\n { type: 7, ip: '127.0.0.1' }, // IPv4\n { type: 7, ip: '::1' } // IPv6\n ]\n }\n ]\n });\n}\n\n/** Create an object store URL from the server address info. */\nfunction createObjectStoreUrl(\n info: AddressInfo,\n noHttps?: true\n): PFrameInternal.ObjectStoreUrl {\n const protocol = noHttps ? 'http' : 'https';\n switch (info.family) {\n case 'IPv4':\n return `${protocol}://${info.address}:${info.port}/` as PFrameInternal.ObjectStoreUrl;\n case 'IPv6':\n return `${protocol}://[${info.address}]:${info.port}/` as PFrameInternal.ObjectStoreUrl;\n default:\n return `${protocol}://localhost:${info.port}/` as PFrameInternal.ObjectStoreUrl;\n }\n}\n\n/**\n * Serve HTTP requests using the provided handler.\n * Returns a promise that resolves when the server is stopped.\n */\nexport async function serve({\n handler,\n port = 0,\n noHttps,\n noAuth\n}: PFrameInternal.HttpServerOptions): Promise<PFrameInternal.HttpServer> {\n const started = new Deferred<PFrameInternal.HttpServer>();\n try {\n let stopped: Deferred<void> | null = null;\n\n let authToken: PFrameInternal.HttpAuthorizationToken | undefined;\n let effectiveHandler: RequestListener = handler;\n if (!noAuth) {\n authToken = randomUUID() as PFrameInternal.HttpAuthorizationToken;\n effectiveHandler = authorizeRequestHandler(effectiveHandler, authToken);\n }\n\n // Create HTTP server\n let encodedCaCert: Base64Encoded<PFrameInternal.PemCertificate> | undefined;\n const defaultOptions: ServerOptions = {\n keepAlive: true\n };\n let server: HttpServer | HttpsServer;\n\n if (noHttps) {\n server = createHttpServer(defaultOptions, effectiveHandler);\n } else {\n const { cert, private: key, public: ca } = await generateCertificate();\n encodedCaCert = base64Encode(cert as PFrameInternal.PemCertificate);\n server = createHttpsServer(\n { ...defaultOptions, cert, key, ca },\n effectiveHandler\n );\n }\n\n server\n .on('listening', () => {\n // Cast is safe by specification <https://nodejs.org/api/net.html#serveraddress>\n const url = createObjectStoreUrl(\n server.address() as AddressInfo,\n noHttps\n );\n stopped = new Deferred<void>();\n\n started.resolve({\n get info(): PFrameInternal.HttpServerInfo {\n return { url, authToken, encodedCaCert };\n },\n get stopped(): Promise<void> {\n return stopped!.promise;\n },\n stop(): Promise<void> {\n server.close();\n return stopped!.promise;\n }\n });\n })\n .on('error', (err) => {\n started.reject(err);\n stopped?.reject(err);\n })\n .on('close', () => stopped?.resolve())\n .listen({\n host: 'localhost',\n port\n });\n } catch (error: unknown) {\n started.reject(ensureError(error));\n }\n\n return started.promise;\n}\n"],"names":["createHttpServer","createHttpsServer"],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ 'use strict';
2
+
3
+ function createETag(filename) {
4
+ // For immutable files, use URL-safe base64 encoded filename as ETag
5
+ const filenameETag = Buffer.from(filename, 'utf8').toString('base64url');
6
+ return `"${filenameETag}"`;
7
+ }
8
+
9
+ exports.createETag = createETag;
10
+ //# sourceMappingURL=etag.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"etag.cjs","sources":["../../src/utils/etag.ts"],"sourcesContent":["import type { Branded } from '@milaboratories/pl-model-common';\nimport type { PFrameInternal } from '@milaboratories/pl-model-middle-layer';\n\n/**\n * See <https://datatracker.ietf.org/doc/html/rfc9110#section-8.8.3>\n *\n * Examples:\n *\n * ```text\n * ETag: \"xyzzy\"\n * ETag: W/\"xyzzy\"\n * ```\n */\nexport type Etag = Branded<string, 'Etag'>;\n\nexport function createETag(filename: PFrameInternal.ParquetFileName): Etag {\n // For immutable files, use URL-safe base64 encoded filename as ETag\n const filenameETag = Buffer.from(filename, 'utf8').toString('base64url');\n return `\"${filenameETag}\"` as Etag;\n}\n"],"names":[],"mappings":";;AAeM,SAAU,UAAU,CAAC,QAAwC,EAAA;;AAEjE,IAAA,MAAM,YAAY,GAAG,MAAM,CAAC,IAAI,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC;IACxE,OAAO,CAAA,CAAA,EAAI,YAAY,CAAA,CAAA,CAAW;AACpC;;;;"}
@@ -0,0 +1,15 @@
1
+ import type { Branded } from '@milaboratories/pl-model-common';
2
+ import type { PFrameInternal } from '@milaboratories/pl-model-middle-layer';
3
+ /**
4
+ * See <https://datatracker.ietf.org/doc/html/rfc9110#section-8.8.3>
5
+ *
6
+ * Examples:
7
+ *
8
+ * ```text
9
+ * ETag: "xyzzy"
10
+ * ETag: W/"xyzzy"
11
+ * ```
12
+ */
13
+ export type Etag = Branded<string, 'Etag'>;
14
+ export declare function createETag(filename: PFrameInternal.ParquetFileName): Etag;
15
+ //# sourceMappingURL=etag.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"etag.d.ts","sourceRoot":"","sources":["../../src/utils/etag.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,iCAAiC,CAAC;AAC/D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uCAAuC,CAAC;AAE5E;;;;;;;;;GASG;AACH,MAAM,MAAM,IAAI,GAAG,OAAO,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AAE3C,wBAAgB,UAAU,CAAC,QAAQ,EAAE,cAAc,CAAC,eAAe,GAAG,IAAI,CAIzE"}
@@ -0,0 +1,8 @@
1
+ function createETag(filename) {
2
+ // For immutable files, use URL-safe base64 encoded filename as ETag
3
+ const filenameETag = Buffer.from(filename, 'utf8').toString('base64url');
4
+ return `"${filenameETag}"`;
5
+ }
6
+
7
+ export { createETag };
8
+ //# sourceMappingURL=etag.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"etag.js","sources":["../../src/utils/etag.ts"],"sourcesContent":["import type { Branded } from '@milaboratories/pl-model-common';\nimport type { PFrameInternal } from '@milaboratories/pl-model-middle-layer';\n\n/**\n * See <https://datatracker.ietf.org/doc/html/rfc9110#section-8.8.3>\n *\n * Examples:\n *\n * ```text\n * ETag: \"xyzzy\"\n * ETag: W/\"xyzzy\"\n * ```\n */\nexport type Etag = Branded<string, 'Etag'>;\n\nexport function createETag(filename: PFrameInternal.ParquetFileName): Etag {\n // For immutable files, use URL-safe base64 encoded filename as ETag\n const filenameETag = Buffer.from(filename, 'utf8').toString('base64url');\n return `\"${filenameETag}\"` as Etag;\n}\n"],"names":[],"mappings":"AAeM,SAAU,UAAU,CAAC,QAAwC,EAAA;;AAEjE,IAAA,MAAM,YAAY,GAAG,MAAM,CAAC,IAAI,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC;IACxE,OAAO,CAAA,CAAA,EAAI,YAAY,CAAA,CAAA,CAAW;AACpC;;;;"}
@@ -0,0 +1,15 @@
1
+ 'use strict';
2
+
3
+ const PARQUET_FILENAME_REGEX = /^\/([\w\-.]+.parquet)$/;
4
+ function getFilenameFromUrl(request) {
5
+ const url = request.url;
6
+ if (url === undefined)
7
+ return null;
8
+ const match = url.match(PARQUET_FILENAME_REGEX);
9
+ if (!match)
10
+ return null;
11
+ return match[1];
12
+ }
13
+
14
+ exports.getFilenameFromUrl = getFilenameFromUrl;
15
+ //# sourceMappingURL=filename.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"filename.cjs","sources":["../../src/utils/filename.ts"],"sourcesContent":["import type { PFrameInternal } from '@milaboratories/pl-model-middle-layer';\nimport type { IncomingMessage } from 'node:http';\n\nconst PARQUET_FILENAME_REGEX = /^\\/([\\w\\-.]+.parquet)$/;\n\nexport function getFilenameFromUrl(\n request: IncomingMessage\n): PFrameInternal.ParquetFileName | null {\n const url = request.url;\n if (url === undefined) return null;\n\n const match = url.match(PARQUET_FILENAME_REGEX);\n if (!match) return null;\n\n return match[1] as PFrameInternal.ParquetFileName;\n}\n"],"names":[],"mappings":";;AAGA,MAAM,sBAAsB,GAAG,wBAAwB;AAEjD,SAAU,kBAAkB,CAChC,OAAwB,EAAA;AAExB,IAAA,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG;IACvB,IAAI,GAAG,KAAK,SAAS;AAAE,QAAA,OAAO,IAAI;IAElC,MAAM,KAAK,GAAG,GAAG,CAAC,KAAK,CAAC,sBAAsB,CAAC;AAC/C,IAAA,IAAI,CAAC,KAAK;AAAE,QAAA,OAAO,IAAI;AAEvB,IAAA,OAAO,KAAK,CAAC,CAAC,CAAmC;AACnD;;;;"}
@@ -0,0 +1,4 @@
1
+ import type { PFrameInternal } from '@milaboratories/pl-model-middle-layer';
2
+ import type { IncomingMessage } from 'node:http';
3
+ export declare function getFilenameFromUrl(request: IncomingMessage): PFrameInternal.ParquetFileName | null;
4
+ //# sourceMappingURL=filename.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"filename.d.ts","sourceRoot":"","sources":["../../src/utils/filename.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uCAAuC,CAAC;AAC5E,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AAIjD,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,eAAe,GACvB,cAAc,CAAC,eAAe,GAAG,IAAI,CAQvC"}
@@ -0,0 +1,13 @@
1
+ const PARQUET_FILENAME_REGEX = /^\/([\w\-.]+.parquet)$/;
2
+ function getFilenameFromUrl(request) {
3
+ const url = request.url;
4
+ if (url === undefined)
5
+ return null;
6
+ const match = url.match(PARQUET_FILENAME_REGEX);
7
+ if (!match)
8
+ return null;
9
+ return match[1];
10
+ }
11
+
12
+ export { getFilenameFromUrl };
13
+ //# sourceMappingURL=filename.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"filename.js","sources":["../../src/utils/filename.ts"],"sourcesContent":["import type { PFrameInternal } from '@milaboratories/pl-model-middle-layer';\nimport type { IncomingMessage } from 'node:http';\n\nconst PARQUET_FILENAME_REGEX = /^\\/([\\w\\-.]+.parquet)$/;\n\nexport function getFilenameFromUrl(\n request: IncomingMessage\n): PFrameInternal.ParquetFileName | null {\n const url = request.url;\n if (url === undefined) return null;\n\n const match = url.match(PARQUET_FILENAME_REGEX);\n if (!match) return null;\n\n return match[1] as PFrameInternal.ParquetFileName;\n}\n"],"names":[],"mappings":"AAGA,MAAM,sBAAsB,GAAG,wBAAwB;AAEjD,SAAU,kBAAkB,CAChC,OAAwB,EAAA;AAExB,IAAA,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG;IACvB,IAAI,GAAG,KAAK,SAAS;AAAE,QAAA,OAAO,IAAI;IAElC,MAAM,KAAK,GAAG,GAAG,CAAC,KAAK,CAAC,sBAAsB,CAAC;AAC/C,IAAA,IAAI,CAAC,KAAK;AAAE,QAAA,OAAO,IAAI;AAEvB,IAAA,OAAO,KAAK,CAAC,CAAC,CAAmC;AACnD;;;;"}
@@ -0,0 +1,29 @@
1
+ 'use strict';
2
+
3
+ /** HTTP header names used in the parquet server handler */
4
+ const HeaderName = {
5
+ AcceptRanges: 'accept-ranges',
6
+ Allow: 'allow',
7
+ Authorization: 'authorization',
8
+ CacheControl: 'cache-control',
9
+ Connection: 'connection',
10
+ ContentLength: 'content-length',
11
+ ContentRange: 'content-range',
12
+ ContentType: 'content-type',
13
+ ETag: 'etag',
14
+ LastModified: 'last-modified',
15
+ WWWAuthenticate: 'www-authenticate'
16
+ };
17
+ /** HTTP header values used in the parquet server handler */
18
+ const HeaderValue = {
19
+ AcceptRanges: 'bytes',
20
+ Allow: 'GET, HEAD',
21
+ CacheControl: 'public, immutable, max-age=31536000',
22
+ Connection: 'close',
23
+ ContentType: 'application/octet-stream',
24
+ WWWAuthenticate: 'Bearer realm="parquet-server"'
25
+ };
26
+
27
+ exports.HeaderName = HeaderName;
28
+ exports.HeaderValue = HeaderValue;
29
+ //# sourceMappingURL=headers.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"headers.cjs","sources":["../../src/utils/headers.ts"],"sourcesContent":["/** HTTP header names used in the parquet server handler */\nexport const HeaderName = {\n Accept: 'accept',\n AcceptRanges: 'accept-ranges',\n Allow: 'allow',\n Authorization: 'authorization',\n CacheControl: 'cache-control',\n Connection: 'connection',\n ContentLength: 'content-length',\n ContentRange: 'content-range',\n ContentType: 'content-type',\n Date: 'date',\n ETag: 'etag',\n IfMatch: 'if-match',\n IfModifiedSince: 'if-modified-since',\n IfNoneMatch: 'if-none-match',\n IfUnmodifiedSince: 'if-unmodified-since',\n LastModified: 'last-modified',\n Range: 'range',\n WWWAuthenticate: 'www-authenticate'\n} as const;\n\n/** HTTP header values used in the parquet server handler */\nexport const HeaderValue = {\n AcceptRanges: 'bytes',\n Allow: 'GET, HEAD',\n CacheControl: 'public, immutable, max-age=31536000',\n Connection: 'close',\n ContentType: 'application/octet-stream',\n WWWAuthenticate: 'Bearer realm=\"parquet-server\"'\n} as const;\n"],"names":[],"mappings":";;AAAA;AACO,MAAM,UAAU,GAAG;AACxB,IACA,YAAY,EAAE,eAAe;AAC7B,IAAA,KAAK,EAAE,OAAO;AACd,IAAA,aAAa,EAAE,eAAe;AAC9B,IAAA,YAAY,EAAE,eAAe;AAC7B,IAAA,UAAU,EAAE,YAAY;AACxB,IAAA,aAAa,EAAE,gBAAgB;AAC/B,IAAA,YAAY,EAAE,eAAe;AAC7B,IAAA,WAAW,EAAE,cAAc;AAC3B,IACA,IAAI,EAAE,MAAM;AACZ,IAIA,YAAY,EAAE,eAAe;AAC7B,IACA,eAAe,EAAE;;AAGnB;AACO,MAAM,WAAW,GAAG;AACzB,IAAA,YAAY,EAAE,OAAO;AACrB,IAAA,KAAK,EAAE,WAAW;AAClB,IAAA,YAAY,EAAE,qCAAqC;AACnD,IAAA,UAAU,EAAE,OAAO;AACnB,IAAA,WAAW,EAAE,0BAA0B;AACvC,IAAA,eAAe,EAAE;;;;;;"}
@@ -0,0 +1,31 @@
1
+ /** HTTP header names used in the parquet server handler */
2
+ export declare const HeaderName: {
3
+ readonly Accept: "accept";
4
+ readonly AcceptRanges: "accept-ranges";
5
+ readonly Allow: "allow";
6
+ readonly Authorization: "authorization";
7
+ readonly CacheControl: "cache-control";
8
+ readonly Connection: "connection";
9
+ readonly ContentLength: "content-length";
10
+ readonly ContentRange: "content-range";
11
+ readonly ContentType: "content-type";
12
+ readonly Date: "date";
13
+ readonly ETag: "etag";
14
+ readonly IfMatch: "if-match";
15
+ readonly IfModifiedSince: "if-modified-since";
16
+ readonly IfNoneMatch: "if-none-match";
17
+ readonly IfUnmodifiedSince: "if-unmodified-since";
18
+ readonly LastModified: "last-modified";
19
+ readonly Range: "range";
20
+ readonly WWWAuthenticate: "www-authenticate";
21
+ };
22
+ /** HTTP header values used in the parquet server handler */
23
+ export declare const HeaderValue: {
24
+ readonly AcceptRanges: "bytes";
25
+ readonly Allow: "GET, HEAD";
26
+ readonly CacheControl: "public, immutable, max-age=31536000";
27
+ readonly Connection: "close";
28
+ readonly ContentType: "application/octet-stream";
29
+ readonly WWWAuthenticate: "Bearer realm=\"parquet-server\"";
30
+ };
31
+ //# sourceMappingURL=headers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"headers.d.ts","sourceRoot":"","sources":["../../src/utils/headers.ts"],"names":[],"mappings":"AAAA,2DAA2D;AAC3D,eAAO,MAAM,UAAU;;;;;;;;;;;;;;;;;;;CAmBb,CAAC;AAEX,4DAA4D;AAC5D,eAAO,MAAM,WAAW;;;;;;;CAOd,CAAC"}
@@ -0,0 +1,26 @@
1
+ /** HTTP header names used in the parquet server handler */
2
+ const HeaderName = {
3
+ AcceptRanges: 'accept-ranges',
4
+ Allow: 'allow',
5
+ Authorization: 'authorization',
6
+ CacheControl: 'cache-control',
7
+ Connection: 'connection',
8
+ ContentLength: 'content-length',
9
+ ContentRange: 'content-range',
10
+ ContentType: 'content-type',
11
+ ETag: 'etag',
12
+ LastModified: 'last-modified',
13
+ WWWAuthenticate: 'www-authenticate'
14
+ };
15
+ /** HTTP header values used in the parquet server handler */
16
+ const HeaderValue = {
17
+ AcceptRanges: 'bytes',
18
+ Allow: 'GET, HEAD',
19
+ CacheControl: 'public, immutable, max-age=31536000',
20
+ Connection: 'close',
21
+ ContentType: 'application/octet-stream',
22
+ WWWAuthenticate: 'Bearer realm="parquet-server"'
23
+ };
24
+
25
+ export { HeaderName, HeaderValue };
26
+ //# sourceMappingURL=headers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"headers.js","sources":["../../src/utils/headers.ts"],"sourcesContent":["/** HTTP header names used in the parquet server handler */\nexport const HeaderName = {\n Accept: 'accept',\n AcceptRanges: 'accept-ranges',\n Allow: 'allow',\n Authorization: 'authorization',\n CacheControl: 'cache-control',\n Connection: 'connection',\n ContentLength: 'content-length',\n ContentRange: 'content-range',\n ContentType: 'content-type',\n Date: 'date',\n ETag: 'etag',\n IfMatch: 'if-match',\n IfModifiedSince: 'if-modified-since',\n IfNoneMatch: 'if-none-match',\n IfUnmodifiedSince: 'if-unmodified-since',\n LastModified: 'last-modified',\n Range: 'range',\n WWWAuthenticate: 'www-authenticate'\n} as const;\n\n/** HTTP header values used in the parquet server handler */\nexport const HeaderValue = {\n AcceptRanges: 'bytes',\n Allow: 'GET, HEAD',\n CacheControl: 'public, immutable, max-age=31536000',\n Connection: 'close',\n ContentType: 'application/octet-stream',\n WWWAuthenticate: 'Bearer realm=\"parquet-server\"'\n} as const;\n"],"names":[],"mappings":"AAAA;AACO,MAAM,UAAU,GAAG;AACxB,IACA,YAAY,EAAE,eAAe;AAC7B,IAAA,KAAK,EAAE,OAAO;AACd,IAAA,aAAa,EAAE,eAAe;AAC9B,IAAA,YAAY,EAAE,eAAe;AAC7B,IAAA,UAAU,EAAE,YAAY;AACxB,IAAA,aAAa,EAAE,gBAAgB;AAC/B,IAAA,YAAY,EAAE,eAAe;AAC7B,IAAA,WAAW,EAAE,cAAc;AAC3B,IACA,IAAI,EAAE,MAAM;AACZ,IAIA,YAAY,EAAE,eAAe;AAC7B,IACA,eAAe,EAAE;;AAGnB;AACO,MAAM,WAAW,GAAG;AACzB,IAAA,YAAY,EAAE,OAAO;AACrB,IAAA,KAAK,EAAE,WAAW;AAClB,IAAA,YAAY,EAAE,qCAAqC;AACnD,IAAA,UAAU,EAAE,OAAO;AACnB,IAAA,WAAW,EAAE,0BAA0B;AACvC,IAAA,eAAe,EAAE;;;;;"}
@@ -0,0 +1,8 @@
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';
8
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,cAAc,QAAQ,CAAC;AACvB,cAAc,WAAW,CAAC;AAC1B,cAAc,WAAW,CAAC;AAC1B,cAAc,SAAS,CAAC;AACxB,cAAc,UAAU,CAAC;AACzB,cAAc,UAAU,CAAC"}
@@ -0,0 +1,16 @@
1
+ 'use strict';
2
+
3
+ function isGetOrHead(method) {
4
+ return method === 'GET' || method === 'HEAD';
5
+ }
6
+ function isGet(method) {
7
+ return method === 'GET';
8
+ }
9
+ function isHead(method) {
10
+ return method === 'HEAD';
11
+ }
12
+
13
+ exports.isGet = isGet;
14
+ exports.isGetOrHead = isGetOrHead;
15
+ exports.isHead = isHead;
16
+ //# sourceMappingURL=method.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"method.cjs","sources":["../../src/utils/method.ts"],"sourcesContent":["import type { PFrameInternal } from '@milaboratories/pl-model-middle-layer';\nimport type { IncomingHttpHeaders } from 'node:http';\n\nexport function isGetOrHead(\n method: IncomingHttpHeaders['method']\n): method is PFrameInternal.HttpMethod {\n return method === 'GET' || method === 'HEAD';\n}\n\nexport function isGet(\n method: PFrameInternal.HttpMethod\n): method is Extract<PFrameInternal.HttpMethod, 'GET'> {\n return method === 'GET';\n}\n\nexport function isHead(\n method: PFrameInternal.HttpMethod\n): method is Extract<PFrameInternal.HttpMethod, 'HEAD'> {\n return method === 'HEAD';\n}\n"],"names":[],"mappings":";;AAGM,SAAU,WAAW,CACzB,MAAqC,EAAA;AAErC,IAAA,OAAO,MAAM,KAAK,KAAK,IAAI,MAAM,KAAK,MAAM;AAC9C;AAEM,SAAU,KAAK,CACnB,MAAiC,EAAA;IAEjC,OAAO,MAAM,KAAK,KAAK;AACzB;AAEM,SAAU,MAAM,CACpB,MAAiC,EAAA;IAEjC,OAAO,MAAM,KAAK,MAAM;AAC1B;;;;;;"}
@@ -0,0 +1,6 @@
1
+ import type { PFrameInternal } from '@milaboratories/pl-model-middle-layer';
2
+ import type { IncomingHttpHeaders } from 'node:http';
3
+ export declare function isGetOrHead(method: IncomingHttpHeaders['method']): method is PFrameInternal.HttpMethod;
4
+ export declare function isGet(method: PFrameInternal.HttpMethod): method is Extract<PFrameInternal.HttpMethod, 'GET'>;
5
+ export declare function isHead(method: PFrameInternal.HttpMethod): method is Extract<PFrameInternal.HttpMethod, 'HEAD'>;
6
+ //# sourceMappingURL=method.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"method.d.ts","sourceRoot":"","sources":["../../src/utils/method.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uCAAuC,CAAC;AAC5E,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,WAAW,CAAC;AAErD,wBAAgB,WAAW,CACzB,MAAM,EAAE,mBAAmB,CAAC,QAAQ,CAAC,GACpC,MAAM,IAAI,cAAc,CAAC,UAAU,CAErC;AAED,wBAAgB,KAAK,CACnB,MAAM,EAAE,cAAc,CAAC,UAAU,GAChC,MAAM,IAAI,OAAO,CAAC,cAAc,CAAC,UAAU,EAAE,KAAK,CAAC,CAErD;AAED,wBAAgB,MAAM,CACpB,MAAM,EAAE,cAAc,CAAC,UAAU,GAChC,MAAM,IAAI,OAAO,CAAC,cAAc,CAAC,UAAU,EAAE,MAAM,CAAC,CAEtD"}
@@ -0,0 +1,12 @@
1
+ function isGetOrHead(method) {
2
+ return method === 'GET' || method === 'HEAD';
3
+ }
4
+ function isGet(method) {
5
+ return method === 'GET';
6
+ }
7
+ function isHead(method) {
8
+ return method === 'HEAD';
9
+ }
10
+
11
+ export { isGet, isGetOrHead, isHead };
12
+ //# sourceMappingURL=method.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"method.js","sources":["../../src/utils/method.ts"],"sourcesContent":["import type { PFrameInternal } from '@milaboratories/pl-model-middle-layer';\nimport type { IncomingHttpHeaders } from 'node:http';\n\nexport function isGetOrHead(\n method: IncomingHttpHeaders['method']\n): method is PFrameInternal.HttpMethod {\n return method === 'GET' || method === 'HEAD';\n}\n\nexport function isGet(\n method: PFrameInternal.HttpMethod\n): method is Extract<PFrameInternal.HttpMethod, 'GET'> {\n return method === 'GET';\n}\n\nexport function isHead(\n method: PFrameInternal.HttpMethod\n): method is Extract<PFrameInternal.HttpMethod, 'HEAD'> {\n return method === 'HEAD';\n}\n"],"names":[],"mappings":"AAGM,SAAU,WAAW,CACzB,MAAqC,EAAA;AAErC,IAAA,OAAO,MAAM,KAAK,KAAK,IAAI,MAAM,KAAK,MAAM;AAC9C;AAEM,SAAU,KAAK,CACnB,MAAiC,EAAA;IAEjC,OAAO,MAAM,KAAK,KAAK;AACzB;AAEM,SAAU,MAAM,CACpB,MAAiC,EAAA;IAEjC,OAAO,MAAM,KAAK,MAAM;AAC1B;;;;"}
@@ -0,0 +1,135 @@
1
+ 'use strict';
2
+
3
+ class EtagMatch {
4
+ etagMatch;
5
+ constructor(etagMatch) {
6
+ this.etagMatch = etagMatch;
7
+ }
8
+ static parse(etagMatch) {
9
+ if (etagMatch === undefined)
10
+ return null;
11
+ if (etagMatch === '*') {
12
+ return new EtagMatch({ type: 'wildcard' });
13
+ }
14
+ else {
15
+ return new EtagMatch({
16
+ type: 'match',
17
+ value: etagMatch.split(',').map((etag) => etag.trim())
18
+ });
19
+ }
20
+ }
21
+ matches(etag) {
22
+ switch (this.etagMatch.type) {
23
+ case 'match':
24
+ return this.etagMatch.value.includes(etag);
25
+ case 'wildcard':
26
+ return true;
27
+ }
28
+ }
29
+ }
30
+ class DateMatch {
31
+ mtime;
32
+ constructor(mtime) {
33
+ this.mtime = mtime;
34
+ }
35
+ static parse(dateMatch) {
36
+ if (dateMatch === undefined)
37
+ return null;
38
+ const time = Date.parse(dateMatch);
39
+ if (isNaN(time))
40
+ return null;
41
+ return new DateMatch(new Date(time));
42
+ }
43
+ matches(mtime) {
44
+ return this.mtime.getTime() / 1_000 === mtime.getTime() / 1_000;
45
+ }
46
+ }
47
+ class Options {
48
+ /**
49
+ * See <https://datatracker.ietf.org/doc/html/rfc9110#name-if-match>
50
+ *
51
+ * Examples:
52
+ *
53
+ * ```text
54
+ * If-Match: "xyzzy"
55
+ * If-Match: "xyzzy", "r2d2xxxx", "c3piozzzz"
56
+ * If-Match: *
57
+ * ```
58
+ */
59
+ ifMatch;
60
+ /**
61
+ * See <https://datatracker.ietf.org/doc/html/rfc9110#section-13.1.2>
62
+ *
63
+ * Examples:
64
+ *
65
+ * ```text
66
+ * If-None-Match: "xyzzy"
67
+ * If-None-Match: "xyzzy", "r2d2xxxx", "c3piozzzz"
68
+ * If-None-Match: *
69
+ * ```
70
+ */
71
+ ifNoneMatch;
72
+ /**
73
+ * See <https://datatracker.ietf.org/doc/html/rfc9110#section-13.1.3>
74
+ *
75
+ * Examples:
76
+ *
77
+ * ```text
78
+ * If-Modified-Since: Mon, 07 Jan 2002 19:43:36 GMT
79
+ * ```
80
+ */
81
+ ifModifiedSince;
82
+ /**
83
+ * See <https://datatracker.ietf.org/doc/html/rfc9110#section-13.1.4>
84
+ *
85
+ * Examples:
86
+ *
87
+ * ```text
88
+ * If-Unmodified-Since: Mon, 07 Jan 2002 19:43:36 GMT
89
+ * ```
90
+ */
91
+ ifUnmodifiedSince;
92
+ constructor(request) {
93
+ this.ifMatch = EtagMatch.parse(request.headers['if-match']);
94
+ this.ifNoneMatch = EtagMatch.parse(request.headers['if-none-match']);
95
+ this.ifModifiedSince = DateMatch.parse(request.headers['if-modified-since']);
96
+ this.ifUnmodifiedSince = DateMatch.parse(request.headers['if-unmodified-since']);
97
+ }
98
+ /**
99
+ * RFC 9110 section 13.1.1: If-Match precondition evaluation
100
+ * RFC 9110 section 13.1.4: If-Unmodified-Since precondition evaluation
101
+ * See <https://datatracker.ietf.org/doc/html/rfc9110#section-13.1.1>
102
+ * See <https://datatracker.ietf.org/doc/html/rfc9110#section-13.1.4>
103
+ */
104
+ preconditionFailed(etag, mtime) {
105
+ // If-Match precondition takes precedence
106
+ if (this.ifMatch !== null && !this.ifMatch.matches(etag)) {
107
+ return true;
108
+ }
109
+ // If-Unmodified-Since precondition (only if no If-Match header)
110
+ if (this.ifMatch === null && this.ifUnmodifiedSince !== null) {
111
+ return !this.ifUnmodifiedSince.matches(mtime);
112
+ }
113
+ return false;
114
+ }
115
+ /**
116
+ * RFC 9110 section 13.1.2: If-None-Match precondition evaluation
117
+ * RFC 9110 section 13.1.3: If-Modified-Since precondition evaluation
118
+ * See <https://datatracker.ietf.org/doc/html/rfc9110#section-13.1.2>
119
+ * See <https://datatracker.ietf.org/doc/html/rfc9110#section-13.1.3>
120
+ */
121
+ notModified(etag, mtime) {
122
+ // If-None-Match takes precedence over If-Modified-Since
123
+ if (this.ifNoneMatch !== null) {
124
+ return this.ifNoneMatch.matches(etag);
125
+ }
126
+ // If-Modified-Since (only if no If-None-Match header)
127
+ if (this.ifModifiedSince !== null) {
128
+ return this.ifModifiedSince.matches(mtime);
129
+ }
130
+ return false;
131
+ }
132
+ }
133
+
134
+ exports.Options = Options;
135
+ //# sourceMappingURL=options.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"options.cjs","sources":["../../src/utils/options.ts"],"sourcesContent":["import type { IncomingMessage } from 'node:http';\nimport type { Etag } from './etag';\n\ntype EtagMatchType =\n | {\n type: 'match';\n value: Etag[];\n }\n | {\n type: 'wildcard';\n };\n\nclass EtagMatch {\n private constructor(private readonly etagMatch: EtagMatchType) {}\n\n static parse(etagMatch: string | undefined): EtagMatch | null {\n if (etagMatch === undefined) return null;\n if (etagMatch === '*') {\n return new EtagMatch({ type: 'wildcard' });\n } else {\n return new EtagMatch({\n type: 'match',\n value: etagMatch.split(',').map((etag) => etag.trim() as Etag)\n });\n }\n }\n\n matches(etag: Etag): boolean {\n switch (this.etagMatch.type) {\n case 'match':\n return this.etagMatch.value.includes(etag);\n case 'wildcard':\n return true;\n }\n }\n}\n\nclass DateMatch {\n private constructor(private readonly mtime: Date) {}\n\n static parse(dateMatch: string | undefined): DateMatch | null {\n if (dateMatch === undefined) return null;\n const time = Date.parse(dateMatch);\n if (isNaN(time)) return null;\n return new DateMatch(new Date(time));\n }\n\n matches(mtime: Date): boolean {\n return this.mtime.getTime() / 1_000 === mtime.getTime() / 1_000;\n }\n}\n\nexport class Options {\n /**\n * See <https://datatracker.ietf.org/doc/html/rfc9110#name-if-match>\n *\n * Examples:\n *\n * ```text\n * If-Match: \"xyzzy\"\n * If-Match: \"xyzzy\", \"r2d2xxxx\", \"c3piozzzz\"\n * If-Match: *\n * ```\n */\n private ifMatch: EtagMatch | null;\n\n /**\n * See <https://datatracker.ietf.org/doc/html/rfc9110#section-13.1.2>\n *\n * Examples:\n *\n * ```text\n * If-None-Match: \"xyzzy\"\n * If-None-Match: \"xyzzy\", \"r2d2xxxx\", \"c3piozzzz\"\n * If-None-Match: *\n * ```\n */\n private ifNoneMatch: EtagMatch | null;\n\n /**\n * See <https://datatracker.ietf.org/doc/html/rfc9110#section-13.1.3>\n *\n * Examples:\n *\n * ```text\n * If-Modified-Since: Mon, 07 Jan 2002 19:43:36 GMT\n * ```\n */\n private ifModifiedSince: DateMatch | null;\n\n /**\n * See <https://datatracker.ietf.org/doc/html/rfc9110#section-13.1.4>\n *\n * Examples:\n *\n * ```text\n * If-Unmodified-Since: Mon, 07 Jan 2002 19:43:36 GMT\n * ```\n */\n private ifUnmodifiedSince: DateMatch | null;\n\n constructor(request: IncomingMessage) {\n this.ifMatch = EtagMatch.parse(request.headers['if-match']);\n this.ifNoneMatch = EtagMatch.parse(request.headers['if-none-match']);\n this.ifModifiedSince = DateMatch.parse(\n request.headers['if-modified-since']\n );\n this.ifUnmodifiedSince = DateMatch.parse(\n request.headers['if-unmodified-since']\n );\n }\n\n /**\n * RFC 9110 section 13.1.1: If-Match precondition evaluation\n * RFC 9110 section 13.1.4: If-Unmodified-Since precondition evaluation\n * See <https://datatracker.ietf.org/doc/html/rfc9110#section-13.1.1>\n * See <https://datatracker.ietf.org/doc/html/rfc9110#section-13.1.4>\n */\n preconditionFailed(etag: Etag, mtime: Date): boolean {\n // If-Match precondition takes precedence\n if (this.ifMatch !== null && !this.ifMatch.matches(etag)) {\n return true;\n }\n\n // If-Unmodified-Since precondition (only if no If-Match header)\n if (this.ifMatch === null && this.ifUnmodifiedSince !== null) {\n return !this.ifUnmodifiedSince.matches(mtime);\n }\n\n return false;\n }\n\n /**\n * RFC 9110 section 13.1.2: If-None-Match precondition evaluation\n * RFC 9110 section 13.1.3: If-Modified-Since precondition evaluation\n * See <https://datatracker.ietf.org/doc/html/rfc9110#section-13.1.2>\n * See <https://datatracker.ietf.org/doc/html/rfc9110#section-13.1.3>\n */\n notModified(etag: Etag, mtime: Date): boolean {\n // If-None-Match takes precedence over If-Modified-Since\n if (this.ifNoneMatch !== null) {\n return this.ifNoneMatch.matches(etag);\n }\n\n // If-Modified-Since (only if no If-None-Match header)\n if (this.ifModifiedSince !== null) {\n return this.ifModifiedSince.matches(mtime);\n }\n\n return false;\n }\n}\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,64 @@
1
+ import type { IncomingMessage } from 'node:http';
2
+ import type { Etag } from './etag';
3
+ export declare class Options {
4
+ /**
5
+ * See <https://datatracker.ietf.org/doc/html/rfc9110#name-if-match>
6
+ *
7
+ * Examples:
8
+ *
9
+ * ```text
10
+ * If-Match: "xyzzy"
11
+ * If-Match: "xyzzy", "r2d2xxxx", "c3piozzzz"
12
+ * If-Match: *
13
+ * ```
14
+ */
15
+ private ifMatch;
16
+ /**
17
+ * See <https://datatracker.ietf.org/doc/html/rfc9110#section-13.1.2>
18
+ *
19
+ * Examples:
20
+ *
21
+ * ```text
22
+ * If-None-Match: "xyzzy"
23
+ * If-None-Match: "xyzzy", "r2d2xxxx", "c3piozzzz"
24
+ * If-None-Match: *
25
+ * ```
26
+ */
27
+ private ifNoneMatch;
28
+ /**
29
+ * See <https://datatracker.ietf.org/doc/html/rfc9110#section-13.1.3>
30
+ *
31
+ * Examples:
32
+ *
33
+ * ```text
34
+ * If-Modified-Since: Mon, 07 Jan 2002 19:43:36 GMT
35
+ * ```
36
+ */
37
+ private ifModifiedSince;
38
+ /**
39
+ * See <https://datatracker.ietf.org/doc/html/rfc9110#section-13.1.4>
40
+ *
41
+ * Examples:
42
+ *
43
+ * ```text
44
+ * If-Unmodified-Since: Mon, 07 Jan 2002 19:43:36 GMT
45
+ * ```
46
+ */
47
+ private ifUnmodifiedSince;
48
+ constructor(request: IncomingMessage);
49
+ /**
50
+ * RFC 9110 section 13.1.1: If-Match precondition evaluation
51
+ * RFC 9110 section 13.1.4: If-Unmodified-Since precondition evaluation
52
+ * See <https://datatracker.ietf.org/doc/html/rfc9110#section-13.1.1>
53
+ * See <https://datatracker.ietf.org/doc/html/rfc9110#section-13.1.4>
54
+ */
55
+ preconditionFailed(etag: Etag, mtime: Date): boolean;
56
+ /**
57
+ * RFC 9110 section 13.1.2: If-None-Match precondition evaluation
58
+ * RFC 9110 section 13.1.3: If-Modified-Since precondition evaluation
59
+ * See <https://datatracker.ietf.org/doc/html/rfc9110#section-13.1.2>
60
+ * See <https://datatracker.ietf.org/doc/html/rfc9110#section-13.1.3>
61
+ */
62
+ notModified(etag: Etag, mtime: Date): boolean;
63
+ }
64
+ //# sourceMappingURL=options.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"options.d.ts","sourceRoot":"","sources":["../../src/utils/options.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AACjD,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAmDnC,qBAAa,OAAO;IAClB;;;;;;;;;;OAUG;IACH,OAAO,CAAC,OAAO,CAAmB;IAElC;;;;;;;;;;OAUG;IACH,OAAO,CAAC,WAAW,CAAmB;IAEtC;;;;;;;;OAQG;IACH,OAAO,CAAC,eAAe,CAAmB;IAE1C;;;;;;;;OAQG;IACH,OAAO,CAAC,iBAAiB,CAAmB;gBAEhC,OAAO,EAAE,eAAe;IAWpC;;;;;OAKG;IACH,kBAAkB,CAAC,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,GAAG,OAAO;IAcpD;;;;;OAKG;IACH,WAAW,CAAC,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,GAAG,OAAO;CAa9C"}