@loaders.gl/parquet 4.5.3 → 5.0.0-alpha.10

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 (790) hide show
  1. package/LICENSE +14 -3
  2. package/dist/bundled.d.ts +9 -0
  3. package/dist/bundled.d.ts.map +1 -0
  4. package/dist/bundled.js +10 -0
  5. package/dist/bundled.js.map +1 -0
  6. package/dist/delta-format.d.ts +9 -0
  7. package/dist/delta-format.d.ts.map +1 -0
  8. package/dist/delta-format.js +12 -0
  9. package/dist/delta-format.js.map +1 -0
  10. package/dist/delta-source-loader-types.d.ts +27 -0
  11. package/dist/delta-source-loader-types.d.ts.map +1 -0
  12. package/dist/delta-source-loader-types.js +28 -0
  13. package/dist/delta-source-loader-types.js.map +1 -0
  14. package/dist/delta-source.d.ts +48 -0
  15. package/dist/delta-source.d.ts.map +1 -0
  16. package/dist/delta-source.js +222 -0
  17. package/dist/delta-source.js.map +1 -0
  18. package/dist/delta-types.d.ts +38 -0
  19. package/dist/delta-types.d.ts.map +1 -0
  20. package/dist/delta-types.js +5 -0
  21. package/dist/delta-types.js.map +1 -0
  22. package/dist/geoparquet-loader-with-parser.d.ts +33 -0
  23. package/dist/geoparquet-loader-with-parser.d.ts.map +1 -0
  24. package/dist/geoparquet-loader-with-parser.js +52 -0
  25. package/dist/geoparquet-loader-with-parser.js.map +1 -0
  26. package/dist/geoparquet-loader.d.ts +59 -0
  27. package/dist/geoparquet-loader.d.ts.map +1 -0
  28. package/dist/geoparquet-loader.js +30 -0
  29. package/dist/geoparquet-loader.js.map +1 -0
  30. package/dist/iceberg-rest-catalog.d.ts +49 -0
  31. package/dist/iceberg-rest-catalog.d.ts.map +1 -0
  32. package/dist/iceberg-rest-catalog.js +78 -0
  33. package/dist/iceberg-rest-catalog.js.map +1 -0
  34. package/dist/iceberg-table-source.d.ts +63 -0
  35. package/dist/iceberg-table-source.d.ts.map +1 -0
  36. package/dist/iceberg-table-source.js +797 -0
  37. package/dist/iceberg-table-source.js.map +1 -0
  38. package/dist/iceberg-types.d.ts +150 -0
  39. package/dist/iceberg-types.d.ts.map +1 -0
  40. package/dist/iceberg-types.js +5 -0
  41. package/dist/iceberg-types.js.map +1 -0
  42. package/dist/index.cjs +41568 -6781
  43. package/dist/index.cjs.map +3 -4
  44. package/dist/index.d.ts +15 -9
  45. package/dist/index.d.ts.map +1 -1
  46. package/dist/index.js +11 -12
  47. package/dist/index.js.map +1 -1
  48. package/dist/lib/arrow/convert-columns-to-row-group.js +3 -0
  49. package/dist/lib/arrow/convert-columns-to-row-group.js.map +1 -1
  50. package/dist/lib/arrow/convert-schema-from-parquet.d.ts +3 -1
  51. package/dist/lib/arrow/convert-schema-from-parquet.d.ts.map +1 -1
  52. package/dist/lib/arrow/convert-schema-from-parquet.js +164 -29
  53. package/dist/lib/arrow/convert-schema-from-parquet.js.map +1 -1
  54. package/dist/lib/arrow/convert-schema-to-parquet.d.ts.map +1 -1
  55. package/dist/lib/arrow/convert-schema-to-parquet.js +16 -7
  56. package/dist/lib/arrow/convert-schema-to-parquet.js.map +1 -1
  57. package/dist/lib/arrow/create-nested-arrow-vector.d.ts +8 -0
  58. package/dist/lib/arrow/create-nested-arrow-vector.d.ts.map +1 -0
  59. package/dist/lib/arrow/create-nested-arrow-vector.js +485 -0
  60. package/dist/lib/arrow/create-nested-arrow-vector.js.map +1 -0
  61. package/dist/lib/constants.d.ts +0 -1
  62. package/dist/lib/constants.d.ts.map +1 -1
  63. package/dist/lib/constants.js +1 -2
  64. package/dist/lib/constants.js.map +1 -1
  65. package/dist/lib/encoders/encode-arrow-to-parquet.d.ts +2 -2
  66. package/dist/lib/encoders/encode-arrow-to-parquet.d.ts.map +1 -1
  67. package/dist/lib/encoders/encode-arrow-to-parquet.js +3 -10
  68. package/dist/lib/encoders/encode-arrow-to-parquet.js.map +1 -1
  69. package/dist/lib/encoders/encode-table-to-parquet-js.d.ts +5 -0
  70. package/dist/lib/encoders/encode-table-to-parquet-js.d.ts.map +1 -0
  71. package/dist/lib/encoders/encode-table-to-parquet-js.js +394 -0
  72. package/dist/lib/encoders/encode-table-to-parquet-js.js.map +1 -0
  73. package/dist/lib/geo/geoparquet-covering.d.ts +19 -0
  74. package/dist/lib/geo/geoparquet-covering.d.ts.map +1 -0
  75. package/dist/lib/geo/geoparquet-covering.js +137 -0
  76. package/dist/lib/geo/geoparquet-covering.js.map +1 -0
  77. package/dist/lib/geo/geospatial-metadata.d.ts +67 -0
  78. package/dist/lib/geo/geospatial-metadata.d.ts.map +1 -0
  79. package/dist/lib/geo/geospatial-metadata.js +412 -0
  80. package/dist/lib/geo/geospatial-metadata.js.map +1 -0
  81. package/dist/lib/parquet-bloom-filter-planner.d.ts +16 -0
  82. package/dist/lib/parquet-bloom-filter-planner.d.ts.map +1 -0
  83. package/dist/lib/parquet-bloom-filter-planner.js +57 -0
  84. package/dist/lib/parquet-bloom-filter-planner.js.map +1 -0
  85. package/dist/lib/parquet-bloom-filter.d.ts +30 -0
  86. package/dist/lib/parquet-bloom-filter.d.ts.map +1 -0
  87. package/dist/lib/parquet-bloom-filter.js +289 -0
  88. package/dist/lib/parquet-bloom-filter.js.map +1 -0
  89. package/dist/lib/parquet-encryption.d.ts +80 -0
  90. package/dist/lib/parquet-encryption.d.ts.map +1 -0
  91. package/dist/lib/parquet-encryption.js +190 -0
  92. package/dist/lib/parquet-encryption.js.map +1 -0
  93. package/dist/lib/parquet-page-index.d.ts +91 -0
  94. package/dist/lib/parquet-page-index.d.ts.map +1 -0
  95. package/dist/lib/parquet-page-index.js +463 -0
  96. package/dist/lib/parquet-page-index.js.map +1 -0
  97. package/dist/lib/parquet-predicate.d.ts +21 -0
  98. package/dist/lib/parquet-predicate.d.ts.map +1 -0
  99. package/dist/lib/parquet-predicate.js +328 -0
  100. package/dist/lib/parquet-predicate.js.map +1 -0
  101. package/dist/lib/parquet-source-worker-client.d.ts +6 -0
  102. package/dist/lib/parquet-source-worker-client.d.ts.map +1 -0
  103. package/dist/lib/parquet-source-worker-client.js +24 -0
  104. package/dist/lib/parquet-source-worker-client.js.map +1 -0
  105. package/dist/lib/parquet-source-worker-decoder.d.ts +7 -0
  106. package/dist/lib/parquet-source-worker-decoder.d.ts.map +1 -0
  107. package/dist/lib/parquet-source-worker-decoder.js +181 -0
  108. package/dist/lib/parquet-source-worker-decoder.js.map +1 -0
  109. package/dist/lib/parquet-source-worker-file.d.ts +24 -0
  110. package/dist/lib/parquet-source-worker-file.d.ts.map +1 -0
  111. package/dist/lib/parquet-source-worker-file.js +40 -0
  112. package/dist/lib/parquet-source-worker-file.js.map +1 -0
  113. package/dist/lib/parquet-source-worker-types.d.ts +121 -0
  114. package/dist/lib/parquet-source-worker-types.d.ts.map +1 -0
  115. package/dist/lib/parquet-source-worker-types.js +5 -0
  116. package/dist/lib/parquet-source-worker-types.js.map +1 -0
  117. package/dist/lib/parquet-worker-transport.d.ts +8 -0
  118. package/dist/lib/parquet-worker-transport.d.ts.map +1 -0
  119. package/dist/lib/parquet-worker-transport.js +40 -0
  120. package/dist/lib/parquet-worker-transport.js.map +1 -0
  121. package/dist/lib/parsers/convert-parquet-tables.d.ts +16 -0
  122. package/dist/lib/parsers/convert-parquet-tables.d.ts.map +1 -0
  123. package/dist/lib/parsers/convert-parquet-tables.js +30 -0
  124. package/dist/lib/parsers/convert-parquet-tables.js.map +1 -0
  125. package/dist/lib/parsers/get-parquet-schema.d.ts.map +1 -1
  126. package/dist/lib/parsers/get-parquet-schema.js +6 -2
  127. package/dist/lib/parsers/get-parquet-schema.js.map +1 -1
  128. package/dist/lib/parsers/parse-geoparquet-to-geojson.d.ts +4 -4
  129. package/dist/lib/parsers/parse-geoparquet-to-geojson.d.ts.map +1 -1
  130. package/dist/lib/parsers/parse-geoparquet-to-geojson.js +19 -6
  131. package/dist/lib/parsers/parse-geoparquet-to-geojson.js.map +1 -1
  132. package/dist/lib/parsers/parse-parquet-tables.d.ts +37 -0
  133. package/dist/lib/parsers/parse-parquet-tables.d.ts.map +1 -0
  134. package/dist/lib/parsers/parse-parquet-tables.js +50 -0
  135. package/dist/lib/parsers/parse-parquet-tables.js.map +1 -0
  136. package/dist/lib/parsers/parse-parquet-to-arrow-js.d.ts +19 -0
  137. package/dist/lib/parsers/parse-parquet-to-arrow-js.d.ts.map +1 -0
  138. package/dist/lib/parsers/parse-parquet-to-arrow-js.js +921 -0
  139. package/dist/lib/parsers/parse-parquet-to-arrow-js.js.map +1 -0
  140. package/dist/lib/parsers/parse-parquet-to-arrow.d.ts +7 -7
  141. package/dist/lib/parsers/parse-parquet-to-arrow.d.ts.map +1 -1
  142. package/dist/lib/parsers/parse-parquet-to-arrow.js +45 -32
  143. package/dist/lib/parsers/parse-parquet-to-arrow.js.map +1 -1
  144. package/dist/lib/parsers/parse-parquet-to-columns.d.ts +3 -3
  145. package/dist/lib/parsers/parse-parquet-to-columns.d.ts.map +1 -1
  146. package/dist/lib/parsers/parse-parquet-to-columns.js +4 -5
  147. package/dist/lib/parsers/parse-parquet-to-columns.js.map +1 -1
  148. package/dist/lib/parsers/parse-parquet-to-json.d.ts +3 -3
  149. package/dist/lib/parsers/parse-parquet-to-json.d.ts.map +1 -1
  150. package/dist/lib/parsers/parse-parquet-to-json.js +95 -28
  151. package/dist/lib/parsers/parse-parquet-to-json.js.map +1 -1
  152. package/dist/lib/slice-parquet-batch.d.ts +9 -0
  153. package/dist/lib/slice-parquet-batch.d.ts.map +1 -0
  154. package/dist/lib/slice-parquet-batch.js +28 -0
  155. package/dist/lib/slice-parquet-batch.js.map +1 -0
  156. package/dist/lib/sources/parquet-range-file.d.ts +80 -0
  157. package/dist/lib/sources/parquet-range-file.d.ts.map +1 -0
  158. package/dist/lib/sources/parquet-range-file.js +267 -0
  159. package/dist/lib/sources/parquet-range-file.js.map +1 -0
  160. package/dist/lib/sources/single-batch-queue.d.ts +24 -0
  161. package/dist/lib/sources/single-batch-queue.d.ts.map +1 -0
  162. package/dist/lib/sources/single-batch-queue.js +91 -0
  163. package/dist/lib/sources/single-batch-queue.js.map +1 -0
  164. package/dist/lib/utils/load-wasm-node.d.ts +3 -0
  165. package/dist/lib/utils/load-wasm-node.d.ts.map +1 -0
  166. package/dist/lib/utils/load-wasm-node.js +23 -0
  167. package/dist/lib/utils/load-wasm-node.js.map +1 -0
  168. package/dist/lib/utils/load-wasm.d.ts +2 -2
  169. package/dist/lib/utils/load-wasm.d.ts.map +1 -1
  170. package/dist/lib/utils/load-wasm.js +28 -12
  171. package/dist/lib/utils/load-wasm.js.map +1 -1
  172. package/dist/lib/utils/make-stream-iterator.d.ts +2 -0
  173. package/dist/lib/utils/make-stream-iterator.d.ts.map +1 -1
  174. package/dist/lib/utils/make-stream-iterator.js +123 -14
  175. package/dist/lib/utils/make-stream-iterator.js.map +1 -1
  176. package/dist/lib/utils/normalize-parquet-options.d.ts +5 -0
  177. package/dist/lib/utils/normalize-parquet-options.d.ts.map +1 -0
  178. package/dist/lib/utils/normalize-parquet-options.js +25 -0
  179. package/dist/lib/utils/normalize-parquet-options.js.map +1 -0
  180. package/dist/parquet-dataset-source.cjs +38974 -0
  181. package/dist/parquet-dataset-source.cjs.map +6 -0
  182. package/dist/parquet-dataset-source.d.ts +81 -0
  183. package/dist/parquet-dataset-source.d.ts.map +1 -0
  184. package/dist/parquet-dataset-source.js +602 -0
  185. package/dist/parquet-dataset-source.js.map +1 -0
  186. package/dist/parquet-encoded-page-types.d.ts +108 -0
  187. package/dist/parquet-encoded-page-types.d.ts.map +1 -0
  188. package/dist/parquet-encoded-page-types.js +5 -0
  189. package/dist/parquet-encoded-page-types.js.map +1 -0
  190. package/dist/parquet-format.d.ts +2 -0
  191. package/dist/parquet-format.d.ts.map +1 -1
  192. package/dist/parquet-format.js +2 -0
  193. package/dist/parquet-format.js.map +1 -1
  194. package/dist/parquet-js-loader-types.d.ts +59 -0
  195. package/dist/parquet-js-loader-types.d.ts.map +1 -0
  196. package/dist/parquet-js-loader-types.js +34 -0
  197. package/dist/parquet-js-loader-types.js.map +1 -0
  198. package/dist/parquet-js-loader.d.ts +18 -22
  199. package/dist/parquet-js-loader.d.ts.map +1 -1
  200. package/dist/parquet-js-loader.js +53 -14
  201. package/dist/parquet-js-loader.js.map +1 -1
  202. package/dist/parquet-js-writer.d.ts +65 -0
  203. package/dist/parquet-js-writer.d.ts.map +1 -0
  204. package/dist/parquet-js-writer.js +28 -0
  205. package/dist/parquet-js-writer.js.map +1 -0
  206. package/dist/parquet-loader-base.d.ts +25 -0
  207. package/dist/parquet-loader-base.d.ts.map +1 -0
  208. package/dist/parquet-loader-base.js +23 -0
  209. package/dist/parquet-loader-base.js.map +1 -0
  210. package/dist/parquet-loader-options.d.ts +55 -0
  211. package/dist/parquet-loader-options.d.ts.map +1 -0
  212. package/dist/parquet-loader-options.js +10 -0
  213. package/dist/parquet-loader-options.js.map +1 -0
  214. package/dist/parquet-loader-types.d.ts +61 -0
  215. package/dist/parquet-loader-types.d.ts.map +1 -0
  216. package/dist/parquet-loader-types.js +17 -0
  217. package/dist/parquet-loader-types.js.map +1 -0
  218. package/dist/parquet-loader.d.ts +42 -0
  219. package/dist/parquet-loader.d.ts.map +1 -0
  220. package/dist/parquet-loader.js +68 -0
  221. package/dist/parquet-loader.js.map +1 -0
  222. package/dist/parquet-source-capabilities.d.ts +43 -0
  223. package/dist/parquet-source-capabilities.d.ts.map +1 -0
  224. package/dist/parquet-source-capabilities.js +32 -0
  225. package/dist/parquet-source-capabilities.js.map +1 -0
  226. package/dist/parquet-source-loader-types.d.ts +82 -0
  227. package/dist/parquet-source-loader-types.d.ts.map +1 -0
  228. package/dist/parquet-source-loader-types.js +84 -0
  229. package/dist/parquet-source-loader-types.js.map +1 -0
  230. package/dist/parquet-source-loader.cjs +38261 -0
  231. package/dist/parquet-source-loader.cjs.map +6 -0
  232. package/dist/parquet-source-loader.d.ts +167 -0
  233. package/dist/parquet-source-loader.d.ts.map +1 -0
  234. package/dist/parquet-source-loader.js +1438 -0
  235. package/dist/parquet-source-loader.js.map +1 -0
  236. package/dist/parquet-source-types.d.ts +534 -0
  237. package/dist/parquet-source-types.d.ts.map +1 -0
  238. package/dist/parquet-source-types.js +5 -0
  239. package/dist/parquet-source-types.js.map +1 -0
  240. package/dist/parquet-source-worker-factory.d.ts +4 -0
  241. package/dist/parquet-source-worker-factory.d.ts.map +1 -0
  242. package/dist/parquet-source-worker-factory.js +6 -0
  243. package/dist/parquet-source-worker-factory.js.map +1 -0
  244. package/dist/parquet-source-worker-url.d.ts +3 -0
  245. package/dist/parquet-source-worker-url.d.ts.map +1 -0
  246. package/dist/parquet-source-worker-url.js +22 -0
  247. package/dist/parquet-source-worker-url.js.map +1 -0
  248. package/dist/parquet-source-worker.js +38871 -0
  249. package/dist/parquet-source-worker.js.map +7 -0
  250. package/dist/parquet-worker-node.cjs +78507 -0
  251. package/dist/parquet-worker-node.cjs.map +7 -0
  252. package/dist/parquet-worker-url.d.ts +3 -0
  253. package/dist/parquet-worker-url.d.ts.map +1 -0
  254. package/dist/parquet-worker-url.js +19 -0
  255. package/dist/parquet-worker-url.js.map +1 -0
  256. package/dist/parquet-worker.js +20094 -0
  257. package/dist/parquet-worker.js.map +7 -0
  258. package/dist/parquet-writer.d.ts +10 -0
  259. package/dist/parquet-writer.d.ts.map +1 -0
  260. package/dist/parquet-writer.js +27 -0
  261. package/dist/parquet-writer.js.map +1 -0
  262. package/dist/parquet_wasm_bg.wasm +0 -0
  263. package/dist/parquetjs/codecs/byte-stream-split.d.ts +9 -0
  264. package/dist/parquetjs/codecs/byte-stream-split.d.ts.map +1 -0
  265. package/dist/parquetjs/codecs/byte-stream-split.js +159 -0
  266. package/dist/parquetjs/codecs/byte-stream-split.js.map +1 -0
  267. package/dist/parquetjs/codecs/declare.d.ts +35 -3
  268. package/dist/parquetjs/codecs/declare.d.ts.map +1 -1
  269. package/dist/parquetjs/codecs/declare.js +17 -1
  270. package/dist/parquetjs/codecs/declare.js.map +1 -1
  271. package/dist/parquetjs/codecs/delta.d.ts +17 -0
  272. package/dist/parquetjs/codecs/delta.d.ts.map +1 -0
  273. package/dist/parquetjs/codecs/delta.js +788 -0
  274. package/dist/parquetjs/codecs/delta.js.map +1 -0
  275. package/dist/parquetjs/codecs/dictionary.d.ts +5 -2
  276. package/dist/parquetjs/codecs/dictionary.d.ts.map +1 -1
  277. package/dist/parquetjs/codecs/dictionary.js +16 -4
  278. package/dist/parquetjs/codecs/dictionary.js.map +1 -1
  279. package/dist/parquetjs/codecs/index.d.ts.map +1 -1
  280. package/dist/parquetjs/codecs/index.js +22 -0
  281. package/dist/parquetjs/codecs/index.js.map +1 -1
  282. package/dist/parquetjs/codecs/plain.d.ts +3 -3
  283. package/dist/parquetjs/codecs/plain.d.ts.map +1 -1
  284. package/dist/parquetjs/codecs/plain.js +231 -78
  285. package/dist/parquetjs/codecs/plain.js.map +1 -1
  286. package/dist/parquetjs/codecs/rle.d.ts +7 -3
  287. package/dist/parquetjs/codecs/rle.d.ts.map +1 -1
  288. package/dist/parquetjs/codecs/rle.js +613 -69
  289. package/dist/parquetjs/codecs/rle.js.map +1 -1
  290. package/dist/parquetjs/compression.d.ts +16 -8
  291. package/dist/parquetjs/compression.d.ts.map +1 -1
  292. package/dist/parquetjs/compression.js +114 -141
  293. package/dist/parquetjs/compression.js.map +1 -1
  294. package/dist/parquetjs/encoder/dictionary-planner.d.ts +17 -0
  295. package/dist/parquetjs/encoder/dictionary-planner.d.ts.map +1 -0
  296. package/dist/parquetjs/encoder/dictionary-planner.js +67 -0
  297. package/dist/parquetjs/encoder/dictionary-planner.js.map +1 -0
  298. package/dist/parquetjs/encoder/page-planner.d.ts +11 -0
  299. package/dist/parquetjs/encoder/page-planner.d.ts.map +1 -0
  300. package/dist/parquetjs/encoder/page-planner.js +60 -0
  301. package/dist/parquetjs/encoder/page-planner.js.map +1 -0
  302. package/dist/parquetjs/encoder/parquet-encoder.d.ts +54 -5
  303. package/dist/parquetjs/encoder/parquet-encoder.d.ts.map +1 -1
  304. package/dist/parquetjs/encoder/parquet-encoder.js +930 -66
  305. package/dist/parquetjs/encoder/parquet-encoder.js.map +1 -1
  306. package/dist/parquetjs/parquet-thrift/BoundingBox.d.ts +29 -0
  307. package/dist/parquetjs/parquet-thrift/BoundingBox.d.ts.map +1 -0
  308. package/dist/parquetjs/parquet-thrift/BoundingBox.js +85 -0
  309. package/dist/parquetjs/parquet-thrift/BoundingBox.js.map +1 -0
  310. package/dist/parquetjs/parquet-thrift/BsonType.d.ts +1 -1
  311. package/dist/parquetjs/parquet-thrift/BsonType.d.ts.map +1 -1
  312. package/dist/parquetjs/parquet-thrift/BsonType.js +1 -1
  313. package/dist/parquetjs/parquet-thrift/BsonType.js.map +1 -1
  314. package/dist/parquetjs/parquet-thrift/ColumnChunk.d.ts +7 -2
  315. package/dist/parquetjs/parquet-thrift/ColumnChunk.d.ts.map +1 -1
  316. package/dist/parquetjs/parquet-thrift/ColumnChunk.js +37 -2
  317. package/dist/parquetjs/parquet-thrift/ColumnChunk.js.map +1 -1
  318. package/dist/parquetjs/parquet-thrift/ColumnCryptoMetaData.d.ts +35 -0
  319. package/dist/parquetjs/parquet-thrift/ColumnCryptoMetaData.d.ts.map +1 -0
  320. package/dist/parquetjs/parquet-thrift/ColumnCryptoMetaData.js +135 -0
  321. package/dist/parquetjs/parquet-thrift/ColumnCryptoMetaData.js.map +1 -0
  322. package/dist/parquetjs/parquet-thrift/ColumnIndex.d.ts +6 -6
  323. package/dist/parquetjs/parquet-thrift/ColumnIndex.d.ts.map +1 -1
  324. package/dist/parquetjs/parquet-thrift/ColumnIndex.js +3 -3
  325. package/dist/parquetjs/parquet-thrift/ColumnIndex.js.map +1 -1
  326. package/dist/parquetjs/parquet-thrift/ColumnMetaData.d.ts +12 -2
  327. package/dist/parquetjs/parquet-thrift/ColumnMetaData.d.ts.map +1 -1
  328. package/dist/parquetjs/parquet-thrift/ColumnMetaData.js +75 -2
  329. package/dist/parquetjs/parquet-thrift/ColumnMetaData.js.map +1 -1
  330. package/dist/parquetjs/parquet-thrift/ColumnOrder.d.ts +1 -1
  331. package/dist/parquetjs/parquet-thrift/ColumnOrder.d.ts.map +1 -1
  332. package/dist/parquetjs/parquet-thrift/ColumnOrder.js +1 -1
  333. package/dist/parquetjs/parquet-thrift/ColumnOrder.js.map +1 -1
  334. package/dist/parquetjs/parquet-thrift/DataPageHeader.d.ts +1 -1
  335. package/dist/parquetjs/parquet-thrift/DataPageHeader.d.ts.map +1 -1
  336. package/dist/parquetjs/parquet-thrift/DataPageHeader.js +1 -1
  337. package/dist/parquetjs/parquet-thrift/DataPageHeader.js.map +1 -1
  338. package/dist/parquetjs/parquet-thrift/DataPageHeaderV2.d.ts +1 -1
  339. package/dist/parquetjs/parquet-thrift/DataPageHeaderV2.d.ts.map +1 -1
  340. package/dist/parquetjs/parquet-thrift/DataPageHeaderV2.js +1 -1
  341. package/dist/parquetjs/parquet-thrift/DataPageHeaderV2.js.map +1 -1
  342. package/dist/parquetjs/parquet-thrift/DateType.d.ts +1 -1
  343. package/dist/parquetjs/parquet-thrift/DateType.d.ts.map +1 -1
  344. package/dist/parquetjs/parquet-thrift/DateType.js +1 -1
  345. package/dist/parquetjs/parquet-thrift/DateType.js.map +1 -1
  346. package/dist/parquetjs/parquet-thrift/DecimalType.d.ts +1 -1
  347. package/dist/parquetjs/parquet-thrift/DecimalType.d.ts.map +1 -1
  348. package/dist/parquetjs/parquet-thrift/DecimalType.js +1 -1
  349. package/dist/parquetjs/parquet-thrift/DecimalType.js.map +1 -1
  350. package/dist/parquetjs/parquet-thrift/DictionaryPageHeader.d.ts +1 -1
  351. package/dist/parquetjs/parquet-thrift/DictionaryPageHeader.d.ts.map +1 -1
  352. package/dist/parquetjs/parquet-thrift/DictionaryPageHeader.js +1 -1
  353. package/dist/parquetjs/parquet-thrift/DictionaryPageHeader.js.map +1 -1
  354. package/dist/parquetjs/parquet-thrift/EdgeInterpolationAlgorithm.d.ts +8 -0
  355. package/dist/parquetjs/parquet-thrift/EdgeInterpolationAlgorithm.d.ts.map +1 -0
  356. package/dist/parquetjs/parquet-thrift/EdgeInterpolationAlgorithm.js +14 -0
  357. package/dist/parquetjs/parquet-thrift/EdgeInterpolationAlgorithm.js.map +1 -0
  358. package/dist/parquetjs/parquet-thrift/Encoding.d.ts +2 -1
  359. package/dist/parquetjs/parquet-thrift/Encoding.d.ts.map +1 -1
  360. package/dist/parquetjs/parquet-thrift/Encoding.js +1 -0
  361. package/dist/parquetjs/parquet-thrift/Encoding.js.map +1 -1
  362. package/dist/parquetjs/parquet-thrift/EncryptionAlgorithm.d.ts +22 -0
  363. package/dist/parquetjs/parquet-thrift/EncryptionAlgorithm.d.ts.map +1 -0
  364. package/dist/parquetjs/parquet-thrift/EncryptionAlgorithm.js +111 -0
  365. package/dist/parquetjs/parquet-thrift/EncryptionAlgorithm.js.map +1 -0
  366. package/dist/parquetjs/parquet-thrift/EnumType.d.ts +1 -1
  367. package/dist/parquetjs/parquet-thrift/EnumType.d.ts.map +1 -1
  368. package/dist/parquetjs/parquet-thrift/EnumType.js +1 -1
  369. package/dist/parquetjs/parquet-thrift/EnumType.js.map +1 -1
  370. package/dist/parquetjs/parquet-thrift/FileCryptoMetaData.d.ts +16 -0
  371. package/dist/parquetjs/parquet-thrift/FileCryptoMetaData.d.ts.map +1 -0
  372. package/dist/parquetjs/parquet-thrift/FileCryptoMetaData.js +57 -0
  373. package/dist/parquetjs/parquet-thrift/FileCryptoMetaData.js.map +1 -0
  374. package/dist/parquetjs/parquet-thrift/FileMetaData.d.ts +7 -2
  375. package/dist/parquetjs/parquet-thrift/FileMetaData.d.ts.map +1 -1
  376. package/dist/parquetjs/parquet-thrift/FileMetaData.js +37 -2
  377. package/dist/parquetjs/parquet-thrift/FileMetaData.js.map +1 -1
  378. package/dist/parquetjs/parquet-thrift/Float16Type.d.ts +9 -0
  379. package/dist/parquetjs/parquet-thrift/Float16Type.d.ts.map +1 -0
  380. package/dist/parquetjs/parquet-thrift/Float16Type.js +27 -0
  381. package/dist/parquetjs/parquet-thrift/Float16Type.js.map +1 -0
  382. package/dist/parquetjs/parquet-thrift/GeographyType.d.ts +14 -0
  383. package/dist/parquetjs/parquet-thrift/GeographyType.d.ts.map +1 -0
  384. package/dist/parquetjs/parquet-thrift/GeographyType.js +51 -0
  385. package/dist/parquetjs/parquet-thrift/GeographyType.js.map +1 -0
  386. package/dist/parquetjs/parquet-thrift/GeometryType.d.ts +11 -0
  387. package/dist/parquetjs/parquet-thrift/GeometryType.d.ts.map +1 -0
  388. package/dist/parquetjs/parquet-thrift/GeometryType.js +41 -0
  389. package/dist/parquetjs/parquet-thrift/GeometryType.js.map +1 -0
  390. package/dist/parquetjs/parquet-thrift/GeospatialStatistics.d.ts +18 -0
  391. package/dist/parquetjs/parquet-thrift/GeospatialStatistics.d.ts.map +1 -0
  392. package/dist/parquetjs/parquet-thrift/GeospatialStatistics.js +62 -0
  393. package/dist/parquetjs/parquet-thrift/GeospatialStatistics.js.map +1 -0
  394. package/dist/parquetjs/parquet-thrift/IndexPageHeader.d.ts +1 -1
  395. package/dist/parquetjs/parquet-thrift/IndexPageHeader.d.ts.map +1 -1
  396. package/dist/parquetjs/parquet-thrift/IndexPageHeader.js +1 -1
  397. package/dist/parquetjs/parquet-thrift/IndexPageHeader.js.map +1 -1
  398. package/dist/parquetjs/parquet-thrift/IntType.d.ts +1 -1
  399. package/dist/parquetjs/parquet-thrift/IntType.d.ts.map +1 -1
  400. package/dist/parquetjs/parquet-thrift/IntType.js +1 -1
  401. package/dist/parquetjs/parquet-thrift/IntType.js.map +1 -1
  402. package/dist/parquetjs/parquet-thrift/JsonType.d.ts +1 -1
  403. package/dist/parquetjs/parquet-thrift/JsonType.d.ts.map +1 -1
  404. package/dist/parquetjs/parquet-thrift/JsonType.js +1 -1
  405. package/dist/parquetjs/parquet-thrift/JsonType.js.map +1 -1
  406. package/dist/parquetjs/parquet-thrift/KeyValue.d.ts +1 -1
  407. package/dist/parquetjs/parquet-thrift/KeyValue.d.ts.map +1 -1
  408. package/dist/parquetjs/parquet-thrift/KeyValue.js +1 -1
  409. package/dist/parquetjs/parquet-thrift/KeyValue.js.map +1 -1
  410. package/dist/parquetjs/parquet-thrift/ListType.d.ts +1 -1
  411. package/dist/parquetjs/parquet-thrift/ListType.d.ts.map +1 -1
  412. package/dist/parquetjs/parquet-thrift/ListType.js +1 -1
  413. package/dist/parquetjs/parquet-thrift/ListType.js.map +1 -1
  414. package/dist/parquetjs/parquet-thrift/LogicalType.d.ts +17 -1
  415. package/dist/parquetjs/parquet-thrift/LogicalType.d.ts.map +1 -1
  416. package/dist/parquetjs/parquet-thrift/LogicalType.js +97 -1
  417. package/dist/parquetjs/parquet-thrift/LogicalType.js.map +1 -1
  418. package/dist/parquetjs/parquet-thrift/MapType.d.ts +1 -1
  419. package/dist/parquetjs/parquet-thrift/MapType.d.ts.map +1 -1
  420. package/dist/parquetjs/parquet-thrift/MapType.js +1 -1
  421. package/dist/parquetjs/parquet-thrift/MapType.js.map +1 -1
  422. package/dist/parquetjs/parquet-thrift/MicroSeconds.d.ts +1 -1
  423. package/dist/parquetjs/parquet-thrift/MicroSeconds.d.ts.map +1 -1
  424. package/dist/parquetjs/parquet-thrift/MicroSeconds.js +1 -1
  425. package/dist/parquetjs/parquet-thrift/MicroSeconds.js.map +1 -1
  426. package/dist/parquetjs/parquet-thrift/MilliSeconds.d.ts +1 -1
  427. package/dist/parquetjs/parquet-thrift/MilliSeconds.d.ts.map +1 -1
  428. package/dist/parquetjs/parquet-thrift/MilliSeconds.js +1 -1
  429. package/dist/parquetjs/parquet-thrift/MilliSeconds.js.map +1 -1
  430. package/dist/parquetjs/parquet-thrift/NanoSeconds.d.ts +9 -0
  431. package/dist/parquetjs/parquet-thrift/NanoSeconds.d.ts.map +1 -0
  432. package/dist/parquetjs/parquet-thrift/NanoSeconds.js +27 -0
  433. package/dist/parquetjs/parquet-thrift/NanoSeconds.js.map +1 -0
  434. package/dist/parquetjs/parquet-thrift/NullType.d.ts +1 -1
  435. package/dist/parquetjs/parquet-thrift/NullType.d.ts.map +1 -1
  436. package/dist/parquetjs/parquet-thrift/NullType.js +1 -1
  437. package/dist/parquetjs/parquet-thrift/NullType.js.map +1 -1
  438. package/dist/parquetjs/parquet-thrift/OffsetIndex.d.ts +1 -1
  439. package/dist/parquetjs/parquet-thrift/OffsetIndex.d.ts.map +1 -1
  440. package/dist/parquetjs/parquet-thrift/OffsetIndex.js +1 -1
  441. package/dist/parquetjs/parquet-thrift/OffsetIndex.js.map +1 -1
  442. package/dist/parquetjs/parquet-thrift/PageEncodingStats.d.ts +1 -1
  443. package/dist/parquetjs/parquet-thrift/PageEncodingStats.d.ts.map +1 -1
  444. package/dist/parquetjs/parquet-thrift/PageEncodingStats.js +1 -1
  445. package/dist/parquetjs/parquet-thrift/PageEncodingStats.js.map +1 -1
  446. package/dist/parquetjs/parquet-thrift/PageHeader.d.ts +1 -1
  447. package/dist/parquetjs/parquet-thrift/PageHeader.d.ts.map +1 -1
  448. package/dist/parquetjs/parquet-thrift/PageHeader.js +1 -1
  449. package/dist/parquetjs/parquet-thrift/PageHeader.js.map +1 -1
  450. package/dist/parquetjs/parquet-thrift/PageLocation.d.ts +2 -2
  451. package/dist/parquetjs/parquet-thrift/PageLocation.d.ts.map +1 -1
  452. package/dist/parquetjs/parquet-thrift/PageLocation.js +2 -2
  453. package/dist/parquetjs/parquet-thrift/PageLocation.js.map +1 -1
  454. package/dist/parquetjs/parquet-thrift/RowGroup.d.ts +2 -2
  455. package/dist/parquetjs/parquet-thrift/RowGroup.d.ts.map +1 -1
  456. package/dist/parquetjs/parquet-thrift/RowGroup.js +2 -2
  457. package/dist/parquetjs/parquet-thrift/RowGroup.js.map +1 -1
  458. package/dist/parquetjs/parquet-thrift/SchemaElement.d.ts +1 -1
  459. package/dist/parquetjs/parquet-thrift/SchemaElement.d.ts.map +1 -1
  460. package/dist/parquetjs/parquet-thrift/SchemaElement.js +1 -1
  461. package/dist/parquetjs/parquet-thrift/SchemaElement.js.map +1 -1
  462. package/dist/parquetjs/parquet-thrift/SizeStatistics.d.ts +26 -0
  463. package/dist/parquetjs/parquet-thrift/SizeStatistics.d.ts.map +1 -0
  464. package/dist/parquetjs/parquet-thrift/SizeStatistics.js +85 -0
  465. package/dist/parquetjs/parquet-thrift/SizeStatistics.js.map +1 -0
  466. package/dist/parquetjs/parquet-thrift/SortingColumn.d.ts +1 -1
  467. package/dist/parquetjs/parquet-thrift/SortingColumn.d.ts.map +1 -1
  468. package/dist/parquetjs/parquet-thrift/SortingColumn.js +1 -1
  469. package/dist/parquetjs/parquet-thrift/SortingColumn.js.map +1 -1
  470. package/dist/parquetjs/parquet-thrift/Statistics.d.ts +14 -10
  471. package/dist/parquetjs/parquet-thrift/Statistics.d.ts.map +1 -1
  472. package/dist/parquetjs/parquet-thrift/Statistics.js +38 -2
  473. package/dist/parquetjs/parquet-thrift/Statistics.js.map +1 -1
  474. package/dist/parquetjs/parquet-thrift/StringType.d.ts +1 -1
  475. package/dist/parquetjs/parquet-thrift/StringType.d.ts.map +1 -1
  476. package/dist/parquetjs/parquet-thrift/StringType.js +1 -1
  477. package/dist/parquetjs/parquet-thrift/StringType.js.map +1 -1
  478. package/dist/parquetjs/parquet-thrift/TimeType.d.ts +1 -1
  479. package/dist/parquetjs/parquet-thrift/TimeType.d.ts.map +1 -1
  480. package/dist/parquetjs/parquet-thrift/TimeType.js +1 -1
  481. package/dist/parquetjs/parquet-thrift/TimeType.js.map +1 -1
  482. package/dist/parquetjs/parquet-thrift/TimeUnit.d.ts +5 -1
  483. package/dist/parquetjs/parquet-thrift/TimeUnit.d.ts.map +1 -1
  484. package/dist/parquetjs/parquet-thrift/TimeUnit.js +25 -1
  485. package/dist/parquetjs/parquet-thrift/TimeUnit.js.map +1 -1
  486. package/dist/parquetjs/parquet-thrift/TimestampType.d.ts +1 -1
  487. package/dist/parquetjs/parquet-thrift/TimestampType.d.ts.map +1 -1
  488. package/dist/parquetjs/parquet-thrift/TimestampType.js +1 -1
  489. package/dist/parquetjs/parquet-thrift/TimestampType.js.map +1 -1
  490. package/dist/parquetjs/parquet-thrift/TypeDefinedOrder.d.ts +1 -1
  491. package/dist/parquetjs/parquet-thrift/TypeDefinedOrder.d.ts.map +1 -1
  492. package/dist/parquetjs/parquet-thrift/TypeDefinedOrder.js +1 -1
  493. package/dist/parquetjs/parquet-thrift/TypeDefinedOrder.js.map +1 -1
  494. package/dist/parquetjs/parquet-thrift/UUIDType.d.ts +1 -1
  495. package/dist/parquetjs/parquet-thrift/UUIDType.d.ts.map +1 -1
  496. package/dist/parquetjs/parquet-thrift/UUIDType.js +1 -1
  497. package/dist/parquetjs/parquet-thrift/UUIDType.js.map +1 -1
  498. package/dist/parquetjs/parquet-thrift/VariantType.d.ts +11 -0
  499. package/dist/parquetjs/parquet-thrift/VariantType.d.ts.map +1 -0
  500. package/dist/parquetjs/parquet-thrift/VariantType.js +41 -0
  501. package/dist/parquetjs/parquet-thrift/VariantType.js.map +1 -0
  502. package/dist/parquetjs/parquet-thrift/index.d.ts +12 -2
  503. package/dist/parquetjs/parquet-thrift/index.d.ts.map +1 -1
  504. package/dist/parquetjs/parquet-thrift/index.js +12 -2
  505. package/dist/parquetjs/parquet-thrift/index.js.map +1 -1
  506. package/dist/parquetjs/parser/decoders.d.ts +23 -3
  507. package/dist/parquetjs/parser/decoders.d.ts.map +1 -1
  508. package/dist/parquetjs/parser/decoders.js +687 -119
  509. package/dist/parquetjs/parser/decoders.js.map +1 -1
  510. package/dist/parquetjs/parser/encoded-pages.d.ts +24 -0
  511. package/dist/parquetjs/parser/encoded-pages.d.ts.map +1 -0
  512. package/dist/parquetjs/parser/encoded-pages.js +206 -0
  513. package/dist/parquetjs/parser/encoded-pages.js.map +1 -0
  514. package/dist/parquetjs/parser/parquet-reader.d.ts +111 -10
  515. package/dist/parquetjs/parser/parquet-reader.d.ts.map +1 -1
  516. package/dist/parquetjs/parser/parquet-reader.js +836 -69
  517. package/dist/parquetjs/parser/parquet-reader.js.map +1 -1
  518. package/dist/parquetjs/schema/declare.d.ts +83 -10
  519. package/dist/parquetjs/schema/declare.d.ts.map +1 -1
  520. package/dist/parquetjs/schema/declare.js.map +1 -1
  521. package/dist/parquetjs/schema/schema.d.ts +3 -0
  522. package/dist/parquetjs/schema/schema.d.ts.map +1 -1
  523. package/dist/parquetjs/schema/schema.js +31 -3
  524. package/dist/parquetjs/schema/schema.js.map +1 -1
  525. package/dist/parquetjs/schema/shred.d.ts +2 -0
  526. package/dist/parquetjs/schema/shred.d.ts.map +1 -1
  527. package/dist/parquetjs/schema/shred.js +285 -29
  528. package/dist/parquetjs/schema/shred.js.map +1 -1
  529. package/dist/parquetjs/schema/types.d.ts.map +1 -1
  530. package/dist/parquetjs/schema/types.js +290 -71
  531. package/dist/parquetjs/schema/types.js.map +1 -1
  532. package/dist/parquetjs/schema/variant.d.ts +3 -0
  533. package/dist/parquetjs/schema/variant.d.ts.map +1 -0
  534. package/dist/parquetjs/schema/variant.js +248 -0
  535. package/dist/parquetjs/schema/variant.js.map +1 -0
  536. package/dist/parquetjs/utils/binary-utils.d.ts +41 -0
  537. package/dist/parquetjs/utils/binary-utils.d.ts.map +1 -0
  538. package/dist/parquetjs/utils/binary-utils.js +106 -0
  539. package/dist/parquetjs/utils/binary-utils.js.map +1 -0
  540. package/dist/parquetjs/utils/crc32.d.ts +3 -0
  541. package/dist/parquetjs/utils/crc32.d.ts.map +1 -0
  542. package/dist/parquetjs/utils/crc32.js +15 -0
  543. package/dist/parquetjs/utils/crc32.js.map +1 -0
  544. package/dist/parquetjs/utils/file-utils.d.ts +1 -1
  545. package/dist/parquetjs/utils/file-utils.d.ts.map +1 -1
  546. package/dist/parquetjs/utils/file-utils.js +3 -3
  547. package/dist/parquetjs/utils/file-utils.js.map +1 -1
  548. package/dist/parquetjs/utils/read-utils.d.ts +25 -7
  549. package/dist/parquetjs/utils/read-utils.d.ts.map +1 -1
  550. package/dist/parquetjs/utils/read-utils.js +66 -39
  551. package/dist/parquetjs/utils/read-utils.js.map +1 -1
  552. package/dist/parquetjs/utils/thrift-runtime.d.ts +106 -0
  553. package/dist/parquetjs/utils/thrift-runtime.d.ts.map +1 -0
  554. package/dist/parquetjs/utils/thrift-runtime.js +50 -0
  555. package/dist/parquetjs/utils/thrift-runtime.js.map +1 -0
  556. package/dist/parquetjs/utils/uint8-array-compact-protocol-writer.d.ts +78 -0
  557. package/dist/parquetjs/utils/uint8-array-compact-protocol-writer.d.ts.map +1 -0
  558. package/dist/parquetjs/utils/uint8-array-compact-protocol-writer.js +213 -0
  559. package/dist/parquetjs/utils/uint8-array-compact-protocol-writer.js.map +1 -0
  560. package/dist/parquetjs/utils/uint8-array-compact-protocol.d.ts +80 -0
  561. package/dist/parquetjs/utils/uint8-array-compact-protocol.d.ts.map +1 -0
  562. package/dist/parquetjs/utils/uint8-array-compact-protocol.js +276 -0
  563. package/dist/parquetjs/utils/uint8-array-compact-protocol.js.map +1 -0
  564. package/dist/parquetjs/utils/uint8-array-transport.d.ts +25 -0
  565. package/dist/parquetjs/utils/uint8-array-transport.d.ts.map +1 -0
  566. package/dist/parquetjs/utils/uint8-array-transport.js +62 -0
  567. package/dist/parquetjs/utils/uint8-array-transport.js.map +1 -0
  568. package/dist/polyfills/process.d.ts +12 -0
  569. package/dist/polyfills/process.d.ts.map +1 -0
  570. package/dist/polyfills/process.js +17 -0
  571. package/dist/polyfills/process.js.map +1 -0
  572. package/dist/unbundled.d.ts +10 -0
  573. package/dist/unbundled.d.ts.map +1 -0
  574. package/dist/unbundled.js +11 -0
  575. package/dist/unbundled.js.map +1 -0
  576. package/dist/wasm.d.ts +3 -0
  577. package/dist/wasm.d.ts.map +1 -0
  578. package/dist/wasm.js +6 -0
  579. package/dist/wasm.js.map +1 -0
  580. package/dist/workers/parquet-source-worker.d.ts +2 -0
  581. package/dist/workers/parquet-source-worker.d.ts.map +1 -0
  582. package/dist/workers/parquet-source-worker.js +17 -0
  583. package/dist/workers/parquet-source-worker.js.map +1 -0
  584. package/dist/workers/parquet-worker-node.d.ts +2 -0
  585. package/dist/workers/parquet-worker-node.d.ts.map +1 -0
  586. package/dist/workers/parquet-worker-node.js +5 -0
  587. package/dist/workers/parquet-worker-node.js.map +1 -0
  588. package/dist/workers/parquet-worker.d.ts +2 -0
  589. package/dist/workers/parquet-worker.d.ts.map +1 -0
  590. package/dist/workers/parquet-worker.js +27 -0
  591. package/dist/workers/parquet-worker.js.map +1 -0
  592. package/package.json +84 -40
  593. package/src/bundled.ts +43 -0
  594. package/src/delta-format.ts +12 -0
  595. package/src/delta-source-loader-types.ts +40 -0
  596. package/src/delta-source.ts +305 -0
  597. package/src/delta-types.ts +40 -0
  598. package/src/geoparquet-loader-with-parser.ts +95 -0
  599. package/src/geoparquet-loader.ts +54 -0
  600. package/src/iceberg-rest-catalog.ts +122 -0
  601. package/src/iceberg-table-source.ts +1037 -0
  602. package/src/iceberg-types.ts +165 -0
  603. package/src/index.ts +105 -26
  604. package/src/lib/arrow/convert-columns-to-row-group.ts +3 -0
  605. package/src/lib/arrow/convert-schema-from-parquet.ts +193 -32
  606. package/src/lib/arrow/convert-schema-to-parquet.ts +16 -7
  607. package/src/lib/arrow/create-nested-arrow-vector.ts +705 -0
  608. package/src/lib/constants.ts +0 -2
  609. package/src/lib/encoders/encode-arrow-to-parquet.ts +10 -16
  610. package/src/lib/encoders/encode-table-to-parquet-js.ts +526 -0
  611. package/src/lib/geo/geoparquet-covering.ts +201 -0
  612. package/src/lib/geo/geospatial-metadata.ts +547 -0
  613. package/src/lib/parquet-bloom-filter-planner.ts +86 -0
  614. package/src/lib/parquet-bloom-filter.ts +345 -0
  615. package/src/lib/parquet-encryption.ts +344 -0
  616. package/src/lib/parquet-page-index.ts +727 -0
  617. package/src/lib/parquet-predicate.ts +410 -0
  618. package/src/lib/parquet-source-worker-client.ts +41 -0
  619. package/src/lib/parquet-source-worker-decoder.ts +252 -0
  620. package/src/lib/parquet-source-worker-file.ts +56 -0
  621. package/src/lib/parquet-source-worker-types.ts +132 -0
  622. package/src/lib/parquet-worker-transport.ts +57 -0
  623. package/src/lib/parsers/convert-parquet-tables.ts +39 -0
  624. package/src/lib/parsers/get-parquet-schema.ts +6 -2
  625. package/src/lib/parsers/parse-geoparquet-to-geojson.ts +27 -14
  626. package/src/lib/parsers/parse-parquet-tables.ts +82 -0
  627. package/src/lib/parsers/parse-parquet-to-arrow-js.ts +1213 -0
  628. package/src/lib/parsers/parse-parquet-to-arrow.ts +62 -36
  629. package/src/lib/parsers/parse-parquet-to-columns.ts +9 -8
  630. package/src/lib/parsers/parse-parquet-to-json.ts +119 -35
  631. package/src/lib/slice-parquet-batch.ts +32 -0
  632. package/src/lib/sources/parquet-range-file.ts +365 -0
  633. package/src/lib/sources/single-batch-queue.ts +95 -0
  634. package/src/lib/utils/load-wasm-node.ts +25 -0
  635. package/src/lib/utils/load-wasm.ts +36 -13
  636. package/src/lib/utils/make-stream-iterator.ts +126 -14
  637. package/src/lib/utils/normalize-parquet-options.ts +30 -0
  638. package/src/parquet-dataset-source.ts +800 -0
  639. package/src/parquet-encoded-page-types.ts +122 -0
  640. package/src/parquet-format.ts +2 -0
  641. package/src/parquet-js-loader-types.ts +53 -0
  642. package/src/parquet-js-loader.ts +76 -19
  643. package/src/parquet-js-writer.ts +98 -0
  644. package/src/parquet-loader-base.ts +34 -0
  645. package/src/parquet-loader-options.ts +62 -0
  646. package/src/parquet-loader-types.ts +35 -0
  647. package/src/parquet-loader.ts +115 -0
  648. package/src/parquet-source-capabilities.ts +73 -0
  649. package/src/parquet-source-loader-types.ts +97 -0
  650. package/src/parquet-source-loader.ts +2254 -0
  651. package/src/parquet-source-types.ts +627 -0
  652. package/src/parquet-source-worker-factory.ts +8 -0
  653. package/src/parquet-source-worker-url.ts +23 -0
  654. package/src/parquet-worker-url.ts +23 -0
  655. package/src/parquet-writer.ts +42 -0
  656. package/src/parquetjs/LICENSE +23 -1
  657. package/src/parquetjs/codecs/byte-stream-split.ts +221 -0
  658. package/src/parquetjs/codecs/declare.ts +62 -3
  659. package/src/parquetjs/codecs/delta.ts +1047 -0
  660. package/src/parquetjs/codecs/dictionary.ts +22 -4
  661. package/src/parquetjs/codecs/index.ts +22 -0
  662. package/src/parquetjs/codecs/plain.ts +317 -89
  663. package/src/parquetjs/codecs/rle.ts +852 -82
  664. package/src/parquetjs/compression.ts +137 -156
  665. package/src/parquetjs/encoder/dictionary-planner.ts +104 -0
  666. package/src/parquetjs/encoder/page-planner.ts +83 -0
  667. package/src/parquetjs/encoder/parquet-encoder.ts +1374 -95
  668. package/src/parquetjs/parquet-thrift/BoundingBox.ts +108 -0
  669. package/src/parquetjs/parquet-thrift/BsonType.ts +1 -1
  670. package/src/parquetjs/parquet-thrift/ColumnChunk.ts +37 -2
  671. package/src/parquetjs/parquet-thrift/ColumnCryptoMetaData.ts +139 -0
  672. package/src/parquetjs/parquet-thrift/ColumnIndex.ts +13 -13
  673. package/src/parquetjs/parquet-thrift/ColumnMetaData.ts +75 -2
  674. package/src/parquetjs/parquet-thrift/ColumnOrder.ts +1 -1
  675. package/src/parquetjs/parquet-thrift/DataPageHeader.ts +1 -1
  676. package/src/parquetjs/parquet-thrift/DataPageHeaderV2.ts +1 -1
  677. package/src/parquetjs/parquet-thrift/DateType.ts +1 -1
  678. package/src/parquetjs/parquet-thrift/DecimalType.ts +1 -1
  679. package/src/parquetjs/parquet-thrift/DictionaryPageHeader.ts +1 -1
  680. package/src/parquetjs/parquet-thrift/EdgeInterpolationAlgorithm.ts +13 -0
  681. package/src/parquetjs/parquet-thrift/Encoding.ts +2 -1
  682. package/src/parquetjs/parquet-thrift/EncryptionAlgorithm.ts +127 -0
  683. package/src/parquetjs/parquet-thrift/EnumType.ts +1 -1
  684. package/src/parquetjs/parquet-thrift/FileCryptoMetaData.ts +60 -0
  685. package/src/parquetjs/parquet-thrift/FileMetaData.ts +37 -2
  686. package/src/parquetjs/parquet-thrift/Float16Type.ts +28 -0
  687. package/src/parquetjs/parquet-thrift/GeographyType.ts +54 -0
  688. package/src/parquetjs/parquet-thrift/GeometryType.ts +43 -0
  689. package/src/parquetjs/parquet-thrift/GeospatialStatistics.ts +68 -0
  690. package/src/parquetjs/parquet-thrift/IndexPageHeader.ts +1 -1
  691. package/src/parquetjs/parquet-thrift/IntType.ts +1 -1
  692. package/src/parquetjs/parquet-thrift/JsonType.ts +1 -1
  693. package/src/parquetjs/parquet-thrift/KeyValue.ts +1 -1
  694. package/src/parquetjs/parquet-thrift/ListType.ts +1 -1
  695. package/src/parquetjs/parquet-thrift/LogicalType.ts +97 -1
  696. package/src/parquetjs/parquet-thrift/MapType.ts +1 -1
  697. package/src/parquetjs/parquet-thrift/MicroSeconds.ts +1 -1
  698. package/src/parquetjs/parquet-thrift/MilliSeconds.ts +1 -1
  699. package/src/parquetjs/parquet-thrift/NanoSeconds.ts +28 -0
  700. package/src/parquetjs/parquet-thrift/NullType.ts +1 -1
  701. package/src/parquetjs/parquet-thrift/OffsetIndex.ts +1 -1
  702. package/src/parquetjs/parquet-thrift/PageEncodingStats.ts +1 -1
  703. package/src/parquetjs/parquet-thrift/PageHeader.ts +1 -1
  704. package/src/parquetjs/parquet-thrift/PageLocation.ts +2 -2
  705. package/src/parquetjs/parquet-thrift/RowGroup.ts +2 -2
  706. package/src/parquetjs/parquet-thrift/SchemaElement.ts +1 -1
  707. package/src/parquetjs/parquet-thrift/SizeStatistics.ts +98 -0
  708. package/src/parquetjs/parquet-thrift/SortingColumn.ts +1 -1
  709. package/src/parquetjs/parquet-thrift/Statistics.ts +50 -14
  710. package/src/parquetjs/parquet-thrift/StringType.ts +1 -1
  711. package/src/parquetjs/parquet-thrift/TimeType.ts +1 -1
  712. package/src/parquetjs/parquet-thrift/TimeUnit.ts +25 -1
  713. package/src/parquetjs/parquet-thrift/TimestampType.ts +1 -1
  714. package/src/parquetjs/parquet-thrift/TypeDefinedOrder.ts +1 -1
  715. package/src/parquetjs/parquet-thrift/UUIDType.ts +1 -1
  716. package/src/parquetjs/parquet-thrift/VariantType.ts +43 -0
  717. package/src/parquetjs/parquet-thrift/index.ts +12 -4
  718. package/src/parquetjs/parser/decoders.ts +922 -143
  719. package/src/parquetjs/parser/encoded-pages.ts +278 -0
  720. package/src/parquetjs/parser/parquet-reader.ts +1334 -83
  721. package/src/parquetjs/schema/declare.ts +121 -9
  722. package/src/parquetjs/schema/schema.ts +33 -3
  723. package/src/parquetjs/schema/shred.ts +345 -41
  724. package/src/parquetjs/schema/types.ts +308 -84
  725. package/src/parquetjs/schema/variant.ts +313 -0
  726. package/src/parquetjs/utils/binary-utils.ts +132 -0
  727. package/src/parquetjs/utils/crc32.ts +15 -0
  728. package/src/parquetjs/utils/file-utils.ts +4 -4
  729. package/src/parquetjs/utils/read-utils.ts +77 -51
  730. package/src/parquetjs/utils/thrift-runtime.ts +109 -0
  731. package/src/parquetjs/utils/uint8-array-compact-protocol-writer.ts +254 -0
  732. package/src/parquetjs/utils/uint8-array-compact-protocol.ts +322 -0
  733. package/src/parquetjs/utils/uint8-array-transport.ts +79 -0
  734. package/src/polyfills/process.ts +17 -0
  735. package/src/unbundled.ts +47 -0
  736. package/src/wasm.ts +6 -0
  737. package/src/workers/parquet-source-worker.ts +21 -0
  738. package/src/workers/parquet-worker-node.ts +5 -0
  739. package/src/workers/parquet-worker.ts +31 -0
  740. package/dist/parquet-arrow-loader.d.ts +0 -43
  741. package/dist/parquet-arrow-loader.d.ts.map +0 -1
  742. package/dist/parquet-arrow-loader.js +0 -49
  743. package/dist/parquet-arrow-loader.js.map +0 -1
  744. package/dist/parquet-arrow-writer.d.ts +0 -9
  745. package/dist/parquet-arrow-writer.d.ts.map +0 -1
  746. package/dist/parquet-arrow-writer.js +0 -23
  747. package/dist/parquet-arrow-writer.js.map +0 -1
  748. package/dist/parquet-json-loader.d.ts +0 -148
  749. package/dist/parquet-json-loader.d.ts.map +0 -1
  750. package/dist/parquet-json-loader.js +0 -106
  751. package/dist/parquet-json-loader.js.map +0 -1
  752. package/dist/parquet-json-writer.d.ts +0 -19
  753. package/dist/parquet-json-writer.d.ts.map +0 -1
  754. package/dist/parquet-json-writer.js +0 -20
  755. package/dist/parquet-json-writer.js.map +0 -1
  756. package/dist/polyfills/buffer/buffer-polyfill.browser.d.ts +0 -3
  757. package/dist/polyfills/buffer/buffer-polyfill.browser.d.ts.map +0 -1
  758. package/dist/polyfills/buffer/buffer-polyfill.browser.js +0 -12
  759. package/dist/polyfills/buffer/buffer-polyfill.browser.js.map +0 -1
  760. package/dist/polyfills/buffer/buffer-polyfill.node.d.ts +0 -3
  761. package/dist/polyfills/buffer/buffer-polyfill.node.d.ts.map +0 -1
  762. package/dist/polyfills/buffer/buffer-polyfill.node.js +0 -17
  763. package/dist/polyfills/buffer/buffer-polyfill.node.js.map +0 -1
  764. package/dist/polyfills/buffer/buffer.d.ts +0 -222
  765. package/dist/polyfills/buffer/buffer.d.ts.map +0 -1
  766. package/dist/polyfills/buffer/buffer.js +0 -1868
  767. package/dist/polyfills/buffer/buffer.js.map +0 -1
  768. package/dist/polyfills/buffer/index.d.ts +0 -4
  769. package/dist/polyfills/buffer/index.d.ts.map +0 -1
  770. package/dist/polyfills/buffer/index.js +0 -9
  771. package/dist/polyfills/buffer/index.js.map +0 -1
  772. package/dist/polyfills/buffer/install-buffer-polyfill.d.ts +0 -2
  773. package/dist/polyfills/buffer/install-buffer-polyfill.d.ts.map +0 -1
  774. package/dist/polyfills/buffer/install-buffer-polyfill.js +0 -10
  775. package/dist/polyfills/buffer/install-buffer-polyfill.js.map +0 -1
  776. package/dist/workers/parquet-json-worker.d.ts +0 -2
  777. package/dist/workers/parquet-json-worker.d.ts.map +0 -1
  778. package/dist/workers/parquet-json-worker.js +0 -7
  779. package/dist/workers/parquet-json-worker.js.map +0 -1
  780. package/src/parquet-arrow-loader.ts +0 -74
  781. package/src/parquet-arrow-writer.ts +0 -33
  782. package/src/parquet-json-loader.ts +0 -160
  783. package/src/parquet-json-writer.ts +0 -27
  784. package/src/parquetjs/modules.d.ts +0 -25
  785. package/src/polyfills/buffer/buffer-polyfill.browser.ts +0 -14
  786. package/src/polyfills/buffer/buffer-polyfill.node.ts +0 -19
  787. package/src/polyfills/buffer/buffer.ts +0 -2205
  788. package/src/polyfills/buffer/index.ts +0 -11
  789. package/src/polyfills/buffer/install-buffer-polyfill.ts +0 -12
  790. package/src/workers/parquet-json-worker.ts +0 -8
@@ -7,31 +7,160 @@
7
7
  import type {ReadableFile} from '@loaders.gl/loader-utils';
8
8
 
9
9
  import {ParquetSchema} from '../schema/schema';
10
- import {decodeSchema, decodeDataPages, decodePage} from './decoders';
10
+ import type {ParquetEncodedColumnChunk} from '../../parquet-encoded-page-types';
11
+ import {
12
+ decodeSchema,
13
+ decodeDataPages,
14
+ decodePage,
15
+ decodeUncompressedDataPages,
16
+ decodeUncompressedDictionaryBuffer
17
+ } from './decoders';
18
+ import {scanParquetEncodedPages} from './encoded-pages';
11
19
  import {materializeRows} from '../schema/shred';
20
+ import {createParquetPageDecompressor, type ParquetPageDecompressor} from '../compression';
12
21
 
13
22
  import {PARQUET_MAGIC, PARQUET_MAGIC_ENCRYPTED} from '../../lib/constants';
14
- import {ColumnChunk, CompressionCodec, FileMetaData, RowGroup, Type} from '../parquet-thrift/index';
23
+ import {
24
+ ColumnChunk,
25
+ CompressionCodec,
26
+ Encoding,
27
+ FileMetaData,
28
+ PageType,
29
+ RowGroup,
30
+ Type
31
+ } from '../parquet-thrift/index';
15
32
  import {
16
33
  ParquetRowGroup,
17
34
  ParquetCompression,
18
35
  ParquetColumnChunk,
19
36
  PrimitiveType,
20
- ParquetReaderContext
37
+ ParquetReaderContext,
38
+ type ParquetLevelBuffer
21
39
  } from '../schema/declare';
22
- import {decodeFileMetadata, getThriftEnum, fieldIndexOf} from '../utils/read-utils';
40
+ import {
41
+ decodeColumnMetadata,
42
+ decodeFileCryptoMetadata,
43
+ decodeFileMetadata,
44
+ decodePageHeader,
45
+ getThriftEnum,
46
+ fieldIndexOf,
47
+ serializeThrift
48
+ } from '../utils/read-utils';
49
+ import {decodeString, readUInt32LE, toUint8Array} from '../utils/binary-utils';
50
+ import {CompactInt64} from '../utils/uint8-array-compact-protocol';
51
+ import type {
52
+ ParquetDataPageLocation,
53
+ ParquetPageLocations,
54
+ ParquetRowRange
55
+ } from '../../lib/parquet-page-index';
56
+ import {
57
+ createParquetModuleAad,
58
+ decryptParquetModule,
59
+ readParquetEncryptedModule,
60
+ verifyParquetFooterSignature,
61
+ type ParquetEncryptionAlgorithm,
62
+ type ParquetKeyRetriever
63
+ } from '../../lib/parquet-encryption';
64
+ import type {ParquetEncryptionModule} from '../../lib/parquet-encryption';
65
+
66
+ /** Bounds concurrent range requests when a row group contains unusually many selected columns. */
67
+ const MAXIMUM_CONCURRENT_COLUMN_READS = 16;
68
+
69
+ /**
70
+ * Returns whether column metadata guarantees that every data page can decode directly into an
71
+ * Arrow-compatible byte buffer. Level encodings are included in the metadata encoding set and do
72
+ * not prevent the byte-array fast path.
73
+ */
74
+ function hasOnlyArrowByteArrayDataPageEncodings(
75
+ encodings: Encoding[],
76
+ dictionaryPageOffset: number | undefined
77
+ ): boolean {
78
+ if (dictionaryPageOffset !== undefined || encodings.length === 0) {
79
+ return false;
80
+ }
81
+ for (const encoding of encodings) {
82
+ if (
83
+ encoding !== Encoding.PLAIN &&
84
+ encoding !== Encoding.DELTA_LENGTH_BYTE_ARRAY &&
85
+ encoding !== Encoding.DELTA_BYTE_ARRAY &&
86
+ encoding !== Encoding.RLE &&
87
+ encoding !== Encoding.BIT_PACKED
88
+ ) {
89
+ return false;
90
+ }
91
+ }
92
+ return true;
93
+ }
94
+
95
+ /** Returns the algorithm name represented by the Thrift encryption union. */
96
+ function getEncryptionAlgorithm(value: {
97
+ AES_GCM_V1?: unknown;
98
+ AES_GCM_CTR_V1?: unknown;
99
+ }): ParquetEncryptionAlgorithm {
100
+ if (value.AES_GCM_V1) return 'AES_GCM_V1';
101
+ if (value.AES_GCM_CTR_V1) return 'AES_GCM_CTR_V1';
102
+ throw new Error('Unsupported Parquet encryption algorithm');
103
+ }
23
104
 
24
105
  export type ParquetReaderProps = {
106
+ /** Maximum dictionary-page read size. */
25
107
  defaultDictionarySize?: number;
108
+ /** Preserve BYTE_ARRAY values instead of decoding them as strings. */
26
109
  preserveBinary?: boolean;
110
+ /** Retain byte arrays as views into decoded page buffers for direct materialization. */
111
+ retainByteArrayViews?: boolean;
112
+ /** Decode supported primitive columns into typed buffers instead of boxed JavaScript arrays. */
113
+ useTypedValueBuffers?: boolean;
114
+ /** Decode repetition and definition levels into compact unsigned typed arrays. */
115
+ useTypedLevelBuffers?: boolean;
116
+ /** Decode eligible PLAIN BYTE_ARRAY columns into Arrow-compatible contiguous buffers. */
117
+ useArrowByteArrayBuffers?: boolean;
118
+ /** Verify page-header CRC values when present. Disabled by default for throughput. */
119
+ verifyPageChecksums?: boolean;
120
+ /** Decode legacy INT96 values as epoch nanoseconds for Arrow timestamp columns. */
121
+ int96AsTimestamp?: boolean;
122
+ /** Verify plaintext-footer signatures when present. Enabled by default. */
123
+ verifyFooterSignature?: boolean;
124
+ /** Abort signal forwarded to every underlying random-access read. */
125
+ signal?: AbortSignal;
126
+ /** Resolves modular-encryption keys from the file's key metadata. */
127
+ keyRetriever?: ParquetKeyRetriever;
128
+ /** AAD prefix for files that intentionally omit it from FileCryptoMetaData. */
129
+ aadPrefix?: Uint8Array;
130
+ /** Pre-resolved encryption context used by worker-local readers. */
131
+ encryptionContext?: ParquetReaderEncryptionContext;
27
132
  };
28
133
 
29
134
  /** Properties for initializing a ParquetRowGroupReader */
30
135
  export type ParquetIterationProps = {
31
136
  /** Filter allowing some columns to be dropped */
32
137
  columnList?: string[] | string[][];
138
+ /** Zero-based row-group indexes to read, in output order. */
139
+ rowGroups?: number[];
140
+ /** Abort signal forwarded to row-group and column-chunk reads. */
141
+ signal?: AbortSignal;
33
142
  };
34
143
 
144
+ type NormalizedParquetReaderProps = Required<
145
+ Omit<ParquetReaderProps, 'signal' | 'keyRetriever' | 'aadPrefix' | 'encryptionContext'>
146
+ > &
147
+ Pick<ParquetReaderProps, 'signal' | 'keyRetriever' | 'aadPrefix' | 'encryptionContext'>;
148
+
149
+ /** File-level parameters needed to authenticate modular-encrypted modules. */
150
+ type ParquetEncryptionContext = {
151
+ algorithm: ParquetEncryptionAlgorithm;
152
+ aadPrefix?: Uint8Array;
153
+ fileUnique: Uint8Array;
154
+ footerKeyMetadata?: Uint8Array;
155
+ footerSigningKeyMetadata?: Uint8Array;
156
+ };
157
+
158
+ /** File-level encryption parameters needed to decrypt worker-transferred page modules. */
159
+ export type ParquetReaderEncryptionContext = Pick<
160
+ ParquetEncryptionContext,
161
+ 'algorithm' | 'aadPrefix' | 'fileUnique' | 'footerKeyMetadata'
162
+ >;
163
+
35
164
  /**
36
165
  * The parquet envelope reader allows direct, unbuffered access to the individual
37
166
  * sections of the parquet file, namely the header, footer and the row groups.
@@ -39,26 +168,154 @@ export type ParquetIterationProps = {
39
168
  * rows from a parquet file use the ParquetReader instead
40
169
  */
41
170
  export class ParquetReader {
42
- static defaultProps: Required<ParquetReaderProps> = {
171
+ static defaultProps: NormalizedParquetReaderProps = {
43
172
  // max ArrayBuffer size in js is 2Gb
44
173
  defaultDictionarySize: 2147483648,
45
- preserveBinary: false
174
+ preserveBinary: false,
175
+ retainByteArrayViews: false,
176
+ useTypedValueBuffers: false,
177
+ useTypedLevelBuffers: false,
178
+ useArrowByteArrayBuffers: false,
179
+ verifyPageChecksums: false,
180
+ int96AsTimestamp: false,
181
+ verifyFooterSignature: true,
182
+ signal: undefined,
183
+ keyRetriever: undefined,
184
+ aadPrefix: undefined,
185
+ encryptionContext: undefined
46
186
  };
47
187
 
48
- props: Required<ParquetReaderProps>;
188
+ props: NormalizedParquetReaderProps;
49
189
  file: ReadableFile;
50
190
  metadata: Promise<FileMetaData> | null = null;
191
+ /** Parsed Parquet schema shared by metadata, iteration, and materialization paths. */
192
+ private schema: Promise<ParquetSchema> | null = null;
193
+ /** Encryption parameters decoded from plaintext or encrypted footer metadata. */
194
+ private encryptionContext?: ParquetEncryptionContext;
195
+ /** Resolved keys shared by encrypted modules in this reader instance. */
196
+ private readonly encryptionKeyCache = new Map<string, Promise<ArrayBuffer | ArrayBufferView>>();
197
+ /** Compression backends selected once and shared by every page in this reader. */
198
+ private readonly pageDecompressors = new Map<ParquetCompression, ParquetPageDecompressor>();
51
199
 
52
200
  constructor(file: ReadableFile, props?: ParquetReaderProps) {
53
201
  this.file = file;
54
202
  this.props = {...ParquetReader.defaultProps, ...props};
203
+ this.encryptionContext = this.props.encryptionContext;
204
+ }
205
+
206
+ /** Whether the decoded footer describes an encrypted Parquet file. */
207
+ get encrypted(): boolean {
208
+ return Boolean(this.encryptionContext);
209
+ }
210
+
211
+ /** Whether this reader decodes legacy INT96 values as epoch nanoseconds. */
212
+ get int96AsTimestamp(): boolean {
213
+ return this.props.int96AsTimestamp;
214
+ }
215
+
216
+ /** Returns a cloneable snapshot of the file encryption context for worker decoding. */
217
+ getEncryptionContextForWorker(): ParquetReaderEncryptionContext | undefined {
218
+ const encryptionContext = this.encryptionContext;
219
+ if (!encryptionContext) return undefined;
220
+ return {
221
+ algorithm: encryptionContext.algorithm,
222
+ aadPrefix: encryptionContext.aadPrefix && new Uint8Array(encryptionContext.aadPrefix),
223
+ fileUnique: new Uint8Array(encryptionContext.fileUnique),
224
+ footerKeyMetadata:
225
+ encryptionContext.footerKeyMetadata && new Uint8Array(encryptionContext.footerKeyMetadata)
226
+ };
227
+ }
228
+
229
+ /** Resolves one column key on the caller thread before transferring encrypted ranges. */
230
+ async getColumnKeyForWorker(
231
+ columnChunk: ColumnChunk,
232
+ rowGroupOrdinal: number,
233
+ columnOrdinal: number
234
+ ): Promise<{keyMetadata?: Uint8Array; keyMaterial: Uint8Array}> {
235
+ if (!this.encryptionContext || !this.props.keyRetriever) {
236
+ throw new Error('Encrypted Parquet worker reads require parquet.keyRetriever');
237
+ }
238
+ const keyMetadata = this.getColumnKeyMetadata(columnChunk);
239
+ const keyMaterial = await this.props.keyRetriever(keyMetadata, {
240
+ algorithm: this.encryptionContext.algorithm,
241
+ aad: createParquetModuleAad(
242
+ this.encryptionContext.aadPrefix,
243
+ this.encryptionContext.fileUnique,
244
+ 'data-page',
245
+ rowGroupOrdinal,
246
+ columnOrdinal,
247
+ 0
248
+ )
249
+ });
250
+ const keyBytes =
251
+ keyMaterial instanceof ArrayBuffer
252
+ ? new Uint8Array(keyMaterial)
253
+ : new Uint8Array(keyMaterial.buffer, keyMaterial.byteOffset, keyMaterial.byteLength);
254
+ return {keyMetadata, keyMaterial: keyBytes};
55
255
  }
56
256
 
57
257
  close(): void {
258
+ this.encryptionKeyCache.clear();
259
+ this.pageDecompressors.clear();
58
260
  // eslint-disable-next-line @typescript-eslint/no-floating-promises
59
261
  this.file.close();
60
262
  }
61
263
 
264
+ /** Returns the reader-scoped decoder for one Parquet compression method. */
265
+ private getPageDecompressor(compression: ParquetCompression): ParquetPageDecompressor {
266
+ let decompressor = this.pageDecompressors.get(compression);
267
+ if (!decompressor) {
268
+ decompressor = createParquetPageDecompressor(compression);
269
+ this.pageDecompressors.set(compression, decompressor);
270
+ }
271
+ return decompressor;
272
+ }
273
+
274
+ /** Reads a byte range, retaining a zero-copy view when the complete file is already in memory. */
275
+ private async readBytes(
276
+ start: number,
277
+ length: number,
278
+ signal?: AbortSignal
279
+ ): Promise<Uint8Array> {
280
+ const effectiveSignal = signal ?? this.props.signal;
281
+ if (effectiveSignal?.aborted) {
282
+ throw new Error('Request aborted');
283
+ }
284
+ if (this.file.handle instanceof ArrayBuffer) {
285
+ return new Uint8Array(this.file.handle, start, length);
286
+ }
287
+ return toUint8Array(await this.file.read(start, length, effectiveSignal));
288
+ }
289
+
290
+ /** Returns a reader-scoped key resolver that avoids repeating key-provider work per module. */
291
+ private getCachedKeyRetriever(): ParquetKeyRetriever {
292
+ const keyRetriever = this.props.keyRetriever;
293
+ if (!keyRetriever) {
294
+ throw new Error('Encrypted Parquet requires parquet.keyRetriever');
295
+ }
296
+ return (keyMetadata, context) => {
297
+ const encryptionContext = this.encryptionContext;
298
+ const cacheKey = createEncryptionKeyCacheKey(
299
+ context.algorithm,
300
+ keyMetadata,
301
+ context.aad,
302
+ encryptionContext?.aadPrefix?.byteLength ?? this.props.aadPrefix?.byteLength,
303
+ encryptionContext?.fileUnique.byteLength
304
+ );
305
+ let keyPromise = this.encryptionKeyCache.get(cacheKey);
306
+ if (!keyPromise) {
307
+ keyPromise = Promise.resolve().then(() => keyRetriever(keyMetadata, context));
308
+ this.encryptionKeyCache.set(cacheKey, keyPromise);
309
+ void keyPromise.catch(() => {
310
+ if (this.encryptionKeyCache.get(cacheKey) === keyPromise) {
311
+ this.encryptionKeyCache.delete(cacheKey);
312
+ }
313
+ });
314
+ }
315
+ return keyPromise;
316
+ };
317
+ }
318
+
62
319
  // HIGH LEVEL METHODS
63
320
 
64
321
  /** Yield one row at a time */
@@ -82,36 +339,42 @@ export class ParquetReader {
82
339
  /** Iterate over the raw row groups */
83
340
  async *rowGroupIterator(props?: ParquetIterationProps) {
84
341
  // Ensure strings are nested in arrays
85
- const columnList: string[][] = (props?.columnList || []).map((x) =>
86
- Array.isArray(x) ? x : [x]
87
- );
342
+ const columnList: string[][] = (props?.columnList || []).map(x => (Array.isArray(x) ? x : [x]));
88
343
 
89
- const metadata = await this.getFileMetadata();
90
- const schema = await this.getSchema();
344
+ const metadata = await this.getFileMetadata(props?.signal);
345
+ const schema = await this.getSchema(props?.signal);
91
346
 
92
347
  const rowGroupCount = metadata?.row_groups.length || 0;
348
+ const rowGroupIndices =
349
+ props?.rowGroups || Array.from({length: rowGroupCount}, (_, index) => index);
93
350
 
94
- for (let rowGroupIndex = 0; rowGroupIndex < rowGroupCount; rowGroupIndex++) {
351
+ for (const rowGroupIndex of rowGroupIndices) {
352
+ if (!Number.isInteger(rowGroupIndex) || rowGroupIndex < 0 || rowGroupIndex >= rowGroupCount) {
353
+ throw new Error(`Invalid Parquet row-group index ${rowGroupIndex}`);
354
+ }
95
355
  const rowGroup = await this.readRowGroup(
96
356
  schema,
97
357
  metadata.row_groups[rowGroupIndex],
98
- columnList
358
+ columnList,
359
+ props?.signal,
360
+ rowGroupIndex
99
361
  );
100
362
  yield rowGroup;
101
363
  }
102
364
  }
103
365
 
104
- async getRowCount(): Promise<number> {
105
- const metadata = await this.getFileMetadata();
366
+ async getRowCount(signal?: AbortSignal): Promise<number> {
367
+ const metadata = await this.getFileMetadata(signal);
106
368
  return Number(metadata.num_rows);
107
369
  }
108
370
 
109
- async getSchema(): Promise<ParquetSchema> {
110
- const metadata = await this.getFileMetadata();
111
- const root = metadata.schema[0];
112
- const {schema: schemaDefinition} = decodeSchema(metadata.schema, 1, root.num_children!);
113
- const schema = new ParquetSchema(schemaDefinition);
114
- return schema;
371
+ async getSchema(signal?: AbortSignal): Promise<ParquetSchema> {
372
+ this.schema ||= this.getFileMetadata(signal).then(metadata => {
373
+ const root = metadata.schema[0];
374
+ const {schema: schemaDefinition} = decodeSchema(metadata.schema, 1, root.num_children!);
375
+ return new ParquetSchema(schemaDefinition);
376
+ });
377
+ return await this.schema;
115
378
  }
116
379
 
117
380
  /**
@@ -127,10 +390,10 @@ export class ParquetReader {
127
390
  return md;
128
391
  }
129
392
 
130
- async getFileMetadata(): Promise<FileMetaData> {
393
+ async getFileMetadata(signal?: AbortSignal): Promise<FileMetaData> {
131
394
  if (!this.metadata) {
132
- await this.readHeader();
133
- this.metadata = this.readFooter();
395
+ await this.readHeader(signal);
396
+ this.metadata = this.readFooter(signal);
134
397
  }
135
398
  return this.metadata;
136
399
  }
@@ -138,43 +401,273 @@ export class ParquetReader {
138
401
  // LOW LEVEL METHODS
139
402
 
140
403
  /** Metadata is stored in the footer */
141
- async readHeader(): Promise<void> {
142
- const arrayBuffer = await this.file.read(0, PARQUET_MAGIC.length);
143
- const buffer = Buffer.from(arrayBuffer);
144
- const magic = buffer.toString();
404
+ async readHeader(signal?: AbortSignal): Promise<void> {
405
+ const magic = decodeString(await this.readBytes(0, PARQUET_MAGIC.length, signal));
145
406
  switch (magic) {
146
407
  case PARQUET_MAGIC:
147
408
  break;
148
409
  case PARQUET_MAGIC_ENCRYPTED:
149
- throw new Error('Encrypted parquet file not supported');
410
+ break;
150
411
  default:
151
412
  throw new Error(`Invalid parquet file (magic=${magic})`);
152
413
  }
153
414
  }
154
415
 
155
416
  /** Metadata is stored in the footer */
156
- async readFooter(): Promise<FileMetaData> {
417
+ async readFooter(signal?: AbortSignal): Promise<FileMetaData> {
157
418
  const trailerLen = PARQUET_MAGIC.length + 4;
158
- const arrayBuffer = await this.file.read(this.file.size - trailerLen, trailerLen);
159
- const trailerBuf = Buffer.from(arrayBuffer);
419
+ const trailer = await this.readBytes(this.file.size - trailerLen, trailerLen, signal);
160
420
 
161
- const magic = trailerBuf.slice(4).toString();
421
+ const magic = decodeString(trailer, 4);
422
+ const metadataSize = readUInt32LE(trailer, 0);
423
+ const metadataOffset = this.file.size - metadataSize - trailerLen;
424
+ if (magic === PARQUET_MAGIC_ENCRYPTED) {
425
+ return await this.readEncryptedFooter(metadataSize, metadataOffset, signal);
426
+ }
162
427
  if (magic !== PARQUET_MAGIC) {
163
428
  throw new Error(`Not a valid parquet file (magic="${magic})`);
164
429
  }
165
430
 
166
- const metadataSize = trailerBuf.readUInt32LE(0);
167
- const metadataOffset = this.file.size - metadataSize - trailerLen;
168
431
  if (metadataOffset < PARQUET_MAGIC.length) {
169
432
  throw new Error(`Invalid metadata size ${metadataOffset}`);
170
433
  }
171
434
 
172
- const arrayBuffer2 = await this.file.read(metadataOffset, metadataSize);
173
- const metadataBuf = Buffer.from(arrayBuffer2);
435
+ const metadataBuf = await this.readBytes(metadataOffset, metadataSize, signal);
174
436
  // let metadata = new parquet_thrift.FileMetaData();
175
437
  // parquet_util.decodeThrift(metadata, metadataBuf);
176
438
 
177
- const {metadata} = decodeFileMetadata(metadataBuf);
439
+ const decoded = decodeFileMetadata(metadataBuf);
440
+ const {metadata} = decoded;
441
+ this.setEncryptionContext(metadata);
442
+ if (this.props.verifyFooterSignature && this.encryptionContext?.footerSigningKeyMetadata) {
443
+ // The compact-protocol decoder may consume bytes from the signature that
444
+ // happen to look like valid Thrift fields. The format defines the
445
+ // signature as the final fixed 28 bytes of the footer range, so use the
446
+ // physical boundary rather than the decoder's consumed length.
447
+ const signatureOffset = metadataBuf.byteLength - 28;
448
+ if (signatureOffset < decoded.length) {
449
+ throw new Error('Invalid Parquet plaintext-footer signature boundary');
450
+ }
451
+ const signature = metadataBuf.subarray(signatureOffset);
452
+ const encryptionContext = this.encryptionContext;
453
+ if (!this.props.keyRetriever) {
454
+ throw new Error('Signed Parquet footer requires parquet.keyRetriever');
455
+ }
456
+ await verifyParquetFooterSignature(metadataBuf.subarray(0, signatureOffset), signature, {
457
+ algorithm: encryptionContext.algorithm,
458
+ aad: createParquetModuleAad(
459
+ encryptionContext.aadPrefix,
460
+ encryptionContext.fileUnique,
461
+ 'footer'
462
+ ),
463
+ keyMetadata: encryptionContext.footerSigningKeyMetadata,
464
+ keyRetriever: this.getCachedKeyRetriever()
465
+ });
466
+ }
467
+ return metadata;
468
+ }
469
+
470
+ /** Reads and decrypts an encrypted footer after the standard trailer. */
471
+ private async readEncryptedFooter(
472
+ metadataSize: number,
473
+ metadataOffset: number,
474
+ signal?: AbortSignal
475
+ ): Promise<FileMetaData> {
476
+ if (!this.props.keyRetriever) {
477
+ throw new Error('Encrypted Parquet footer requires parquet.keyRetriever');
478
+ }
479
+ const encryptedFooter = await this.readBytes(metadataOffset, metadataSize, signal);
480
+ const cryptoMetadata = decodeFileCryptoMetadata(encryptedFooter);
481
+ const algorithm = getEncryptionAlgorithm(cryptoMetadata.metadata.encryption_algorithm);
482
+ const algorithmMetadata =
483
+ cryptoMetadata.metadata.encryption_algorithm.AES_GCM_V1 ||
484
+ cryptoMetadata.metadata.encryption_algorithm.AES_GCM_CTR_V1;
485
+ const aadPrefix = algorithmMetadata?.aad_prefix || this.props.aadPrefix;
486
+ if (!algorithmMetadata?.aad_file_unique) {
487
+ throw new Error('Encrypted Parquet footer is missing aad_file_unique');
488
+ }
489
+ const aad = createParquetModuleAad(aadPrefix, algorithmMetadata.aad_file_unique, 'footer');
490
+ const plaintext = await decryptParquetModule(encryptedFooter.subarray(cryptoMetadata.length), {
491
+ algorithm,
492
+ aad,
493
+ keyMetadata: cryptoMetadata.metadata.key_metadata,
494
+ keyRetriever: this.getCachedKeyRetriever()
495
+ });
496
+ this.encryptionContext = {
497
+ algorithm,
498
+ aadPrefix,
499
+ fileUnique: algorithmMetadata.aad_file_unique,
500
+ footerKeyMetadata: cryptoMetadata.metadata.key_metadata
501
+ };
502
+ const metadata = decodeFileMetadata(plaintext).metadata;
503
+ return metadata;
504
+ }
505
+
506
+ /** Records the encryption parameters exposed by plaintext-footer files. */
507
+ private setEncryptionContext(metadata: FileMetaData): void {
508
+ const encryptionAlgorithm = metadata.encryption_algorithm;
509
+ if (!encryptionAlgorithm) return;
510
+ const algorithm = getEncryptionAlgorithm(encryptionAlgorithm);
511
+ const algorithmMetadata = encryptionAlgorithm.AES_GCM_V1 || encryptionAlgorithm.AES_GCM_CTR_V1;
512
+ if (!algorithmMetadata?.aad_file_unique) {
513
+ throw new Error('Encrypted Parquet footer is missing aad_file_unique');
514
+ }
515
+ this.encryptionContext = {
516
+ algorithm,
517
+ aadPrefix: algorithmMetadata.aad_prefix || this.props.aadPrefix,
518
+ fileUnique: algorithmMetadata.aad_file_unique,
519
+ footerSigningKeyMetadata: metadata.footer_signing_key_metadata
520
+ };
521
+ }
522
+
523
+ /** Resolves only the selected encrypted columns, using crypto metadata paths before decryption. */
524
+ async resolveColumnMetadata(
525
+ rowGroup: RowGroup,
526
+ rowGroupOrdinal: number,
527
+ selectedColumnPaths?: readonly string[][]
528
+ ): Promise<void> {
529
+ for (let columnOrdinal = 0; columnOrdinal < rowGroup.columns.length; columnOrdinal++) {
530
+ const columnChunk = rowGroup.columns[columnOrdinal];
531
+ const path = getColumnChunkPath(columnChunk);
532
+ if (
533
+ selectedColumnPaths &&
534
+ path &&
535
+ !selectedColumnPaths.some(
536
+ selectedPath => fieldIndexOf([Array.from(selectedPath)], path) >= 0
537
+ )
538
+ ) {
539
+ continue;
540
+ }
541
+ if (columnChunk.encrypted_column_metadata && !columnChunk.meta_data) {
542
+ columnChunk.meta_data = await this.getColumnMetadata(
543
+ columnChunk,
544
+ rowGroupOrdinal,
545
+ getPhysicalColumnOrdinal(columnChunk, columnOrdinal)
546
+ );
547
+ }
548
+ }
549
+ }
550
+
551
+ /** Decrypts one encrypted column index or offset index module for selective planning. */
552
+ async decryptIndexModule(
553
+ bytes: Uint8Array,
554
+ module: 'column-index' | 'offset-index',
555
+ rowGroupOrdinal: number,
556
+ columnOrdinal: number,
557
+ columnChunk: ColumnChunk
558
+ ): Promise<Uint8Array> {
559
+ if (!columnChunk.crypto_metadata) return bytes;
560
+ const encryptionContext = this.encryptionContext;
561
+ if (!encryptionContext || !this.props.keyRetriever) {
562
+ throw new Error('Encrypted Parquet indexes require parquet.keyRetriever');
563
+ }
564
+ const aad = createParquetModuleAad(
565
+ encryptionContext.aadPrefix,
566
+ encryptionContext.fileUnique,
567
+ module,
568
+ rowGroupOrdinal,
569
+ columnOrdinal
570
+ );
571
+ return await decryptParquetModule(bytes, {
572
+ algorithm: encryptionContext.algorithm,
573
+ aad,
574
+ keyMetadata: this.getColumnKeyMetadata(columnChunk),
575
+ keyRetriever: this.getCachedKeyRetriever()
576
+ });
577
+ }
578
+
579
+ /** Decrypts the encrypted Bloom-filter header and bitset modules for one column chunk. */
580
+ async decryptBloomFilter(
581
+ bytes: Uint8Array,
582
+ rowGroupOrdinal: number,
583
+ columnOrdinal: number,
584
+ columnChunk: ColumnChunk
585
+ ): Promise<Uint8Array> {
586
+ if (!columnChunk.crypto_metadata) return bytes;
587
+ const encryptionContext = this.encryptionContext;
588
+ if (!encryptionContext || !this.props.keyRetriever) {
589
+ throw new Error('Encrypted Parquet Bloom filters require parquet.keyRetriever');
590
+ }
591
+ const header = readParquetEncryptedModule(bytes);
592
+ const bitset = readParquetEncryptedModule(bytes, header.nextOffset);
593
+ const keyMetadata = this.getColumnKeyMetadata(columnChunk);
594
+ const headerBytes = await decryptParquetModule(header.bytes, {
595
+ algorithm: encryptionContext.algorithm,
596
+ aad: createParquetModuleAad(
597
+ encryptionContext.aadPrefix,
598
+ encryptionContext.fileUnique,
599
+ 'bloom-filter-header',
600
+ rowGroupOrdinal,
601
+ columnOrdinal
602
+ ),
603
+ keyMetadata,
604
+ keyRetriever: this.getCachedKeyRetriever()
605
+ });
606
+ const bitsetBytes = await decryptParquetModule(bitset.bytes, {
607
+ algorithm: encryptionContext.algorithm,
608
+ aad: createParquetModuleAad(
609
+ encryptionContext.aadPrefix,
610
+ encryptionContext.fileUnique,
611
+ 'bloom-filter-bitset',
612
+ rowGroupOrdinal,
613
+ columnOrdinal
614
+ ),
615
+ keyMetadata,
616
+ keyRetriever: this.getCachedKeyRetriever()
617
+ });
618
+ const plaintext = new Uint8Array(headerBytes.byteLength + bitsetBytes.byteLength);
619
+ plaintext.set(headerBytes);
620
+ plaintext.set(bitsetBytes, headerBytes.byteLength);
621
+ return plaintext;
622
+ }
623
+
624
+ /** Resolves a column's plaintext metadata, decrypting column-specific metadata when present. */
625
+ private async getColumnMetadata(
626
+ columnChunk: ColumnChunk,
627
+ rowGroupOrdinal: number,
628
+ columnOrdinal: number
629
+ ): Promise<NonNullable<ColumnChunk['meta_data']>> {
630
+ if (!columnChunk.encrypted_column_metadata) {
631
+ if (!columnChunk.meta_data) {
632
+ throw new Error('Parquet column metadata is missing');
633
+ }
634
+ return columnChunk.meta_data;
635
+ }
636
+ if (!this.props.keyRetriever) {
637
+ throw new Error('Encrypted Parquet column metadata requires parquet.keyRetriever');
638
+ }
639
+ const encryptionContext = this.encryptionContext;
640
+ if (!encryptionContext) {
641
+ throw new Error('Encrypted Parquet column metadata has no file encryption context');
642
+ }
643
+ const columnKey = columnChunk.crypto_metadata?.ENCRYPTION_WITH_COLUMN_KEY;
644
+ const usesFooterKey = Boolean(columnChunk.crypto_metadata?.ENCRYPTION_WITH_FOOTER_KEY);
645
+ if (!columnKey && !usesFooterKey) {
646
+ throw new Error('Encrypted Parquet column metadata has no key reference');
647
+ }
648
+ const aad = createParquetModuleAad(
649
+ encryptionContext.aadPrefix,
650
+ encryptionContext.fileUnique,
651
+ 'column-metadata',
652
+ rowGroupOrdinal,
653
+ columnOrdinal
654
+ );
655
+ const plaintext = await decryptParquetModule(columnChunk.encrypted_column_metadata, {
656
+ algorithm: encryptionContext.algorithm,
657
+ aad,
658
+ keyMetadata:
659
+ columnKey?.key_metadata ??
660
+ encryptionContext.footerKeyMetadata ??
661
+ encryptionContext.footerSigningKeyMetadata,
662
+ keyRetriever: this.getCachedKeyRetriever()
663
+ });
664
+ const metadata = decodeColumnMetadata(plaintext).metadata;
665
+ if (
666
+ columnKey &&
667
+ JSON.stringify(metadata.path_in_schema) !== JSON.stringify(columnKey.path_in_schema)
668
+ ) {
669
+ throw new Error('Encrypted Parquet column metadata path does not match crypto metadata');
670
+ }
178
671
  return metadata;
179
672
  }
180
673
 
@@ -182,52 +675,329 @@ export class ParquetReader {
182
675
  async readRowGroup(
183
676
  schema: ParquetSchema,
184
677
  rowGroup: RowGroup,
185
- columnList: string[][]
678
+ columnList: string[][],
679
+ signal?: AbortSignal,
680
+ rowGroupOrdinal = 0
186
681
  ): Promise<ParquetRowGroup> {
187
- const buffer: ParquetRowGroup = {
682
+ await this.resolveColumnMetadata(rowGroup, rowGroupOrdinal, columnList);
683
+ const selectedColumnChunks: Array<{
684
+ columnChunk: ColumnChunk;
685
+ metadata: NonNullable<ColumnChunk['meta_data']>;
686
+ columnOrdinal: number;
687
+ }> = [];
688
+ for (let columnOrdinal = 0; columnOrdinal < rowGroup.columns.length; columnOrdinal++) {
689
+ const columnChunk = rowGroup.columns[columnOrdinal];
690
+ const path = getColumnChunkPath(columnChunk);
691
+ if (columnList.length > 0 && (!path || fieldIndexOf(columnList, path) < 0)) {
692
+ continue;
693
+ }
694
+ const physicalColumnOrdinal = getPhysicalColumnOrdinal(columnChunk, columnOrdinal);
695
+ const metadata =
696
+ columnChunk.meta_data ||
697
+ (await this.getColumnMetadata(columnChunk, rowGroupOrdinal, physicalColumnOrdinal));
698
+ if (columnList.length === 0 || fieldIndexOf(columnList, metadata.path_in_schema) >= 0) {
699
+ selectedColumnChunks.push({columnChunk, metadata, columnOrdinal: physicalColumnOrdinal});
700
+ }
701
+ }
702
+ if (
703
+ this.file.handle instanceof ArrayBuffer &&
704
+ selectedColumnChunks.every(
705
+ ({columnChunk, metadata}) =>
706
+ (columnChunk.file_path === undefined || columnChunk.file_path === null) &&
707
+ !columnChunk.crypto_metadata &&
708
+ getThriftEnum(CompressionCodec, metadata.codec) === 'UNCOMPRESSED'
709
+ )
710
+ ) {
711
+ return {
712
+ rowCount: Number(rowGroup.num_rows),
713
+ columnData: Object.fromEntries(
714
+ selectedColumnChunks.map(({columnChunk, metadata}) => [
715
+ metadata.path_in_schema.join(),
716
+ this.readUncompressedColumnChunkFromMemory(schema, columnChunk, metadata)
717
+ ])
718
+ )
719
+ };
720
+ }
721
+ const columnEntries: Array<readonly [string, ParquetColumnChunk]> = [];
722
+ for (
723
+ let batchStart = 0;
724
+ batchStart < selectedColumnChunks.length;
725
+ batchStart += MAXIMUM_CONCURRENT_COLUMN_READS
726
+ ) {
727
+ const columnBatch = selectedColumnChunks.slice(
728
+ batchStart,
729
+ batchStart + MAXIMUM_CONCURRENT_COLUMN_READS
730
+ );
731
+ columnEntries.push(
732
+ ...(await Promise.all(
733
+ columnBatch.map(({columnChunk, metadata, columnOrdinal}) => {
734
+ const columnKey = metadata.path_in_schema.join();
735
+ if (
736
+ this.file.handle instanceof ArrayBuffer &&
737
+ (columnChunk.file_path === undefined || columnChunk.file_path === null) &&
738
+ !columnChunk.crypto_metadata &&
739
+ getThriftEnum(CompressionCodec, metadata.codec) === 'UNCOMPRESSED'
740
+ ) {
741
+ return [
742
+ columnKey,
743
+ this.readUncompressedColumnChunkFromMemory(schema, columnChunk, metadata)
744
+ ] as const;
745
+ }
746
+ return this.readColumnChunk(
747
+ schema,
748
+ columnChunk,
749
+ signal,
750
+ rowGroupOrdinal,
751
+ columnOrdinal,
752
+ metadata
753
+ ).then(columnData => [columnKey, columnData] as const);
754
+ })
755
+ ))
756
+ );
757
+ }
758
+ return {
188
759
  rowCount: Number(rowGroup.num_rows),
189
- columnData: {}
760
+ columnData: Object.fromEntries(columnEntries)
190
761
  };
191
- for (const colChunk of rowGroup.columns) {
192
- const colMetadata = colChunk.meta_data;
193
- const colKey = colMetadata?.path_in_schema;
194
- if (columnList.length > 0 && fieldIndexOf(columnList, colKey!) < 0) {
195
- continue; // eslint-disable-line no-continue
762
+ }
763
+
764
+ /**
765
+ * Reads selected column chunks as encoded page descriptors without materializing values.
766
+ *
767
+ * Page headers, encryption, checksums, and optional decompression remain reader concerns. Value,
768
+ * dictionary, and level decoding is deliberately left to the downstream consumer.
769
+ */
770
+ async readRowGroupEncodedPages(
771
+ schema: ParquetSchema,
772
+ rowGroup: RowGroup,
773
+ columnList: string[][],
774
+ preserveCompression: ReadonlySet<string>,
775
+ signal?: AbortSignal,
776
+ rowGroupOrdinal = 0
777
+ ): Promise<readonly ParquetEncodedColumnChunk[]> {
778
+ await this.resolveColumnMetadata(rowGroup, rowGroupOrdinal, columnList);
779
+ const selectedColumnChunks: Array<{
780
+ columnChunk: ColumnChunk;
781
+ metadata: NonNullable<ColumnChunk['meta_data']>;
782
+ columnOrdinal: number;
783
+ }> = [];
784
+ for (let columnOrdinal = 0; columnOrdinal < rowGroup.columns.length; columnOrdinal++) {
785
+ const columnChunk = rowGroup.columns[columnOrdinal];
786
+ const path = getColumnChunkPath(columnChunk);
787
+ if (columnList.length > 0 && (!path || fieldIndexOf(columnList, path) < 0)) continue;
788
+ const physicalColumnOrdinal = getPhysicalColumnOrdinal(columnChunk, columnOrdinal);
789
+ const metadata =
790
+ columnChunk.meta_data ||
791
+ (await this.getColumnMetadata(columnChunk, rowGroupOrdinal, physicalColumnOrdinal));
792
+ if (columnList.length === 0 || fieldIndexOf(columnList, metadata.path_in_schema) >= 0) {
793
+ selectedColumnChunks.push({
794
+ columnChunk,
795
+ metadata,
796
+ columnOrdinal: physicalColumnOrdinal
797
+ });
196
798
  }
197
- buffer.columnData[colKey!.join()] = await this.readColumnChunk(schema, colChunk);
198
799
  }
199
- return buffer;
800
+ const columns: ParquetEncodedColumnChunk[] = [];
801
+ for (
802
+ let batchStart = 0;
803
+ batchStart < selectedColumnChunks.length;
804
+ batchStart += MAXIMUM_CONCURRENT_COLUMN_READS
805
+ ) {
806
+ columns.push(
807
+ ...(await Promise.all(
808
+ selectedColumnChunks
809
+ .slice(batchStart, batchStart + MAXIMUM_CONCURRENT_COLUMN_READS)
810
+ .map(async ({columnChunk, metadata, columnOrdinal}) => {
811
+ const encodedColumn = await this.readColumnChunkEncodedPages(
812
+ schema,
813
+ columnChunk,
814
+ preserveCompression,
815
+ signal,
816
+ rowGroupOrdinal,
817
+ columnOrdinal,
818
+ metadata
819
+ );
820
+ return encodedColumn;
821
+ })
822
+ ))
823
+ );
824
+ }
825
+ return Object.freeze(columns);
826
+ }
827
+
828
+ /** Reads independently materializable non-repeated columns for one row range using page indexes. */
829
+ async readRowGroupRange(
830
+ schema: ParquetSchema,
831
+ rowGroup: RowGroup,
832
+ columnList: string[][],
833
+ rowRange: ParquetRowRange,
834
+ pageLocations: ParquetPageLocations,
835
+ signal?: AbortSignal,
836
+ rowGroupOrdinal = 0
837
+ ): Promise<ParquetRowGroup> {
838
+ await this.resolveColumnMetadata(rowGroup, rowGroupOrdinal, columnList);
839
+ const selectedColumnChunks: Array<{
840
+ columnChunk: ColumnChunk;
841
+ metadata: NonNullable<ColumnChunk['meta_data']>;
842
+ columnOrdinal: number;
843
+ }> = [];
844
+ for (let columnOrdinal = 0; columnOrdinal < rowGroup.columns.length; columnOrdinal++) {
845
+ const columnChunk = rowGroup.columns[columnOrdinal];
846
+ const path = getColumnChunkPath(columnChunk);
847
+ if (columnList.length > 0 && (!path || fieldIndexOf(columnList, path) < 0)) {
848
+ continue;
849
+ }
850
+ const physicalColumnOrdinal = getPhysicalColumnOrdinal(columnChunk, columnOrdinal);
851
+ const metadata =
852
+ columnChunk.meta_data ||
853
+ (await this.getColumnMetadata(columnChunk, rowGroupOrdinal, physicalColumnOrdinal));
854
+ if (columnList.length === 0 || fieldIndexOf(columnList, metadata.path_in_schema) >= 0) {
855
+ selectedColumnChunks.push({columnChunk, metadata, columnOrdinal: physicalColumnOrdinal});
856
+ }
857
+ }
858
+ const columnEntries = await Promise.all(
859
+ selectedColumnChunks.map(async ({columnChunk, metadata, columnOrdinal}) => {
860
+ const columnKey = metadata.path_in_schema.join();
861
+ const pages = pageLocations[JSON.stringify(metadata.path_in_schema)];
862
+ if (!pages) {
863
+ throw new Error(`Parquet offset index missing for ${columnKey}`);
864
+ }
865
+ const columnData = await this.readColumnChunkRange(
866
+ schema,
867
+ columnChunk,
868
+ rowRange,
869
+ pages,
870
+ signal,
871
+ rowGroupOrdinal,
872
+ columnOrdinal,
873
+ metadata
874
+ );
875
+ return [columnKey, columnData] as const;
876
+ })
877
+ );
878
+ return {
879
+ rowCount: rowRange.end - rowRange.start,
880
+ columnData: Object.fromEntries(columnEntries)
881
+ };
200
882
  }
201
883
 
202
884
  /**
203
885
  * Each row group contains column chunks for all the columns.
204
886
  */
205
- async readColumnChunk(schema: ParquetSchema, colChunk: ColumnChunk): Promise<ParquetColumnChunk> {
887
+ private readUncompressedColumnChunkFromMemory(
888
+ schema: ParquetSchema,
889
+ columnChunk: ColumnChunk,
890
+ metadata: NonNullable<ColumnChunk['meta_data']>
891
+ ): ParquetColumnChunk {
892
+ if (!(this.file.handle instanceof ArrayBuffer)) {
893
+ throw new Error('Synchronous Parquet column reads require an in-memory ArrayBuffer');
894
+ }
895
+ const field = schema.findField(metadata.path_in_schema);
896
+ const type: PrimitiveType = getThriftEnum(Type, metadata.type) as PrimitiveType;
897
+ if (type !== field.primitiveType) {
898
+ throw new Error(`chunk type not matching schema: ${type}`);
899
+ }
900
+
901
+ const pagesOffset = Number(metadata.data_page_offset);
902
+ const dictionaryPageOffset = metadata.dictionary_page_offset;
903
+ const validDictionaryPageOffset =
904
+ dictionaryPageOffset !== undefined && Number(dictionaryPageOffset) > 0
905
+ ? Number(dictionaryPageOffset)
906
+ : undefined;
907
+ const chunkOffset = Math.min(pagesOffset, validDictionaryPageOffset ?? pagesOffset);
908
+ const chunkEnd = chunkOffset + Number(metadata.total_compressed_size);
909
+ const chunkSize = Math.min(this.file.size - chunkOffset, Math.max(0, chunkEnd - chunkOffset));
910
+ const chunkBuffer = new Uint8Array(this.file.handle, chunkOffset, chunkSize);
911
+ const pagesRelativeOffset = pagesOffset - chunkOffset;
912
+ const pagesSize = Math.min(
913
+ chunkBuffer.length - pagesRelativeOffset,
914
+ Math.max(0, chunkEnd - pagesOffset)
915
+ );
916
+ const context: ParquetReaderContext = {
917
+ type,
918
+ rLevelMax: field.rLevelMax,
919
+ dLevelMax: field.dLevelMax,
920
+ compression: 'UNCOMPRESSED',
921
+ column: field,
922
+ numValues: metadata.num_values,
923
+ dictionary: [],
924
+ decompressPage: this.getPageDecompressor('UNCOMPRESSED'),
925
+ preserveBinary: this.props.preserveBinary,
926
+ retainByteArrayViews: this.props.retainByteArrayViews,
927
+ useTypedValueBuffers: this.props.useTypedValueBuffers,
928
+ useTypedLevelBuffers: this.props.useTypedLevelBuffers,
929
+ useArrowByteArrayBuffers: this.props.useArrowByteArrayBuffers,
930
+ // Metadata proves whether the decoder may skip its otherwise duplicated page-header scan.
931
+ // Keep the scan for mixed/unknown encodings: it is both a correctness guard and fallback.
932
+ hasOnlyArrowByteArrayDataPages: hasOnlyArrowByteArrayDataPageEncodings(
933
+ metadata.encodings,
934
+ validDictionaryPageOffset
935
+ ),
936
+ verifyPageChecksums: this.props.verifyPageChecksums,
937
+ int96AsTimestamp: this.props.int96AsTimestamp
938
+ };
939
+ let dictionary: unknown[] | undefined;
940
+ if (validDictionaryPageOffset !== undefined) {
941
+ const dictionaryRelativeOffset = validDictionaryPageOffset - chunkOffset;
942
+ const dictionarySize = Math.min(
943
+ Math.max(0, pagesOffset - validDictionaryPageOffset),
944
+ chunkBuffer.length - dictionaryRelativeOffset,
945
+ this.props.defaultDictionarySize
946
+ );
947
+ dictionary = decodeUncompressedDictionaryBuffer(
948
+ chunkBuffer.subarray(dictionaryRelativeOffset, dictionaryRelativeOffset + dictionarySize),
949
+ context
950
+ );
951
+ }
952
+ return decodeUncompressedDataPages(
953
+ chunkBuffer.subarray(pagesRelativeOffset, pagesRelativeOffset + pagesSize),
954
+ {...context, dictionary}
955
+ );
956
+ }
957
+
958
+ /**
959
+ * Each row group contains column chunks for all the columns.
960
+ */
961
+ async readColumnChunk(
962
+ schema: ParquetSchema,
963
+ colChunk: ColumnChunk,
964
+ signal?: AbortSignal,
965
+ rowGroupOrdinal = 0,
966
+ columnOrdinal = 0,
967
+ resolvedMetadata?: NonNullable<ColumnChunk['meta_data']>
968
+ ): Promise<ParquetColumnChunk> {
206
969
  if (colChunk.file_path !== undefined && colChunk.file_path !== null) {
207
970
  throw new Error('external references are not supported');
208
971
  }
209
972
 
210
- const field = schema.findField(colChunk.meta_data?.path_in_schema!);
211
- const type: PrimitiveType = getThriftEnum(Type, colChunk.meta_data?.type!) as any;
973
+ const metadata = resolvedMetadata || colChunk.meta_data;
974
+ if (!metadata) {
975
+ throw new Error('Parquet column metadata is missing');
976
+ }
977
+ const field = schema.findField(metadata.path_in_schema);
978
+ const type: PrimitiveType = getThriftEnum(Type, metadata.type) as any;
212
979
 
213
980
  if (type !== field.primitiveType) {
214
981
  throw new Error(`chunk type not matching schema: ${type}`);
215
982
  }
216
983
 
217
- const compression: ParquetCompression = getThriftEnum(
218
- CompressionCodec,
219
- colChunk.meta_data?.codec!
220
- ) as any;
221
-
222
- const pagesOffset = Number(colChunk.meta_data?.data_page_offset!);
223
- let pagesSize = Number(colChunk.meta_data?.total_compressed_size!);
984
+ const compression: ParquetCompression = getThriftEnum(CompressionCodec, metadata.codec) as any;
224
985
 
225
- if (!colChunk.file_path) {
226
- pagesSize = Math.min(
227
- this.file.size - pagesOffset,
228
- Number(colChunk.meta_data?.total_compressed_size)
229
- );
230
- }
986
+ const pagesOffset = Number(metadata.data_page_offset);
987
+ const dictionaryPageOffset = metadata.dictionary_page_offset;
988
+ const validDictionaryPageOffset =
989
+ dictionaryPageOffset !== undefined && Number(dictionaryPageOffset) > 0
990
+ ? Number(dictionaryPageOffset)
991
+ : undefined;
992
+ const chunkOffset = Math.min(pagesOffset, validDictionaryPageOffset ?? pagesOffset);
993
+ const chunkEnd = chunkOffset + Number(metadata.total_compressed_size);
994
+ const chunkSize = Math.min(this.file.size - chunkOffset, Math.max(0, chunkEnd - chunkOffset));
995
+ const chunkBuffer = await this.readBytes(chunkOffset, chunkSize, signal);
996
+ const pagesRelativeOffset = pagesOffset - chunkOffset;
997
+ const pagesSize = Math.min(
998
+ chunkBuffer.length - pagesRelativeOffset,
999
+ Math.max(0, chunkEnd - pagesOffset)
1000
+ );
231
1001
 
232
1002
  const context: ParquetReaderContext = {
233
1003
  type,
@@ -235,28 +1005,387 @@ export class ParquetReader {
235
1005
  dLevelMax: field.dLevelMax,
236
1006
  compression,
237
1007
  column: field,
238
- numValues: colChunk.meta_data?.num_values,
1008
+ numValues: metadata.num_values,
239
1009
  dictionary: [],
1010
+ decompressPage: this.getPageDecompressor(compression),
240
1011
  // Options - TBD is this the right place for these?
241
- preserveBinary: this.props.preserveBinary
1012
+ preserveBinary: this.props.preserveBinary,
1013
+ retainByteArrayViews: this.props.retainByteArrayViews,
1014
+ useTypedValueBuffers: this.props.useTypedValueBuffers,
1015
+ useTypedLevelBuffers: this.props.useTypedLevelBuffers,
1016
+ useArrowByteArrayBuffers: this.props.useArrowByteArrayBuffers,
1017
+ // Avoid parsing every page header twice when trusted column metadata already establishes the
1018
+ // direct Arrow byte-array invariant. Unknown/mixed encodings retain the defensive scan.
1019
+ hasOnlyArrowByteArrayDataPages: hasOnlyArrowByteArrayDataPageEncodings(
1020
+ metadata.encodings,
1021
+ validDictionaryPageOffset
1022
+ ),
1023
+ verifyPageChecksums: this.props.verifyPageChecksums,
1024
+ int96AsTimestamp: this.props.int96AsTimestamp
242
1025
  };
243
1026
 
244
- let dictionary;
245
-
246
- const dictionaryPageOffset = colChunk?.meta_data?.dictionary_page_offset;
1027
+ let dictionary: any[] | undefined;
247
1028
 
248
- if (dictionaryPageOffset) {
249
- const dictionaryOffset = Number(dictionaryPageOffset);
250
- // Getting dictionary from column chunk to iterate all over indexes to get dataPage values.
251
- dictionary = await this.getDictionary(dictionaryOffset, context, pagesOffset);
1029
+ if (validDictionaryPageOffset !== undefined) {
1030
+ const dictionaryRelativeOffset = validDictionaryPageOffset - chunkOffset;
1031
+ const dictionarySize = Math.min(
1032
+ Math.max(0, pagesOffset - validDictionaryPageOffset),
1033
+ chunkBuffer.length - dictionaryRelativeOffset,
1034
+ this.props.defaultDictionarySize
1035
+ );
1036
+ const dictionaryBuffer = chunkBuffer.subarray(
1037
+ dictionaryRelativeOffset,
1038
+ dictionaryRelativeOffset + dictionarySize
1039
+ );
1040
+ const decodedDictionaryBuffer = colChunk.crypto_metadata
1041
+ ? await this.decryptColumnPages(
1042
+ dictionaryBuffer,
1043
+ context,
1044
+ rowGroupOrdinal,
1045
+ columnOrdinal,
1046
+ this.getColumnKeyMetadata(colChunk),
1047
+ true
1048
+ )
1049
+ : dictionaryBuffer;
1050
+ dictionary = await decodeDictionaryBuffer(decodedDictionaryBuffer, context);
252
1051
  }
253
1052
 
254
1053
  dictionary = context.dictionary?.length ? context.dictionary : dictionary;
255
- const arrayBuffer = await this.file.read(pagesOffset, pagesSize);
256
- const pagesBuf = Buffer.from(arrayBuffer);
1054
+ const pagesBuf = colChunk.crypto_metadata
1055
+ ? await this.decryptColumnPages(
1056
+ chunkBuffer.subarray(pagesRelativeOffset, pagesRelativeOffset + pagesSize),
1057
+ context,
1058
+ rowGroupOrdinal,
1059
+ columnOrdinal,
1060
+ this.getColumnKeyMetadata(colChunk)
1061
+ )
1062
+ : chunkBuffer.subarray(pagesRelativeOffset, pagesRelativeOffset + pagesSize);
257
1063
  return await decodeDataPages(pagesBuf, {...context, dictionary});
258
1064
  }
259
1065
 
1066
+ /** Reads one column chunk as encoded dictionaries, levels, and values for deferred decoding. */
1067
+ async readColumnChunkEncodedPages(
1068
+ schema: ParquetSchema,
1069
+ columnChunk: ColumnChunk,
1070
+ preserveCompression: ReadonlySet<string>,
1071
+ signal?: AbortSignal,
1072
+ rowGroupOrdinal = 0,
1073
+ columnOrdinal = 0,
1074
+ resolvedMetadata?: NonNullable<ColumnChunk['meta_data']>
1075
+ ): Promise<ParquetEncodedColumnChunk> {
1076
+ if (columnChunk.file_path !== undefined && columnChunk.file_path !== null) {
1077
+ throw new Error('external references are not supported');
1078
+ }
1079
+ const metadata = resolvedMetadata || columnChunk.meta_data;
1080
+ if (!metadata) throw new Error('Parquet column metadata is missing');
1081
+ const field = schema.findField(metadata.path_in_schema);
1082
+ const physicalType: PrimitiveType = getThriftEnum(Type, metadata.type) as PrimitiveType;
1083
+ if (physicalType !== field.primitiveType) {
1084
+ throw new Error(`chunk type not matching schema: ${physicalType}`);
1085
+ }
1086
+ const compression = getThriftEnum(CompressionCodec, metadata.codec) as ParquetCompression;
1087
+ const dataPageOffset = Number(metadata.data_page_offset);
1088
+ const dictionaryPageOffset = metadata.dictionary_page_offset;
1089
+ const validDictionaryPageOffset =
1090
+ dictionaryPageOffset !== undefined && Number(dictionaryPageOffset) > 0
1091
+ ? Number(dictionaryPageOffset)
1092
+ : undefined;
1093
+ const chunkOffset = Math.min(dataPageOffset, validDictionaryPageOffset ?? dataPageOffset);
1094
+ const chunkEnd = chunkOffset + Number(metadata.total_compressed_size);
1095
+ const chunkByteLength = Math.min(
1096
+ this.file.size - chunkOffset,
1097
+ Math.max(0, chunkEnd - chunkOffset)
1098
+ );
1099
+ const chunkBuffer = await this.readBytes(chunkOffset, chunkByteLength, signal);
1100
+ const dataPageRelativeOffset = dataPageOffset - chunkOffset;
1101
+ const dataPageByteLength = Math.min(
1102
+ chunkBuffer.byteLength - dataPageRelativeOffset,
1103
+ Math.max(0, chunkEnd - dataPageOffset)
1104
+ );
1105
+ const context = {
1106
+ column: field,
1107
+ compression,
1108
+ decompressPage: this.getPageDecompressor(compression),
1109
+ preserveCompression,
1110
+ verifyPageChecksums: this.props.verifyPageChecksums
1111
+ };
1112
+
1113
+ let dictionary;
1114
+ if (validDictionaryPageOffset !== undefined) {
1115
+ const dictionaryRelativeOffset = validDictionaryPageOffset - chunkOffset;
1116
+ const dictionaryByteLength = Math.min(
1117
+ Math.max(0, dataPageOffset - validDictionaryPageOffset),
1118
+ chunkBuffer.byteLength - dictionaryRelativeOffset,
1119
+ this.props.defaultDictionarySize
1120
+ );
1121
+ const dictionaryBuffer = chunkBuffer.subarray(
1122
+ dictionaryRelativeOffset,
1123
+ dictionaryRelativeOffset + dictionaryByteLength
1124
+ );
1125
+ const plaintextDictionaryBuffer = columnChunk.crypto_metadata
1126
+ ? await this.decryptColumnPages(
1127
+ dictionaryBuffer,
1128
+ {
1129
+ type: physicalType,
1130
+ rLevelMax: field.rLevelMax,
1131
+ dLevelMax: field.dLevelMax,
1132
+ compression,
1133
+ column: field
1134
+ },
1135
+ rowGroupOrdinal,
1136
+ columnOrdinal,
1137
+ this.getColumnKeyMetadata(columnChunk),
1138
+ true
1139
+ )
1140
+ : dictionaryBuffer;
1141
+ dictionary = (await scanParquetEncodedPages(plaintextDictionaryBuffer, context))[0];
1142
+ }
1143
+
1144
+ const dataPageBuffer = chunkBuffer.subarray(
1145
+ dataPageRelativeOffset,
1146
+ dataPageRelativeOffset + dataPageByteLength
1147
+ );
1148
+ const plaintextDataPageBuffer = columnChunk.crypto_metadata
1149
+ ? await this.decryptColumnPages(
1150
+ dataPageBuffer,
1151
+ {
1152
+ type: physicalType,
1153
+ rLevelMax: field.rLevelMax,
1154
+ dLevelMax: field.dLevelMax,
1155
+ compression,
1156
+ column: field
1157
+ },
1158
+ rowGroupOrdinal,
1159
+ columnOrdinal,
1160
+ this.getColumnKeyMetadata(columnChunk)
1161
+ )
1162
+ : dataPageBuffer;
1163
+ const pages = await scanParquetEncodedPages(plaintextDataPageBuffer, context);
1164
+ return Object.freeze({
1165
+ path: Object.freeze([...metadata.path_in_schema]),
1166
+ physicalType,
1167
+ typeLength: field.typeLength,
1168
+ maxRepetitionLevel: field.rLevelMax,
1169
+ maxDefinitionLevel: field.dLevelMax,
1170
+ compression,
1171
+ valueCount: Number(metadata.num_values),
1172
+ dictionary,
1173
+ pages: Object.freeze(pages)
1174
+ });
1175
+ }
1176
+
1177
+ /** Decrypts the length-prefixed page header and data modules in one column chunk. */
1178
+ private async decryptColumnPages(
1179
+ encryptedPages: Uint8Array,
1180
+ context: ParquetReaderContext,
1181
+ rowGroupOrdinal: number,
1182
+ columnOrdinal: number,
1183
+ keyMetadata: Uint8Array | undefined,
1184
+ includesDictionary = false,
1185
+ dataPageOrdinalStart = 0
1186
+ ): Promise<Uint8Array> {
1187
+ if (!this.encryptionContext || !this.props.keyRetriever) {
1188
+ throw new Error('Encrypted Parquet pages require parquet.keyRetriever');
1189
+ }
1190
+ const plaintextPages: Uint8Array[] = [];
1191
+ let offset = 0;
1192
+ let dataPageOrdinal = dataPageOrdinalStart;
1193
+ let pageIndex = 0;
1194
+ while (offset < encryptedPages.length) {
1195
+ const headerModule = readParquetEncryptedModule(encryptedPages, offset);
1196
+ offset = headerModule.nextOffset;
1197
+ const dictionaryPage = includesDictionary && pageIndex === 0;
1198
+ const headerModuleType: ParquetEncryptionModule = dictionaryPage
1199
+ ? 'dictionary-page-header'
1200
+ : 'data-page-header';
1201
+ const headerAad = createParquetModuleAad(
1202
+ this.encryptionContext.aadPrefix,
1203
+ this.encryptionContext.fileUnique,
1204
+ headerModuleType,
1205
+ rowGroupOrdinal,
1206
+ columnOrdinal,
1207
+ dictionaryPage ? undefined : dataPageOrdinal
1208
+ );
1209
+ const headerBytes = await decryptParquetModule(headerModule.bytes, {
1210
+ algorithm: this.encryptionContext.algorithm,
1211
+ aad: headerAad,
1212
+ keyMetadata,
1213
+ keyRetriever: this.getCachedKeyRetriever()
1214
+ });
1215
+ const {pageHeader} = decodePageHeader(headerBytes);
1216
+ const pageType = getThriftEnum(PageType, pageHeader.type);
1217
+ const dataModule = readParquetEncryptedModule(encryptedPages, offset);
1218
+ offset = dataModule.nextOffset;
1219
+ const dataAad = createParquetModuleAad(
1220
+ this.encryptionContext.aadPrefix,
1221
+ this.encryptionContext.fileUnique,
1222
+ dictionaryPage ? 'dictionary-page' : 'data-page',
1223
+ rowGroupOrdinal,
1224
+ columnOrdinal,
1225
+ dictionaryPage ? undefined : dataPageOrdinal
1226
+ );
1227
+ const dataBytes = await decryptParquetModule(dataModule.bytes, {
1228
+ algorithm: this.encryptionContext.algorithm,
1229
+ aad: dataAad,
1230
+ keyMetadata,
1231
+ keyRetriever: this.getCachedKeyRetriever(),
1232
+ page: true
1233
+ });
1234
+ plaintextPages.push(serializeThrift(pageHeader), dataBytes);
1235
+ if (pageType !== 'DICTIONARY_PAGE') dataPageOrdinal++;
1236
+ pageIndex++;
1237
+ }
1238
+ const plaintextLength = plaintextPages.reduce((total, page) => total + page.length, 0);
1239
+ const plaintext = new Uint8Array(plaintextLength);
1240
+ let plaintextOffset = 0;
1241
+ for (const page of plaintextPages) {
1242
+ plaintext.set(page, plaintextOffset);
1243
+ plaintextOffset += page.length;
1244
+ }
1245
+ return plaintext;
1246
+ }
1247
+
1248
+ /** Resolves the key metadata reference for an encrypted column. */
1249
+ private getColumnKeyMetadata(columnChunk: ColumnChunk): Uint8Array | undefined {
1250
+ return (
1251
+ columnChunk.crypto_metadata?.ENCRYPTION_WITH_COLUMN_KEY?.key_metadata ??
1252
+ this.encryptionContext?.footerKeyMetadata ??
1253
+ this.encryptionContext?.footerSigningKeyMetadata
1254
+ );
1255
+ }
1256
+
1257
+ /** Reads and decodes contiguous data pages that begin and end on complete row boundaries. */
1258
+ async readColumnChunkRange(
1259
+ schema: ParquetSchema,
1260
+ columnChunk: ColumnChunk,
1261
+ rowRange: ParquetRowRange,
1262
+ pages: readonly ParquetDataPageLocation[],
1263
+ signal?: AbortSignal,
1264
+ rowGroupOrdinal = 0,
1265
+ columnOrdinal = 0,
1266
+ resolvedMetadata?: NonNullable<ColumnChunk['meta_data']>
1267
+ ): Promise<ParquetColumnChunk> {
1268
+ if (columnChunk.file_path !== undefined && columnChunk.file_path !== null) {
1269
+ throw new Error('external references are not supported');
1270
+ }
1271
+ const columnMetadata = resolvedMetadata || columnChunk.meta_data;
1272
+ if (!columnMetadata) {
1273
+ throw new Error('Parquet column metadata is missing');
1274
+ }
1275
+ const encrypted = Boolean(columnChunk.crypto_metadata);
1276
+ const field = schema.findField(columnMetadata.path_in_schema);
1277
+ const type: PrimitiveType = getThriftEnum(Type, columnMetadata.type) as any;
1278
+ if (type !== field.primitiveType) {
1279
+ throw new Error(`chunk type not matching schema: ${type}`);
1280
+ }
1281
+ const compression: ParquetCompression = getThriftEnum(
1282
+ CompressionCodec,
1283
+ columnMetadata.codec
1284
+ ) as any;
1285
+ const overlappingPages = pages.filter(
1286
+ page => page.endRowIndex > rowRange.start && page.firstRowIndex < rowRange.end
1287
+ );
1288
+ if (!overlappingPages.length) {
1289
+ throw new Error('Parquet page plan does not overlap the requested row range');
1290
+ }
1291
+ let firstPageIndex = pages.indexOf(overlappingPages[0]);
1292
+ const lastPage = overlappingPages[overlappingPages.length - 1];
1293
+ let firstPage = pages[firstPageIndex];
1294
+ const context: ParquetReaderContext = {
1295
+ type,
1296
+ rLevelMax: field.rLevelMax,
1297
+ dLevelMax: field.dLevelMax,
1298
+ compression,
1299
+ column: field,
1300
+ // Repeated leaves have one level entry per physical value, not one per logical row. Let the
1301
+ // page decoder consume the selected page range and preserve all repetition levels.
1302
+ numValues:
1303
+ field.rLevelMax === 0
1304
+ ? new CompactInt64(lastPage.endRowIndex - firstPage.firstRowIndex)
1305
+ : undefined,
1306
+ dictionary: [],
1307
+ decompressPage: this.getPageDecompressor(compression),
1308
+ preserveBinary: this.props.preserveBinary,
1309
+ retainByteArrayViews: this.props.retainByteArrayViews,
1310
+ useTypedValueBuffers: this.props.useTypedValueBuffers,
1311
+ useArrowByteArrayBuffers: this.props.useArrowByteArrayBuffers,
1312
+ verifyPageChecksums: this.props.verifyPageChecksums,
1313
+ int96AsTimestamp: this.props.int96AsTimestamp
1314
+ };
1315
+
1316
+ let dictionary: any[] | undefined;
1317
+ const dictionaryPageOffset = Number(columnMetadata.dictionary_page_offset);
1318
+ if (Number.isSafeInteger(dictionaryPageOffset) && dictionaryPageOffset > 0) {
1319
+ const dictionaryLength = Math.max(0, pages[0].offset - dictionaryPageOffset);
1320
+ const dictionaryBuffer = await this.readBytes(dictionaryPageOffset, dictionaryLength, signal);
1321
+ const decodedDictionaryBuffer = encrypted
1322
+ ? await this.decryptColumnPages(
1323
+ dictionaryBuffer,
1324
+ context,
1325
+ rowGroupOrdinal,
1326
+ columnOrdinal,
1327
+ this.getColumnKeyMetadata(columnChunk),
1328
+ true
1329
+ )
1330
+ : dictionaryBuffer;
1331
+ dictionary = await decodeDictionaryBuffer(decodedDictionaryBuffer, context);
1332
+ }
1333
+
1334
+ if (field.rLevelMax !== 0) {
1335
+ // Probe predecessor pages independently, then decode the final range once. This avoids
1336
+ // quadratic re-decoding when a large repeated row spans many pages.
1337
+ while (firstPageIndex > 0) {
1338
+ const probeBuffer = await this.readBytes(
1339
+ firstPage.offset,
1340
+ firstPage.compressedByteLength,
1341
+ signal
1342
+ );
1343
+ const decodedProbeBuffer = encrypted
1344
+ ? await this.decryptColumnPages(
1345
+ probeBuffer,
1346
+ context,
1347
+ rowGroupOrdinal,
1348
+ columnOrdinal,
1349
+ this.getColumnKeyMetadata(columnChunk),
1350
+ false,
1351
+ firstPageIndex
1352
+ )
1353
+ : probeBuffer;
1354
+ const probe = await decodeDataPages(decodedProbeBuffer, {...context, dictionary});
1355
+ if (probe.rlevels[0] === 0) {
1356
+ break;
1357
+ }
1358
+ firstPage = pages[--firstPageIndex];
1359
+ }
1360
+ }
1361
+ const dataLength = lastPage.offset + lastPage.compressedByteLength - firstPage.offset;
1362
+ const dataBuffer = await this.readBytes(firstPage.offset, dataLength, signal);
1363
+ const decodedDataBuffer = encrypted
1364
+ ? await this.decryptColumnPages(
1365
+ dataBuffer,
1366
+ context,
1367
+ rowGroupOrdinal,
1368
+ columnOrdinal,
1369
+ this.getColumnKeyMetadata(columnChunk),
1370
+ false,
1371
+ firstPageIndex
1372
+ )
1373
+ : dataBuffer;
1374
+ const decoded = await decodeDataPages(decodedDataBuffer, {...context, dictionary});
1375
+ if (field.rLevelMax !== 0) {
1376
+ const rowStart = rowRange.start - firstPage.firstRowIndex;
1377
+ return sliceRepeatedColumnChunk(
1378
+ decoded,
1379
+ Math.max(0, rowStart),
1380
+ rowRange.end - rowRange.start,
1381
+ field.dLevelMax
1382
+ );
1383
+ }
1384
+ const relativeStart = rowRange.start - firstPage.firstRowIndex;
1385
+ const relativeEnd = relativeStart + rowRange.end - rowRange.start;
1386
+ return sliceNonRepeatedColumnChunk(decoded, field.dLevelMax, relativeStart, relativeEnd);
1387
+ }
1388
+
260
1389
  /**
261
1390
  * Getting dictionary for allows to flatten values by indices.
262
1391
  * @param dictionaryPageOffset
@@ -267,8 +1396,9 @@ export class ParquetReader {
267
1396
  async getDictionary(
268
1397
  dictionaryPageOffset: number,
269
1398
  context: ParquetReaderContext,
270
- pagesOffset: number
271
- ): Promise<string[]> {
1399
+ pagesOffset: number,
1400
+ signal?: AbortSignal
1401
+ ): Promise<any[]> {
272
1402
  if (dictionaryPageOffset === 0) {
273
1403
  // dictionarySize = Math.min(this.fileSize - pagesOffset, this.defaultDictionarySize);
274
1404
  // pagesBuf = await this.read(pagesOffset, dictionarySize);
@@ -281,15 +1411,136 @@ export class ParquetReader {
281
1411
  }
282
1412
 
283
1413
  const dictionarySize = Math.min(
1414
+ Math.max(0, pagesOffset - dictionaryPageOffset),
284
1415
  this.file.size - dictionaryPageOffset,
285
1416
  this.props.defaultDictionarySize
286
1417
  );
287
- const arrayBuffer = await this.file.read(dictionaryPageOffset, dictionarySize);
288
- const pagesBuf = Buffer.from(arrayBuffer);
1418
+ const pagesBuf = await this.readBytes(dictionaryPageOffset, dictionarySize, signal);
1419
+ return await decodeDictionaryBuffer(pagesBuf, context);
1420
+ }
1421
+ }
1422
+
1423
+ /** Returns a column path available without decrypting column metadata when possible. */
1424
+ function getColumnChunkPath(columnChunk: ColumnChunk): string[] | undefined {
1425
+ return (
1426
+ columnChunk.meta_data?.path_in_schema ??
1427
+ columnChunk.crypto_metadata?.ENCRYPTION_WITH_COLUMN_KEY?.path_in_schema
1428
+ );
1429
+ }
1430
+
1431
+ /** Builds a stable cache key while preserving module scope for context-dependent key providers. */
1432
+ function createEncryptionKeyCacheKey(
1433
+ algorithm: ParquetEncryptionAlgorithm,
1434
+ keyMetadata: Uint8Array | undefined,
1435
+ aad: Uint8Array,
1436
+ aadPrefixByteLength: number | undefined,
1437
+ fileUniqueByteLength: number | undefined
1438
+ ): string {
1439
+ const metadataKey = keyMetadata ? Array.from(keyMetadata).join(',') : 'footer';
1440
+ if (fileUniqueByteLength === undefined) {
1441
+ return `${algorithm}:${metadataKey}:aad:${Array.from(aad).join(',')}`;
1442
+ }
1443
+
1444
+ const moduleOffset = (aadPrefixByteLength ?? 0) + fileUniqueByteLength;
1445
+ if (moduleOffset + 5 > aad.byteLength) {
1446
+ return `${algorithm}:${metadataKey}:aad:${Array.from(aad).join(',')}`;
1447
+ }
1448
+
1449
+ const moduleType = aad[moduleOffset];
1450
+ if (moduleType === 0) {
1451
+ return `${algorithm}:${metadataKey}:footer`;
1452
+ }
1453
+ const view = new DataView(
1454
+ aad.buffer,
1455
+ aad.byteOffset + moduleOffset,
1456
+ aad.byteLength - moduleOffset
1457
+ );
1458
+ const rowGroupOrdinal = view.getInt16(1, true);
1459
+ const columnOrdinal = view.getInt16(3, true);
1460
+ return `${algorithm}:${metadataKey}:module:${moduleType}:row-group:${rowGroupOrdinal}:column:${columnOrdinal}`;
1461
+ }
1462
+
1463
+ /** Returns the original row-group ordinal for a worker-reduced column list. */
1464
+ function getPhysicalColumnOrdinal(columnChunk: ColumnChunk, fallbackOrdinal: number): number {
1465
+ return (
1466
+ (columnChunk as ColumnChunk & {parquetColumnOrdinal?: number}).parquetColumnOrdinal ??
1467
+ fallbackOrdinal
1468
+ );
1469
+ }
1470
+
1471
+ /** Decodes one dictionary page from an already-read column-chunk range. */
1472
+ async function decodeDictionaryBuffer(
1473
+ dictionaryBuffer: Uint8Array,
1474
+ context: ParquetReaderContext
1475
+ ): Promise<any[]> {
1476
+ const cursor = {buffer: dictionaryBuffer, offset: 0, size: dictionaryBuffer.length};
1477
+ const decodedPage = await decodePage(cursor, context);
1478
+ return decodedPage.dictionary!;
1479
+ }
1480
+
1481
+ /** Slices one decoded non-repeated column chunk while preserving optional-value alignment. */
1482
+ function sliceNonRepeatedColumnChunk(
1483
+ columnChunk: ParquetColumnChunk,
1484
+ definitionLevelMaximum: number,
1485
+ start: number,
1486
+ end: number
1487
+ ): ParquetColumnChunk {
1488
+ const valueStart = countDefinedValues(columnChunk.dlevels, definitionLevelMaximum, 0, start);
1489
+ const valueEnd =
1490
+ valueStart + countDefinedValues(columnChunk.dlevels, definitionLevelMaximum, start, end);
1491
+ return {
1492
+ rlevels: columnChunk.rlevels.slice(start, end),
1493
+ dlevels: columnChunk.dlevels.slice(start, end),
1494
+ values: columnChunk.values.slice(valueStart, valueEnd) as typeof columnChunk.values,
1495
+ count: end - start,
1496
+ pageHeaders: columnChunk.pageHeaders
1497
+ };
1498
+ }
289
1499
 
290
- const cursor = {buffer: pagesBuf, offset: 0, size: pagesBuf.length};
291
- const decodedPage = await decodePage(cursor, context);
1500
+ /** Slices a repeated column by logical rows while preserving its level/value alignment. */
1501
+ function sliceRepeatedColumnChunk(
1502
+ columnChunk: ParquetColumnChunk,
1503
+ rowStart: number,
1504
+ rowCount: number,
1505
+ definitionLevelMaximum: number
1506
+ ): ParquetColumnChunk {
1507
+ const rowStarts: number[] = [];
1508
+ for (let levelIndex = 0; levelIndex < columnChunk.rlevels.length; levelIndex++) {
1509
+ if (columnChunk.rlevels[levelIndex] === 0) {
1510
+ rowStarts.push(levelIndex);
1511
+ }
1512
+ }
1513
+ const levelStart = rowStarts[rowStart];
1514
+ const endRow = rowStart + rowCount;
1515
+ const levelEnd = rowStarts[endRow] ?? columnChunk.rlevels.length;
1516
+ if (levelStart === undefined || endRow > rowStarts.length || levelEnd < levelStart) {
1517
+ throw new Error('Parquet repeated page range does not contain the requested rows');
1518
+ }
1519
+ const valueStart = countDefinedValues(columnChunk.dlevels, definitionLevelMaximum, 0, levelStart);
1520
+ const valueEnd =
1521
+ valueStart +
1522
+ countDefinedValues(columnChunk.dlevels, definitionLevelMaximum, levelStart, levelEnd);
1523
+ return {
1524
+ rlevels: columnChunk.rlevels.slice(levelStart, levelEnd),
1525
+ dlevels: columnChunk.dlevels.slice(levelStart, levelEnd),
1526
+ values: columnChunk.values.slice(valueStart, valueEnd) as typeof columnChunk.values,
1527
+ count: levelEnd - levelStart,
1528
+ pageHeaders: columnChunk.pageHeaders
1529
+ };
1530
+ }
292
1531
 
293
- return decodedPage.dictionary!;
1532
+ /** Counts defined primitive values represented by one non-repeated level interval. */
1533
+ function countDefinedValues(
1534
+ definitionLevels: ParquetLevelBuffer,
1535
+ definitionLevelMaximum: number,
1536
+ start: number,
1537
+ end: number
1538
+ ): number {
1539
+ let count = 0;
1540
+ for (let index = start; index < Math.min(end, definitionLevels.length); index++) {
1541
+ if (definitionLevels[index] === definitionLevelMaximum) {
1542
+ count++;
1543
+ }
294
1544
  }
1545
+ return count;
295
1546
  }