@milaboratories/pframes-rs-serv 1.0.109 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (82) hide show
  1. package/package.json +18 -18
  2. package/src/serve.ts +13 -25
  3. package/dist/export.cjs +0 -20
  4. package/dist/export.cjs.map +0 -1
  5. package/dist/export.d.ts +0 -3
  6. package/dist/export.d.ts.map +0 -1
  7. package/dist/export.js +0 -18
  8. package/dist/export.js.map +0 -1
  9. package/dist/fs-store.cjs +0 -91
  10. package/dist/fs-store.cjs.map +0 -1
  11. package/dist/fs-store.d.ts +0 -14
  12. package/dist/fs-store.d.ts.map +0 -1
  13. package/dist/fs-store.js +0 -89
  14. package/dist/fs-store.js.map +0 -1
  15. package/dist/handler.cjs +0 -171
  16. package/dist/handler.cjs.map +0 -1
  17. package/dist/handler.d.ts +0 -15
  18. package/dist/handler.d.ts.map +0 -1
  19. package/dist/handler.js +0 -168
  20. package/dist/handler.js.map +0 -1
  21. package/dist/index.cjs +0 -18
  22. package/dist/index.cjs.map +0 -1
  23. package/dist/index.d.ts +0 -6
  24. package/dist/index.d.ts.map +0 -1
  25. package/dist/index.js +0 -6
  26. package/dist/index.js.map +0 -1
  27. package/dist/parquet-server.cjs +0 -109
  28. package/dist/parquet-server.cjs.map +0 -1
  29. package/dist/parquet-server.d.ts +0 -24
  30. package/dist/parquet-server.d.ts.map +0 -1
  31. package/dist/parquet-server.js +0 -105
  32. package/dist/parquet-server.js.map +0 -1
  33. package/dist/serve.cjs +0 -112
  34. package/dist/serve.cjs.map +0 -1
  35. package/dist/serve.d.ts +0 -7
  36. package/dist/serve.d.ts.map +0 -1
  37. package/dist/serve.js +0 -110
  38. package/dist/serve.js.map +0 -1
  39. package/dist/utils/etag.cjs +0 -10
  40. package/dist/utils/etag.cjs.map +0 -1
  41. package/dist/utils/etag.d.ts +0 -15
  42. package/dist/utils/etag.d.ts.map +0 -1
  43. package/dist/utils/etag.js +0 -8
  44. package/dist/utils/etag.js.map +0 -1
  45. package/dist/utils/filename.cjs +0 -15
  46. package/dist/utils/filename.cjs.map +0 -1
  47. package/dist/utils/filename.d.ts +0 -4
  48. package/dist/utils/filename.d.ts.map +0 -1
  49. package/dist/utils/filename.js +0 -13
  50. package/dist/utils/filename.js.map +0 -1
  51. package/dist/utils/headers.cjs +0 -29
  52. package/dist/utils/headers.cjs.map +0 -1
  53. package/dist/utils/headers.d.ts +0 -31
  54. package/dist/utils/headers.d.ts.map +0 -1
  55. package/dist/utils/headers.js +0 -26
  56. package/dist/utils/headers.js.map +0 -1
  57. package/dist/utils/index.d.ts +0 -8
  58. package/dist/utils/index.d.ts.map +0 -1
  59. package/dist/utils/method.cjs +0 -16
  60. package/dist/utils/method.cjs.map +0 -1
  61. package/dist/utils/method.d.ts +0 -6
  62. package/dist/utils/method.d.ts.map +0 -1
  63. package/dist/utils/method.js +0 -12
  64. package/dist/utils/method.js.map +0 -1
  65. package/dist/utils/options.cjs +0 -135
  66. package/dist/utils/options.cjs.map +0 -1
  67. package/dist/utils/options.d.ts +0 -64
  68. package/dist/utils/options.d.ts.map +0 -1
  69. package/dist/utils/options.js +0 -133
  70. package/dist/utils/options.js.map +0 -1
  71. package/dist/utils/range.cjs +0 -34
  72. package/dist/utils/range.cjs.map +0 -1
  73. package/dist/utils/range.d.ts +0 -4
  74. package/dist/utils/range.d.ts.map +0 -1
  75. package/dist/utils/range.js +0 -32
  76. package/dist/utils/range.js.map +0 -1
  77. package/dist/utils/status.cjs +0 -21
  78. package/dist/utils/status.cjs.map +0 -1
  79. package/dist/utils/status.d.ts +0 -17
  80. package/dist/utils/status.d.ts.map +0 -1
  81. package/dist/utils/status.js +0 -19
  82. package/dist/utils/status.js.map +0 -1
package/dist/handler.js DELETED
@@ -1,168 +0,0 @@
1
- import { pipeline } from 'node:stream/promises';
2
- import { timingSafeEqual } from 'node:crypto';
3
- import { getFilenameFromUrl } from './utils/filename.js';
4
- import { createETag } from './utils/etag.js';
5
- import { HeaderName, HeaderValue } from './utils/headers.js';
6
- import { Options } from './utils/options.js';
7
- import { parseRange } from './utils/range.js';
8
- import { isGetOrHead, isGet, isHead } from './utils/method.js';
9
- import { StatusCode } from './utils/status.js';
10
- import { isAbortError } from '@milaboratories/pl-model-common';
11
-
12
- /** Main request handler for parquet files */
13
- function handleRequest(request, response, store) {
14
- // RFC 9110 section 6.6.1: Date header should be present in all responses
15
- response.sendDate = true;
16
- // RFC 9110 section 8.6: Content-Length 0 as default for error responses
17
- response.strictContentLength = true;
18
- response.setHeader(HeaderName.ContentLength, 0);
19
- // Note: setting Content-Length disables Node.js default Transfer-Encoding: chunked
20
- // RFC 9111 section 5.2: Cache-Control header with public allows to cache authenticated responses
21
- response.setHeader(HeaderName.CacheControl, HeaderValue.CacheControl);
22
- // RFC 9110 section 15.5.6: Method not allowed
23
- const method = request.method;
24
- if (!isGetOrHead(method)) {
25
- response.setHeader(HeaderName.Allow, HeaderValue.Allow);
26
- return void response.writeHead(StatusCode.MethodNotAllowed).end();
27
- }
28
- const filename = getFilenameFromUrl(request);
29
- if (filename === null) {
30
- return void response.writeHead(StatusCode.Gone).end();
31
- }
32
- // From now on we are sure that the response would be a Parquet file
33
- response.setHeader(HeaderName.AcceptRanges, HeaderValue.AcceptRanges);
34
- response.setHeader(HeaderName.ContentType, HeaderValue.ContentType);
35
- // RFC 9110 section 8.8.3: ETag header is used for cache versioning
36
- const etag = createETag(filename);
37
- // RFC 9110 section 8.8.2: Last-Modified header field for cache validation
38
- const mtime = new Date(0); // Using fake fixed date since files are immutable
39
- // RFC 9111 section 5.2: Cache-Control header with public allows to cache authenticated responses
40
- response.setHeader(HeaderName.CacheControl, HeaderValue.CacheControl);
41
- response.setHeader(HeaderName.ETag, etag);
42
- response.setHeader(HeaderName.LastModified, mtime.toUTCString());
43
- const options = new Options(request);
44
- // RFC 9110 section 13.1.1: If-Match precondition evaluation
45
- // RFC 9110 section 13.1.4: If-Unmodified-Since precondition evaluation
46
- if (options.preconditionFailed(etag, mtime)) {
47
- return void response.writeHead(StatusCode.PreconditionFailed).end();
48
- }
49
- // RFC 9110 section 13.1.2: If-None-Match precondition evaluation
50
- // RFC 9110 section 13.1.3: If-Modified-Since precondition evaluation
51
- else if (options.notModified(etag, mtime)) {
52
- return void response.writeHead(StatusCode.NotModified).end();
53
- }
54
- const range = parseRange(request);
55
- if (range === null) {
56
- return void response.writeHead(StatusCode.BadRequest).end();
57
- }
58
- const abortController = new AbortController();
59
- request.on('close', () => abortController.abort());
60
- const signal = abortController.signal;
61
- store.request(filename, {
62
- method,
63
- range,
64
- signal,
65
- // pipeline automatically destroys the streams if they were not gracefully closed
66
- callback: async (result) => {
67
- if (request.destroyed) {
68
- // request has timed out, close the connection
69
- if (response.destroyed) {
70
- return;
71
- }
72
- else if (response.headersSent) {
73
- return void response.end();
74
- }
75
- else {
76
- response.setHeader(HeaderName.Connection, HeaderValue.Connection);
77
- return void response.writeHead(StatusCode.RequestTimeout).end();
78
- }
79
- }
80
- switch (result.type) {
81
- case 'InternalError':
82
- // object store encountered network error, retry by client can help
83
- return void response.writeHead(StatusCode.InternalServerError).end();
84
- case 'NotFound':
85
- // RFC 9110 section 15.4.5: Not found
86
- return void response.writeHead(StatusCode.NotFound).end();
87
- case 'RangeNotSatisfiable':
88
- // RFC 9110 section 15.5.17: Range not satisfiable
89
- response.setHeader(HeaderName.ContentRange, `bytes */${result.size}`);
90
- return void response.writeHead(StatusCode.RangeNotSatisfiable).end();
91
- }
92
- if (isGet(method) && !result.data) {
93
- // object store implementation is incorrect, retry by client cannot help
94
- return void response.writeHead(StatusCode.GatewayTimeout).end();
95
- }
96
- if (range) {
97
- // RFC 9110 section 14.4: Partial content response
98
- response.setHeader(HeaderName.ContentLength, result.range.end - result.range.start + 1);
99
- response.setHeader(HeaderName.ContentRange, `bytes ${result.range.start}-${result.range.end}/${result.size}`);
100
- response.writeHead(StatusCode.PartialContent);
101
- }
102
- else {
103
- // RFC 9110 section 15.3.1: OK response
104
- response.setHeader(HeaderName.ContentLength, result.size);
105
- response.writeHead(StatusCode.Ok);
106
- }
107
- // RFC 9110 section 9.3.2: HEAD method must not return message body
108
- if (isHead(method)) {
109
- return void response.end();
110
- }
111
- try {
112
- return await pipeline(result.data, response, { signal });
113
- }
114
- catch (error) {
115
- if (!isAbortError(error))
116
- throw error;
117
- }
118
- }
119
- });
120
- }
121
- /**
122
- * Create a request handler for serving files from an object store
123
- * compatible with HTTP/1.1 as defined in RFC 9110 and RFC 9111:
124
- * - <https://datatracker.ietf.org/doc/html/rfc9110>
125
- * - <https://datatracker.ietf.org/doc/html/rfc9111>
126
- *
127
- * Accepts only paths of the form `/<filename>.parquet`, returns 410 Gone otherwise
128
- * Assumes that files are immutable (and sets cache headers accordingly)
129
- */
130
- function createRequestHandler(options) {
131
- const { store } = options;
132
- return (request, response) => handleRequest(request, response, store);
133
- }
134
- /** Request authorization middleware */
135
- function authorizeRequest(request, response, handler, authHeader) {
136
- // RFC 9110 section 6.6.1: Date header should be present in all responses
137
- response.sendDate = true;
138
- // RFC 9110 section 8.6: Content-Length 0 as default for error responses
139
- response.strictContentLength = true;
140
- response.setHeader(HeaderName.ContentLength, 0);
141
- // Note: setting Content-Length disables Node.js default Transfer-Encoding: chunked
142
- const actualHeader = request.headers[HeaderName.Authorization];
143
- // Early length check to avoid unnecessary processing
144
- if (!actualHeader || actualHeader.length !== authHeader.length) {
145
- // RFC 9110 section 11.6.1: WWW-Authenticate header field
146
- response.setHeader(HeaderName.WWWAuthenticate, HeaderValue.WWWAuthenticate);
147
- return void response.writeHead(StatusCode.Unauthorized).end();
148
- }
149
- // Use timing-safe comparison to prevent timing attacks
150
- // <https://developers.cloudflare.com/workers/examples/protect-against-timing-attacks/>
151
- const encoder = new TextEncoder();
152
- const receivedBuffer = encoder.encode(actualHeader);
153
- const expectedBuffer = encoder.encode(authHeader);
154
- if (receivedBuffer.byteLength !== expectedBuffer.byteLength ||
155
- !timingSafeEqual(receivedBuffer, expectedBuffer)) {
156
- response.setHeader(HeaderName.WWWAuthenticate, HeaderValue.WWWAuthenticate);
157
- return void response.writeHead(StatusCode.Unauthorized).end();
158
- }
159
- return handler(request, response);
160
- }
161
- /** Apply Bearer token authorization to @param handler */
162
- function authorizeRequestHandler(handler, authToken) {
163
- const authHeader = `Bearer ${authToken}`;
164
- return (request, response) => authorizeRequest(request, response, handler, authHeader);
165
- }
166
-
167
- export { authorizeRequestHandler, createRequestHandler };
168
- //# sourceMappingURL=handler.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"handler.js","sources":["../src/handler.ts"],"sourcesContent":["import type {\n IncomingMessage,\n RequestListener,\n ServerResponse\n} from 'node:http';\nimport { pipeline } from 'node:stream/promises';\nimport { timingSafeEqual } from 'node:crypto';\nimport type { PFrameInternal } from '@milaboratories/pl-model-middle-layer';\nimport {\n createETag,\n getFilenameFromUrl,\n parseRange,\n isGetOrHead,\n isGet,\n isHead,\n Options,\n StatusCode,\n HeaderName,\n HeaderValue\n} from './utils';\nimport { isAbortError } from '@milaboratories/pl-model-common';\n\n/** Main request handler for parquet files */\nfunction handleRequest(\n request: IncomingMessage,\n response: ServerResponse,\n store: PFrameInternal.ObjectStore\n): void {\n // RFC 9110 section 6.6.1: Date header should be present in all responses\n response.sendDate = true;\n // RFC 9110 section 8.6: Content-Length 0 as default for error responses\n response.strictContentLength = true;\n response.setHeader(HeaderName.ContentLength, 0);\n // Note: setting Content-Length disables Node.js default Transfer-Encoding: chunked\n\n // RFC 9111 section 5.2: Cache-Control header with public allows to cache authenticated responses\n response.setHeader(HeaderName.CacheControl, HeaderValue.CacheControl);\n\n // RFC 9110 section 15.5.6: Method not allowed\n const method = request.method;\n if (!isGetOrHead(method)) {\n response.setHeader(HeaderName.Allow, HeaderValue.Allow);\n return void response.writeHead(StatusCode.MethodNotAllowed).end();\n }\n\n const filename = getFilenameFromUrl(request);\n if (filename === null) {\n return void response.writeHead(StatusCode.Gone).end();\n }\n\n // From now on we are sure that the response would be a Parquet file\n response.setHeader(HeaderName.AcceptRanges, HeaderValue.AcceptRanges);\n response.setHeader(HeaderName.ContentType, HeaderValue.ContentType);\n\n // RFC 9110 section 8.8.3: ETag header is used for cache versioning\n const etag = createETag(filename);\n // RFC 9110 section 8.8.2: Last-Modified header field for cache validation\n const mtime = new Date(0); // Using fake fixed date since files are immutable\n // RFC 9111 section 5.2: Cache-Control header with public allows to cache authenticated responses\n response.setHeader(HeaderName.CacheControl, HeaderValue.CacheControl);\n response.setHeader(HeaderName.ETag, etag);\n response.setHeader(HeaderName.LastModified, mtime.toUTCString());\n\n const options = new Options(request);\n // RFC 9110 section 13.1.1: If-Match precondition evaluation\n // RFC 9110 section 13.1.4: If-Unmodified-Since precondition evaluation\n if (options.preconditionFailed(etag, mtime)) {\n return void response.writeHead(StatusCode.PreconditionFailed).end();\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 else if (options.notModified(etag, mtime)) {\n return void response.writeHead(StatusCode.NotModified).end();\n }\n\n const range = parseRange(request);\n if (range === null) {\n return void response.writeHead(StatusCode.BadRequest).end();\n }\n\n const abortController = new AbortController();\n request.on('close', () => abortController.abort());\n const signal = abortController.signal;\n\n store.request(filename, {\n method,\n range,\n signal,\n // pipeline automatically destroys the streams if they were not gracefully closed\n callback: async (result) => {\n if (request.destroyed) {\n // request has timed out, close the connection\n if (response.destroyed) {\n return;\n } else if (response.headersSent) {\n return void response.end();\n } else {\n response.setHeader(HeaderName.Connection, HeaderValue.Connection);\n return void response.writeHead(StatusCode.RequestTimeout).end();\n }\n }\n\n switch (result.type) {\n case 'InternalError':\n // object store encountered network error, retry by client can help\n return void response.writeHead(StatusCode.InternalServerError).end();\n case 'NotFound':\n // RFC 9110 section 15.4.5: Not found\n return void response.writeHead(StatusCode.NotFound).end();\n case 'RangeNotSatisfiable':\n // RFC 9110 section 15.5.17: Range not satisfiable\n response.setHeader(HeaderName.ContentRange, `bytes */${result.size}`);\n return void response.writeHead(StatusCode.RangeNotSatisfiable).end();\n case 'Ok':\n break;\n }\n\n if (isGet(method) && !result.data) {\n // object store implementation is incorrect, retry by client cannot help\n return void response.writeHead(StatusCode.GatewayTimeout).end();\n }\n\n if (range) {\n // RFC 9110 section 14.4: Partial content response\n response.setHeader(\n HeaderName.ContentLength,\n result.range.end - result.range.start + 1\n );\n response.setHeader(\n HeaderName.ContentRange,\n `bytes ${result.range.start}-${result.range.end}/${result.size}`\n );\n response.writeHead(StatusCode.PartialContent);\n } else {\n // RFC 9110 section 15.3.1: OK response\n response.setHeader(HeaderName.ContentLength, result.size);\n response.writeHead(StatusCode.Ok);\n }\n\n // RFC 9110 section 9.3.2: HEAD method must not return message body\n if (isHead(method)) {\n return void response.end();\n }\n\n try {\n return await pipeline(result.data!, response, { signal });\n } catch (error: unknown) {\n if (!isAbortError(error)) throw error;\n }\n }\n });\n}\n\n/**\n * Create a request handler for serving files from an object store\n * compatible with HTTP/1.1 as defined in RFC 9110 and RFC 9111:\n * - <https://datatracker.ietf.org/doc/html/rfc9110>\n * - <https://datatracker.ietf.org/doc/html/rfc9111>\n *\n * Accepts only paths of the form `/<filename>.parquet`, returns 410 Gone otherwise\n * Assumes that files are immutable (and sets cache headers accordingly)\n */\nexport function createRequestHandler(\n options: PFrameInternal.RequestHandlerOptions\n): RequestListener {\n const { store } = options;\n return (request, response) => handleRequest(request, response, store);\n}\n\n/** Request authorization middleware */\nfunction authorizeRequest(\n request: IncomingMessage,\n response: ServerResponse,\n handler: RequestListener,\n authHeader: string\n): void {\n // RFC 9110 section 6.6.1: Date header should be present in all responses\n response.sendDate = true;\n // RFC 9110 section 8.6: Content-Length 0 as default for error responses\n response.strictContentLength = true;\n response.setHeader(HeaderName.ContentLength, 0);\n // Note: setting Content-Length disables Node.js default Transfer-Encoding: chunked\n\n const actualHeader = request.headers[HeaderName.Authorization];\n\n // Early length check to avoid unnecessary processing\n if (!actualHeader || actualHeader.length !== authHeader.length) {\n // RFC 9110 section 11.6.1: WWW-Authenticate header field\n response.setHeader(HeaderName.WWWAuthenticate, HeaderValue.WWWAuthenticate);\n return void response.writeHead(StatusCode.Unauthorized).end();\n }\n\n // Use timing-safe comparison to prevent timing attacks\n // <https://developers.cloudflare.com/workers/examples/protect-against-timing-attacks/>\n const encoder = new TextEncoder();\n const receivedBuffer = encoder.encode(actualHeader);\n const expectedBuffer = encoder.encode(authHeader);\n\n if (\n receivedBuffer.byteLength !== expectedBuffer.byteLength ||\n !timingSafeEqual(receivedBuffer, expectedBuffer)\n ) {\n response.setHeader(HeaderName.WWWAuthenticate, HeaderValue.WWWAuthenticate);\n return void response.writeHead(StatusCode.Unauthorized).end();\n }\n\n return handler(request, response);\n}\n\n/** Apply Bearer token authorization to @param handler */\nexport function authorizeRequestHandler(\n handler: RequestListener,\n authToken: PFrameInternal.HttpAuthorizationToken\n): RequestListener {\n const authHeader = `Bearer ${authToken}`;\n return (request, response) =>\n authorizeRequest(request, response, handler, authHeader);\n}\n"],"names":[],"mappings":"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"}
package/dist/index.cjs DELETED
@@ -1,18 +0,0 @@
1
- 'use strict';
2
-
3
- var handler = require('./handler.cjs');
4
- var fsStore = require('./fs-store.cjs');
5
- var serve = require('./serve.cjs');
6
- var _export = require('./export.cjs');
7
- var parquetServer = require('./parquet-server.cjs');
8
-
9
-
10
-
11
- exports.authorizeRequestHandler = handler.authorizeRequestHandler;
12
- exports.createRequestHandler = handler.createRequestHandler;
13
- exports.FileSystemStore = fsStore.FileSystemStore;
14
- exports.serve = serve.serve;
15
- exports.HttpHelpers = _export.HttpHelpers;
16
- exports.ParquetServer = parquetServer.ParquetServer;
17
- exports.runParquetServer = parquetServer.runParquetServer;
18
- //# sourceMappingURL=index.cjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.cjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;"}
package/dist/index.d.ts DELETED
@@ -1,6 +0,0 @@
1
- export * from './handler';
2
- export * from './fs-store';
3
- export * from './serve';
4
- export * from './export';
5
- export * from './parquet-server';
6
- //# sourceMappingURL=index.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,WAAW,CAAC;AAC1B,cAAc,YAAY,CAAC;AAC3B,cAAc,SAAS,CAAC;AACxB,cAAc,UAAU,CAAC;AACzB,cAAc,kBAAkB,CAAC"}
package/dist/index.js DELETED
@@ -1,6 +0,0 @@
1
- export { authorizeRequestHandler, createRequestHandler } from './handler.js';
2
- export { FileSystemStore } from './fs-store.js';
3
- export { serve } from './serve.js';
4
- export { HttpHelpers } from './export.js';
5
- export { ParquetServer, runParquetServer } from './parquet-server.js';
6
- //# sourceMappingURL=index.js.map
package/dist/index.js.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;"}
@@ -1,109 +0,0 @@
1
- 'use strict';
2
-
3
- var node_child_process = require('node:child_process');
4
- var promises = require('node:readline/promises');
5
- var node_path = require('node:path');
6
- var node_url = require('node:url');
7
- var commander = require('commander');
8
- var _export = require('./export.cjs');
9
- var plModelCommon = require('@milaboratories/pl-model-common');
10
-
11
- var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
12
- const Options = {
13
- NoHttps: '--no-https',
14
- NoAuth: '--no-auth',
15
- Port: '--port'
16
- };
17
- /**
18
- * Serves parquet files from the given root directory.
19
- * Manages the server lifecycle with graceful shutdown.
20
- */
21
- async function runParquetServer() {
22
- const program = new commander.Command();
23
- program
24
- .name('parquet-server')
25
- .description('Serve parquet files from a directory over HTTP(S)')
26
- .argument('<root-directory>', 'Root directory containing parquet files')
27
- .option(Options.NoHttps, 'Downgrade HTTPS to HTTP')
28
- .option(Options.NoAuth, 'Disable authorization')
29
- .option(`${Options.Port} <number>`, 'Port to listen on', (value) => {
30
- const port = parseInt(value, 10);
31
- if (isNaN(port) || port < 0 || port > 65535) {
32
- throw new commander.InvalidArgumentError('valid port numbers are 0-65535');
33
- }
34
- return port;
35
- }, 0)
36
- .action(async (rootDir, options) => {
37
- const abortController = new AbortController();
38
- process
39
- .on('SIGINT', () => abortController.abort())
40
- .on('SIGTERM', () => abortController.abort());
41
- abortController.signal.throwIfAborted();
42
- const store = await _export.HttpHelpers.createFsStore({
43
- rootDir,
44
- logger: (level, message) => {
45
- const timestamp = new Date(Date.now()).toISOString();
46
- console.log(`[${timestamp}] [${level}] ${message}`);
47
- }
48
- });
49
- abortController.signal.throwIfAborted();
50
- const handler = _export.HttpHelpers.createRequestHandler({ store });
51
- const server = await _export.HttpHelpers.createHttpServer({
52
- handler,
53
- ...(!options.https && { noHttps: true }),
54
- ...(!options.auth && { noAuth: true }),
55
- port: options.port
56
- });
57
- abortController.signal.onabort = () => server.stop();
58
- abortController.signal.throwIfAborted();
59
- const serverInfo = plModelCommon.stringifyJson(server.info);
60
- console.log(serverInfo);
61
- await server.stopped;
62
- });
63
- await program.parseAsync();
64
- }
65
- /**
66
- * Reference implementation of a parquet server runner for tests:
67
- * - Reads the server configuration from the spawned process stdout
68
- * - Forwards the server logs to the console
69
- * - Shuts down the server on dispose
70
- */
71
- class ParquetServer {
72
- #process;
73
- #info;
74
- #lineReader;
75
- constructor(process, info, lineReader) {
76
- this.#process = process;
77
- this.#info = info;
78
- this.#lineReader = lineReader;
79
- }
80
- get info() {
81
- return this.#info;
82
- }
83
- static async serve(rootDir, options) {
84
- const nodeDirname = node_path.dirname(node_url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('parquet-server.cjs', document.baseURI).href))));
85
- const binPath = node_path.join(nodeDirname, '..', 'bin', 'parquet-server.mjs');
86
- const serverProcess = node_child_process.spawn('node', [
87
- binPath,
88
- rootDir,
89
- ...(options?.noHttps ? [Options.NoHttps] : []),
90
- ...(options?.noAuth ? [Options.NoAuth] : []),
91
- ...(options?.port ? [Options.Port, options.port.toString()] : [])
92
- ], {
93
- stdio: ['ignore', 'pipe', 'ignore']
94
- });
95
- const lineReader = promises.createInterface({ input: serverProcess.stdout });
96
- const firstLine = await lineReader[Symbol.asyncIterator]().next();
97
- const serverInfo = plModelCommon.parseJson(firstLine.value);
98
- lineReader.on('line', console.log);
99
- return new ParquetServer(serverProcess, serverInfo, lineReader);
100
- }
101
- [Symbol.dispose]() {
102
- this.#lineReader.close();
103
- this.#process.kill();
104
- }
105
- }
106
-
107
- exports.ParquetServer = ParquetServer;
108
- exports.runParquetServer = runParquetServer;
109
- //# sourceMappingURL=parquet-server.cjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"parquet-server.cjs","sources":["../src/parquet-server.ts"],"sourcesContent":["import { type ChildProcess, spawn } from 'node:child_process';\nimport { createInterface, type Interface } from 'node:readline/promises';\nimport { join, dirname } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport { Command, InvalidArgumentError } from 'commander';\nimport { HttpHelpers } from './export';\nimport {\n parseJson,\n stringifyJson,\n type StringifiedJson\n} from '@milaboratories/pl-model-common';\nimport { type PFrameInternal } from '@milaboratories/pl-model-middle-layer';\n\nconst Options = {\n NoHttps: '--no-https',\n NoAuth: '--no-auth',\n Port: '--port'\n} as const;\n\ntype Info = StringifiedJson<PFrameInternal.HttpServerInfo>;\n\n/**\n * Serves parquet files from the given root directory.\n * Manages the server lifecycle with graceful shutdown.\n */\nexport async function runParquetServer(): Promise<void> {\n const program = new Command();\n\n program\n .name('parquet-server')\n .description('Serve parquet files from a directory over HTTP(S)')\n .argument('<root-directory>', 'Root directory containing parquet files')\n .option(Options.NoHttps, 'Downgrade HTTPS to HTTP')\n .option(Options.NoAuth, 'Disable authorization')\n .option(\n `${Options.Port} <number>`,\n 'Port to listen on',\n (value) => {\n const port = parseInt(value, 10);\n if (isNaN(port) || port < 0 || port > 65535) {\n throw new InvalidArgumentError('valid port numbers are 0-65535');\n }\n return port;\n },\n 0\n )\n .action(\n async (\n rootDir: string,\n options: {\n https: boolean;\n auth: boolean;\n port: number;\n }\n ) => {\n const abortController = new AbortController();\n process\n .on('SIGINT', () => abortController.abort())\n .on('SIGTERM', () => abortController.abort());\n abortController.signal.throwIfAborted();\n\n const store = await HttpHelpers.createFsStore({\n rootDir,\n logger: (level, message) => {\n const timestamp = new Date(Date.now()).toISOString();\n console.log(`[${timestamp}] [${level}] ${message}`);\n }\n });\n abortController.signal.throwIfAborted();\n const handler = HttpHelpers.createRequestHandler({ store });\n\n const server = await HttpHelpers.createHttpServer({\n handler,\n ...(!options.https && { noHttps: true }),\n ...(!options.auth && { noAuth: true }),\n port: options.port\n });\n abortController.signal.onabort = () => server.stop();\n abortController.signal.throwIfAborted();\n\n const serverInfo: Info = stringifyJson(server.info);\n console.log(serverInfo);\n\n await server.stopped;\n }\n );\n\n await program.parseAsync();\n}\n\n/**\n * Reference implementation of a parquet server runner for tests:\n * - Reads the server configuration from the spawned process stdout\n * - Forwards the server logs to the console\n * - Shuts down the server on dispose\n */\nexport class ParquetServer implements Disposable {\n readonly #process: ChildProcess;\n readonly #info: PFrameInternal.HttpServerInfo;\n readonly #lineReader: Interface;\n\n private constructor(\n process: ChildProcess,\n info: PFrameInternal.HttpServerInfo,\n lineReader: Interface\n ) {\n this.#process = process;\n this.#info = info;\n this.#lineReader = lineReader;\n }\n\n get info(): PFrameInternal.HttpServerInfo {\n return this.#info;\n }\n\n static async serve(\n rootDir: string,\n options?: {\n noHttps?: true;\n noAuth?: true;\n port?: number;\n }\n ): Promise<ParquetServer> {\n const nodeDirname = dirname(fileURLToPath(import.meta.url));\n const binPath = join(nodeDirname, '..', 'bin', 'parquet-server.mjs');\n\n const serverProcess = spawn(\n 'node',\n [\n binPath,\n rootDir,\n ...(options?.noHttps ? [Options.NoHttps] : []),\n ...(options?.noAuth ? [Options.NoAuth] : []),\n ...(options?.port ? [Options.Port, options.port.toString()] : [])\n ],\n {\n stdio: ['ignore', 'pipe', 'ignore']\n }\n );\n\n const lineReader = createInterface({ input: serverProcess.stdout! });\n\n const firstLine = await lineReader[Symbol.asyncIterator]().next();\n const serverInfo = parseJson(firstLine.value as Info);\n\n lineReader.on('line', console.log);\n\n return new ParquetServer(serverProcess, serverInfo, lineReader);\n }\n\n [Symbol.dispose](): void {\n this.#lineReader.close();\n this.#process.kill();\n }\n}\n"],"names":["Command","InvalidArgumentError","HttpHelpers","stringifyJson","dirname","fileURLToPath","join","spawn","createInterface","parseJson"],"mappings":"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"}
@@ -1,24 +0,0 @@
1
- import { type PFrameInternal } from '@milaboratories/pl-model-middle-layer';
2
- /**
3
- * Serves parquet files from the given root directory.
4
- * Manages the server lifecycle with graceful shutdown.
5
- */
6
- export declare function runParquetServer(): Promise<void>;
7
- /**
8
- * Reference implementation of a parquet server runner for tests:
9
- * - Reads the server configuration from the spawned process stdout
10
- * - Forwards the server logs to the console
11
- * - Shuts down the server on dispose
12
- */
13
- export declare class ParquetServer implements Disposable {
14
- #private;
15
- private constructor();
16
- get info(): PFrameInternal.HttpServerInfo;
17
- static serve(rootDir: string, options?: {
18
- noHttps?: true;
19
- noAuth?: true;
20
- port?: number;
21
- }): Promise<ParquetServer>;
22
- [Symbol.dispose](): void;
23
- }
24
- //# sourceMappingURL=parquet-server.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"parquet-server.d.ts","sourceRoot":"","sources":["../src/parquet-server.ts"],"names":[],"mappings":"AAWA,OAAO,EAAE,KAAK,cAAc,EAAE,MAAM,uCAAuC,CAAC;AAU5E;;;GAGG;AACH,wBAAsB,gBAAgB,IAAI,OAAO,CAAC,IAAI,CAAC,CA+DtD;AAED;;;;;GAKG;AACH,qBAAa,aAAc,YAAW,UAAU;;IAK9C,OAAO;IAUP,IAAI,IAAI,IAAI,cAAc,CAAC,cAAc,CAExC;WAEY,KAAK,CAChB,OAAO,EAAE,MAAM,EACf,OAAO,CAAC,EAAE;QACR,OAAO,CAAC,EAAE,IAAI,CAAC;QACf,MAAM,CAAC,EAAE,IAAI,CAAC;QACd,IAAI,CAAC,EAAE,MAAM,CAAC;KACf,GACA,OAAO,CAAC,aAAa,CAAC;IA4BzB,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,IAAI;CAIzB"}
@@ -1,105 +0,0 @@
1
- import { spawn } from 'node:child_process';
2
- import { createInterface } from 'node:readline/promises';
3
- import { dirname, join } from 'node:path';
4
- import { fileURLToPath } from 'node:url';
5
- import { Command, InvalidArgumentError } from 'commander';
6
- import { HttpHelpers } from './export.js';
7
- import { stringifyJson, parseJson } from '@milaboratories/pl-model-common';
8
-
9
- const Options = {
10
- NoHttps: '--no-https',
11
- NoAuth: '--no-auth',
12
- Port: '--port'
13
- };
14
- /**
15
- * Serves parquet files from the given root directory.
16
- * Manages the server lifecycle with graceful shutdown.
17
- */
18
- async function runParquetServer() {
19
- const program = new Command();
20
- program
21
- .name('parquet-server')
22
- .description('Serve parquet files from a directory over HTTP(S)')
23
- .argument('<root-directory>', 'Root directory containing parquet files')
24
- .option(Options.NoHttps, 'Downgrade HTTPS to HTTP')
25
- .option(Options.NoAuth, 'Disable authorization')
26
- .option(`${Options.Port} <number>`, 'Port to listen on', (value) => {
27
- const port = parseInt(value, 10);
28
- if (isNaN(port) || port < 0 || port > 65535) {
29
- throw new InvalidArgumentError('valid port numbers are 0-65535');
30
- }
31
- return port;
32
- }, 0)
33
- .action(async (rootDir, options) => {
34
- const abortController = new AbortController();
35
- process
36
- .on('SIGINT', () => abortController.abort())
37
- .on('SIGTERM', () => abortController.abort());
38
- abortController.signal.throwIfAborted();
39
- const store = await HttpHelpers.createFsStore({
40
- rootDir,
41
- logger: (level, message) => {
42
- const timestamp = new Date(Date.now()).toISOString();
43
- console.log(`[${timestamp}] [${level}] ${message}`);
44
- }
45
- });
46
- abortController.signal.throwIfAborted();
47
- const handler = HttpHelpers.createRequestHandler({ store });
48
- const server = await HttpHelpers.createHttpServer({
49
- handler,
50
- ...(!options.https && { noHttps: true }),
51
- ...(!options.auth && { noAuth: true }),
52
- port: options.port
53
- });
54
- abortController.signal.onabort = () => server.stop();
55
- abortController.signal.throwIfAborted();
56
- const serverInfo = stringifyJson(server.info);
57
- console.log(serverInfo);
58
- await server.stopped;
59
- });
60
- await program.parseAsync();
61
- }
62
- /**
63
- * Reference implementation of a parquet server runner for tests:
64
- * - Reads the server configuration from the spawned process stdout
65
- * - Forwards the server logs to the console
66
- * - Shuts down the server on dispose
67
- */
68
- class ParquetServer {
69
- #process;
70
- #info;
71
- #lineReader;
72
- constructor(process, info, lineReader) {
73
- this.#process = process;
74
- this.#info = info;
75
- this.#lineReader = lineReader;
76
- }
77
- get info() {
78
- return this.#info;
79
- }
80
- static async serve(rootDir, options) {
81
- const nodeDirname = dirname(fileURLToPath(import.meta.url));
82
- const binPath = join(nodeDirname, '..', 'bin', 'parquet-server.mjs');
83
- const serverProcess = spawn('node', [
84
- binPath,
85
- rootDir,
86
- ...(options?.noHttps ? [Options.NoHttps] : []),
87
- ...(options?.noAuth ? [Options.NoAuth] : []),
88
- ...(options?.port ? [Options.Port, options.port.toString()] : [])
89
- ], {
90
- stdio: ['ignore', 'pipe', 'ignore']
91
- });
92
- const lineReader = createInterface({ input: serverProcess.stdout });
93
- const firstLine = await lineReader[Symbol.asyncIterator]().next();
94
- const serverInfo = parseJson(firstLine.value);
95
- lineReader.on('line', console.log);
96
- return new ParquetServer(serverProcess, serverInfo, lineReader);
97
- }
98
- [Symbol.dispose]() {
99
- this.#lineReader.close();
100
- this.#process.kill();
101
- }
102
- }
103
-
104
- export { ParquetServer, runParquetServer };
105
- //# sourceMappingURL=parquet-server.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"parquet-server.js","sources":["../src/parquet-server.ts"],"sourcesContent":["import { type ChildProcess, spawn } from 'node:child_process';\nimport { createInterface, type Interface } from 'node:readline/promises';\nimport { join, dirname } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport { Command, InvalidArgumentError } from 'commander';\nimport { HttpHelpers } from './export';\nimport {\n parseJson,\n stringifyJson,\n type StringifiedJson\n} from '@milaboratories/pl-model-common';\nimport { type PFrameInternal } from '@milaboratories/pl-model-middle-layer';\n\nconst Options = {\n NoHttps: '--no-https',\n NoAuth: '--no-auth',\n Port: '--port'\n} as const;\n\ntype Info = StringifiedJson<PFrameInternal.HttpServerInfo>;\n\n/**\n * Serves parquet files from the given root directory.\n * Manages the server lifecycle with graceful shutdown.\n */\nexport async function runParquetServer(): Promise<void> {\n const program = new Command();\n\n program\n .name('parquet-server')\n .description('Serve parquet files from a directory over HTTP(S)')\n .argument('<root-directory>', 'Root directory containing parquet files')\n .option(Options.NoHttps, 'Downgrade HTTPS to HTTP')\n .option(Options.NoAuth, 'Disable authorization')\n .option(\n `${Options.Port} <number>`,\n 'Port to listen on',\n (value) => {\n const port = parseInt(value, 10);\n if (isNaN(port) || port < 0 || port > 65535) {\n throw new InvalidArgumentError('valid port numbers are 0-65535');\n }\n return port;\n },\n 0\n )\n .action(\n async (\n rootDir: string,\n options: {\n https: boolean;\n auth: boolean;\n port: number;\n }\n ) => {\n const abortController = new AbortController();\n process\n .on('SIGINT', () => abortController.abort())\n .on('SIGTERM', () => abortController.abort());\n abortController.signal.throwIfAborted();\n\n const store = await HttpHelpers.createFsStore({\n rootDir,\n logger: (level, message) => {\n const timestamp = new Date(Date.now()).toISOString();\n console.log(`[${timestamp}] [${level}] ${message}`);\n }\n });\n abortController.signal.throwIfAborted();\n const handler = HttpHelpers.createRequestHandler({ store });\n\n const server = await HttpHelpers.createHttpServer({\n handler,\n ...(!options.https && { noHttps: true }),\n ...(!options.auth && { noAuth: true }),\n port: options.port\n });\n abortController.signal.onabort = () => server.stop();\n abortController.signal.throwIfAborted();\n\n const serverInfo: Info = stringifyJson(server.info);\n console.log(serverInfo);\n\n await server.stopped;\n }\n );\n\n await program.parseAsync();\n}\n\n/**\n * Reference implementation of a parquet server runner for tests:\n * - Reads the server configuration from the spawned process stdout\n * - Forwards the server logs to the console\n * - Shuts down the server on dispose\n */\nexport class ParquetServer implements Disposable {\n readonly #process: ChildProcess;\n readonly #info: PFrameInternal.HttpServerInfo;\n readonly #lineReader: Interface;\n\n private constructor(\n process: ChildProcess,\n info: PFrameInternal.HttpServerInfo,\n lineReader: Interface\n ) {\n this.#process = process;\n this.#info = info;\n this.#lineReader = lineReader;\n }\n\n get info(): PFrameInternal.HttpServerInfo {\n return this.#info;\n }\n\n static async serve(\n rootDir: string,\n options?: {\n noHttps?: true;\n noAuth?: true;\n port?: number;\n }\n ): Promise<ParquetServer> {\n const nodeDirname = dirname(fileURLToPath(import.meta.url));\n const binPath = join(nodeDirname, '..', 'bin', 'parquet-server.mjs');\n\n const serverProcess = spawn(\n 'node',\n [\n binPath,\n rootDir,\n ...(options?.noHttps ? [Options.NoHttps] : []),\n ...(options?.noAuth ? [Options.NoAuth] : []),\n ...(options?.port ? [Options.Port, options.port.toString()] : [])\n ],\n {\n stdio: ['ignore', 'pipe', 'ignore']\n }\n );\n\n const lineReader = createInterface({ input: serverProcess.stdout! });\n\n const firstLine = await lineReader[Symbol.asyncIterator]().next();\n const serverInfo = parseJson(firstLine.value as Info);\n\n lineReader.on('line', console.log);\n\n return new ParquetServer(serverProcess, serverInfo, lineReader);\n }\n\n [Symbol.dispose](): void {\n this.#lineReader.close();\n this.#process.kill();\n }\n}\n"],"names":[],"mappings":"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"}
package/dist/serve.cjs DELETED
@@ -1,112 +0,0 @@
1
- 'use strict';
2
-
3
- var node_http = require('node:http');
4
- var node_https = require('node:https');
5
- var helpers = require('@milaboratories/helpers');
6
- var plModelCommon = require('@milaboratories/pl-model-common');
7
- var selfsigned = require('selfsigned');
8
- var node_crypto = require('node:crypto');
9
- var handler = require('./handler.cjs');
10
-
11
- /** Generate a self-signed certificate for localhost */
12
- async function generateCertificate() {
13
- const generateResult = new helpers.Deferred();
14
- selfsigned.generate([{ name: 'commonName', value: 'localhost' }], {
15
- keySize: 2048,
16
- algorithm: 'sha256',
17
- extensions: [
18
- {
19
- name: 'subjectAltName',
20
- altNames: [
21
- { type: 2, value: 'localhost' }, // DNS
22
- { type: 7, ip: '127.0.0.1' }, // IPv4
23
- { type: 7, ip: '::1' } // IPv6
24
- ]
25
- }
26
- ]
27
- }, (error, result) => {
28
- if (error) {
29
- generateResult.reject(error);
30
- }
31
- else {
32
- generateResult.resolve(result);
33
- }
34
- });
35
- return await generateResult.promise;
36
- }
37
- /** Create an object store URL from the server address info. */
38
- function createObjectStoreUrl(info, noHttps) {
39
- const protocol = noHttps ? 'http' : 'https';
40
- switch (info.family) {
41
- case 'IPv4':
42
- return `${protocol}://${info.address}:${info.port}/`;
43
- case 'IPv6':
44
- return `${protocol}://[${info.address}]:${info.port}/`;
45
- default:
46
- return `${protocol}://localhost:${info.port}/`;
47
- }
48
- }
49
- /**
50
- * Serve HTTP requests using the provided handler.
51
- * Returns a promise that resolves when the server is stopped.
52
- */
53
- async function serve({ handler: handler$1, port = 0, noHttps, noAuth }) {
54
- const started = new helpers.Deferred();
55
- try {
56
- let stopped = null;
57
- let authToken;
58
- let effectiveHandler = handler$1;
59
- if (!noAuth) {
60
- authToken = node_crypto.randomUUID();
61
- effectiveHandler = handler.authorizeRequestHandler(effectiveHandler, authToken);
62
- }
63
- // Create HTTP server
64
- let encodedCaCert;
65
- const defaultOptions = {
66
- keepAlive: true
67
- };
68
- let server;
69
- if (noHttps) {
70
- server = node_http.createServer(defaultOptions, effectiveHandler);
71
- }
72
- else {
73
- const { cert, private: key, public: ca } = await generateCertificate();
74
- encodedCaCert = plModelCommon.base64Encode(cert);
75
- server = node_https.createServer({ ...defaultOptions, cert, key, ca }, effectiveHandler);
76
- }
77
- server
78
- .on('listening', () => {
79
- // Cast is safe by specification <https://nodejs.org/api/net.html#serveraddress>
80
- const url = createObjectStoreUrl(server.address(), noHttps);
81
- stopped = new helpers.Deferred();
82
- started.resolve({
83
- get info() {
84
- return { url, authToken, encodedCaCert };
85
- },
86
- get stopped() {
87
- return stopped.promise;
88
- },
89
- stop() {
90
- server.close();
91
- return stopped.promise;
92
- }
93
- });
94
- })
95
- .on('error', (err) => {
96
- started.reject(err);
97
- stopped?.reject(err);
98
- })
99
- .on('close', () => stopped?.resolve())
100
- .listen({
101
- host: 'localhost',
102
- port
103
- });
104
- }
105
- catch (error) {
106
- started.reject(plModelCommon.ensureError(error));
107
- }
108
- return started.promise;
109
- }
110
-
111
- exports.serve = serve;
112
- //# sourceMappingURL=serve.cjs.map