@milaboratories/pframes-rs-serv 1.0.61 → 1.0.64

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 (89) hide show
  1. package/dist/export.cjs +20 -0
  2. package/dist/export.cjs.map +1 -0
  3. package/dist/export.d.ts +3 -0
  4. package/dist/export.d.ts.map +1 -0
  5. package/dist/export.js +18 -0
  6. package/dist/export.js.map +1 -0
  7. package/dist/fs-store.cjs +81 -0
  8. package/dist/fs-store.cjs.map +1 -0
  9. package/dist/fs-store.d.ts +14 -0
  10. package/dist/fs-store.d.ts.map +1 -0
  11. package/dist/fs-store.js +79 -0
  12. package/dist/fs-store.js.map +1 -0
  13. package/dist/handler.cjs +157 -0
  14. package/dist/handler.cjs.map +1 -0
  15. package/dist/handler.d.ts +15 -0
  16. package/dist/handler.d.ts.map +1 -0
  17. package/dist/handler.js +154 -0
  18. package/dist/handler.js.map +1 -0
  19. package/dist/index.cjs +17 -0
  20. package/dist/index.cjs.map +1 -0
  21. package/dist/index.d.ts +6 -0
  22. package/dist/index.d.ts.map +1 -0
  23. package/dist/index.js +6 -0
  24. package/dist/index.js.map +1 -0
  25. package/dist/parquet-server.cjs +53 -0
  26. package/dist/parquet-server.cjs.map +1 -0
  27. package/dist/parquet-server.d.ts +6 -0
  28. package/dist/parquet-server.d.ts.map +1 -0
  29. package/dist/parquet-server.js +51 -0
  30. package/dist/parquet-server.js.map +1 -0
  31. package/dist/serve.cjs +120 -0
  32. package/dist/serve.cjs.map +1 -0
  33. package/dist/serve.d.ts +7 -0
  34. package/dist/serve.d.ts.map +1 -0
  35. package/dist/serve.js +118 -0
  36. package/dist/serve.js.map +1 -0
  37. package/dist/utils/etag.cjs +10 -0
  38. package/dist/utils/etag.cjs.map +1 -0
  39. package/dist/utils/etag.d.ts +15 -0
  40. package/dist/utils/etag.d.ts.map +1 -0
  41. package/dist/utils/etag.js +8 -0
  42. package/dist/utils/etag.js.map +1 -0
  43. package/dist/utils/filename.cjs +15 -0
  44. package/dist/utils/filename.cjs.map +1 -0
  45. package/dist/utils/filename.d.ts +4 -0
  46. package/dist/utils/filename.d.ts.map +1 -0
  47. package/dist/utils/filename.js +13 -0
  48. package/dist/utils/filename.js.map +1 -0
  49. package/dist/utils/headers.cjs +26 -0
  50. package/dist/utils/headers.cjs.map +1 -0
  51. package/dist/utils/headers.d.ts +29 -0
  52. package/dist/utils/headers.d.ts.map +1 -0
  53. package/dist/utils/headers.js +23 -0
  54. package/dist/utils/headers.js.map +1 -0
  55. package/dist/utils/index.d.ts +8 -0
  56. package/dist/utils/index.d.ts.map +1 -0
  57. package/dist/utils/method.cjs +16 -0
  58. package/dist/utils/method.cjs.map +1 -0
  59. package/dist/utils/method.d.ts +6 -0
  60. package/dist/utils/method.d.ts.map +1 -0
  61. package/dist/utils/method.js +12 -0
  62. package/dist/utils/method.js.map +1 -0
  63. package/dist/utils/options.cjs +135 -0
  64. package/dist/utils/options.cjs.map +1 -0
  65. package/dist/utils/options.d.ts +64 -0
  66. package/dist/utils/options.d.ts.map +1 -0
  67. package/dist/utils/options.js +133 -0
  68. package/dist/utils/options.js.map +1 -0
  69. package/dist/utils/range.cjs +34 -0
  70. package/dist/utils/range.cjs.map +1 -0
  71. package/dist/utils/range.d.ts +4 -0
  72. package/dist/utils/range.d.ts.map +1 -0
  73. package/dist/utils/range.js +32 -0
  74. package/dist/utils/range.js.map +1 -0
  75. package/dist/utils/status.cjs +20 -0
  76. package/dist/utils/status.cjs.map +1 -0
  77. package/dist/utils/status.d.ts +16 -0
  78. package/dist/utils/status.d.ts.map +1 -0
  79. package/dist/utils/status.js +18 -0
  80. package/dist/utils/status.js.map +1 -0
  81. package/package.json +8 -6
  82. package/src/export.ts +2 -2
  83. package/src/serve.ts +3 -3
  84. package/src/utils/etag.ts +2 -2
  85. package/src/utils/filename.ts +2 -2
  86. package/src/utils/headers.ts +0 -2
  87. package/src/utils/method.ts +2 -2
  88. package/src/utils/options.ts +2 -2
  89. package/src/utils/range.ts +2 -2
@@ -0,0 +1 @@
1
+ {"version":3,"file":"handler.js","sources":["../src/handler.ts"],"sourcesContent":["import type {\n IncomingMessage,\n RequestListener,\n ServerResponse\n} from 'node:http';\nimport { pipeline } from 'node:stream/promises';\nimport { timingSafeEqual } from 'node:crypto';\nimport type { PFrameInternal } from '@milaboratories/pl-model-middle-layer';\nimport {\n createETag,\n getFilenameFromUrl,\n parseRange,\n isGetOrHead,\n isHead,\n Options,\n StatusCode,\n HeaderName,\n HeaderValue,\n isGet\n} from './utils';\n\n/** Main request handler for parquet files */\nasync function handleRequest(\n request: IncomingMessage,\n response: ServerResponse,\n store: PFrameInternal.ObjectStore\n): Promise<void> {\n // RFC 9110 section 6.6.1: Date header should be present in all responses\n response.sendDate = true;\n // RFC 9110 section 8.6: Content-Length 0 as default for error responses\n response.strictContentLength = true;\n response.setHeader(HeaderName.ContentLength, 0);\n // Note: setting Content-Length disables Node.js default Transfer-Encoding: chunked\n\n // RFC 9111 section 5.2: Cache-Control header with public allows to cache authenticated responses\n response.setHeader(HeaderName.CacheControl, HeaderValue.CacheControl);\n\n // RFC 9110 section 15.5.6: Method not allowed\n const method = request.method;\n if (!isGetOrHead(method)) {\n response.setHeader(HeaderName.Allow, HeaderValue.Allow);\n return void response.writeHead(StatusCode.MethodNotAllowed).end();\n }\n\n const filename = getFilenameFromUrl(request);\n if (filename === null) {\n return void response.writeHead(StatusCode.Gone).end();\n }\n\n // From now on we are sure that the response would be a Parquet file\n response.setHeader(HeaderName.AcceptRanges, HeaderValue.AcceptRanges);\n response.setHeader(HeaderName.ContentType, HeaderValue.ContentType);\n\n // RFC 9110 section 8.8.3: ETag header is used for cache versioning\n const etag = createETag(filename);\n // RFC 9110 section 8.8.2: Last-Modified header field for cache validation\n const mtime = new Date(0); // Using fake fixed date since files are immutable\n // RFC 9111 section 5.2: Cache-Control header with public allows to cache authenticated responses\n response.setHeader(HeaderName.CacheControl, HeaderValue.CacheControl);\n response.setHeader(HeaderName.ETag, etag);\n response.setHeader(HeaderName.LastModified, mtime.toUTCString());\n\n const options = new Options(request);\n // RFC 9110 section 13.1.1: If-Match precondition evaluation\n // RFC 9110 section 13.1.4: If-Unmodified-Since precondition evaluation\n if (options.preconditionFailed(etag, mtime)) {\n return void response.writeHead(StatusCode.PreconditionFailed).end();\n }\n // RFC 9110 section 13.1.2: If-None-Match precondition evaluation\n // RFC 9110 section 13.1.3: If-Modified-Since precondition evaluation\n else if (options.notModified(etag, mtime)) {\n return void response.writeHead(StatusCode.NotModified).end();\n }\n\n const range = parseRange(request);\n if (range === null) {\n return void response.writeHead(StatusCode.BadRequest).end();\n }\n\n const abortController = new AbortController();\n request.on('close', () => abortController.abort());\n const signal = abortController.signal;\n\n store.request(filename, {\n method: 'GET',\n range,\n signal,\n // pipeline automatically destroys the streams if they were not gracefully closed\n callback: async (result) => {\n if (response.destroyed) return void response.destroy();\n\n switch (result.type) {\n case 'InternalError':\n // object store encountered network error, retry by client can help\n return void response.writeHead(StatusCode.InternalServerError).end();\n case 'NotFound':\n // RFC 9110 section 15.4.5: Not found\n return void response.writeHead(StatusCode.NotFound).end();\n case 'RangeNotSatisfiable':\n // RFC 9110 section 15.5.17: Range not satisfiable\n response.setHeader(HeaderName.ContentRange, `bytes */${result.size}`);\n return void response.writeHead(StatusCode.RangeNotSatisfiable).end();\n case 'Ok':\n break;\n }\n\n if (isGet(method) && !result.data) {\n // object store implementation is incorrect, retry by client cannot help\n return void response.writeHead(StatusCode.GatewayTimeout).end();\n }\n\n if (range) {\n // RFC 9110 section 14.4: Partial content response\n response.setHeader(\n HeaderName.ContentLength,\n result.range.end - result.range.start + 1\n );\n response.setHeader(\n HeaderName.ContentRange,\n `bytes ${result.range.start}-${result.range.end}/${result.size}`\n );\n response.writeHead(StatusCode.PartialContent);\n } else {\n // RFC 9110 section 15.3.1: OK response\n response.setHeader(HeaderName.ContentLength, result.size);\n response.writeHead(StatusCode.Ok);\n }\n\n // RFC 9110 section 9.3.2: HEAD method must not return message body\n if (isHead(method)) {\n return void response.end();\n }\n\n return await pipeline(result.data!, response, { signal }).catch(() => {\n // Pipeline errors are expected when request is aborted or connection is lost\n // Response head was already written, so we can't change status code\n // Just mute the error - pipeline destroys the response stream\n });\n }\n });\n}\n\n/**\n * Create a request handler for serving files from an object store\n * compatible with HTTP/1.1 as defined in RFC 9110 and RFC 9111:\n * - <https://datatracker.ietf.org/doc/html/rfc9110>\n * - <https://datatracker.ietf.org/doc/html/rfc9111>\n *\n * Accepts only paths of the form `/<filename>.parquet`, returns 404 otherwise\n * Assumes that files are immutable (and sets cache headers accordingly)\n */\nexport function createRequestHandler(\n options: PFrameInternal.RequestHandlerOptions\n): RequestListener {\n const { store } = options;\n return (request, response) => void handleRequest(request, response, store);\n}\n\n/** Request authorization middleware */\nfunction authorizeRequest(\n request: IncomingMessage,\n response: ServerResponse,\n handler: RequestListener,\n authHeader: string\n): void {\n // RFC 9110 section 6.6.1: Date header should be present in all responses\n response.sendDate = true;\n // RFC 9110 section 8.6: Content-Length 0 as default for error responses\n response.strictContentLength = true;\n response.setHeader(HeaderName.ContentLength, 0);\n // Note: setting Content-Length disables Node.js default Transfer-Encoding: chunked\n\n const actualHeader = request.headers['authorization'];\n\n // Early length check to avoid unnecessary processing\n if (!actualHeader || actualHeader.length !== authHeader.length) {\n // RFC 9110 section 11.6.1: WWW-Authenticate header field\n response.setHeader(HeaderName.WWWAuthenticate, HeaderValue.WWWAuthenticate);\n return void response.writeHead(StatusCode.Unauthorized).end();\n }\n\n // Use timing-safe comparison to prevent timing attacks\n // <https://developers.cloudflare.com/workers/examples/protect-against-timing-attacks/>\n const encoder = new TextEncoder();\n const receivedBuffer = encoder.encode(actualHeader);\n const expectedBuffer = encoder.encode(authHeader);\n\n if (\n receivedBuffer.byteLength !== expectedBuffer.byteLength ||\n !timingSafeEqual(receivedBuffer, expectedBuffer)\n ) {\n response.setHeader(HeaderName.WWWAuthenticate, HeaderValue.WWWAuthenticate);\n return void response.writeHead(StatusCode.Unauthorized).end();\n }\n\n return handler(request, response);\n}\n\n/** Apply Bearer token authorization to @param handler */\nexport function authorizeRequestHandler(\n handler: RequestListener,\n authToken: PFrameInternal.HttpAuthorizationToken\n): RequestListener {\n const authHeader = `Bearer ${authToken}`;\n return (request, response) =>\n authorizeRequest(request, response, handler, authHeader);\n}\n"],"names":[],"mappings":";;;;;;;;;;AAqBA;AACA,eAAe,aAAa,CAC1B,OAAwB,EACxB,QAAwB,EACxB,KAAiC,EAAA;;AAGjC,IAAA,QAAQ,CAAC,QAAQ,GAAG,IAAI;;AAExB,IAAA,QAAQ,CAAC,mBAAmB,GAAG,IAAI;IACnC,QAAQ,CAAC,SAAS,CAAC,UAAU,CAAC,aAAa,EAAE,CAAC,CAAC;;;IAI/C,QAAQ,CAAC,SAAS,CAAC,UAAU,CAAC,YAAY,EAAE,WAAW,CAAC,YAAY,CAAC;;AAGrE,IAAA,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM;AAC7B,IAAA,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,EAAE;QACxB,QAAQ,CAAC,SAAS,CAAC,UAAU,CAAC,KAAK,EAAE,WAAW,CAAC,KAAK,CAAC;AACvD,QAAA,OAAO,KAAK,QAAQ,CAAC,SAAS,CAAC,UAAU,CAAC,gBAAgB,CAAC,CAAC,GAAG,EAAE;IACnE;AAEA,IAAA,MAAM,QAAQ,GAAG,kBAAkB,CAAC,OAAO,CAAC;AAC5C,IAAA,IAAI,QAAQ,KAAK,IAAI,EAAE;AACrB,QAAA,OAAO,KAAK,QAAQ,CAAC,SAAS,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE;IACvD;;IAGA,QAAQ,CAAC,SAAS,CAAC,UAAU,CAAC,YAAY,EAAE,WAAW,CAAC,YAAY,CAAC;IACrE,QAAQ,CAAC,SAAS,CAAC,UAAU,CAAC,WAAW,EAAE,WAAW,CAAC,WAAW,CAAC;;AAGnE,IAAA,MAAM,IAAI,GAAG,UAAU,CAAC,QAAQ,CAAC;;IAEjC,MAAM,KAAK,GAAG,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC;;IAE1B,QAAQ,CAAC,SAAS,CAAC,UAAU,CAAC,YAAY,EAAE,WAAW,CAAC,YAAY,CAAC;IACrE,QAAQ,CAAC,SAAS,CAAC,UAAU,CAAC,IAAI,EAAE,IAAI,CAAC;AACzC,IAAA,QAAQ,CAAC,SAAS,CAAC,UAAU,CAAC,YAAY,EAAE,KAAK,CAAC,WAAW,EAAE,CAAC;AAEhE,IAAA,MAAM,OAAO,GAAG,IAAI,OAAO,CAAC,OAAO,CAAC;;;IAGpC,IAAI,OAAO,CAAC,kBAAkB,CAAC,IAAI,EAAE,KAAK,CAAC,EAAE;AAC3C,QAAA,OAAO,KAAK,QAAQ,CAAC,SAAS,CAAC,UAAU,CAAC,kBAAkB,CAAC,CAAC,GAAG,EAAE;IACrE;;;SAGK,IAAI,OAAO,CAAC,WAAW,CAAC,IAAI,EAAE,KAAK,CAAC,EAAE;AACzC,QAAA,OAAO,KAAK,QAAQ,CAAC,SAAS,CAAC,UAAU,CAAC,WAAW,CAAC,CAAC,GAAG,EAAE;IAC9D;AAEA,IAAA,MAAM,KAAK,GAAG,UAAU,CAAC,OAAO,CAAC;AACjC,IAAA,IAAI,KAAK,KAAK,IAAI,EAAE;AAClB,QAAA,OAAO,KAAK,QAAQ,CAAC,SAAS,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC,GAAG,EAAE;IAC7D;AAEA,IAAA,MAAM,eAAe,GAAG,IAAI,eAAe,EAAE;AAC7C,IAAA,OAAO,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,eAAe,CAAC,KAAK,EAAE,CAAC;AAClD,IAAA,MAAM,MAAM,GAAG,eAAe,CAAC,MAAM;AAErC,IAAA,KAAK,CAAC,OAAO,CAAC,QAAQ,EAAE;AACtB,QAAA,MAAM,EAAE,KAAK;QACb,KAAK;QACL,MAAM;;AAEN,QAAA,QAAQ,EAAE,OAAO,MAAM,KAAI;YACzB,IAAI,QAAQ,CAAC,SAAS;AAAE,gBAAA,OAAO,KAAK,QAAQ,CAAC,OAAO,EAAE;AAEtD,YAAA,QAAQ,MAAM,CAAC,IAAI;AACjB,gBAAA,KAAK,eAAe;;AAElB,oBAAA,OAAO,KAAK,QAAQ,CAAC,SAAS,CAAC,UAAU,CAAC,mBAAmB,CAAC,CAAC,GAAG,EAAE;AACtE,gBAAA,KAAK,UAAU;;AAEb,oBAAA,OAAO,KAAK,QAAQ,CAAC,SAAS,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,GAAG,EAAE;AAC3D,gBAAA,KAAK,qBAAqB;;AAExB,oBAAA,QAAQ,CAAC,SAAS,CAAC,UAAU,CAAC,YAAY,EAAE,CAAA,QAAA,EAAW,MAAM,CAAC,IAAI,CAAA,CAAE,CAAC;AACrE,oBAAA,OAAO,KAAK,QAAQ,CAAC,SAAS,CAAC,UAAU,CAAC,mBAAmB,CAAC,CAAC,GAAG,EAAE;;YAKxE,IAAI,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE;;AAEjC,gBAAA,OAAO,KAAK,QAAQ,CAAC,SAAS,CAAC,UAAU,CAAC,cAAc,CAAC,CAAC,GAAG,EAAE;YACjE;YAEA,IAAI,KAAK,EAAE;;gBAET,QAAQ,CAAC,SAAS,CAChB,UAAU,CAAC,aAAa,EACxB,MAAM,CAAC,KAAK,CAAC,GAAG,GAAG,MAAM,CAAC,KAAK,CAAC,KAAK,GAAG,CAAC,CAC1C;gBACD,QAAQ,CAAC,SAAS,CAChB,UAAU,CAAC,YAAY,EACvB,CAAA,MAAA,EAAS,MAAM,CAAC,KAAK,CAAC,KAAK,CAAA,CAAA,EAAI,MAAM,CAAC,KAAK,CAAC,GAAG,CAAA,CAAA,EAAI,MAAM,CAAC,IAAI,CAAA,CAAE,CACjE;AACD,gBAAA,QAAQ,CAAC,SAAS,CAAC,UAAU,CAAC,cAAc,CAAC;YAC/C;iBAAO;;gBAEL,QAAQ,CAAC,SAAS,CAAC,UAAU,CAAC,aAAa,EAAE,MAAM,CAAC,IAAI,CAAC;AACzD,gBAAA,QAAQ,CAAC,SAAS,CAAC,UAAU,CAAC,EAAE,CAAC;YACnC;;AAGA,YAAA,IAAI,MAAM,CAAC,MAAM,CAAC,EAAE;AAClB,gBAAA,OAAO,KAAK,QAAQ,CAAC,GAAG,EAAE;YAC5B;AAEA,YAAA,OAAO,MAAM,QAAQ,CAAC,MAAM,CAAC,IAAK,EAAE,QAAQ,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC,KAAK,CAAC,MAAK;;;;AAIrE,YAAA,CAAC,CAAC;QACJ;AACD,KAAA,CAAC;AACJ;AAEA;;;;;;;;AAQG;AACG,SAAU,oBAAoB,CAClC,OAA6C,EAAA;AAE7C,IAAA,MAAM,EAAE,KAAK,EAAE,GAAG,OAAO;AACzB,IAAA,OAAO,CAAC,OAAO,EAAE,QAAQ,KAAK,KAAK,aAAa,CAAC,OAAO,EAAE,QAAQ,EAAE,KAAK,CAAC;AAC5E;AAEA;AACA,SAAS,gBAAgB,CACvB,OAAwB,EACxB,QAAwB,EACxB,OAAwB,EACxB,UAAkB,EAAA;;AAGlB,IAAA,QAAQ,CAAC,QAAQ,GAAG,IAAI;;AAExB,IAAA,QAAQ,CAAC,mBAAmB,GAAG,IAAI;IACnC,QAAQ,CAAC,SAAS,CAAC,UAAU,CAAC,aAAa,EAAE,CAAC,CAAC;;IAG/C,MAAM,YAAY,GAAG,OAAO,CAAC,OAAO,CAAC,eAAe,CAAC;;IAGrD,IAAI,CAAC,YAAY,IAAI,YAAY,CAAC,MAAM,KAAK,UAAU,CAAC,MAAM,EAAE;;QAE9D,QAAQ,CAAC,SAAS,CAAC,UAAU,CAAC,eAAe,EAAE,WAAW,CAAC,eAAe,CAAC;AAC3E,QAAA,OAAO,KAAK,QAAQ,CAAC,SAAS,CAAC,UAAU,CAAC,YAAY,CAAC,CAAC,GAAG,EAAE;IAC/D;;;AAIA,IAAA,MAAM,OAAO,GAAG,IAAI,WAAW,EAAE;IACjC,MAAM,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,YAAY,CAAC;IACnD,MAAM,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,UAAU,CAAC;AAEjD,IAAA,IACE,cAAc,CAAC,UAAU,KAAK,cAAc,CAAC,UAAU;AACvD,QAAA,CAAC,eAAe,CAAC,cAAc,EAAE,cAAc,CAAC,EAChD;QACA,QAAQ,CAAC,SAAS,CAAC,UAAU,CAAC,eAAe,EAAE,WAAW,CAAC,eAAe,CAAC;AAC3E,QAAA,OAAO,KAAK,QAAQ,CAAC,SAAS,CAAC,UAAU,CAAC,YAAY,CAAC,CAAC,GAAG,EAAE;IAC/D;AAEA,IAAA,OAAO,OAAO,CAAC,OAAO,EAAE,QAAQ,CAAC;AACnC;AAEA;AACM,SAAU,uBAAuB,CACrC,OAAwB,EACxB,SAAgD,EAAA;AAEhD,IAAA,MAAM,UAAU,GAAG,CAAA,OAAA,EAAU,SAAS,EAAE;AACxC,IAAA,OAAO,CAAC,OAAO,EAAE,QAAQ,KACvB,gBAAgB,CAAC,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,UAAU,CAAC;AAC5D;;;;"}
package/dist/index.cjs ADDED
@@ -0,0 +1,17 @@
1
+ 'use strict';
2
+
3
+ var handler = require('./handler.cjs');
4
+ var fsStore = require('./fs-store.cjs');
5
+ var serve = require('./serve.cjs');
6
+ var _export = require('./export.cjs');
7
+ var parquetServer = require('./parquet-server.cjs');
8
+
9
+
10
+
11
+ exports.authorizeRequestHandler = handler.authorizeRequestHandler;
12
+ exports.createRequestHandler = handler.createRequestHandler;
13
+ exports.FileSystemStore = fsStore.FileSystemStore;
14
+ exports.serve = serve.serve;
15
+ exports.HttpHelpers = _export.HttpHelpers;
16
+ exports.runParquetServer = parquetServer.runParquetServer;
17
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.cjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;"}
@@ -0,0 +1,6 @@
1
+ export * from './handler';
2
+ export * from './fs-store';
3
+ export * from './serve';
4
+ export * from './export';
5
+ export * from './parquet-server';
6
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,WAAW,CAAC;AAC1B,cAAc,YAAY,CAAC;AAC3B,cAAc,SAAS,CAAC;AACxB,cAAc,UAAU,CAAC;AACzB,cAAc,kBAAkB,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,6 @@
1
+ export { authorizeRequestHandler, createRequestHandler } from './handler.js';
2
+ export { FileSystemStore } from './fs-store.js';
3
+ export { serve } from './serve.js';
4
+ export { HttpHelpers } from './export.js';
5
+ export { runParquetServer } from './parquet-server.js';
6
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;"}
@@ -0,0 +1,53 @@
1
+ 'use strict';
2
+
3
+ var commander = require('commander');
4
+ var fsStore = require('./fs-store.cjs');
5
+ var handler = require('./handler.cjs');
6
+ var serve = require('./serve.cjs');
7
+
8
+ /**
9
+ * Serves parquet files from the given root directory.
10
+ * Manages the server lifecycle with graceful shutdown.
11
+ */
12
+ async function runParquetServer() {
13
+ const program = new commander.Command();
14
+ program
15
+ .name('parquet-server')
16
+ .description('Serve parquet files from a directory over HTTP(S)')
17
+ .argument('<root-directory>', 'Root directory containing parquet files')
18
+ .option('--http', 'Use HTTP instead of HTTPS', false)
19
+ .option('--no-auth', 'Disable authentication')
20
+ .action(async (rootDir, options) => {
21
+ const abortController = new AbortController();
22
+ process
23
+ .on('SIGINT', () => abortController.abort())
24
+ .on('SIGTERM', () => abortController.abort());
25
+ abortController.signal.throwIfAborted();
26
+ const store = await fsStore.FileSystemStore.init({
27
+ rootDir,
28
+ logger: (level, message) => {
29
+ const timestamp = new Date(Date.now()).toISOString();
30
+ console.log(`[${timestamp}] [${level}] ${message}`);
31
+ }
32
+ });
33
+ abortController.signal.throwIfAborted();
34
+ const handler$1 = handler.createRequestHandler({ store });
35
+ const server = await serve.serve({
36
+ handler: handler$1,
37
+ ...(options.http && { http: true }),
38
+ ...(!options.auth && { noAuth: true })
39
+ });
40
+ abortController.signal.onabort = () => server.stop();
41
+ abortController.signal.throwIfAborted();
42
+ console.log(JSON.stringify({
43
+ url: server.address,
44
+ ...(server.authToken && { authToken: server.authToken }),
45
+ ...(server.encodedCaCert && { caCert: server.encodedCaCert })
46
+ }));
47
+ await server.stopped;
48
+ });
49
+ await program.parseAsync();
50
+ }
51
+
52
+ exports.runParquetServer = runParquetServer;
53
+ //# sourceMappingURL=parquet-server.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parquet-server.cjs","sources":["../src/parquet-server.ts"],"sourcesContent":["import { Command } from 'commander';\nimport { FileSystemStore } from './fs-store';\nimport { createRequestHandler } from './handler';\nimport { serve } from './serve';\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('--http', 'Use HTTP instead of HTTPS', false)\n .option('--no-auth', 'Disable authentication')\n .action(\n async (rootDir: string, options: { http: boolean; auth: boolean }) => {\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 FileSystemStore.init({\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 = createRequestHandler({ store });\n\n const server = await serve({\n handler,\n ...(options.http && { http: true }),\n ...(!options.auth && { noAuth: true })\n });\n abortController.signal.onabort = () => server.stop();\n abortController.signal.throwIfAborted();\n\n console.log(\n JSON.stringify({\n url: server.address,\n ...(server.authToken && { authToken: server.authToken }),\n ...(server.encodedCaCert && { caCert: server.encodedCaCert })\n })\n );\n\n await server.stopped;\n }\n );\n\n await program.parseAsync();\n}\n"],"names":["Command","FileSystemStore","handler","createRequestHandler","serve"],"mappings":";;;;;;;AAKA;;;AAGG;AACI,eAAe,gBAAgB,GAAA;AACpC,IAAA,MAAM,OAAO,GAAG,IAAIA,iBAAO,EAAE;IAE7B;SACG,IAAI,CAAC,gBAAgB;SACrB,WAAW,CAAC,mDAAmD;AAC/D,SAAA,QAAQ,CAAC,kBAAkB,EAAE,yCAAyC;AACtE,SAAA,MAAM,CAAC,QAAQ,EAAE,2BAA2B,EAAE,KAAK;AACnD,SAAA,MAAM,CAAC,WAAW,EAAE,wBAAwB;AAC5C,SAAA,MAAM,CACL,OAAO,OAAe,EAAE,OAAyC,KAAI;AACnE,QAAA,MAAM,eAAe,GAAG,IAAI,eAAe,EAAE;QAC7C;aACG,EAAE,CAAC,QAAQ,EAAE,MAAM,eAAe,CAAC,KAAK,EAAE;aAC1C,EAAE,CAAC,SAAS,EAAE,MAAM,eAAe,CAAC,KAAK,EAAE,CAAC;AAC/C,QAAA,eAAe,CAAC,MAAM,CAAC,cAAc,EAAE;AAEvC,QAAA,MAAM,KAAK,GAAG,MAAMC,uBAAe,CAAC,IAAI,CAAC;YACvC,OAAO;AACP,YAAA,MAAM,EAAE,CAAC,KAAK,EAAE,OAAO,KAAI;AACzB,gBAAA,MAAM,SAAS,GAAG,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,WAAW,EAAE;gBACpD,OAAO,CAAC,GAAG,CAAC,CAAA,CAAA,EAAI,SAAS,CAAA,GAAA,EAAM,KAAK,CAAA,EAAA,EAAK,OAAO,CAAA,CAAE,CAAC;YACrD;AACD,SAAA,CAAC;AACF,QAAA,eAAe,CAAC,MAAM,CAAC,cAAc,EAAE;QACvC,MAAMC,SAAO,GAAGC,4BAAoB,CAAC,EAAE,KAAK,EAAE,CAAC;AAE/C,QAAA,MAAM,MAAM,GAAG,MAAMC,WAAK,CAAC;qBACzBF,SAAO;YACP,IAAI,OAAO,CAAC,IAAI,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;YACnC,IAAI,CAAC,OAAO,CAAC,IAAI,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE;AACtC,SAAA,CAAC;AACF,QAAA,eAAe,CAAC,MAAM,CAAC,OAAO,GAAG,MAAM,MAAM,CAAC,IAAI,EAAE;AACpD,QAAA,eAAe,CAAC,MAAM,CAAC,cAAc,EAAE;AAEvC,QAAA,OAAO,CAAC,GAAG,CACT,IAAI,CAAC,SAAS,CAAC;YACb,GAAG,EAAE,MAAM,CAAC,OAAO;AACnB,YAAA,IAAI,MAAM,CAAC,SAAS,IAAI,EAAE,SAAS,EAAE,MAAM,CAAC,SAAS,EAAE,CAAC;AACxD,YAAA,IAAI,MAAM,CAAC,aAAa,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,aAAa,EAAE;AAC7D,SAAA,CAAC,CACH;QAED,MAAM,MAAM,CAAC,OAAO;AACtB,IAAA,CAAC,CACF;AAEH,IAAA,MAAM,OAAO,CAAC,UAAU,EAAE;AAC5B;;;;"}
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Serves parquet files from the given root directory.
3
+ * Manages the server lifecycle with graceful shutdown.
4
+ */
5
+ export declare function runParquetServer(): Promise<void>;
6
+ //# sourceMappingURL=parquet-server.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parquet-server.d.ts","sourceRoot":"","sources":["../src/parquet-server.ts"],"names":[],"mappings":"AAKA;;;GAGG;AACH,wBAAsB,gBAAgB,IAAI,OAAO,CAAC,IAAI,CAAC,CAgDtD"}
@@ -0,0 +1,51 @@
1
+ import { Command } from 'commander';
2
+ import { FileSystemStore } from './fs-store.js';
3
+ import { createRequestHandler } from './handler.js';
4
+ import { serve } from './serve.js';
5
+
6
+ /**
7
+ * Serves parquet files from the given root directory.
8
+ * Manages the server lifecycle with graceful shutdown.
9
+ */
10
+ async function runParquetServer() {
11
+ const program = new Command();
12
+ program
13
+ .name('parquet-server')
14
+ .description('Serve parquet files from a directory over HTTP(S)')
15
+ .argument('<root-directory>', 'Root directory containing parquet files')
16
+ .option('--http', 'Use HTTP instead of HTTPS', false)
17
+ .option('--no-auth', 'Disable authentication')
18
+ .action(async (rootDir, options) => {
19
+ const abortController = new AbortController();
20
+ process
21
+ .on('SIGINT', () => abortController.abort())
22
+ .on('SIGTERM', () => abortController.abort());
23
+ abortController.signal.throwIfAborted();
24
+ const store = await FileSystemStore.init({
25
+ rootDir,
26
+ logger: (level, message) => {
27
+ const timestamp = new Date(Date.now()).toISOString();
28
+ console.log(`[${timestamp}] [${level}] ${message}`);
29
+ }
30
+ });
31
+ abortController.signal.throwIfAborted();
32
+ const handler = createRequestHandler({ store });
33
+ const server = await serve({
34
+ handler,
35
+ ...(options.http && { http: true }),
36
+ ...(!options.auth && { noAuth: true })
37
+ });
38
+ abortController.signal.onabort = () => server.stop();
39
+ abortController.signal.throwIfAborted();
40
+ console.log(JSON.stringify({
41
+ url: server.address,
42
+ ...(server.authToken && { authToken: server.authToken }),
43
+ ...(server.encodedCaCert && { caCert: server.encodedCaCert })
44
+ }));
45
+ await server.stopped;
46
+ });
47
+ await program.parseAsync();
48
+ }
49
+
50
+ export { runParquetServer };
51
+ //# sourceMappingURL=parquet-server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parquet-server.js","sources":["../src/parquet-server.ts"],"sourcesContent":["import { Command } from 'commander';\nimport { FileSystemStore } from './fs-store';\nimport { createRequestHandler } from './handler';\nimport { serve } from './serve';\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('--http', 'Use HTTP instead of HTTPS', false)\n .option('--no-auth', 'Disable authentication')\n .action(\n async (rootDir: string, options: { http: boolean; auth: boolean }) => {\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 FileSystemStore.init({\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 = createRequestHandler({ store });\n\n const server = await serve({\n handler,\n ...(options.http && { http: true }),\n ...(!options.auth && { noAuth: true })\n });\n abortController.signal.onabort = () => server.stop();\n abortController.signal.throwIfAborted();\n\n console.log(\n JSON.stringify({\n url: server.address,\n ...(server.authToken && { authToken: server.authToken }),\n ...(server.encodedCaCert && { caCert: server.encodedCaCert })\n })\n );\n\n await server.stopped;\n }\n );\n\n await program.parseAsync();\n}\n"],"names":[],"mappings":";;;;;AAKA;;;AAGG;AACI,eAAe,gBAAgB,GAAA;AACpC,IAAA,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE;IAE7B;SACG,IAAI,CAAC,gBAAgB;SACrB,WAAW,CAAC,mDAAmD;AAC/D,SAAA,QAAQ,CAAC,kBAAkB,EAAE,yCAAyC;AACtE,SAAA,MAAM,CAAC,QAAQ,EAAE,2BAA2B,EAAE,KAAK;AACnD,SAAA,MAAM,CAAC,WAAW,EAAE,wBAAwB;AAC5C,SAAA,MAAM,CACL,OAAO,OAAe,EAAE,OAAyC,KAAI;AACnE,QAAA,MAAM,eAAe,GAAG,IAAI,eAAe,EAAE;QAC7C;aACG,EAAE,CAAC,QAAQ,EAAE,MAAM,eAAe,CAAC,KAAK,EAAE;aAC1C,EAAE,CAAC,SAAS,EAAE,MAAM,eAAe,CAAC,KAAK,EAAE,CAAC;AAC/C,QAAA,eAAe,CAAC,MAAM,CAAC,cAAc,EAAE;AAEvC,QAAA,MAAM,KAAK,GAAG,MAAM,eAAe,CAAC,IAAI,CAAC;YACvC,OAAO;AACP,YAAA,MAAM,EAAE,CAAC,KAAK,EAAE,OAAO,KAAI;AACzB,gBAAA,MAAM,SAAS,GAAG,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,WAAW,EAAE;gBACpD,OAAO,CAAC,GAAG,CAAC,CAAA,CAAA,EAAI,SAAS,CAAA,GAAA,EAAM,KAAK,CAAA,EAAA,EAAK,OAAO,CAAA,CAAE,CAAC;YACrD;AACD,SAAA,CAAC;AACF,QAAA,eAAe,CAAC,MAAM,CAAC,cAAc,EAAE;QACvC,MAAM,OAAO,GAAG,oBAAoB,CAAC,EAAE,KAAK,EAAE,CAAC;AAE/C,QAAA,MAAM,MAAM,GAAG,MAAM,KAAK,CAAC;YACzB,OAAO;YACP,IAAI,OAAO,CAAC,IAAI,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;YACnC,IAAI,CAAC,OAAO,CAAC,IAAI,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE;AACtC,SAAA,CAAC;AACF,QAAA,eAAe,CAAC,MAAM,CAAC,OAAO,GAAG,MAAM,MAAM,CAAC,IAAI,EAAE;AACpD,QAAA,eAAe,CAAC,MAAM,CAAC,cAAc,EAAE;AAEvC,QAAA,OAAO,CAAC,GAAG,CACT,IAAI,CAAC,SAAS,CAAC;YACb,GAAG,EAAE,MAAM,CAAC,OAAO;AACnB,YAAA,IAAI,MAAM,CAAC,SAAS,IAAI,EAAE,SAAS,EAAE,MAAM,CAAC,SAAS,EAAE,CAAC;AACxD,YAAA,IAAI,MAAM,CAAC,aAAa,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,aAAa,EAAE;AAC7D,SAAA,CAAC,CACH;QAED,MAAM,MAAM,CAAC,OAAO;AACtB,IAAA,CAAC,CACF;AAEH,IAAA,MAAM,OAAO,CAAC,UAAU,EAAE;AAC5B;;;;"}
package/dist/serve.cjs ADDED
@@ -0,0 +1,120 @@
1
+ 'use strict';
2
+
3
+ var node_http = require('node:http');
4
+ var node_https = require('node:https');
5
+ var helpers = require('@milaboratories/helpers');
6
+ var plModelCommon = require('@milaboratories/pl-model-common');
7
+ var selfsigned = require('selfsigned');
8
+ var node_crypto = require('node:crypto');
9
+ var handler = require('./handler.cjs');
10
+
11
+ /** Generate a self-signed certificate for localhost */
12
+ async function generateCertificate() {
13
+ const generateResult = new helpers.Deferred();
14
+ selfsigned.generate([{ name: 'commonName', value: 'localhost' }], {
15
+ keySize: 2048,
16
+ algorithm: 'sha256',
17
+ extensions: [
18
+ {
19
+ name: 'subjectAltName',
20
+ altNames: [
21
+ { type: 2, value: 'localhost' }, // DNS
22
+ { type: 7, ip: '127.0.0.1' }, // IPv4
23
+ { type: 7, ip: '::1' } // IPv6
24
+ ]
25
+ }
26
+ ]
27
+ }, (error, result) => {
28
+ if (error) {
29
+ generateResult.reject(error);
30
+ }
31
+ else {
32
+ generateResult.resolve(result);
33
+ }
34
+ });
35
+ return await generateResult.promise;
36
+ }
37
+ /** Create an object store URL from the server address info. */
38
+ function createObjectStoreUrl(info, http) {
39
+ const protocol = http ? 'http' : 'https';
40
+ switch (info.family) {
41
+ case 'IPv4':
42
+ return `${protocol}://${info.address}:${info.port}/`;
43
+ case 'IPv6':
44
+ return `${protocol}://[${info.address}]:${info.port}/`;
45
+ default:
46
+ throw new plModelCommon.PFrameError(`PFrame helper HTTP(S) server bound to 'localhost' has unknown address family: ${info.family}`);
47
+ }
48
+ }
49
+ /**
50
+ * Serve HTTP requests using the provided handler.
51
+ * Returns a promise that resolves when the server is stopped.
52
+ */
53
+ async function serve({ handler: handler$1, port = 0, http, noAuth }) {
54
+ const started = new helpers.Deferred();
55
+ try {
56
+ let stopped = null;
57
+ let authToken;
58
+ let effectiveHandler = handler$1;
59
+ if (!noAuth) {
60
+ authToken = node_crypto.randomUUID();
61
+ effectiveHandler = handler.authorizeRequestHandler(effectiveHandler, authToken);
62
+ }
63
+ // Create HTTP server
64
+ let certificateBase64;
65
+ const defaultOptions = {
66
+ keepAlive: true
67
+ };
68
+ let server;
69
+ if (http) {
70
+ server = node_http.createServer(defaultOptions, effectiveHandler);
71
+ }
72
+ else {
73
+ const { cert, private: key, public: ca } = await generateCertificate();
74
+ certificateBase64 = plModelCommon.base64Encode(cert);
75
+ server = node_https.createServer({ ...defaultOptions, cert, key, ca }, effectiveHandler);
76
+ }
77
+ const abortController = new AbortController();
78
+ server
79
+ .on('listening', () => {
80
+ // Cast is safe by specification <https://nodejs.org/api/net.html#serveraddress>
81
+ const address = createObjectStoreUrl(server.address(), http);
82
+ stopped = new helpers.Deferred();
83
+ started.resolve({
84
+ get address() {
85
+ return address;
86
+ },
87
+ get authToken() {
88
+ return authToken;
89
+ },
90
+ get encodedCaCert() {
91
+ return certificateBase64;
92
+ },
93
+ get stopped() {
94
+ return stopped.promise;
95
+ },
96
+ stop() {
97
+ abortController.abort();
98
+ return stopped.promise;
99
+ }
100
+ });
101
+ })
102
+ .on('error', (err) => {
103
+ started.reject(err);
104
+ stopped?.reject(err);
105
+ })
106
+ .on('close', () => stopped?.resolve())
107
+ .listen({
108
+ host: 'localhost',
109
+ port,
110
+ signal: abortController.signal
111
+ });
112
+ }
113
+ catch (error) {
114
+ started.reject(plModelCommon.ensureError(error));
115
+ }
116
+ return started.promise;
117
+ }
118
+
119
+ exports.serve = serve;
120
+ //# sourceMappingURL=serve.cjs.map
@@ -0,0 +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 PFrameError\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 const generateResult = new Deferred<GenerateResult>();\n generate(\n [{ name: 'commonName', value: 'localhost' }],\n {\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 (error, result) => {\n if (error) {\n generateResult.reject(error);\n } else {\n generateResult.resolve(result);\n }\n }\n );\n return await generateResult.promise;\n}\n\n/** Create an object store URL from the server address info. */\nfunction createObjectStoreUrl(\n info: AddressInfo,\n http?: true\n): PFrameInternal.ObjectStoreUrl {\n const protocol = http ? '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 throw new PFrameError(\n `PFrame helper HTTP(S) server bound to 'localhost' has unknown address family: ${info.family}`\n );\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 http,\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 certificateBase64:\n | Base64Encoded<PFrameInternal.PemCertificate>\n | undefined;\n const defaultOptions: ServerOptions = {\n keepAlive: true\n };\n let server: HttpServer | HttpsServer;\n\n if (http) {\n server = createHttpServer(defaultOptions, effectiveHandler);\n } else {\n const { cert, private: key, public: ca } = await generateCertificate();\n certificateBase64 = base64Encode(cert as PFrameInternal.PemCertificate);\n server = createHttpsServer(\n { ...defaultOptions, cert, key, ca },\n effectiveHandler\n );\n }\n\n const abortController = new AbortController();\n server\n .on('listening', () => {\n // Cast is safe by specification <https://nodejs.org/api/net.html#serveraddress>\n const address = createObjectStoreUrl(\n server.address() as AddressInfo,\n http\n );\n stopped = new Deferred<void>();\n\n started.resolve({\n get address(): PFrameInternal.ObjectStoreUrl {\n return address;\n },\n get authToken(): PFrameInternal.HttpAuthorizationToken | undefined {\n return authToken;\n },\n get encodedCaCert():\n | Base64Encoded<PFrameInternal.PemCertificate>\n | undefined {\n return certificateBase64;\n },\n get stopped(): Promise<void> {\n return stopped!.promise;\n },\n stop(): Promise<void> {\n abortController.abort();\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 signal: abortController.signal\n });\n } catch (error: unknown) {\n started.reject(ensureError(error));\n }\n\n return started.promise;\n}\n"],"names":["Deferred","generate","PFrameError","handler","randomUUID","authorizeRequestHandler","createHttpServer","base64Encode","createHttpsServer","ensureError"],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ import type { PFrameInternal } from '@milaboratories/pl-model-middle-layer';
2
+ /**
3
+ * Serve HTTP requests using the provided handler.
4
+ * Returns a promise that resolves when the server is stopped.
5
+ */
6
+ export declare function serve({ handler, port, http, noAuth }: PFrameInternal.HttpServerOptions): Promise<PFrameInternal.HttpServer>;
7
+ //# sourceMappingURL=serve.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"serve.d.ts","sourceRoot":"","sources":["../src/serve.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uCAAuC,CAAC;AA2D5E;;;GAGG;AACH,wBAAsB,KAAK,CAAC,EAC1B,OAAO,EACP,IAAQ,EACR,IAAI,EACJ,MAAM,EACP,EAAE,cAAc,CAAC,iBAAiB,GAAG,OAAO,CAAC,cAAc,CAAC,UAAU,CAAC,CA8EvE"}
package/dist/serve.js ADDED
@@ -0,0 +1,118 @@
1
+ import { createServer } from 'node:http';
2
+ import { createServer as createServer$1 } from 'node:https';
3
+ import { Deferred } from '@milaboratories/helpers';
4
+ import { base64Encode, ensureError, PFrameError } from '@milaboratories/pl-model-common';
5
+ import { generate } from 'selfsigned';
6
+ import { randomUUID } from 'node:crypto';
7
+ import { authorizeRequestHandler } from './handler.js';
8
+
9
+ /** Generate a self-signed certificate for localhost */
10
+ async function generateCertificate() {
11
+ const generateResult = new Deferred();
12
+ generate([{ name: 'commonName', value: 'localhost' }], {
13
+ keySize: 2048,
14
+ algorithm: 'sha256',
15
+ extensions: [
16
+ {
17
+ name: 'subjectAltName',
18
+ altNames: [
19
+ { type: 2, value: 'localhost' }, // DNS
20
+ { type: 7, ip: '127.0.0.1' }, // IPv4
21
+ { type: 7, ip: '::1' } // IPv6
22
+ ]
23
+ }
24
+ ]
25
+ }, (error, result) => {
26
+ if (error) {
27
+ generateResult.reject(error);
28
+ }
29
+ else {
30
+ generateResult.resolve(result);
31
+ }
32
+ });
33
+ return await generateResult.promise;
34
+ }
35
+ /** Create an object store URL from the server address info. */
36
+ function createObjectStoreUrl(info, http) {
37
+ const protocol = http ? 'http' : 'https';
38
+ switch (info.family) {
39
+ case 'IPv4':
40
+ return `${protocol}://${info.address}:${info.port}/`;
41
+ case 'IPv6':
42
+ return `${protocol}://[${info.address}]:${info.port}/`;
43
+ default:
44
+ throw new PFrameError(`PFrame helper HTTP(S) server bound to 'localhost' has unknown address family: ${info.family}`);
45
+ }
46
+ }
47
+ /**
48
+ * Serve HTTP requests using the provided handler.
49
+ * Returns a promise that resolves when the server is stopped.
50
+ */
51
+ async function serve({ handler, port = 0, http, noAuth }) {
52
+ const started = new Deferred();
53
+ try {
54
+ let stopped = null;
55
+ let authToken;
56
+ let effectiveHandler = handler;
57
+ if (!noAuth) {
58
+ authToken = randomUUID();
59
+ effectiveHandler = authorizeRequestHandler(effectiveHandler, authToken);
60
+ }
61
+ // Create HTTP server
62
+ let certificateBase64;
63
+ const defaultOptions = {
64
+ keepAlive: true
65
+ };
66
+ let server;
67
+ if (http) {
68
+ server = createServer(defaultOptions, effectiveHandler);
69
+ }
70
+ else {
71
+ const { cert, private: key, public: ca } = await generateCertificate();
72
+ certificateBase64 = base64Encode(cert);
73
+ server = createServer$1({ ...defaultOptions, cert, key, ca }, effectiveHandler);
74
+ }
75
+ const abortController = new AbortController();
76
+ server
77
+ .on('listening', () => {
78
+ // Cast is safe by specification <https://nodejs.org/api/net.html#serveraddress>
79
+ const address = createObjectStoreUrl(server.address(), http);
80
+ stopped = new Deferred();
81
+ started.resolve({
82
+ get address() {
83
+ return address;
84
+ },
85
+ get authToken() {
86
+ return authToken;
87
+ },
88
+ get encodedCaCert() {
89
+ return certificateBase64;
90
+ },
91
+ get stopped() {
92
+ return stopped.promise;
93
+ },
94
+ stop() {
95
+ abortController.abort();
96
+ return stopped.promise;
97
+ }
98
+ });
99
+ })
100
+ .on('error', (err) => {
101
+ started.reject(err);
102
+ stopped?.reject(err);
103
+ })
104
+ .on('close', () => stopped?.resolve())
105
+ .listen({
106
+ host: 'localhost',
107
+ port,
108
+ signal: abortController.signal
109
+ });
110
+ }
111
+ catch (error) {
112
+ started.reject(ensureError(error));
113
+ }
114
+ return started.promise;
115
+ }
116
+
117
+ export { serve };
118
+ //# sourceMappingURL=serve.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"serve.js","sources":["../src/serve.ts"],"sourcesContent":["import {\n createServer as createHttpServer,\n type RequestListener,\n type Server as HttpServer,\n type ServerOptions\n} from 'node:http';\nimport {\n createServer as createHttpsServer,\n type Server as HttpsServer\n} from 'node:https';\nimport type { AddressInfo } from 'node:net';\nimport { Deferred } from '@milaboratories/helpers';\nimport type { PFrameInternal } from '@milaboratories/pl-model-middle-layer';\nimport {\n base64Encode,\n Base64Encoded,\n ensureError,\n PFrameError\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 const generateResult = new Deferred<GenerateResult>();\n generate(\n [{ name: 'commonName', value: 'localhost' }],\n {\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 (error, result) => {\n if (error) {\n generateResult.reject(error);\n } else {\n generateResult.resolve(result);\n }\n }\n );\n return await generateResult.promise;\n}\n\n/** Create an object store URL from the server address info. */\nfunction createObjectStoreUrl(\n info: AddressInfo,\n http?: true\n): PFrameInternal.ObjectStoreUrl {\n const protocol = http ? '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 throw new PFrameError(\n `PFrame helper HTTP(S) server bound to 'localhost' has unknown address family: ${info.family}`\n );\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 http,\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 certificateBase64:\n | Base64Encoded<PFrameInternal.PemCertificate>\n | undefined;\n const defaultOptions: ServerOptions = {\n keepAlive: true\n };\n let server: HttpServer | HttpsServer;\n\n if (http) {\n server = createHttpServer(defaultOptions, effectiveHandler);\n } else {\n const { cert, private: key, public: ca } = await generateCertificate();\n certificateBase64 = base64Encode(cert as PFrameInternal.PemCertificate);\n server = createHttpsServer(\n { ...defaultOptions, cert, key, ca },\n effectiveHandler\n );\n }\n\n const abortController = new AbortController();\n server\n .on('listening', () => {\n // Cast is safe by specification <https://nodejs.org/api/net.html#serveraddress>\n const address = createObjectStoreUrl(\n server.address() as AddressInfo,\n http\n );\n stopped = new Deferred<void>();\n\n started.resolve({\n get address(): PFrameInternal.ObjectStoreUrl {\n return address;\n },\n get authToken(): PFrameInternal.HttpAuthorizationToken | undefined {\n return authToken;\n },\n get encodedCaCert():\n | Base64Encoded<PFrameInternal.PemCertificate>\n | undefined {\n return certificateBase64;\n },\n get stopped(): Promise<void> {\n return stopped!.promise;\n },\n stop(): Promise<void> {\n abortController.abort();\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 signal: abortController.signal\n });\n } catch (error: unknown) {\n started.reject(ensureError(error));\n }\n\n return started.promise;\n}\n"],"names":["createHttpServer","createHttpsServer"],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ 'use strict';
2
+
3
+ function createETag(filename) {
4
+ // For immutable files, use URL-safe base64 encoded filename as ETag
5
+ const filenameETag = Buffer.from(filename, 'utf8').toString('base64url');
6
+ return `"${filenameETag}"`;
7
+ }
8
+
9
+ exports.createETag = createETag;
10
+ //# sourceMappingURL=etag.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"etag.cjs","sources":["../../src/utils/etag.ts"],"sourcesContent":["import type { Branded } from '@milaboratories/pl-model-common';\nimport type { PFrameInternal } from '@milaboratories/pl-model-middle-layer';\n\n/**\n * See <https://datatracker.ietf.org/doc/html/rfc9110#section-8.8.3>\n *\n * Examples:\n *\n * ```text\n * ETag: \"xyzzy\"\n * ETag: W/\"xyzzy\"\n * ```\n */\nexport type Etag = Branded<string, 'Etag'>;\n\nexport function createETag(filename: PFrameInternal.ParquetFileName): Etag {\n // For immutable files, use URL-safe base64 encoded filename as ETag\n const filenameETag = Buffer.from(filename, 'utf8').toString('base64url');\n return `\"${filenameETag}\"` as Etag;\n}\n"],"names":[],"mappings":";;AAeM,SAAU,UAAU,CAAC,QAAwC,EAAA;;AAEjE,IAAA,MAAM,YAAY,GAAG,MAAM,CAAC,IAAI,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC;IACxE,OAAO,CAAA,CAAA,EAAI,YAAY,CAAA,CAAA,CAAW;AACpC;;;;"}
@@ -0,0 +1,15 @@
1
+ import type { Branded } from '@milaboratories/pl-model-common';
2
+ import type { PFrameInternal } from '@milaboratories/pl-model-middle-layer';
3
+ /**
4
+ * See <https://datatracker.ietf.org/doc/html/rfc9110#section-8.8.3>
5
+ *
6
+ * Examples:
7
+ *
8
+ * ```text
9
+ * ETag: "xyzzy"
10
+ * ETag: W/"xyzzy"
11
+ * ```
12
+ */
13
+ export type Etag = Branded<string, 'Etag'>;
14
+ export declare function createETag(filename: PFrameInternal.ParquetFileName): Etag;
15
+ //# sourceMappingURL=etag.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"etag.d.ts","sourceRoot":"","sources":["../../src/utils/etag.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,iCAAiC,CAAC;AAC/D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uCAAuC,CAAC;AAE5E;;;;;;;;;GASG;AACH,MAAM,MAAM,IAAI,GAAG,OAAO,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AAE3C,wBAAgB,UAAU,CAAC,QAAQ,EAAE,cAAc,CAAC,eAAe,GAAG,IAAI,CAIzE"}
@@ -0,0 +1,8 @@
1
+ function createETag(filename) {
2
+ // For immutable files, use URL-safe base64 encoded filename as ETag
3
+ const filenameETag = Buffer.from(filename, 'utf8').toString('base64url');
4
+ return `"${filenameETag}"`;
5
+ }
6
+
7
+ export { createETag };
8
+ //# sourceMappingURL=etag.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"etag.js","sources":["../../src/utils/etag.ts"],"sourcesContent":["import type { Branded } from '@milaboratories/pl-model-common';\nimport type { PFrameInternal } from '@milaboratories/pl-model-middle-layer';\n\n/**\n * See <https://datatracker.ietf.org/doc/html/rfc9110#section-8.8.3>\n *\n * Examples:\n *\n * ```text\n * ETag: \"xyzzy\"\n * ETag: W/\"xyzzy\"\n * ```\n */\nexport type Etag = Branded<string, 'Etag'>;\n\nexport function createETag(filename: PFrameInternal.ParquetFileName): Etag {\n // For immutable files, use URL-safe base64 encoded filename as ETag\n const filenameETag = Buffer.from(filename, 'utf8').toString('base64url');\n return `\"${filenameETag}\"` as Etag;\n}\n"],"names":[],"mappings":"AAeM,SAAU,UAAU,CAAC,QAAwC,EAAA;;AAEjE,IAAA,MAAM,YAAY,GAAG,MAAM,CAAC,IAAI,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC;IACxE,OAAO,CAAA,CAAA,EAAI,YAAY,CAAA,CAAA,CAAW;AACpC;;;;"}
@@ -0,0 +1,15 @@
1
+ 'use strict';
2
+
3
+ const PARQUET_FILENAME_REGEX = /^\/([\w\-.]+.parquet)$/;
4
+ function getFilenameFromUrl(request) {
5
+ const url = request.url;
6
+ if (url === undefined)
7
+ return null;
8
+ const match = url.match(PARQUET_FILENAME_REGEX);
9
+ if (!match)
10
+ return null;
11
+ return match[1];
12
+ }
13
+
14
+ exports.getFilenameFromUrl = getFilenameFromUrl;
15
+ //# sourceMappingURL=filename.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"filename.cjs","sources":["../../src/utils/filename.ts"],"sourcesContent":["import type { PFrameInternal } from '@milaboratories/pl-model-middle-layer';\nimport type { IncomingMessage } from 'node:http';\n\nconst PARQUET_FILENAME_REGEX = /^\\/([\\w\\-.]+.parquet)$/;\n\nexport function getFilenameFromUrl(\n request: IncomingMessage\n): PFrameInternal.ParquetFileName | null {\n const url = request.url;\n if (url === undefined) return null;\n\n const match = url.match(PARQUET_FILENAME_REGEX);\n if (!match) return null;\n\n return match[1] as PFrameInternal.ParquetFileName;\n}\n"],"names":[],"mappings":";;AAGA,MAAM,sBAAsB,GAAG,wBAAwB;AAEjD,SAAU,kBAAkB,CAChC,OAAwB,EAAA;AAExB,IAAA,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG;IACvB,IAAI,GAAG,KAAK,SAAS;AAAE,QAAA,OAAO,IAAI;IAElC,MAAM,KAAK,GAAG,GAAG,CAAC,KAAK,CAAC,sBAAsB,CAAC;AAC/C,IAAA,IAAI,CAAC,KAAK;AAAE,QAAA,OAAO,IAAI;AAEvB,IAAA,OAAO,KAAK,CAAC,CAAC,CAAmC;AACnD;;;;"}
@@ -0,0 +1,4 @@
1
+ import type { PFrameInternal } from '@milaboratories/pl-model-middle-layer';
2
+ import type { IncomingMessage } from 'node:http';
3
+ export declare function getFilenameFromUrl(request: IncomingMessage): PFrameInternal.ParquetFileName | null;
4
+ //# sourceMappingURL=filename.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"filename.d.ts","sourceRoot":"","sources":["../../src/utils/filename.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uCAAuC,CAAC;AAC5E,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AAIjD,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,eAAe,GACvB,cAAc,CAAC,eAAe,GAAG,IAAI,CAQvC"}
@@ -0,0 +1,13 @@
1
+ const PARQUET_FILENAME_REGEX = /^\/([\w\-.]+.parquet)$/;
2
+ function getFilenameFromUrl(request) {
3
+ const url = request.url;
4
+ if (url === undefined)
5
+ return null;
6
+ const match = url.match(PARQUET_FILENAME_REGEX);
7
+ if (!match)
8
+ return null;
9
+ return match[1];
10
+ }
11
+
12
+ export { getFilenameFromUrl };
13
+ //# sourceMappingURL=filename.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"filename.js","sources":["../../src/utils/filename.ts"],"sourcesContent":["import type { PFrameInternal } from '@milaboratories/pl-model-middle-layer';\nimport type { IncomingMessage } from 'node:http';\n\nconst PARQUET_FILENAME_REGEX = /^\\/([\\w\\-.]+.parquet)$/;\n\nexport function getFilenameFromUrl(\n request: IncomingMessage\n): PFrameInternal.ParquetFileName | null {\n const url = request.url;\n if (url === undefined) return null;\n\n const match = url.match(PARQUET_FILENAME_REGEX);\n if (!match) return null;\n\n return match[1] as PFrameInternal.ParquetFileName;\n}\n"],"names":[],"mappings":"AAGA,MAAM,sBAAsB,GAAG,wBAAwB;AAEjD,SAAU,kBAAkB,CAChC,OAAwB,EAAA;AAExB,IAAA,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG;IACvB,IAAI,GAAG,KAAK,SAAS;AAAE,QAAA,OAAO,IAAI;IAElC,MAAM,KAAK,GAAG,GAAG,CAAC,KAAK,CAAC,sBAAsB,CAAC;AAC/C,IAAA,IAAI,CAAC,KAAK;AAAE,QAAA,OAAO,IAAI;AAEvB,IAAA,OAAO,KAAK,CAAC,CAAC,CAAmC;AACnD;;;;"}
@@ -0,0 +1,26 @@
1
+ 'use strict';
2
+
3
+ /** HTTP header names used in the parquet server handler */
4
+ const HeaderName = {
5
+ AcceptRanges: 'accept-ranges',
6
+ Allow: 'allow',
7
+ CacheControl: 'cache-control',
8
+ ContentLength: 'content-length',
9
+ ContentRange: 'content-range',
10
+ ContentType: 'content-type',
11
+ ETag: 'etag',
12
+ LastModified: 'last-modified',
13
+ WWWAuthenticate: 'www-authenticate'
14
+ };
15
+ /** HTTP header values used in the parquet server handler */
16
+ const HeaderValue = {
17
+ AcceptRanges: 'bytes',
18
+ Allow: 'GET, HEAD',
19
+ CacheControl: 'public, immutable, max-age=31536000',
20
+ ContentType: 'application/octet-stream',
21
+ WWWAuthenticate: 'Bearer realm="parquet-server"'
22
+ };
23
+
24
+ exports.HeaderName = HeaderName;
25
+ exports.HeaderValue = HeaderValue;
26
+ //# sourceMappingURL=headers.cjs.map