@milaboratories/pframes-rs-serv 1.1.18 → 1.1.20
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/dist/export.cjs +16 -17
- package/dist/export.cjs.map +1 -1
- package/dist/export.d.ts +6 -2
- package/dist/export.d.ts.map +1 -1
- package/dist/export.js +16 -15
- package/dist/export.js.map +1 -1
- package/dist/fs-store.cjs +83 -87
- package/dist/fs-store.cjs.map +1 -1
- package/dist/fs-store.d.ts +14 -10
- package/dist/fs-store.d.ts.map +1 -1
- package/dist/fs-store.js +83 -85
- package/dist/fs-store.js.map +1 -1
- package/dist/handler.cjs +132 -157
- package/dist/handler.cjs.map +1 -1
- package/dist/handler.d.ts +8 -4
- package/dist/handler.d.ts.map +1 -1
- package/dist/handler.js +132 -155
- package/dist/handler.js.map +1 -1
- package/dist/index.cjs +13 -18
- package/dist/index.d.ts +6 -6
- package/dist/index.js +6 -6
- package/dist/parquet-server.cjs +95 -101
- package/dist/parquet-server.cjs.map +1 -1
- package/dist/parquet-server.d.ts +16 -12
- package/dist/parquet-server.d.ts.map +1 -1
- package/dist/parquet-server.js +95 -98
- package/dist/parquet-server.js.map +1 -1
- package/dist/serve.cjs +98 -94
- package/dist/serve.cjs.map +1 -1
- package/dist/serve.d.ts +11 -2
- package/dist/serve.d.ts.map +1 -1
- package/dist/serve.js +98 -92
- package/dist/serve.js.map +1 -1
- package/dist/utils/etag.cjs +5 -7
- package/dist/utils/etag.cjs.map +1 -1
- package/dist/utils/etag.js +5 -5
- package/dist/utils/etag.js.map +1 -1
- package/dist/utils/filename.cjs +9 -11
- package/dist/utils/filename.cjs.map +1 -1
- package/dist/utils/filename.js +9 -9
- package/dist/utils/filename.js.map +1 -1
- package/dist/utils/headers.cjs +28 -21
- package/dist/utils/headers.cjs.map +1 -1
- package/dist/utils/headers.js +28 -19
- package/dist/utils/headers.js.map +1 -1
- package/dist/utils/method.cjs +7 -7
- package/dist/utils/method.cjs.map +1 -1
- package/dist/utils/method.js +7 -5
- package/dist/utils/method.js.map +1 -1
- package/dist/utils/options.cjs +111 -134
- package/dist/utils/options.cjs.map +1 -1
- package/dist/utils/options.js +111 -132
- package/dist/utils/options.js.map +1 -1
- package/dist/utils/range.cjs +26 -31
- package/dist/utils/range.cjs.map +1 -1
- package/dist/utils/range.js +26 -29
- package/dist/utils/range.js.map +1 -1
- package/dist/utils/status.cjs +17 -17
- package/dist/utils/status.cjs.map +1 -1
- package/dist/utils/status.js +17 -15
- package/dist/utils/status.js.map +1 -1
- package/package.json +4 -4
- package/src/parquet-server.ts +16 -2
- package/dist/index.cjs.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/utils/etag.d.ts +0 -15
- package/dist/utils/etag.d.ts.map +0 -1
- package/dist/utils/filename.d.ts +0 -4
- package/dist/utils/filename.d.ts.map +0 -1
- package/dist/utils/headers.d.ts +0 -31
- package/dist/utils/headers.d.ts.map +0 -1
- package/dist/utils/index.d.ts +0 -8
- package/dist/utils/index.d.ts.map +0 -1
- package/dist/utils/method.d.ts +0 -6
- package/dist/utils/method.d.ts.map +0 -1
- package/dist/utils/options.d.ts +0 -64
- package/dist/utils/options.d.ts.map +0 -1
- package/dist/utils/range.d.ts +0 -4
- package/dist/utils/range.d.ts.map +0 -1
- package/dist/utils/status.d.ts +0 -17
- package/dist/utils/status.d.ts.map +0 -1
package/dist/parquet-server.cjs
CHANGED
|
@@ -1,109 +1,103 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
var plModelCommon = require('@milaboratories/pl-model-common');
|
|
10
|
-
|
|
11
|
-
var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
|
|
1
|
+
const require_export = require("./export.cjs");
|
|
2
|
+
let _milaboratories_pl_model_common = require("@milaboratories/pl-model-common");
|
|
3
|
+
let node_path = require("node:path");
|
|
4
|
+
let node_child_process = require("node:child_process");
|
|
5
|
+
let node_readline_promises = require("node:readline/promises");
|
|
6
|
+
let node_url = require("node:url");
|
|
7
|
+
let commander = require("commander");
|
|
8
|
+
//#region src/parquet-server.ts
|
|
12
9
|
const Options = {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
10
|
+
NoHttps: "--no-https",
|
|
11
|
+
NoAuth: "--no-auth",
|
|
12
|
+
Port: "--port"
|
|
16
13
|
};
|
|
17
14
|
/**
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
15
|
+
* Serves parquet files from the given root directory.
|
|
16
|
+
* Manages the server lifecycle with graceful shutdown.
|
|
17
|
+
*/
|
|
21
18
|
async function runParquetServer() {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
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();
|
|
19
|
+
const program = new commander.Command();
|
|
20
|
+
program.name("parquet-server").description("Serve parquet files from a directory over HTTP(S)").argument("<root-directory>", "Root directory containing parquet files").option(Options.NoHttps, "Downgrade HTTPS to HTTP").option(Options.NoAuth, "Disable authorization").option(`${Options.Port} <number>`, "Port to listen on", (value) => {
|
|
21
|
+
const port = parseInt(value, 10);
|
|
22
|
+
if (isNaN(port) || port < 0 || port > 65535) throw new commander.InvalidArgumentError("valid port numbers are 0-65535");
|
|
23
|
+
return port;
|
|
24
|
+
}, 0).action(async (rootDir, options) => {
|
|
25
|
+
const abortController = new AbortController();
|
|
26
|
+
process.on("SIGINT", () => abortController.abort()).on("SIGTERM", () => abortController.abort());
|
|
27
|
+
abortController.signal.throwIfAborted();
|
|
28
|
+
const store = await require_export.HttpHelpers.createFsStore({
|
|
29
|
+
rootDir,
|
|
30
|
+
logger: (level, message) => {
|
|
31
|
+
const timestamp = new Date(Date.now()).toISOString();
|
|
32
|
+
console.log(`[${timestamp}] [${level}] ${message}`);
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
abortController.signal.throwIfAborted();
|
|
36
|
+
const handler = require_export.HttpHelpers.createRequestHandler({ store });
|
|
37
|
+
const server = await require_export.HttpHelpers.createHttpServer({
|
|
38
|
+
handler,
|
|
39
|
+
...!options.https && { noHttps: true },
|
|
40
|
+
...!options.auth && { noAuth: true },
|
|
41
|
+
port: options.port
|
|
42
|
+
});
|
|
43
|
+
abortController.signal.onabort = () => server.stop();
|
|
44
|
+
abortController.signal.throwIfAborted();
|
|
45
|
+
const serverInfo = (0, _milaboratories_pl_model_common.stringifyJson)(server.info);
|
|
46
|
+
console.log(serverInfo);
|
|
47
|
+
await server.stopped;
|
|
48
|
+
});
|
|
49
|
+
await program.parseAsync();
|
|
64
50
|
}
|
|
65
51
|
/**
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
class ParquetServer {
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
52
|
+
* Reference implementation of a parquet server runner for tests:
|
|
53
|
+
* - Reads the server configuration from the spawned process stdout
|
|
54
|
+
* - Forwards the server logs to the console
|
|
55
|
+
* - Shuts down the server on dispose
|
|
56
|
+
*/
|
|
57
|
+
var ParquetServer = class ParquetServer {
|
|
58
|
+
#process;
|
|
59
|
+
#info;
|
|
60
|
+
#lineReader;
|
|
61
|
+
constructor(process, info, lineReader) {
|
|
62
|
+
this.#process = process;
|
|
63
|
+
this.#info = info;
|
|
64
|
+
this.#lineReader = lineReader;
|
|
65
|
+
}
|
|
66
|
+
get info() {
|
|
67
|
+
return this.#info;
|
|
68
|
+
}
|
|
69
|
+
static async serve(rootDir, options) {
|
|
70
|
+
const serverProcess = (0, node_child_process.spawn)("node", [
|
|
71
|
+
(0, node_path.join)((0, node_path.dirname)((0, node_url.fileURLToPath)(require("url").pathToFileURL(__filename).href)), "..", "bin", "parquet-server.mjs"),
|
|
72
|
+
rootDir,
|
|
73
|
+
...options?.noHttps ? [Options.NoHttps] : [],
|
|
74
|
+
...options?.noAuth ? [Options.NoAuth] : [],
|
|
75
|
+
...options?.port ? [Options.Port, options.port.toString()] : []
|
|
76
|
+
], { stdio: [
|
|
77
|
+
"ignore",
|
|
78
|
+
"pipe",
|
|
79
|
+
"inherit"
|
|
80
|
+
] });
|
|
81
|
+
const lineReader = (0, node_readline_promises.createInterface)({ input: serverProcess.stdout });
|
|
82
|
+
const exitPromise = new Promise((_, reject) => {
|
|
83
|
+
serverProcess.once("exit", (code, signal) => {
|
|
84
|
+
reject(/* @__PURE__ */ new Error(`parquet-server exited before emitting server info (code=${code}, signal=${signal})`));
|
|
85
|
+
});
|
|
86
|
+
serverProcess.once("error", reject);
|
|
87
|
+
});
|
|
88
|
+
const firstLine = await Promise.race([lineReader[Symbol.asyncIterator]().next(), exitPromise]);
|
|
89
|
+
if (firstLine.value === void 0) throw new Error("parquet-server stdout closed without emitting server info");
|
|
90
|
+
const serverInfo = (0, _milaboratories_pl_model_common.parseJson)(firstLine.value);
|
|
91
|
+
lineReader.on("line", console.log);
|
|
92
|
+
return new ParquetServer(serverProcess, serverInfo, lineReader);
|
|
93
|
+
}
|
|
94
|
+
[Symbol.dispose]() {
|
|
95
|
+
this.#lineReader.close();
|
|
96
|
+
this.#process.kill();
|
|
97
|
+
}
|
|
98
|
+
};
|
|
99
|
+
//#endregion
|
|
107
100
|
exports.ParquetServer = ParquetServer;
|
|
108
101
|
exports.runParquetServer = runParquetServer;
|
|
109
|
-
|
|
102
|
+
|
|
103
|
+
//# sourceMappingURL=parquet-server.cjs.map
|
|
@@ -1 +1 @@
|
|
|
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 { parseJson, stringifyJson, type StringifiedJson } 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\", \"
|
|
1
|
+
{"version":3,"file":"parquet-server.cjs","names":["Command","InvalidArgumentError","HttpHelpers","#process","#info","#lineReader"],"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 { parseJson, stringifyJson, type StringifiedJson } 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\", \"inherit\"],\n },\n );\n\n const lineReader = createInterface({ input: serverProcess.stdout! });\n\n const exitPromise = new Promise<never>((_, reject) => {\n serverProcess.once(\"exit\", (code, signal) => {\n reject(\n new Error(\n `parquet-server exited before emitting server info (code=${code}, signal=${signal})`,\n ),\n );\n });\n serverProcess.once(\"error\", reject);\n });\n\n const firstLine = await Promise.race([lineReader[Symbol.asyncIterator]().next(), exitPromise]);\n if (firstLine.value === undefined) {\n throw new Error(\"parquet-server stdout closed without emitting server info\");\n }\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"],"mappings":"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"}
|
package/dist/parquet-server.d.ts
CHANGED
|
@@ -1,24 +1,28 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { PFrameInternal } from "@milaboratories/pl-model-middle-layer";
|
|
2
|
+
|
|
3
|
+
//#region src/parquet-server.d.ts
|
|
2
4
|
/**
|
|
3
5
|
* Serves parquet files from the given root directory.
|
|
4
6
|
* Manages the server lifecycle with graceful shutdown.
|
|
5
7
|
*/
|
|
6
|
-
|
|
8
|
+
declare function runParquetServer(): Promise<void>;
|
|
7
9
|
/**
|
|
8
10
|
* Reference implementation of a parquet server runner for tests:
|
|
9
11
|
* - Reads the server configuration from the spawned process stdout
|
|
10
12
|
* - Forwards the server logs to the console
|
|
11
13
|
* - Shuts down the server on dispose
|
|
12
14
|
*/
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
15
|
+
declare class ParquetServer implements Disposable {
|
|
16
|
+
#private;
|
|
17
|
+
private constructor();
|
|
18
|
+
get info(): PFrameInternal.HttpServerInfo;
|
|
19
|
+
static serve(rootDir: string, options?: {
|
|
20
|
+
noHttps?: true;
|
|
21
|
+
noAuth?: true;
|
|
22
|
+
port?: number;
|
|
23
|
+
}): Promise<ParquetServer>;
|
|
24
|
+
[Symbol.dispose](): void;
|
|
23
25
|
}
|
|
26
|
+
//#endregion
|
|
27
|
+
export { ParquetServer, runParquetServer };
|
|
24
28
|
//# sourceMappingURL=parquet-server.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"parquet-server.d.ts","
|
|
1
|
+
{"version":3,"file":"parquet-server.d.ts","names":[],"sources":["../src/parquet-server.ts"],"mappings":";;;;;AAqBA;;iBAAsB,gBAAA,CAAA,GAAoB,OAAA;;;AAuE1C;;;;cAAa,aAAA,YAAyB,UAAA;EAAA;UAK7B,WAAA,CAAA;EAAA,IAUH,IAAA,CAAA,GAAQ,cAAA,CAAe,cAAA;EAAA,OAId,KAAA,CACX,OAAA,UACA,OAAA;IACE,OAAA;IACA,MAAA;IACA,IAAA;EAAA,IAED,OAAA,CAAQ,aAAA;EAAA,CA0CV,MAAA,CAAO,OAAA;AAAA"}
|
package/dist/parquet-server.js
CHANGED
|
@@ -1,105 +1,102 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import { dirname, join } from
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
|
|
1
|
+
import { HttpHelpers } from "./export.js";
|
|
2
|
+
import { parseJson, stringifyJson } from "@milaboratories/pl-model-common";
|
|
3
|
+
import { dirname, join } from "node:path";
|
|
4
|
+
import { spawn } from "node:child_process";
|
|
5
|
+
import { createInterface } from "node:readline/promises";
|
|
6
|
+
import { fileURLToPath } from "node:url";
|
|
7
|
+
import { Command, InvalidArgumentError } from "commander";
|
|
8
|
+
//#region src/parquet-server.ts
|
|
9
9
|
const Options = {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
10
|
+
NoHttps: "--no-https",
|
|
11
|
+
NoAuth: "--no-auth",
|
|
12
|
+
Port: "--port"
|
|
13
13
|
};
|
|
14
14
|
/**
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
15
|
+
* Serves parquet files from the given root directory.
|
|
16
|
+
* Manages the server lifecycle with graceful shutdown.
|
|
17
|
+
*/
|
|
18
18
|
async function runParquetServer() {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
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();
|
|
19
|
+
const program = new Command();
|
|
20
|
+
program.name("parquet-server").description("Serve parquet files from a directory over HTTP(S)").argument("<root-directory>", "Root directory containing parquet files").option(Options.NoHttps, "Downgrade HTTPS to HTTP").option(Options.NoAuth, "Disable authorization").option(`${Options.Port} <number>`, "Port to listen on", (value) => {
|
|
21
|
+
const port = parseInt(value, 10);
|
|
22
|
+
if (isNaN(port) || port < 0 || port > 65535) throw new InvalidArgumentError("valid port numbers are 0-65535");
|
|
23
|
+
return port;
|
|
24
|
+
}, 0).action(async (rootDir, options) => {
|
|
25
|
+
const abortController = new AbortController();
|
|
26
|
+
process.on("SIGINT", () => abortController.abort()).on("SIGTERM", () => abortController.abort());
|
|
27
|
+
abortController.signal.throwIfAborted();
|
|
28
|
+
const store = await HttpHelpers.createFsStore({
|
|
29
|
+
rootDir,
|
|
30
|
+
logger: (level, message) => {
|
|
31
|
+
const timestamp = new Date(Date.now()).toISOString();
|
|
32
|
+
console.log(`[${timestamp}] [${level}] ${message}`);
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
abortController.signal.throwIfAborted();
|
|
36
|
+
const handler = HttpHelpers.createRequestHandler({ store });
|
|
37
|
+
const server = await HttpHelpers.createHttpServer({
|
|
38
|
+
handler,
|
|
39
|
+
...!options.https && { noHttps: true },
|
|
40
|
+
...!options.auth && { noAuth: true },
|
|
41
|
+
port: options.port
|
|
42
|
+
});
|
|
43
|
+
abortController.signal.onabort = () => server.stop();
|
|
44
|
+
abortController.signal.throwIfAborted();
|
|
45
|
+
const serverInfo = stringifyJson(server.info);
|
|
46
|
+
console.log(serverInfo);
|
|
47
|
+
await server.stopped;
|
|
48
|
+
});
|
|
49
|
+
await program.parseAsync();
|
|
61
50
|
}
|
|
62
51
|
/**
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
class ParquetServer {
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
52
|
+
* Reference implementation of a parquet server runner for tests:
|
|
53
|
+
* - Reads the server configuration from the spawned process stdout
|
|
54
|
+
* - Forwards the server logs to the console
|
|
55
|
+
* - Shuts down the server on dispose
|
|
56
|
+
*/
|
|
57
|
+
var ParquetServer = class ParquetServer {
|
|
58
|
+
#process;
|
|
59
|
+
#info;
|
|
60
|
+
#lineReader;
|
|
61
|
+
constructor(process, info, lineReader) {
|
|
62
|
+
this.#process = process;
|
|
63
|
+
this.#info = info;
|
|
64
|
+
this.#lineReader = lineReader;
|
|
65
|
+
}
|
|
66
|
+
get info() {
|
|
67
|
+
return this.#info;
|
|
68
|
+
}
|
|
69
|
+
static async serve(rootDir, options) {
|
|
70
|
+
const serverProcess = spawn("node", [
|
|
71
|
+
join(dirname(fileURLToPath(import.meta.url)), "..", "bin", "parquet-server.mjs"),
|
|
72
|
+
rootDir,
|
|
73
|
+
...options?.noHttps ? [Options.NoHttps] : [],
|
|
74
|
+
...options?.noAuth ? [Options.NoAuth] : [],
|
|
75
|
+
...options?.port ? [Options.Port, options.port.toString()] : []
|
|
76
|
+
], { stdio: [
|
|
77
|
+
"ignore",
|
|
78
|
+
"pipe",
|
|
79
|
+
"inherit"
|
|
80
|
+
] });
|
|
81
|
+
const lineReader = createInterface({ input: serverProcess.stdout });
|
|
82
|
+
const exitPromise = new Promise((_, reject) => {
|
|
83
|
+
serverProcess.once("exit", (code, signal) => {
|
|
84
|
+
reject(/* @__PURE__ */ new Error(`parquet-server exited before emitting server info (code=${code}, signal=${signal})`));
|
|
85
|
+
});
|
|
86
|
+
serverProcess.once("error", reject);
|
|
87
|
+
});
|
|
88
|
+
const firstLine = await Promise.race([lineReader[Symbol.asyncIterator]().next(), exitPromise]);
|
|
89
|
+
if (firstLine.value === void 0) throw new Error("parquet-server stdout closed without emitting server info");
|
|
90
|
+
const serverInfo = parseJson(firstLine.value);
|
|
91
|
+
lineReader.on("line", console.log);
|
|
92
|
+
return new ParquetServer(serverProcess, serverInfo, lineReader);
|
|
93
|
+
}
|
|
94
|
+
[Symbol.dispose]() {
|
|
95
|
+
this.#lineReader.close();
|
|
96
|
+
this.#process.kill();
|
|
97
|
+
}
|
|
98
|
+
};
|
|
99
|
+
//#endregion
|
|
104
100
|
export { ParquetServer, runParquetServer };
|
|
105
|
-
|
|
101
|
+
|
|
102
|
+
//# sourceMappingURL=parquet-server.js.map
|
|
@@ -1 +1 @@
|
|
|
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 { parseJson, stringifyJson, type StringifiedJson } 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\", \"
|
|
1
|
+
{"version":3,"file":"parquet-server.js","names":["#process","#info","#lineReader"],"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 { parseJson, stringifyJson, type StringifiedJson } 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\", \"inherit\"],\n },\n );\n\n const lineReader = createInterface({ input: serverProcess.stdout! });\n\n const exitPromise = new Promise<never>((_, reject) => {\n serverProcess.once(\"exit\", (code, signal) => {\n reject(\n new Error(\n `parquet-server exited before emitting server info (code=${code}, signal=${signal})`,\n ),\n );\n });\n serverProcess.once(\"error\", reject);\n });\n\n const firstLine = await Promise.race([lineReader[Symbol.asyncIterator]().next(), exitPromise]);\n if (firstLine.value === undefined) {\n throw new Error(\"parquet-server stdout closed without emitting server info\");\n }\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"],"mappings":"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"}
|