@milaboratories/pframes-rs-serv 1.1.3 → 1.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (81) hide show
  1. package/bin/parquet-server.mjs +2 -2
  2. package/dist/export.cjs +1 -1
  3. package/dist/export.cjs.map +1 -1
  4. package/dist/export.d.ts +1 -1
  5. package/dist/export.d.ts.map +1 -1
  6. package/dist/export.js +1 -1
  7. package/dist/export.js.map +1 -1
  8. package/dist/fs-store.cjs +18 -20
  9. package/dist/fs-store.cjs.map +1 -1
  10. package/dist/fs-store.d.ts +1 -1
  11. package/dist/fs-store.d.ts.map +1 -1
  12. package/dist/fs-store.js +18 -20
  13. package/dist/fs-store.js.map +1 -1
  14. package/dist/handler.cjs +5 -5
  15. package/dist/handler.cjs.map +1 -1
  16. package/dist/handler.d.ts +2 -2
  17. package/dist/handler.d.ts.map +1 -1
  18. package/dist/handler.js +5 -5
  19. package/dist/handler.js.map +1 -1
  20. package/dist/index.d.ts +5 -5
  21. package/dist/parquet-server.cjs +19 -19
  22. package/dist/parquet-server.cjs.map +1 -1
  23. package/dist/parquet-server.d.ts +1 -1
  24. package/dist/parquet-server.d.ts.map +1 -1
  25. package/dist/parquet-server.js +19 -19
  26. package/dist/parquet-server.js.map +1 -1
  27. package/dist/serve.cjs +20 -20
  28. package/dist/serve.cjs.map +1 -1
  29. package/dist/serve.d.ts +2 -2
  30. package/dist/serve.d.ts.map +1 -1
  31. package/dist/serve.js +20 -20
  32. package/dist/serve.js.map +1 -1
  33. package/dist/utils/etag.cjs +1 -1
  34. package/dist/utils/etag.cjs.map +1 -1
  35. package/dist/utils/etag.d.ts +3 -3
  36. package/dist/utils/etag.js +1 -1
  37. package/dist/utils/etag.js.map +1 -1
  38. package/dist/utils/filename.cjs.map +1 -1
  39. package/dist/utils/filename.d.ts +2 -2
  40. package/dist/utils/filename.js.map +1 -1
  41. package/dist/utils/headers.cjs +17 -17
  42. package/dist/utils/headers.cjs.map +1 -1
  43. package/dist/utils/headers.js +17 -17
  44. package/dist/utils/headers.js.map +1 -1
  45. package/dist/utils/index.d.ts +7 -7
  46. package/dist/utils/method.cjs +3 -3
  47. package/dist/utils/method.cjs.map +1 -1
  48. package/dist/utils/method.d.ts +5 -5
  49. package/dist/utils/method.js +3 -3
  50. package/dist/utils/method.js.map +1 -1
  51. package/dist/utils/options.cjs +10 -10
  52. package/dist/utils/options.cjs.map +1 -1
  53. package/dist/utils/options.d.ts +2 -2
  54. package/dist/utils/options.d.ts.map +1 -1
  55. package/dist/utils/options.js +10 -10
  56. package/dist/utils/options.js.map +1 -1
  57. package/dist/utils/range.cjs +4 -4
  58. package/dist/utils/range.cjs.map +1 -1
  59. package/dist/utils/range.d.ts +2 -2
  60. package/dist/utils/range.d.ts.map +1 -1
  61. package/dist/utils/range.js +4 -4
  62. package/dist/utils/range.js.map +1 -1
  63. package/dist/utils/status.cjs +1 -1
  64. package/dist/utils/status.cjs.map +1 -1
  65. package/dist/utils/status.js +1 -1
  66. package/dist/utils/status.js.map +1 -1
  67. package/package.json +23 -24
  68. package/src/export.ts +9 -11
  69. package/src/fs-store.ts +32 -43
  70. package/src/handler.ts +20 -28
  71. package/src/index.ts +5 -5
  72. package/src/parquet-server.ts +33 -37
  73. package/src/serve.ts +33 -49
  74. package/src/utils/etag.ts +4 -4
  75. package/src/utils/filename.ts +3 -3
  76. package/src/utils/headers.ts +24 -24
  77. package/src/utils/index.ts +7 -7
  78. package/src/utils/method.ts +10 -10
  79. package/src/utils/options.ts +14 -18
  80. package/src/utils/range.ts +7 -9
  81. package/src/utils/status.ts +1 -1
@@ -10,9 +10,9 @@ var plModelCommon = require('@milaboratories/pl-model-common');
10
10
 
11
11
  var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
12
12
  const Options = {
13
- NoHttps: '--no-https',
14
- NoAuth: '--no-auth',
15
- Port: '--port'
13
+ NoHttps: "--no-https",
14
+ NoAuth: "--no-auth",
15
+ Port: "--port",
16
16
  };
17
17
  /**
18
18
  * Serves parquet files from the given root directory.
@@ -21,30 +21,30 @@ const Options = {
21
21
  async function runParquetServer() {
22
22
  const program = new commander.Command();
23
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) => {
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
30
  const port = parseInt(value, 10);
31
31
  if (isNaN(port) || port < 0 || port > 65535) {
32
- throw new commander.InvalidArgumentError('valid port numbers are 0-65535');
32
+ throw new commander.InvalidArgumentError("valid port numbers are 0-65535");
33
33
  }
34
34
  return port;
35
35
  }, 0)
36
36
  .action(async (rootDir, options) => {
37
37
  const abortController = new AbortController();
38
38
  process
39
- .on('SIGINT', () => abortController.abort())
40
- .on('SIGTERM', () => abortController.abort());
39
+ .on("SIGINT", () => abortController.abort())
40
+ .on("SIGTERM", () => abortController.abort());
41
41
  abortController.signal.throwIfAborted();
42
42
  const store = await _export.HttpHelpers.createFsStore({
43
43
  rootDir,
44
44
  logger: (level, message) => {
45
45
  const timestamp = new Date(Date.now()).toISOString();
46
46
  console.log(`[${timestamp}] [${level}] ${message}`);
47
- }
47
+ },
48
48
  });
49
49
  abortController.signal.throwIfAborted();
50
50
  const handler = _export.HttpHelpers.createRequestHandler({ store });
@@ -52,7 +52,7 @@ async function runParquetServer() {
52
52
  handler,
53
53
  ...(!options.https && { noHttps: true }),
54
54
  ...(!options.auth && { noAuth: true }),
55
- port: options.port
55
+ port: options.port,
56
56
  });
57
57
  abortController.signal.onabort = () => server.stop();
58
58
  abortController.signal.throwIfAborted();
@@ -82,20 +82,20 @@ class ParquetServer {
82
82
  }
83
83
  static async serve(rootDir, options) {
84
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', [
85
+ const binPath = node_path.join(nodeDirname, "..", "bin", "parquet-server.mjs");
86
+ const serverProcess = node_child_process.spawn("node", [
87
87
  binPath,
88
88
  rootDir,
89
89
  ...(options?.noHttps ? [Options.NoHttps] : []),
90
90
  ...(options?.noAuth ? [Options.NoAuth] : []),
91
- ...(options?.port ? [Options.Port, options.port.toString()] : [])
91
+ ...(options?.port ? [Options.Port, options.port.toString()] : []),
92
92
  ], {
93
- stdio: ['ignore', 'pipe', 'ignore']
93
+ stdio: ["ignore", "pipe", "ignore"],
94
94
  });
95
95
  const lineReader = promises.createInterface({ input: serverProcess.stdout });
96
96
  const firstLine = await lineReader[Symbol.asyncIterator]().next();
97
97
  const serverInfo = plModelCommon.parseJson(firstLine.value);
98
- lineReader.on('line', console.log);
98
+ lineReader.on("line", console.log);
99
99
  return new ParquetServer(serverProcess, serverInfo, lineReader);
100
100
  }
101
101
  [Symbol.dispose]() {
@@ -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 {\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
+ {"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\", \"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,4 +1,4 @@
1
- import { type PFrameInternal } from '@milaboratories/pl-model-middle-layer';
1
+ import { type PFrameInternal } from "@milaboratories/pl-model-middle-layer";
2
2
  /**
3
3
  * Serves parquet files from the given root directory.
4
4
  * Manages the server lifecycle with graceful shutdown.
@@ -1 +1 @@
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
+ {"version":3,"file":"parquet-server.d.ts","sourceRoot":"","sources":["../src/parquet-server.ts"],"names":[],"mappings":"AAOA,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"}
@@ -7,9 +7,9 @@ import { HttpHelpers } from './export.js';
7
7
  import { stringifyJson, parseJson } from '@milaboratories/pl-model-common';
8
8
 
9
9
  const Options = {
10
- NoHttps: '--no-https',
11
- NoAuth: '--no-auth',
12
- Port: '--port'
10
+ NoHttps: "--no-https",
11
+ NoAuth: "--no-auth",
12
+ Port: "--port",
13
13
  };
14
14
  /**
15
15
  * Serves parquet files from the given root directory.
@@ -18,30 +18,30 @@ const Options = {
18
18
  async function runParquetServer() {
19
19
  const program = new Command();
20
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) => {
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
27
  const port = parseInt(value, 10);
28
28
  if (isNaN(port) || port < 0 || port > 65535) {
29
- throw new InvalidArgumentError('valid port numbers are 0-65535');
29
+ throw new InvalidArgumentError("valid port numbers are 0-65535");
30
30
  }
31
31
  return port;
32
32
  }, 0)
33
33
  .action(async (rootDir, options) => {
34
34
  const abortController = new AbortController();
35
35
  process
36
- .on('SIGINT', () => abortController.abort())
37
- .on('SIGTERM', () => abortController.abort());
36
+ .on("SIGINT", () => abortController.abort())
37
+ .on("SIGTERM", () => abortController.abort());
38
38
  abortController.signal.throwIfAborted();
39
39
  const store = await HttpHelpers.createFsStore({
40
40
  rootDir,
41
41
  logger: (level, message) => {
42
42
  const timestamp = new Date(Date.now()).toISOString();
43
43
  console.log(`[${timestamp}] [${level}] ${message}`);
44
- }
44
+ },
45
45
  });
46
46
  abortController.signal.throwIfAborted();
47
47
  const handler = HttpHelpers.createRequestHandler({ store });
@@ -49,7 +49,7 @@ async function runParquetServer() {
49
49
  handler,
50
50
  ...(!options.https && { noHttps: true }),
51
51
  ...(!options.auth && { noAuth: true }),
52
- port: options.port
52
+ port: options.port,
53
53
  });
54
54
  abortController.signal.onabort = () => server.stop();
55
55
  abortController.signal.throwIfAborted();
@@ -79,20 +79,20 @@ class ParquetServer {
79
79
  }
80
80
  static async serve(rootDir, options) {
81
81
  const nodeDirname = dirname(fileURLToPath(import.meta.url));
82
- const binPath = join(nodeDirname, '..', 'bin', 'parquet-server.mjs');
83
- const serverProcess = spawn('node', [
82
+ const binPath = join(nodeDirname, "..", "bin", "parquet-server.mjs");
83
+ const serverProcess = spawn("node", [
84
84
  binPath,
85
85
  rootDir,
86
86
  ...(options?.noHttps ? [Options.NoHttps] : []),
87
87
  ...(options?.noAuth ? [Options.NoAuth] : []),
88
- ...(options?.port ? [Options.Port, options.port.toString()] : [])
88
+ ...(options?.port ? [Options.Port, options.port.toString()] : []),
89
89
  ], {
90
- stdio: ['ignore', 'pipe', 'ignore']
90
+ stdio: ["ignore", "pipe", "ignore"],
91
91
  });
92
92
  const lineReader = createInterface({ input: serverProcess.stdout });
93
93
  const firstLine = await lineReader[Symbol.asyncIterator]().next();
94
94
  const serverInfo = parseJson(firstLine.value);
95
- lineReader.on('line', console.log);
95
+ lineReader.on("line", console.log);
96
96
  return new ParquetServer(serverProcess, serverInfo, lineReader);
97
97
  }
98
98
  [Symbol.dispose]() {
@@ -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 {\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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"}
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\", \"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 CHANGED
@@ -10,28 +10,28 @@ var handler = require('./handler.cjs');
10
10
 
11
11
  /** Generate a self-signed certificate for localhost */
12
12
  async function generateCertificate() {
13
- return await selfsigned.generate([{ name: 'commonName', value: 'localhost' }], {
13
+ return await selfsigned.generate([{ name: "commonName", value: "localhost" }], {
14
14
  keySize: 2048,
15
- algorithm: 'sha256',
15
+ algorithm: "sha256",
16
16
  extensions: [
17
17
  {
18
- name: 'subjectAltName',
18
+ name: "subjectAltName",
19
19
  altNames: [
20
- { type: 2, value: 'localhost' }, // DNS
21
- { type: 7, ip: '127.0.0.1' }, // IPv4
22
- { type: 7, ip: '::1' } // IPv6
23
- ]
24
- }
25
- ]
20
+ { type: 2, value: "localhost" }, // DNS
21
+ { type: 7, ip: "127.0.0.1" }, // IPv4
22
+ { type: 7, ip: "::1" }, // IPv6
23
+ ],
24
+ },
25
+ ],
26
26
  });
27
27
  }
28
28
  /** Create an object store URL from the server address info. */
29
29
  function createObjectStoreUrl(info, noHttps) {
30
- const protocol = noHttps ? 'http' : 'https';
30
+ const protocol = noHttps ? "http" : "https";
31
31
  switch (info.family) {
32
- case 'IPv4':
32
+ case "IPv4":
33
33
  return `${protocol}://${info.address}:${info.port}/`;
34
- case 'IPv6':
34
+ case "IPv6":
35
35
  return `${protocol}://[${info.address}]:${info.port}/`;
36
36
  default:
37
37
  return `${protocol}://localhost:${info.port}/`;
@@ -41,7 +41,7 @@ function createObjectStoreUrl(info, noHttps) {
41
41
  * Serve HTTP requests using the provided handler.
42
42
  * Returns a promise that resolves when the server is stopped.
43
43
  */
44
- async function serve({ handler: handler$1, port = 0, noHttps, noAuth }) {
44
+ async function serve({ handler: handler$1, port = 0, noHttps, noAuth, }) {
45
45
  const started = new helpers.Deferred();
46
46
  try {
47
47
  let stopped = null;
@@ -54,7 +54,7 @@ async function serve({ handler: handler$1, port = 0, noHttps, noAuth }) {
54
54
  // Create HTTP server
55
55
  let encodedCaCert;
56
56
  const defaultOptions = {
57
- keepAlive: true
57
+ keepAlive: true,
58
58
  };
59
59
  let server;
60
60
  if (noHttps) {
@@ -66,7 +66,7 @@ async function serve({ handler: handler$1, port = 0, noHttps, noAuth }) {
66
66
  server = node_https.createServer({ ...defaultOptions, cert, key, ca }, effectiveHandler);
67
67
  }
68
68
  server
69
- .on('listening', () => {
69
+ .on("listening", () => {
70
70
  // Cast is safe by specification <https://nodejs.org/api/net.html#serveraddress>
71
71
  const url = createObjectStoreUrl(server.address(), noHttps);
72
72
  stopped = new helpers.Deferred();
@@ -80,17 +80,17 @@ async function serve({ handler: handler$1, port = 0, noHttps, noAuth }) {
80
80
  stop() {
81
81
  server.close();
82
82
  return stopped.promise;
83
- }
83
+ },
84
84
  });
85
85
  })
86
- .on('error', (err) => {
86
+ .on("error", (err) => {
87
87
  started.reject(err);
88
88
  stopped?.reject(err);
89
89
  })
90
- .on('close', () => stopped?.resolve())
90
+ .on("close", () => stopped?.resolve())
91
91
  .listen({
92
- host: 'localhost',
93
- port
92
+ host: "localhost",
93
+ port,
94
94
  });
95
95
  }
96
96
  catch (error) {
@@ -1 +1 @@
1
- {"version":3,"file":"serve.cjs","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":["generate","handler","Deferred","randomUUID","authorizeRequestHandler","createHttpServer","base64Encode","createHttpsServer","ensureError"],"mappings":"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"}
1
+ {"version":3,"file":"serve.cjs","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 { createServer as createHttpsServer, type Server as HttpsServer } from \"node:https\";\nimport type { AddressInfo } from \"node:net\";\nimport { Deferred } from \"@milaboratories/helpers\";\nimport type { PFrameInternal } from \"@milaboratories/pl-model-middle-layer\";\nimport { base64Encode, Base64Encoded, ensureError } 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(info: AddressInfo, noHttps?: true): 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({ ...defaultOptions, cert, key, ca }, effectiveHandler);\n }\n\n server\n .on(\"listening\", () => {\n // Cast is safe by specification <https://nodejs.org/api/net.html#serveraddress>\n const url = createObjectStoreUrl(server.address() as AddressInfo, noHttps);\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":["generate","handler","Deferred","randomUUID","authorizeRequestHandler","createHttpServer","base64Encode","createHttpsServer","ensureError"],"mappings":"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"}
package/dist/serve.d.ts CHANGED
@@ -1,7 +1,7 @@
1
- import type { PFrameInternal } from '@milaboratories/pl-model-middle-layer';
1
+ import type { PFrameInternal } from "@milaboratories/pl-model-middle-layer";
2
2
  /**
3
3
  * Serve HTTP requests using the provided handler.
4
4
  * Returns a promise that resolves when the server is stopped.
5
5
  */
6
- export declare function serve({ handler, port, noHttps, noAuth }: PFrameInternal.HttpServerOptions): Promise<PFrameInternal.HttpServer>;
6
+ export declare function serve({ handler, port, noHttps, noAuth, }: PFrameInternal.HttpServerOptions): Promise<PFrameInternal.HttpServer>;
7
7
  //# sourceMappingURL=serve.d.ts.map
@@ -1 +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"}
1
+ {"version":3,"file":"serve.d.ts","sourceRoot":"","sources":["../src/serve.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uCAAuC,CAAC;AAqC5E;;;GAGG;AACH,wBAAsB,KAAK,CAAC,EAC1B,OAAO,EACP,IAAQ,EACR,OAAO,EACP,MAAM,GACP,EAAE,cAAc,CAAC,iBAAiB,GAAG,OAAO,CAAC,cAAc,CAAC,UAAU,CAAC,CA4DvE"}
package/dist/serve.js CHANGED
@@ -8,28 +8,28 @@ import { authorizeRequestHandler } from './handler.js';
8
8
 
9
9
  /** Generate a self-signed certificate for localhost */
10
10
  async function generateCertificate() {
11
- return await generate([{ name: 'commonName', value: 'localhost' }], {
11
+ return await generate([{ name: "commonName", value: "localhost" }], {
12
12
  keySize: 2048,
13
- algorithm: 'sha256',
13
+ algorithm: "sha256",
14
14
  extensions: [
15
15
  {
16
- name: 'subjectAltName',
16
+ name: "subjectAltName",
17
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
- ]
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
24
  });
25
25
  }
26
26
  /** Create an object store URL from the server address info. */
27
27
  function createObjectStoreUrl(info, noHttps) {
28
- const protocol = noHttps ? 'http' : 'https';
28
+ const protocol = noHttps ? "http" : "https";
29
29
  switch (info.family) {
30
- case 'IPv4':
30
+ case "IPv4":
31
31
  return `${protocol}://${info.address}:${info.port}/`;
32
- case 'IPv6':
32
+ case "IPv6":
33
33
  return `${protocol}://[${info.address}]:${info.port}/`;
34
34
  default:
35
35
  return `${protocol}://localhost:${info.port}/`;
@@ -39,7 +39,7 @@ function createObjectStoreUrl(info, noHttps) {
39
39
  * Serve HTTP requests using the provided handler.
40
40
  * Returns a promise that resolves when the server is stopped.
41
41
  */
42
- async function serve({ handler, port = 0, noHttps, noAuth }) {
42
+ async function serve({ handler, port = 0, noHttps, noAuth, }) {
43
43
  const started = new Deferred();
44
44
  try {
45
45
  let stopped = null;
@@ -52,7 +52,7 @@ async function serve({ handler, port = 0, noHttps, noAuth }) {
52
52
  // Create HTTP server
53
53
  let encodedCaCert;
54
54
  const defaultOptions = {
55
- keepAlive: true
55
+ keepAlive: true,
56
56
  };
57
57
  let server;
58
58
  if (noHttps) {
@@ -64,7 +64,7 @@ async function serve({ handler, port = 0, noHttps, noAuth }) {
64
64
  server = createServer$1({ ...defaultOptions, cert, key, ca }, effectiveHandler);
65
65
  }
66
66
  server
67
- .on('listening', () => {
67
+ .on("listening", () => {
68
68
  // Cast is safe by specification <https://nodejs.org/api/net.html#serveraddress>
69
69
  const url = createObjectStoreUrl(server.address(), noHttps);
70
70
  stopped = new Deferred();
@@ -78,17 +78,17 @@ async function serve({ handler, port = 0, noHttps, noAuth }) {
78
78
  stop() {
79
79
  server.close();
80
80
  return stopped.promise;
81
- }
81
+ },
82
82
  });
83
83
  })
84
- .on('error', (err) => {
84
+ .on("error", (err) => {
85
85
  started.reject(err);
86
86
  stopped?.reject(err);
87
87
  })
88
- .on('close', () => stopped?.resolve())
88
+ .on("close", () => stopped?.resolve())
89
89
  .listen({
90
- host: 'localhost',
91
- port
90
+ host: "localhost",
91
+ port,
92
92
  });
93
93
  }
94
94
  catch (error) {
package/dist/serve.js.map CHANGED
@@ -1 +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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"}
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 { createServer as createHttpsServer, type Server as HttpsServer } from \"node:https\";\nimport type { AddressInfo } from \"node:net\";\nimport { Deferred } from \"@milaboratories/helpers\";\nimport type { PFrameInternal } from \"@milaboratories/pl-model-middle-layer\";\nimport { base64Encode, Base64Encoded, ensureError } 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(info: AddressInfo, noHttps?: true): 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({ ...defaultOptions, cert, key, ca }, effectiveHandler);\n }\n\n server\n .on(\"listening\", () => {\n // Cast is safe by specification <https://nodejs.org/api/net.html#serveraddress>\n const url = createObjectStoreUrl(server.address() as AddressInfo, noHttps);\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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"}
@@ -2,7 +2,7 @@
2
2
 
3
3
  function createETag(filename) {
4
4
  // For immutable files, use URL-safe base64 encoded filename as ETag
5
- const filenameETag = Buffer.from(filename, 'utf8').toString('base64url');
5
+ const filenameETag = Buffer.from(filename, "utf8").toString("base64url");
6
6
  return `"${filenameETag}"`;
7
7
  }
8
8
 
@@ -1 +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;;;;"}
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;;;;"}
@@ -1,5 +1,5 @@
1
- import type { Branded } from '@milaboratories/pl-model-common';
2
- import type { PFrameInternal } from '@milaboratories/pl-model-middle-layer';
1
+ import type { Branded } from "@milaboratories/pl-model-common";
2
+ import type { PFrameInternal } from "@milaboratories/pl-model-middle-layer";
3
3
  /**
4
4
  * See <https://datatracker.ietf.org/doc/html/rfc9110#section-8.8.3>
5
5
  *
@@ -10,6 +10,6 @@ import type { PFrameInternal } from '@milaboratories/pl-model-middle-layer';
10
10
  * ETag: W/"xyzzy"
11
11
  * ```
12
12
  */
13
- export type Etag = Branded<string, 'Etag'>;
13
+ export type Etag = Branded<string, "Etag">;
14
14
  export declare function createETag(filename: PFrameInternal.ParquetFileName): Etag;
15
15
  //# sourceMappingURL=etag.d.ts.map
@@ -1,6 +1,6 @@
1
1
  function createETag(filename) {
2
2
  // For immutable files, use URL-safe base64 encoded filename as ETag
3
- const filenameETag = Buffer.from(filename, 'utf8').toString('base64url');
3
+ const filenameETag = Buffer.from(filename, "utf8").toString("base64url");
4
4
  return `"${filenameETag}"`;
5
5
  }
6
6
 
@@ -1 +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;;;;"}
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;;;;"}