@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
@@ -6,43 +6,105 @@
6
6
 
7
7
  /* eslint-disable camelcase */
8
8
  import {stream} from '@loaders.gl/loader-utils';
9
+ import {getWKBGeometryStatistics, type WKBGeometryBoundingBox} from '@loaders.gl/gis';
9
10
  import {ParquetCodecOptions, PARQUET_CODECS} from '../codecs/index';
10
11
  import * as Compression from '../compression';
11
- import {
12
+ import type {
12
13
  ParquetRowGroup,
13
14
  ParquetCodec,
14
15
  ParquetColumnChunk,
15
16
  ParquetField,
17
+ ParquetLogicalType,
16
18
  PrimitiveType,
17
19
  ParquetRow
18
20
  } from '../schema/declare';
19
21
  import {ParquetSchema} from '../schema/schema';
20
22
  import * as Shred from '../schema/shred';
21
23
  import {
24
+ BsonType,
25
+ BoundingBox,
22
26
  ColumnChunk,
27
+ ColumnCryptoMetaData,
28
+ ColumnIndex,
23
29
  ColumnMetaData,
30
+ SizeStatistics,
31
+ Statistics,
32
+ SortingColumn,
24
33
  CompressionCodec,
25
34
  ConvertedType,
26
35
  DataPageHeader,
27
36
  DataPageHeaderV2,
37
+ DateType,
38
+ DecimalType,
39
+ DictionaryPageHeader,
40
+ EdgeInterpolationAlgorithm,
28
41
  Encoding,
42
+ EnumType,
29
43
  FieldRepetitionType,
30
44
  FileMetaData,
45
+ Float16Type,
46
+ GeographyType,
47
+ GeospatialStatistics,
48
+ GeometryType,
49
+ IntType,
50
+ JsonType,
31
51
  KeyValue,
52
+ ListType,
53
+ LogicalType,
54
+ MapType,
55
+ MicroSeconds,
56
+ MilliSeconds,
57
+ NanoSeconds,
58
+ NullType,
32
59
  PageHeader,
60
+ PageEncodingStats,
33
61
  PageType,
62
+ PageLocation,
34
63
  RowGroup,
35
64
  SchemaElement,
36
- Type
65
+ StringType,
66
+ TimeType,
67
+ TimeUnit,
68
+ TimestampType,
69
+ Type,
70
+ UUIDType,
71
+ VariantType,
72
+ OffsetIndex
37
73
  } from '../parquet-thrift/index';
38
74
  import {osopen, oswrite, osclose} from '../utils/file-utils';
39
75
  import {getBitWidth, serializeThrift} from '../utils/read-utils';
40
- import Int64 from 'node-int64';
76
+ import {concatUint8Arrays, encodeUtf8, writeUInt32LE} from '../utils/binary-utils';
77
+ import {crc32} from '../utils/crc32';
78
+ import {CompactInt64} from '../utils/uint8-array-compact-protocol';
79
+ import {planColumnPages} from './page-planner';
80
+ import {planDictionary, type ParquetDictionaryPolicy} from './dictionary-planner';
81
+ import {
82
+ decodeParquetSplitBlockBloomFilter,
83
+ encodeParquetBloomFilterValue,
84
+ encodeParquetSplitBlockBloomFilter
85
+ } from '../../lib/parquet-bloom-filter';
86
+ import {BoundaryOrder} from '../parquet-thrift/BoundaryOrder';
87
+ import {
88
+ EncryptionWithColumnKey,
89
+ EncryptionWithFooterKey
90
+ } from '../parquet-thrift/ColumnCryptoMetaData';
91
+ import {EncryptionAlgorithm} from '../parquet-thrift/EncryptionAlgorithm';
92
+ import {FileCryptoMetaData} from '../parquet-thrift/FileCryptoMetaData';
93
+ import {PARQUET_MAGIC_ENCRYPTED} from '../../lib/constants';
94
+ import {
95
+ createParquetModuleAad,
96
+ createParquetFooterSignature,
97
+ encryptParquetModule,
98
+ type ParquetWriterEncryptionOptions,
99
+ type ParquetWriterFooterSignatureOptions
100
+ } from '../../lib/parquet-encryption';
41
101
 
42
102
  /**
43
103
  * Parquet File Magic String
44
104
  */
45
105
  const PARQUET_MAGIC = 'PAR1';
106
+ const PARQUET_MAGIC_BYTES = encodeUtf8(PARQUET_MAGIC);
107
+ const PARQUET_MAGIC_ENCRYPTED_BYTES = encodeUtf8(PARQUET_MAGIC_ENCRYPTED);
46
108
 
47
109
  /**
48
110
  * Parquet File Format Version
@@ -66,6 +128,29 @@ export interface ParquetEncoderOptions {
66
128
  rowGroupSize?: number;
67
129
  pageSize?: number;
68
130
  useDataPageV2?: boolean;
131
+ dictionary?: ParquetDictionaryPolicy;
132
+ columnDictionaries?: Record<string, ParquetDictionaryPolicy>;
133
+ dictionaryPageSizeLimit?: number;
134
+ bloomFilter?: boolean | Record<string, boolean>;
135
+ pageIndex?: boolean | Record<string, boolean>;
136
+ /** Emit CRC-32 checksums for page bodies. */
137
+ writePageChecksums?: boolean;
138
+ /** Emit optional SizeStatistics metadata for each column chunk. */
139
+ writeSizeStatistics?: boolean;
140
+ /** Emit optional min/max/null-count statistics for each column chunk. */
141
+ writeStatistics?: boolean | Record<string, boolean>;
142
+ /** Declare the row-group sort order using top-level or dotted leaf column names. */
143
+ sortingColumns?: readonly ParquetSortingColumnOption[];
144
+ /** Encode INT96 input values as epoch nanoseconds using the canonical Julian-day layout. */
145
+ int96AsTimestamp?: boolean;
146
+ /** Encrypt the footer using Parquet modular encryption. */
147
+ encryption?: ParquetWriterEncryptionOptions;
148
+ /** Authenticate a plaintext footer with a Parquet modular-encryption signature. */
149
+ footerSignature?: ParquetWriterFooterSignatureOptions;
150
+ /** Stable file identifier shared by encrypted column modules and the footer. */
151
+ encryptionFileUnique?: Uint8Array;
152
+ /** Zero-based row-group ordinal used by encrypted module AAD. */
153
+ rowGroupOrdinal?: number;
69
154
 
70
155
  // Write Stream Options
71
156
  flags?: string;
@@ -76,13 +161,23 @@ export interface ParquetEncoderOptions {
76
161
  start?: number;
77
162
  }
78
163
 
164
+ /** Writer-facing declaration for one row-group sort key. */
165
+ export type ParquetSortingColumnOption = {
166
+ /** Top-level field name or dotted nested leaf path. */
167
+ column: string;
168
+ /** Sort direction; defaults to ascending. */
169
+ descending?: boolean;
170
+ /** Whether null values sort before non-null values; defaults to false. */
171
+ nullsFirst?: boolean;
172
+ };
173
+
79
174
  /**
80
175
  * Write a parquet file to an output stream. The ParquetEncoder will perform
81
176
  * buffering/batching for performance, so close() must be called after all rows
82
177
  * are written.
83
178
  */
84
179
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
85
- export class ParquetEncoder<T> {
180
+ export class ParquetEncoder<_T> {
86
181
  /**
87
182
  * Convenience method to create a new buffered parquet writer that writes to
88
183
  * the specified file
@@ -126,8 +221,7 @@ export class ParquetEncoder<T> {
126
221
  ) {
127
222
  this.schema = schema;
128
223
  this.envelopeWriter = envelopeWriter;
129
- // @ts-ignore Row buffer typings...
130
- this.rowBuffer = {};
224
+ this.rowBuffer = schema.rowGroup();
131
225
  this.rowGroupSize = opts.rowGroupSize || PARQUET_DEFAULT_ROW_GROUP_SIZE;
132
226
  this.closed = false;
133
227
  this.userMetadata = {};
@@ -156,8 +250,8 @@ export class ParquetEncoder<T> {
156
250
  }
157
251
  Shred.shredRecord(this.schema, row, this.rowBuffer);
158
252
  if (this.rowBuffer.rowCount >= this.rowGroupSize) {
159
- // @ts-ignore
160
- this.rowBuffer = {};
253
+ await this.envelopeWriter.writeRowGroup(this.rowBuffer);
254
+ this.rowBuffer = this.schema.rowGroup();
161
255
  }
162
256
  }
163
257
 
@@ -175,8 +269,8 @@ export class ParquetEncoder<T> {
175
269
  this.closed = true;
176
270
 
177
271
  if (this.rowBuffer.rowCount > 0 || this.rowBuffer.rowCount >= this.rowGroupSize) {
178
- // @ts-ignore
179
- this.rowBuffer = {};
272
+ await this.envelopeWriter.writeRowGroup(this.rowBuffer);
273
+ this.rowBuffer = this.schema.rowGroup();
180
274
  }
181
275
 
182
276
  await this.envelopeWriter.writeFooter(this.userMetadata);
@@ -236,17 +330,32 @@ export class ParquetEnvelopeWriter {
236
330
  }
237
331
 
238
332
  public schema: ParquetSchema;
239
- public write: (buf: Buffer) => Promise<void>;
333
+ public write: (buf: Uint8Array) => Promise<void>;
240
334
  public close: () => Promise<void>;
241
335
  public offset: number;
242
336
  public rowCount: number;
243
337
  public rowGroups: RowGroup[];
244
338
  public pageSize: number;
245
339
  public useDataPageV2: boolean;
340
+ public dictionary: ParquetDictionaryPolicy;
341
+ public columnDictionaries: Record<string, ParquetDictionaryPolicy>;
342
+ public dictionaryPageSizeLimit: number;
343
+ public bloomFilter?: boolean | Record<string, boolean>;
344
+ public pageIndex?: boolean | Record<string, boolean>;
345
+ public writePageChecksums: boolean;
346
+ public writeSizeStatistics: boolean;
347
+ public writeStatistics?: boolean | Record<string, boolean>;
348
+ public sortingColumns?: readonly ParquetSortingColumnOption[];
349
+ /** Optional modular-encryption configuration for the footer. */
350
+ public encryption?: ParquetWriterEncryptionOptions;
351
+ /** Optional plaintext-footer signature configuration. */
352
+ public footerSignature?: ParquetWriterFooterSignatureOptions;
353
+ /** File identifier used for encrypted column-module AAD. */
354
+ public encryptionFileUnique?: Uint8Array;
246
355
 
247
356
  constructor(
248
357
  schema: ParquetSchema,
249
- writeFn: (buf: Buffer) => Promise<void>,
358
+ writeFn: (buf: Uint8Array) => Promise<void>,
250
359
  closeFn: () => Promise<void>,
251
360
  fileOffset: number,
252
361
  opts: ParquetEncoderOptions
@@ -259,9 +368,49 @@ export class ParquetEnvelopeWriter {
259
368
  this.rowGroups = [];
260
369
  this.pageSize = opts.pageSize || PARQUET_DEFAULT_PAGE_SIZE;
261
370
  this.useDataPageV2 = 'useDataPageV2' in opts ? Boolean(opts.useDataPageV2) : false;
371
+ this.dictionary = opts.dictionary ?? 'auto';
372
+ this.columnDictionaries = opts.columnDictionaries || {};
373
+ this.dictionaryPageSizeLimit = opts.dictionaryPageSizeLimit ?? 1024 * 1024;
374
+ this.bloomFilter = opts.bloomFilter;
375
+ this.pageIndex = opts.pageIndex;
376
+ this.writePageChecksums = Boolean(opts.writePageChecksums);
377
+ this.writeSizeStatistics = Boolean(opts.writeSizeStatistics);
378
+ this.writeStatistics = opts.writeStatistics;
379
+ this.sortingColumns = opts.sortingColumns;
380
+ const encryptionFileUnique = opts.encryption?.fileUnique
381
+ ? new Uint8Array(opts.encryption.fileUnique)
382
+ : undefined;
383
+ const footerSignatureFileUnique = opts.footerSignature?.fileUnique
384
+ ? new Uint8Array(opts.footerSignature.fileUnique)
385
+ : undefined;
386
+ if (
387
+ encryptionFileUnique &&
388
+ footerSignatureFileUnique &&
389
+ !encryptionFileUnique.every((value, index) => value === footerSignatureFileUnique[index])
390
+ ) {
391
+ throw new Error('Parquet encryption and footer signature file_unique values must match');
392
+ }
393
+ const sharedFileUnique =
394
+ encryptionFileUnique ??
395
+ footerSignatureFileUnique ??
396
+ (opts.encryption || opts.footerSignature ? createFileUnique() : undefined);
397
+ this.encryption = opts.encryption
398
+ ? {...opts.encryption, fileUnique: new Uint8Array(sharedFileUnique!)}
399
+ : undefined;
400
+ this.footerSignature = opts.footerSignature
401
+ ? {
402
+ ...opts.footerSignature,
403
+ keyMetadata: new Uint8Array(opts.footerSignature.keyMetadata),
404
+ aadPrefix: opts.footerSignature.aadPrefix
405
+ ? new Uint8Array(opts.footerSignature.aadPrefix)
406
+ : undefined,
407
+ fileUnique: new Uint8Array(sharedFileUnique!)
408
+ }
409
+ : undefined;
410
+ this.encryptionFileUnique = sharedFileUnique;
262
411
  }
263
412
 
264
- writeSection(buf: Buffer): Promise<void> {
413
+ writeSection(buf: Uint8Array): Promise<void> {
265
414
  this.offset += buf.length;
266
415
  return this.write(buf);
267
416
  }
@@ -270,7 +419,9 @@ export class ParquetEnvelopeWriter {
270
419
  * Encode the parquet file header
271
420
  */
272
421
  writeHeader(): Promise<void> {
273
- return this.writeSection(Buffer.from(PARQUET_MAGIC));
422
+ return this.writeSection(
423
+ this.encryption && !this.footerSignature ? PARQUET_MAGIC_ENCRYPTED_BYTES : PARQUET_MAGIC_BYTES
424
+ );
274
425
  }
275
426
 
276
427
  /**
@@ -281,7 +432,19 @@ export class ParquetEnvelopeWriter {
281
432
  const rgroup = await encodeRowGroup(this.schema, records, {
282
433
  baseOffset: this.offset,
283
434
  pageSize: this.pageSize,
284
- useDataPageV2: this.useDataPageV2
435
+ useDataPageV2: this.useDataPageV2,
436
+ dictionary: this.dictionary,
437
+ columnDictionaries: this.columnDictionaries,
438
+ dictionaryPageSizeLimit: this.dictionaryPageSizeLimit,
439
+ bloomFilter: this.bloomFilter,
440
+ pageIndex: this.pageIndex,
441
+ writePageChecksums: this.writePageChecksums,
442
+ writeSizeStatistics: this.writeSizeStatistics,
443
+ writeStatistics: this.writeStatistics,
444
+ sortingColumns: this.sortingColumns,
445
+ encryption: this.encryption,
446
+ encryptionFileUnique: this.encryptionFileUnique,
447
+ rowGroupOrdinal: this.rowGroups.length
285
448
  });
286
449
 
287
450
  this.rowCount += records.rowCount;
@@ -292,15 +455,30 @@ export class ParquetEnvelopeWriter {
292
455
  /**
293
456
  * Write the parquet file footer
294
457
  */
295
- writeFooter(userMetadata: Record<string, string>): Promise<void> {
458
+ async writeFooter(userMetadata: Record<string, string>): Promise<void> {
296
459
  if (!userMetadata) {
297
460
  // tslint:disable-next-line:no-parameter-reassignment
298
461
  userMetadata = {};
299
462
  }
300
463
 
301
- return this.writeSection(
302
- encodeFooter(this.schema, this.rowCount, this.rowGroups, userMetadata)
303
- );
464
+ if (this.footerSignature) {
465
+ await this.writeSection(
466
+ await encodeSignedFooter(
467
+ this.schema,
468
+ this.rowCount,
469
+ this.rowGroups,
470
+ userMetadata,
471
+ this.footerSignature
472
+ )
473
+ );
474
+ return;
475
+ }
476
+ const footer = encodeFooter(this.schema, this.rowCount, this.rowGroups, userMetadata);
477
+ if (!this.encryption) {
478
+ await this.writeSection(footer);
479
+ return;
480
+ }
481
+ await this.writeSection(await encodeEncryptedFooter(footer, this.encryption));
304
482
  }
305
483
 
306
484
  /**
@@ -369,36 +547,52 @@ function encodeValues(
369
547
  */
370
548
  async function encodeDataPage(
371
549
  column: ParquetField,
372
- data: ParquetColumnChunk
550
+ data: ParquetColumnChunk,
551
+ valueEncoding: ParquetCodec = column.encoding!,
552
+ valueBitWidth?: number,
553
+ writePageChecksums = false,
554
+ statistics?: Statistics,
555
+ int96AsTimestamp = false
373
556
  ): Promise<{
374
557
  header: PageHeader;
375
558
  headerSize: number;
376
- page: Buffer;
559
+ page: Uint8Array;
377
560
  }> {
378
561
  /* encode repetition and definition levels */
379
- let rLevelsBuf: Buffer = Buffer.alloc(0);
562
+ let rLevelsBuf: Uint8Array = new Uint8Array(0);
380
563
  if (column.rLevelMax > 0) {
381
- rLevelsBuf = encodeValues(PARQUET_RDLVL_TYPE, PARQUET_RDLVL_ENCODING, data.rlevels, {
382
- bitWidth: getBitWidth(column.rLevelMax)
383
- // disableEnvelope: false
384
- });
564
+ rLevelsBuf = encodeValues(
565
+ PARQUET_RDLVL_TYPE,
566
+ PARQUET_RDLVL_ENCODING,
567
+ data.rlevels as number[],
568
+ {
569
+ bitWidth: getBitWidth(column.rLevelMax)
570
+ // disableEnvelope: false
571
+ }
572
+ );
385
573
  }
386
574
 
387
- let dLevelsBuf: Buffer = Buffer.alloc(0);
575
+ let dLevelsBuf: Uint8Array = new Uint8Array(0);
388
576
  if (column.dLevelMax > 0) {
389
- dLevelsBuf = encodeValues(PARQUET_RDLVL_TYPE, PARQUET_RDLVL_ENCODING, data.dlevels, {
390
- bitWidth: getBitWidth(column.dLevelMax)
391
- // disableEnvelope: false
392
- });
577
+ dLevelsBuf = encodeValues(
578
+ PARQUET_RDLVL_TYPE,
579
+ PARQUET_RDLVL_ENCODING,
580
+ data.dlevels as number[],
581
+ {
582
+ bitWidth: getBitWidth(column.dLevelMax)
583
+ // disableEnvelope: false
584
+ }
585
+ );
393
586
  }
394
587
 
395
588
  /* encode values */
396
- const valuesBuf = encodeValues(column.primitiveType!, column.encoding!, data.values, {
589
+ const valuesBuf = encodeValues(column.primitiveType!, valueEncoding, data.values as any[], {
397
590
  typeLength: column.typeLength,
398
- bitWidth: column.typeLength
591
+ bitWidth: valueBitWidth ?? column.typeLength,
592
+ int96AsTimestamp
399
593
  });
400
594
 
401
- const dataBuf = Buffer.concat([rLevelsBuf, dLevelsBuf, valuesBuf] as Uint8Array[]);
595
+ const dataBuf = concatUint8Arrays([rLevelsBuf, dLevelsBuf, valuesBuf]);
402
596
 
403
597
  // compression = column.compression === 'UNCOMPRESSED' ? (compression || 'UNCOMPRESSED') : column.compression;
404
598
  const compressedBuf = await Compression.deflate(column.compression!, dataBuf);
@@ -408,17 +602,19 @@ async function encodeDataPage(
408
602
  type: PageType.DATA_PAGE,
409
603
  data_page_header: new DataPageHeader({
410
604
  num_values: data.count,
411
- encoding: Encoding[column.encoding!] as any,
605
+ encoding: Encoding[valueEncoding] as any,
412
606
  definition_level_encoding: Encoding[PARQUET_RDLVL_ENCODING], // [PARQUET_RDLVL_ENCODING],
413
- repetition_level_encoding: Encoding[PARQUET_RDLVL_ENCODING] // [PARQUET_RDLVL_ENCODING]
607
+ repetition_level_encoding: Encoding[PARQUET_RDLVL_ENCODING], // [PARQUET_RDLVL_ENCODING]
608
+ statistics
414
609
  }),
415
610
  uncompressed_page_size: dataBuf.length,
416
- compressed_page_size: compressedBuf.length
611
+ compressed_page_size: compressedBuf.length,
612
+ crc: writePageChecksums ? crc32(compressedBuf) : undefined
417
613
  });
418
614
 
419
615
  /* concat page header, repetition and definition levels and values */
420
616
  const headerBuf = serializeThrift(header);
421
- const page = Buffer.concat([headerBuf, compressedBuf] as Uint8Array[]);
617
+ const page = concatUint8Arrays([headerBuf, compressedBuf]);
422
618
 
423
619
  return {header, headerSize: headerBuf.length, page};
424
620
  }
@@ -429,36 +625,52 @@ async function encodeDataPage(
429
625
  async function encodeDataPageV2(
430
626
  column: ParquetField,
431
627
  data: ParquetColumnChunk,
432
- rowCount: number
628
+ rowCount: number,
629
+ valueEncoding: ParquetCodec = column.encoding!,
630
+ valueBitWidth?: number,
631
+ writePageChecksums = false,
632
+ statistics?: Statistics,
633
+ int96AsTimestamp = false
433
634
  ): Promise<{
434
635
  header: PageHeader;
435
636
  headerSize: number;
436
- page: Buffer;
637
+ page: Uint8Array;
437
638
  }> {
438
639
  /* encode values */
439
- const valuesBuf = encodeValues(column.primitiveType!, column.encoding!, data.values, {
640
+ const valuesBuf = encodeValues(column.primitiveType!, valueEncoding, data.values as any[], {
440
641
  typeLength: column.typeLength,
441
- bitWidth: column.typeLength
642
+ bitWidth: valueBitWidth ?? column.typeLength,
643
+ int96AsTimestamp
442
644
  });
443
645
 
444
646
  // compression = column.compression === 'UNCOMPRESSED' ? (compression || 'UNCOMPRESSED') : column.compression;
445
647
  const compressedBuf = await Compression.deflate(column.compression!, valuesBuf);
446
648
 
447
649
  /* encode repetition and definition levels */
448
- let rLevelsBuf: Buffer = Buffer.alloc(0);
650
+ let rLevelsBuf: Uint8Array = new Uint8Array(0);
449
651
  if (column.rLevelMax > 0) {
450
- rLevelsBuf = encodeValues(PARQUET_RDLVL_TYPE, PARQUET_RDLVL_ENCODING, data.rlevels, {
451
- bitWidth: getBitWidth(column.rLevelMax),
452
- disableEnvelope: true
453
- });
652
+ rLevelsBuf = encodeValues(
653
+ PARQUET_RDLVL_TYPE,
654
+ PARQUET_RDLVL_ENCODING,
655
+ data.rlevels as number[],
656
+ {
657
+ bitWidth: getBitWidth(column.rLevelMax),
658
+ disableEnvelope: true
659
+ }
660
+ );
454
661
  }
455
662
 
456
- let dLevelsBuf: Buffer = Buffer.alloc(0);
663
+ let dLevelsBuf: Uint8Array = new Uint8Array(0);
457
664
  if (column.dLevelMax > 0) {
458
- dLevelsBuf = encodeValues(PARQUET_RDLVL_TYPE, PARQUET_RDLVL_ENCODING, data.dlevels, {
459
- bitWidth: getBitWidth(column.dLevelMax),
460
- disableEnvelope: true
461
- });
665
+ dLevelsBuf = encodeValues(
666
+ PARQUET_RDLVL_TYPE,
667
+ PARQUET_RDLVL_ENCODING,
668
+ data.dlevels as number[],
669
+ {
670
+ bitWidth: getBitWidth(column.dLevelMax),
671
+ disableEnvelope: true
672
+ }
673
+ );
462
674
  }
463
675
 
464
676
  /* build page header */
@@ -468,21 +680,114 @@ async function encodeDataPageV2(
468
680
  num_values: data.count,
469
681
  num_nulls: data.count - data.values.length,
470
682
  num_rows: rowCount,
471
- encoding: Encoding[column.encoding!] as any,
683
+ encoding: Encoding[valueEncoding] as any,
472
684
  definition_levels_byte_length: dLevelsBuf.length,
473
685
  repetition_levels_byte_length: rLevelsBuf.length,
474
- is_compressed: column.compression !== 'UNCOMPRESSED'
686
+ is_compressed: column.compression !== 'UNCOMPRESSED',
687
+ statistics
475
688
  }),
476
689
  uncompressed_page_size: rLevelsBuf.length + dLevelsBuf.length + valuesBuf.length,
477
- compressed_page_size: rLevelsBuf.length + dLevelsBuf.length + compressedBuf.length
690
+ compressed_page_size: rLevelsBuf.length + dLevelsBuf.length + compressedBuf.length,
691
+ crc: writePageChecksums
692
+ ? crc32(concatUint8Arrays([rLevelsBuf, dLevelsBuf, compressedBuf]))
693
+ : undefined
478
694
  });
479
695
 
480
696
  /* concat page header, repetition and definition levels and values */
481
697
  const headerBuf = serializeThrift(header);
482
- const page = Buffer.concat([headerBuf, rLevelsBuf, dLevelsBuf, compressedBuf] as Uint8Array[]);
698
+ const page = concatUint8Arrays([headerBuf, rLevelsBuf, dLevelsBuf, compressedBuf]);
483
699
  return {header, headerSize: headerBuf.length, page};
484
700
  }
485
701
 
702
+ /** Encodes one chunk-wide PLAIN dictionary page. */
703
+ async function encodeDictionaryPage(
704
+ column: ParquetField,
705
+ dictionaryValues: unknown[],
706
+ writePageChecksums = false,
707
+ int96AsTimestamp = false
708
+ ): Promise<{header: PageHeader; headerSize: number; page: Uint8Array}> {
709
+ const valuesBuffer = encodeValues(column.primitiveType!, 'PLAIN', dictionaryValues as any[], {
710
+ typeLength: column.typeLength,
711
+ int96AsTimestamp
712
+ });
713
+ const compressedBuffer = await Compression.deflate(column.compression!, valuesBuffer);
714
+ const header = new PageHeader({
715
+ type: PageType.DICTIONARY_PAGE,
716
+ dictionary_page_header: new DictionaryPageHeader({
717
+ num_values: dictionaryValues.length,
718
+ encoding: Encoding.PLAIN
719
+ }),
720
+ uncompressed_page_size: valuesBuffer.length,
721
+ compressed_page_size: compressedBuffer.length,
722
+ crc: writePageChecksums ? crc32(compressedBuffer) : undefined
723
+ });
724
+ const headerBuffer = serializeThrift(header);
725
+ return {
726
+ header,
727
+ headerSize: headerBuffer.length,
728
+ page: concatUint8Arrays([headerBuffer, compressedBuffer])
729
+ };
730
+ }
731
+
732
+ /** Computes native Parquet geospatial statistics for a GEOMETRY or GEOGRAPHY column chunk. */
733
+ function createGeospatialStatistics(
734
+ column: ParquetField,
735
+ values: ParquetColumnChunk['values']
736
+ ): GeospatialStatistics | undefined {
737
+ const logicalType = column.logicalType?.type || column.originalType;
738
+ if (logicalType !== 'GEOMETRY' && logicalType !== 'GEOGRAPHY') return undefined;
739
+ const geometryTypes = new Set<number>();
740
+ let bbox: WKBGeometryBoundingBox | undefined;
741
+ // Vertex bounds are not conservative for GEOGRAPHY: non-linear edge algorithms can reach
742
+ // extrema between vertices. Until edge-aware geodesic bounds are available, omit the bbox while
743
+ // retaining the useful geometry-type statistics.
744
+ const canWriteBoundingBox = logicalType === 'GEOMETRY';
745
+ for (const value of values) {
746
+ if (!(value instanceof ArrayBuffer) && !ArrayBuffer.isView(value)) {
747
+ throw new Error(`${logicalType} columns must contain WKB binary values`);
748
+ }
749
+ const statistics = getWKBGeometryStatistics(value);
750
+ geometryTypes.add(statistics.geometryType);
751
+ if (canWriteBoundingBox) {
752
+ bbox = mergeGeometryBoundingBoxes(bbox, statistics.bbox);
753
+ }
754
+ }
755
+ return new GeospatialStatistics({
756
+ bbox: bbox ? new BoundingBox(bbox) : undefined,
757
+ geospatial_types: [...geometryTypes].sort((left, right) => left - right)
758
+ });
759
+ }
760
+
761
+ /** Merges finite per-dimension bounds without inventing absent Z or M dimensions. */
762
+ function mergeGeometryBoundingBoxes(
763
+ target: WKBGeometryBoundingBox | undefined,
764
+ source: WKBGeometryBoundingBox | undefined
765
+ ): WKBGeometryBoundingBox | undefined {
766
+ if (!source) return target;
767
+ if (!target) return {...source};
768
+ target.xmin = Math.min(target.xmin, source.xmin);
769
+ target.xmax = Math.max(target.xmax, source.xmax);
770
+ target.ymin = Math.min(target.ymin, source.ymin);
771
+ target.ymax = Math.max(target.ymax, source.ymax);
772
+ mergeOptionalBounds(target, source, 'zmin', Math.min);
773
+ mergeOptionalBounds(target, source, 'zmax', Math.max);
774
+ mergeOptionalBounds(target, source, 'mmin', Math.min);
775
+ mergeOptionalBounds(target, source, 'mmax', Math.max);
776
+ return target;
777
+ }
778
+
779
+ /** Merges one optional bound while preserving a dimension absent from every value. */
780
+ function mergeOptionalBounds(
781
+ target: WKBGeometryBoundingBox,
782
+ source: WKBGeometryBoundingBox,
783
+ key: 'zmin' | 'zmax' | 'mmin' | 'mmax',
784
+ merge: (left: number, right: number) => number
785
+ ): void {
786
+ const sourceValue = source[key];
787
+ if (sourceValue === undefined) return;
788
+ target[key] = target[key] === undefined ? sourceValue : merge(target[key]!, sourceValue);
789
+ }
790
+
486
791
  /**
487
792
  * Encode an array of values into a parquet column chunk
488
793
  */
@@ -490,54 +795,746 @@ async function encodeColumnChunk(
490
795
  column: ParquetField,
491
796
  buffer: ParquetRowGroup,
492
797
  offset: number,
493
- opts: ParquetEncoderOptions
798
+ opts: ParquetEncoderOptions,
799
+ rowGroupOrdinal: number,
800
+ columnOrdinal: number
494
801
  ): Promise<{
495
- body: Buffer;
802
+ body: Uint8Array;
496
803
  metadata: ColumnMetaData;
804
+ encryptedColumnMetadata?: Uint8Array;
805
+ cryptoMetadata?: ColumnCryptoMetaData;
497
806
  metadataOffset: number;
807
+ offsetIndexOffset?: number;
808
+ offsetIndexLength?: number;
809
+ columnIndexOffset?: number;
810
+ columnIndexLength?: number;
498
811
  }> {
499
812
  const data = buffer.columnData[column.path.join()];
500
813
  const baseOffset = (opts.baseOffset || 0) + offset;
501
814
  /* encode data page(s) */
502
- // const pages: Buffer[] = [];
503
- let pageBuf: Buffer;
815
+ const pageRecords: Array<{
816
+ page: Uint8Array;
817
+ headerSize: number;
818
+ dictionary: boolean;
819
+ }> = [];
820
+ const pageLocations: PageLocation[] = [];
504
821
  // tslint:disable-next-line:variable-name
505
822
  let total_uncompressed_size = 0;
506
823
  // tslint:disable-next-line:variable-name
507
824
  let total_compressed_size = 0;
508
- {
825
+ const plannedPages = planColumnPages(
826
+ column,
827
+ data,
828
+ buffer.rowCount,
829
+ opts.pageSize || PARQUET_DEFAULT_PAGE_SIZE
830
+ );
831
+ const dictionaryPolicy =
832
+ opts.columnDictionaries?.[column.path[0]] ?? opts.dictionary ?? ('auto' as const);
833
+ const dictionaryPlan = planDictionary(
834
+ column,
835
+ Array.from(data.values),
836
+ dictionaryPolicy,
837
+ opts.dictionaryPageSizeLimit ?? 1024 * 1024
838
+ );
839
+ if (dictionaryPlan) {
840
+ const result = await encodeDictionaryPage(
841
+ column,
842
+ dictionaryPlan.values,
843
+ opts.writePageChecksums,
844
+ opts.int96AsTimestamp
845
+ );
846
+ pageRecords.push({page: result.page, headerSize: result.headerSize, dictionary: true});
847
+ total_uncompressed_size += result.header.uncompressed_page_size + result.headerSize;
848
+ total_compressed_size += result.header.compressed_page_size + result.headerSize;
849
+ }
850
+
851
+ const valueEncoding: ParquetCodec = dictionaryPlan
852
+ ? column.encoding === 'PLAIN_DICTIONARY'
853
+ ? 'PLAIN_DICTIONARY'
854
+ : 'RLE_DICTIONARY'
855
+ : column.encoding === 'PLAIN_DICTIONARY' || column.encoding === 'RLE_DICTIONARY'
856
+ ? 'PLAIN'
857
+ : column.encoding!;
858
+ let dictionaryIndexOffset = 0;
859
+ for (const plannedPage of plannedPages) {
860
+ const pageData = dictionaryPlan
861
+ ? {
862
+ ...plannedPage.data,
863
+ values: dictionaryPlan.indices.slice(
864
+ dictionaryIndexOffset,
865
+ dictionaryIndexOffset + plannedPage.data.values.length
866
+ )
867
+ }
868
+ : plannedPage.data;
869
+ dictionaryIndexOffset += plannedPage.data.values.length;
509
870
  const result = opts.useDataPageV2
510
- ? await encodeDataPageV2(column, data, buffer.rowCount)
511
- : await encodeDataPage(column, data);
512
- // pages.push(result.page);
513
- pageBuf = result.page;
871
+ ? await encodeDataPageV2(
872
+ column,
873
+ pageData,
874
+ plannedPage.rowCount,
875
+ valueEncoding,
876
+ dictionaryPlan?.bitWidth,
877
+ opts.writePageChecksums,
878
+ isStatisticsEnabled(opts.writeStatistics, column)
879
+ ? createColumnStatistics(column, plannedPage.data)
880
+ : undefined,
881
+ opts.int96AsTimestamp
882
+ )
883
+ : await encodeDataPage(
884
+ column,
885
+ pageData,
886
+ valueEncoding,
887
+ dictionaryPlan?.bitWidth,
888
+ opts.writePageChecksums,
889
+ isStatisticsEnabled(opts.writeStatistics, column)
890
+ ? createColumnStatistics(column, plannedPage.data)
891
+ : undefined,
892
+ opts.int96AsTimestamp
893
+ );
894
+ pageRecords.push({page: result.page, headerSize: result.headerSize, dictionary: false});
514
895
  total_uncompressed_size += result.header.uncompressed_page_size + result.headerSize;
515
896
  total_compressed_size += result.header.compressed_page_size + result.headerSize;
516
897
  }
517
898
 
518
- // const pagesBuf = Buffer.concat(pages);
899
+ const encryption = opts.encryption;
900
+ const encryptedColumn = Boolean(encryption && shouldEncryptColumn(encryption, column));
901
+ const columnKeyMetadata = encryption ? getColumnKeyMetadata(encryption, column) : undefined;
902
+ const pages = encryptedColumn
903
+ ? await encryptParquetColumnPages(
904
+ pageRecords,
905
+ encryption!,
906
+ opts.encryptionFileUnique!,
907
+ opts.rowGroupOrdinal ?? 0,
908
+ columnOrdinal,
909
+ columnKeyMetadata
910
+ )
911
+ : pageRecords.map(record => record.page);
912
+ let pageOffset = 0;
913
+ let dataPageIndex = 0;
914
+ let firstRowIndex = 0;
915
+ for (const [pageIndex, page] of pages.entries()) {
916
+ if (!pageRecords[pageIndex].dictionary) {
917
+ pageLocations.push(
918
+ new PageLocation({
919
+ offset: baseOffset + pageOffset,
920
+ compressed_page_size: page.length,
921
+ first_row_index: firstRowIndex
922
+ })
923
+ );
924
+ firstRowIndex += plannedPages[dataPageIndex++].rowCount;
925
+ }
926
+ pageOffset += page.length;
927
+ }
928
+ total_compressed_size = pages.reduce((total, page) => total + page.length, 0);
929
+ const pagesBuf = concatUint8Arrays(pages);
519
930
  // const compression = column.compression === 'UNCOMPRESSED' ? (opts.compression || 'UNCOMPRESSED') : column.compression;
931
+ const bloomFilterEnabled =
932
+ opts.bloomFilter === true || opts.bloomFilter?.[column.path[0]] === true;
933
+ const bloomFilter = bloomFilterEnabled ? createColumnBloomFilter(column, data.values) : undefined;
934
+ const encodedBloomFilter =
935
+ bloomFilter && encryptedColumn
936
+ ? await encryptParquetBloomFilter(
937
+ bloomFilter,
938
+ encryption!,
939
+ opts.encryptionFileUnique!,
940
+ opts.rowGroupOrdinal ?? 0,
941
+ columnOrdinal,
942
+ columnKeyMetadata
943
+ )
944
+ : bloomFilter;
945
+ const pageIndexEnabled = opts.pageIndex === true || opts.pageIndex?.[column.path[0]] === true;
946
+ const pageIndexes = pageIndexEnabled
947
+ ? createColumnPageIndexes(column, plannedPages, pageLocations, opts.int96AsTimestamp)
948
+ : undefined;
949
+ const encodedPageIndexes =
950
+ pageIndexes && encryptedColumn
951
+ ? await encryptParquetPageIndexes(
952
+ pageIndexes,
953
+ encryption!,
954
+ opts.encryptionFileUnique!,
955
+ opts.rowGroupOrdinal ?? 0,
956
+ columnOrdinal,
957
+ columnKeyMetadata
958
+ )
959
+ : pageIndexes;
520
960
 
521
961
  /* prepare metadata header */
522
962
  const metadata = new ColumnMetaData({
523
963
  path_in_schema: column.path,
524
- num_values: data.count,
525
- data_page_offset: baseOffset,
964
+ num_values: int64(data.count),
965
+ data_page_offset: int64(baseOffset + (dictionaryPlan ? pages[0].length : 0)),
966
+ dictionary_page_offset: dictionaryPlan ? int64(baseOffset) : undefined,
526
967
  encodings: [],
527
- total_uncompressed_size, // : pagesBuf.length,
528
- total_compressed_size,
968
+ encoding_stats: [
969
+ ...(dictionaryPlan
970
+ ? [
971
+ new PageEncodingStats({
972
+ page_type: PageType.DICTIONARY_PAGE,
973
+ encoding: Encoding.PLAIN,
974
+ count: 1
975
+ })
976
+ ]
977
+ : []),
978
+ new PageEncodingStats({
979
+ page_type: opts.useDataPageV2 ? PageType.DATA_PAGE_V2 : PageType.DATA_PAGE,
980
+ encoding: Encoding[valueEncoding],
981
+ count: plannedPages.length
982
+ })
983
+ ],
984
+ total_uncompressed_size: int64(total_uncompressed_size), // : pagesBuf.length,
985
+ total_compressed_size: int64(total_compressed_size),
529
986
  type: Type[column.primitiveType!],
530
- codec: CompressionCodec[column.compression!]
987
+ codec: CompressionCodec[column.compression!],
988
+ bloom_filter_offset: encodedBloomFilter ? int64(baseOffset + pagesBuf.length) : undefined,
989
+ bloom_filter_length: encodedBloomFilter?.length,
990
+ geospatial_statistics: createGeospatialStatistics(column, data.values),
991
+ size_statistics: opts.writeSizeStatistics ? createSizeStatistics(column, data) : undefined,
992
+ statistics: isStatisticsEnabled(opts.writeStatistics, column)
993
+ ? createColumnStatistics(column, data)
994
+ : undefined
531
995
  });
532
996
 
533
997
  /* list encodings */
534
998
  metadata.encodings.push(Encoding[PARQUET_RDLVL_ENCODING]);
535
- metadata.encodings.push(Encoding[column.encoding!]);
999
+ if (dictionaryPlan) metadata.encodings.push(Encoding.PLAIN);
1000
+ metadata.encodings.push(Encoding[valueEncoding]);
536
1001
 
537
1002
  /* concat metadata header and data pages */
538
- const metadataOffset = baseOffset + pageBuf.length;
539
- const body = Buffer.concat([pageBuf, serializeThrift(metadata)] as Uint8Array[]);
540
- return {body, metadata, metadataOffset};
1003
+ const metadataEncoded = serializeThrift(metadata);
1004
+ const metadataSection = encryptedColumn ? undefined : metadataEncoded;
1005
+ const metadataOffset = baseOffset + pagesBuf.length + (encodedBloomFilter?.length || 0);
1006
+ const offsetIndexOffset = encodedPageIndexes
1007
+ ? metadataOffset + (metadataSection?.length || 0)
1008
+ : undefined;
1009
+ const columnIndexOffset = encodedPageIndexes?.columnIndex
1010
+ ? metadataOffset + (metadataSection?.length || 0) + encodedPageIndexes.offsetIndex.length
1011
+ : undefined;
1012
+ const body = concatUint8Arrays([
1013
+ pagesBuf,
1014
+ ...(encodedBloomFilter ? [encodedBloomFilter] : []),
1015
+ ...(metadataSection ? [metadataSection] : []),
1016
+ ...(encodedPageIndexes
1017
+ ? [
1018
+ encodedPageIndexes.offsetIndex,
1019
+ ...(encodedPageIndexes.columnIndex ? [encodedPageIndexes.columnIndex] : [])
1020
+ ]
1021
+ : [])
1022
+ ]);
1023
+ return {
1024
+ body,
1025
+ metadata,
1026
+ metadataOffset,
1027
+ offsetIndexOffset,
1028
+ offsetIndexLength: encodedPageIndexes?.offsetIndex.length,
1029
+ columnIndexOffset,
1030
+ columnIndexLength: encodedPageIndexes?.columnIndex?.length,
1031
+ ...(encryptedColumn
1032
+ ? {
1033
+ encryptedColumnMetadata: await encryptParquetModule(metadataEncoded, {
1034
+ algorithm: encryption!.algorithm ?? 'AES_GCM_V1',
1035
+ aad: createParquetModuleAad(
1036
+ encryption!.aadPrefix,
1037
+ opts.encryptionFileUnique!,
1038
+ 'column-metadata',
1039
+ opts.rowGroupOrdinal ?? 0,
1040
+ columnOrdinal
1041
+ ),
1042
+ keyMetadata: columnKeyMetadata ?? encryption!.keyMetadata,
1043
+ keyRetriever: encryption!.keyRetriever
1044
+ }),
1045
+ cryptoMetadata: new ColumnCryptoMetaData({
1046
+ ...(columnKeyMetadata
1047
+ ? {
1048
+ ENCRYPTION_WITH_COLUMN_KEY: new EncryptionWithColumnKey({
1049
+ path_in_schema: [...column.path],
1050
+ key_metadata: new Uint8Array(columnKeyMetadata)
1051
+ })
1052
+ }
1053
+ : {ENCRYPTION_WITH_FOOTER_KEY: new EncryptionWithFooterKey()})
1054
+ })
1055
+ }
1056
+ : {})
1057
+ };
1058
+ }
1059
+
1060
+ /** Returns whether a writer column should use modular encryption. */
1061
+ function shouldEncryptColumn(
1062
+ options: ParquetWriterEncryptionOptions,
1063
+ column: ParquetField
1064
+ ): boolean {
1065
+ return Boolean(
1066
+ options.columnKeyMetadata?.[column.path[0]] ||
1067
+ options.encryptColumns === true ||
1068
+ options.encryptColumns?.[column.path[0]] === true
1069
+ );
1070
+ }
1071
+
1072
+ /** Returns a defensive copy of the key metadata assigned to one top-level column. */
1073
+ function getColumnKeyMetadata(
1074
+ options: ParquetWriterEncryptionOptions,
1075
+ column: ParquetField
1076
+ ): Uint8Array | undefined {
1077
+ const keyMetadata = options.columnKeyMetadata?.[column.path[0]];
1078
+ return keyMetadata ? new Uint8Array(keyMetadata) : undefined;
1079
+ }
1080
+
1081
+ /** Adds the four-byte module length required by encrypted page and Bloom-filter modules. */
1082
+ function prefixEncryptedModule(module: Uint8Array): Uint8Array {
1083
+ const prefixed = new Uint8Array(module.byteLength + 4);
1084
+ writeUInt32LE(prefixed, module.byteLength, 0);
1085
+ prefixed.set(module, 4);
1086
+ return prefixed;
1087
+ }
1088
+
1089
+ /** Encrypts one column's dictionary and data pages using the Parquet module AAD rules. */
1090
+ async function encryptParquetColumnPages(
1091
+ pageRecords: readonly {page: Uint8Array; headerSize: number; dictionary: boolean}[],
1092
+ options: ParquetWriterEncryptionOptions,
1093
+ fileUnique: Uint8Array,
1094
+ rowGroupOrdinal: number,
1095
+ columnOrdinal: number,
1096
+ keyMetadata?: Uint8Array
1097
+ ): Promise<Uint8Array[]> {
1098
+ const encryptedPages: Uint8Array[] = [];
1099
+ let dataPageOrdinal = 0;
1100
+ const algorithm = options.algorithm ?? 'AES_GCM_V1';
1101
+ for (const pageRecord of pageRecords) {
1102
+ const moduleType = pageRecord.dictionary ? 'dictionary-page' : 'data-page';
1103
+ const headerModuleType = pageRecord.dictionary ? 'dictionary-page-header' : 'data-page-header';
1104
+ const header = await encryptParquetModule(pageRecord.page.subarray(0, pageRecord.headerSize), {
1105
+ algorithm,
1106
+ aad: createParquetModuleAad(
1107
+ options.aadPrefix,
1108
+ fileUnique,
1109
+ headerModuleType,
1110
+ rowGroupOrdinal,
1111
+ columnOrdinal,
1112
+ pageRecord.dictionary ? undefined : dataPageOrdinal
1113
+ ),
1114
+ keyMetadata: keyMetadata ?? options.keyMetadata,
1115
+ keyRetriever: options.keyRetriever
1116
+ });
1117
+ const body = await encryptParquetModule(pageRecord.page.subarray(pageRecord.headerSize), {
1118
+ algorithm,
1119
+ aad: createParquetModuleAad(
1120
+ options.aadPrefix,
1121
+ fileUnique,
1122
+ moduleType,
1123
+ rowGroupOrdinal,
1124
+ columnOrdinal,
1125
+ pageRecord.dictionary ? undefined : dataPageOrdinal
1126
+ ),
1127
+ keyMetadata: keyMetadata ?? options.keyMetadata,
1128
+ keyRetriever: options.keyRetriever,
1129
+ page: true
1130
+ });
1131
+ encryptedPages.push(
1132
+ concatUint8Arrays([prefixEncryptedModule(header), prefixEncryptedModule(body)])
1133
+ );
1134
+ if (!pageRecord.dictionary) dataPageOrdinal++;
1135
+ }
1136
+ return encryptedPages;
1137
+ }
1138
+
1139
+ /** Encrypts a split-block Bloom filter header and bitset as independent Parquet modules. */
1140
+ async function encryptParquetBloomFilter(
1141
+ bloomFilter: Uint8Array,
1142
+ options: ParquetWriterEncryptionOptions,
1143
+ fileUnique: Uint8Array,
1144
+ rowGroupOrdinal: number,
1145
+ columnOrdinal: number,
1146
+ keyMetadata?: Uint8Array
1147
+ ): Promise<Uint8Array> {
1148
+ const parsed = decodeParquetSplitBlockBloomFilter(bloomFilter);
1149
+ const algorithm = options.algorithm ?? 'AES_GCM_V1';
1150
+ const keyOptions = {
1151
+ algorithm,
1152
+ keyMetadata: keyMetadata ?? options.keyMetadata,
1153
+ keyRetriever: options.keyRetriever
1154
+ };
1155
+ const header = await encryptParquetModule(bloomFilter.subarray(0, parsed.headerByteLength), {
1156
+ ...keyOptions,
1157
+ aad: createParquetModuleAad(
1158
+ options.aadPrefix,
1159
+ fileUnique,
1160
+ 'bloom-filter-header',
1161
+ rowGroupOrdinal,
1162
+ columnOrdinal
1163
+ )
1164
+ });
1165
+ const bitset = await encryptParquetModule(parsed.bitset, {
1166
+ ...keyOptions,
1167
+ aad: createParquetModuleAad(
1168
+ options.aadPrefix,
1169
+ fileUnique,
1170
+ 'bloom-filter-bitset',
1171
+ rowGroupOrdinal,
1172
+ columnOrdinal
1173
+ )
1174
+ });
1175
+ return concatUint8Arrays([prefixEncryptedModule(header), prefixEncryptedModule(bitset)]);
1176
+ }
1177
+
1178
+ /** Encrypts column and offset indexes independently while preserving their footer offsets. */
1179
+ async function encryptParquetPageIndexes(
1180
+ pageIndexes: {offsetIndex: Uint8Array; columnIndex?: Uint8Array},
1181
+ options: ParquetWriterEncryptionOptions,
1182
+ fileUnique: Uint8Array,
1183
+ rowGroupOrdinal: number,
1184
+ columnOrdinal: number,
1185
+ keyMetadata?: Uint8Array
1186
+ ): Promise<{offsetIndex: Uint8Array; columnIndex?: Uint8Array}> {
1187
+ const algorithm = options.algorithm ?? 'AES_GCM_V1';
1188
+ const encryptIndex = (bytes: Uint8Array, module: 'offset-index' | 'column-index') =>
1189
+ encryptParquetModule(bytes, {
1190
+ algorithm,
1191
+ aad: createParquetModuleAad(
1192
+ options.aadPrefix,
1193
+ fileUnique,
1194
+ module,
1195
+ rowGroupOrdinal,
1196
+ columnOrdinal
1197
+ ),
1198
+ keyMetadata: keyMetadata ?? options.keyMetadata,
1199
+ keyRetriever: options.keyRetriever
1200
+ });
1201
+ return {
1202
+ offsetIndex: await encryptIndex(pageIndexes.offsetIndex, 'offset-index'),
1203
+ columnIndex: pageIndexes.columnIndex
1204
+ ? await encryptIndex(pageIndexes.columnIndex, 'column-index')
1205
+ : undefined
1206
+ };
1207
+ }
1208
+
1209
+ /** Builds page indexes for primitive leaves, including nested and repeated leaves. */
1210
+ function createColumnPageIndexes(
1211
+ column: ParquetField,
1212
+ plannedPages: ReturnType<typeof planColumnPages>,
1213
+ pageLocations: PageLocation[],
1214
+ int96AsTimestamp = false
1215
+ ): {offsetIndex: Uint8Array; columnIndex?: Uint8Array} | undefined {
1216
+ if (
1217
+ !column.primitiveType ||
1218
+ !isPageIndexPhysicalType(column.primitiveType) ||
1219
+ plannedPages.length !== pageLocations.length ||
1220
+ plannedPages.length === 0
1221
+ ) {
1222
+ return undefined;
1223
+ }
1224
+
1225
+ const offsetIndex = serializeThrift(new OffsetIndex({page_locations: pageLocations}));
1226
+ if (!supportsStatisticsSortOrder(column)) {
1227
+ return {offsetIndex};
1228
+ }
1229
+
1230
+ const nullPages: boolean[] = [];
1231
+ const minValues: Uint8Array[] = [];
1232
+ const maxValues: Uint8Array[] = [];
1233
+ // A repeated leaf's definition-level count is not a row-level null count.
1234
+ // Omit null_counts for those leaves so readers do not interpret entry counts
1235
+ // as null counts while still benefiting from min/max page pruning.
1236
+ const nullCounts: number[] | undefined = column.rLevelMax === 0 ? [] : undefined;
1237
+ for (const plannedPage of plannedPages) {
1238
+ const values = plannedPage.data.values;
1239
+ const nullCount = plannedPage.data.count - values.length;
1240
+ nullCounts?.push(nullCount);
1241
+ if (values.length === 0) {
1242
+ nullPages.push(true);
1243
+ minValues.push(new Uint8Array(0));
1244
+ maxValues.push(new Uint8Array(0));
1245
+ continue;
1246
+ }
1247
+ const bounds = getPageBounds(
1248
+ values,
1249
+ column.primitiveType!,
1250
+ column.typeLength,
1251
+ int96AsTimestamp
1252
+ );
1253
+ if (!bounds) return undefined;
1254
+ nullPages.push(false);
1255
+ minValues.push(bounds.min);
1256
+ maxValues.push(bounds.max);
1257
+ }
1258
+
1259
+ return {
1260
+ offsetIndex,
1261
+ columnIndex: serializeThrift(
1262
+ new ColumnIndex({
1263
+ null_pages: nullPages,
1264
+ min_values: minValues,
1265
+ max_values: maxValues,
1266
+ boundary_order: BoundaryOrder.UNORDERED,
1267
+ null_counts: nullCounts
1268
+ })
1269
+ )
1270
+ };
1271
+ }
1272
+
1273
+ function isPageIndexPhysicalType(
1274
+ physicalType: ParquetField['primitiveType']
1275
+ ): physicalType is PageIndexPhysicalType {
1276
+ return (
1277
+ physicalType === 'BOOLEAN' ||
1278
+ physicalType === 'INT32' ||
1279
+ physicalType === 'INT64' ||
1280
+ physicalType === 'INT96' ||
1281
+ physicalType === 'FLOAT' ||
1282
+ physicalType === 'DOUBLE' ||
1283
+ physicalType === 'BYTE_ARRAY' ||
1284
+ physicalType === 'FIXED_LEN_BYTE_ARRAY'
1285
+ );
1286
+ }
1287
+
1288
+ type PageIndexPhysicalType =
1289
+ | 'BOOLEAN'
1290
+ | 'INT32'
1291
+ | 'INT64'
1292
+ | 'INT96'
1293
+ | 'FLOAT'
1294
+ | 'DOUBLE'
1295
+ | 'BYTE_ARRAY'
1296
+ | 'FIXED_LEN_BYTE_ARRAY';
1297
+
1298
+ function getPageBounds(
1299
+ values: ParquetColumnChunk['values'],
1300
+ physicalType: PageIndexPhysicalType,
1301
+ typeLength?: number,
1302
+ int96AsTimestamp = false
1303
+ ): {min: Uint8Array; max: Uint8Array} | undefined {
1304
+ if (physicalType === 'INT96' && !int96AsTimestamp) return undefined;
1305
+ const normalizedValues = Array.from(values as Iterable<unknown>, value =>
1306
+ normalizePageIndexValue(value, physicalType)
1307
+ );
1308
+ if (normalizedValues.some(value => value === undefined)) return undefined;
1309
+ const comparableValues = normalizedValues as Array<
1310
+ boolean | number | bigint | string | Uint8Array
1311
+ >;
1312
+ let min = comparableValues[0];
1313
+ let max = comparableValues[0];
1314
+ for (const value of comparableValues.slice(1)) {
1315
+ if (comparePageIndexValues(value, min, physicalType) < 0) min = value;
1316
+ if (comparePageIndexValues(value, max, physicalType) > 0) max = value;
1317
+ }
1318
+ try {
1319
+ return {
1320
+ min: encodePageIndexValue(min, physicalType, typeLength, int96AsTimestamp),
1321
+ max: encodePageIndexValue(max, physicalType, typeLength, int96AsTimestamp)
1322
+ };
1323
+ } catch {
1324
+ return undefined;
1325
+ }
1326
+ }
1327
+
1328
+ function encodePageIndexValue(
1329
+ value: boolean | number | bigint | string | Uint8Array,
1330
+ physicalType: PageIndexPhysicalType,
1331
+ typeLength: number | undefined,
1332
+ int96AsTimestamp: boolean
1333
+ ): Uint8Array {
1334
+ if (physicalType === 'INT96') {
1335
+ if (!int96AsTimestamp) {
1336
+ throw new Error('INT96 page indexes require timestamp mode');
1337
+ }
1338
+ return PARQUET_CODECS.PLAIN.encodeValues('INT96', [value], {int96AsTimestamp});
1339
+ }
1340
+ return encodeParquetBloomFilterValue(value, physicalType, typeLength);
1341
+ }
1342
+
1343
+ function normalizePageIndexValue(
1344
+ value: unknown,
1345
+ physicalType: PageIndexPhysicalType
1346
+ ): boolean | number | bigint | string | Uint8Array | undefined {
1347
+ if (physicalType === 'BYTE_ARRAY' || physicalType === 'FIXED_LEN_BYTE_ARRAY') {
1348
+ if (typeof value === 'string') return new TextEncoder().encode(value);
1349
+ if (value instanceof ArrayBuffer) return new Uint8Array(value);
1350
+ if (ArrayBuffer.isView(value)) {
1351
+ return new Uint8Array(value.buffer, value.byteOffset, value.byteLength).slice();
1352
+ }
1353
+ return undefined;
1354
+ }
1355
+ if (
1356
+ typeof value === 'boolean' ||
1357
+ typeof value === 'number' ||
1358
+ typeof value === 'bigint' ||
1359
+ typeof value === 'string'
1360
+ ) {
1361
+ return value;
1362
+ }
1363
+ return undefined;
1364
+ }
1365
+
1366
+ function comparePageIndexValues(
1367
+ left: boolean | number | bigint | string | Uint8Array,
1368
+ right: boolean | number | bigint | string | Uint8Array,
1369
+ physicalType: PageIndexPhysicalType
1370
+ ): number {
1371
+ if (physicalType === 'BYTE_ARRAY' || physicalType === 'FIXED_LEN_BYTE_ARRAY') {
1372
+ const leftBytes = typeof left === 'string' ? new TextEncoder().encode(left) : left;
1373
+ const rightBytes = typeof right === 'string' ? new TextEncoder().encode(right) : right;
1374
+ if (!(leftBytes instanceof Uint8Array) || !(rightBytes instanceof Uint8Array)) return 0;
1375
+ const length = Math.min(leftBytes.length, rightBytes.length);
1376
+ for (let index = 0; index < length; index++) {
1377
+ if (leftBytes[index] !== rightBytes[index]) {
1378
+ return leftBytes[index] < rightBytes[index] ? -1 : 1;
1379
+ }
1380
+ }
1381
+ return leftBytes.length - rightBytes.length;
1382
+ }
1383
+ if (typeof left === 'boolean' && typeof right === 'boolean') return Number(left) - Number(right);
1384
+ if (physicalType !== 'INT64' && physicalType !== 'INT96') {
1385
+ const leftNumber = Number(left);
1386
+ const rightNumber = Number(right);
1387
+ return leftNumber < rightNumber ? -1 : leftNumber > rightNumber ? 1 : 0;
1388
+ }
1389
+ try {
1390
+ const leftNumber = BigInt(left as number | bigint | string);
1391
+ const rightNumber = BigInt(right as number | bigint | string);
1392
+ return leftNumber < rightNumber ? -1 : leftNumber > rightNumber ? 1 : 0;
1393
+ } catch {
1394
+ const leftNumber = Number(left);
1395
+ const rightNumber = Number(right);
1396
+ return leftNumber < rightNumber ? -1 : leftNumber > rightNumber ? 1 : 0;
1397
+ }
1398
+ }
1399
+
1400
+ /** Builds a Bloom filter for writer-supported physical scalar columns. */
1401
+ function createColumnBloomFilter(
1402
+ column: ParquetField,
1403
+ values: ParquetColumnChunk['values']
1404
+ ): Uint8Array | undefined {
1405
+ const physicalType = column.primitiveType;
1406
+ if (
1407
+ physicalType !== 'BOOLEAN' &&
1408
+ physicalType !== 'INT32' &&
1409
+ physicalType !== 'INT64' &&
1410
+ physicalType !== 'FLOAT' &&
1411
+ physicalType !== 'DOUBLE' &&
1412
+ physicalType !== 'BYTE_ARRAY' &&
1413
+ physicalType !== 'FIXED_LEN_BYTE_ARRAY'
1414
+ ) {
1415
+ return undefined;
1416
+ }
1417
+ const bloomValues = values.map(value => {
1418
+ if (value instanceof ArrayBuffer) return new Uint8Array(value);
1419
+ if (ArrayBuffer.isView(value)) {
1420
+ return new Uint8Array(value.buffer, value.byteOffset, value.byteLength).slice();
1421
+ }
1422
+ return value;
1423
+ });
1424
+ return encodeParquetSplitBlockBloomFilter(
1425
+ bloomValues as Parameters<typeof encodeParquetSplitBlockBloomFilter>[0],
1426
+ physicalType,
1427
+ column.typeLength
1428
+ );
1429
+ }
1430
+
1431
+ /** Builds optional size statistics from one shredded column chunk. */
1432
+ function createSizeStatistics(column: ParquetField, data: ParquetColumnChunk): SizeStatistics {
1433
+ const repetitionLevelHistogram = createLevelHistogram(data.rlevels, column.rLevelMax);
1434
+ const definitionLevelHistogram = createLevelHistogram(data.dlevels, column.dLevelMax);
1435
+ let unencodedByteArrayDataBytes = 0;
1436
+ if (column.primitiveType === 'BYTE_ARRAY') {
1437
+ for (const value of data.values) {
1438
+ if (typeof value === 'string') {
1439
+ unencodedByteArrayDataBytes += encodeUtf8(value).byteLength;
1440
+ } else if (value instanceof ArrayBuffer || ArrayBuffer.isView(value)) {
1441
+ unencodedByteArrayDataBytes += value.byteLength;
1442
+ }
1443
+ }
1444
+ }
1445
+ return new SizeStatistics({
1446
+ unencoded_byte_array_data_bytes:
1447
+ column.primitiveType === 'BYTE_ARRAY' ? unencodedByteArrayDataBytes : undefined,
1448
+ repetition_level_histogram: repetitionLevelHistogram,
1449
+ definition_level_histogram: definitionLevelHistogram
1450
+ });
1451
+ }
1452
+
1453
+ /** Returns whether standard column statistics are enabled for one top-level field. */
1454
+ function isStatisticsEnabled(
1455
+ option: boolean | Record<string, boolean> | undefined,
1456
+ column: ParquetField
1457
+ ): boolean {
1458
+ return option === true || option?.[column.path[0]] === true;
1459
+ }
1460
+
1461
+ /** Builds conservative standard min/max/null-count statistics for one column chunk. */
1462
+ function createColumnStatistics(
1463
+ column: ParquetField,
1464
+ data: ParquetColumnChunk
1465
+ ): Statistics | undefined {
1466
+ if (
1467
+ !column.primitiveType ||
1468
+ !isPageIndexPhysicalType(column.primitiveType) ||
1469
+ !supportsStatisticsSortOrder(column)
1470
+ ) {
1471
+ return undefined;
1472
+ }
1473
+ const statistics = new Statistics({
1474
+ null_count: column.rLevelMax === 0 ? data.count - data.values.length : undefined,
1475
+ is_min_value_exact: true,
1476
+ is_max_value_exact: true
1477
+ });
1478
+ if (data.values.length === 0) return statistics;
1479
+ const bounds = getPageBounds(data.values, column.primitiveType, column.typeLength);
1480
+ if (!bounds) return undefined;
1481
+ statistics.min_value = bounds.min;
1482
+ statistics.max_value = bounds.max;
1483
+ return statistics;
1484
+ }
1485
+
1486
+ /** Returns whether the physical representation preserves the logical sort order. */
1487
+ function supportsStatisticsSortOrder(column: ParquetField): boolean {
1488
+ const logicalType = column.logicalType?.type || column.originalType;
1489
+ if (logicalType === 'FLOAT16') return false;
1490
+ if (logicalType === 'DECIMAL' || logicalType?.startsWith('DECIMAL_')) {
1491
+ return column.primitiveType !== 'BYTE_ARRAY' && column.primitiveType !== 'FIXED_LEN_BYTE_ARRAY';
1492
+ }
1493
+ if (logicalType === 'INTEGER' || logicalType?.startsWith('UINT_')) {
1494
+ return column.logicalType?.isSigned !== false && !logicalType.startsWith('UINT_');
1495
+ }
1496
+ return true;
1497
+ }
1498
+
1499
+ /** Counts the occurrences of each definition or repetition level. */
1500
+ function createLevelHistogram(
1501
+ levels: ParquetColumnChunk['rlevels'],
1502
+ maximumLevel: number
1503
+ ): number[] {
1504
+ const histogram = new Array<number>(maximumLevel + 1).fill(0);
1505
+ for (const level of levels) {
1506
+ if (level >= 0 && level <= maximumLevel) histogram[level]++;
1507
+ }
1508
+ return histogram;
1509
+ }
1510
+
1511
+ /** Converts writer-facing sort keys into Parquet leaf-column indexes. */
1512
+ function createSortingColumns(
1513
+ schema: ParquetSchema,
1514
+ sortingColumns: readonly ParquetSortingColumnOption[] | undefined
1515
+ ): SortingColumn[] | undefined {
1516
+ if (!sortingColumns?.length) return undefined;
1517
+ const leafFields = schema.fieldList.filter(field => !field.isNested);
1518
+ const usedColumnIndexes = new Set<number>();
1519
+ return sortingColumns.map(sortKey => {
1520
+ const columnIndex = leafFields.findIndex(
1521
+ field =>
1522
+ (field.path.length === 1 && field.name === sortKey.column) ||
1523
+ (field.path.length > 1 && field.path.join('.') === sortKey.column)
1524
+ );
1525
+ if (columnIndex < 0) {
1526
+ throw new Error(`Unknown Parquet sorting column ${sortKey.column}`);
1527
+ }
1528
+ if (usedColumnIndexes.has(columnIndex)) {
1529
+ throw new Error(`Duplicate Parquet sorting column ${sortKey.column}`);
1530
+ }
1531
+ usedColumnIndexes.add(columnIndex);
1532
+ return new SortingColumn({
1533
+ column_idx: columnIndex,
1534
+ descending: Boolean(sortKey.descending),
1535
+ nulls_first: Boolean(sortKey.nullsFirst)
1536
+ });
1537
+ });
541
1538
  }
542
1539
 
543
1540
  /**
@@ -548,32 +1545,52 @@ async function encodeRowGroup(
548
1545
  data: ParquetRowGroup,
549
1546
  opts: ParquetEncoderOptions
550
1547
  ): Promise<{
551
- body: Buffer;
1548
+ body: Uint8Array;
552
1549
  metadata: RowGroup;
553
1550
  }> {
554
1551
  const metadata = new RowGroup({
555
- num_rows: data.rowCount,
1552
+ num_rows: int64(data.rowCount),
556
1553
  columns: [],
557
- total_byte_size: 0
1554
+ total_byte_size: int64(0),
1555
+ sorting_columns: createSortingColumns(schema, opts.sortingColumns)
558
1556
  });
559
1557
 
560
- let body = Buffer.alloc(0);
1558
+ let body: Uint8Array = new Uint8Array(0);
561
1559
  for (const field of schema.fieldList) {
562
1560
  if (field.isNested) {
563
1561
  continue; // eslint-disable-line no-continue
564
1562
  }
565
1563
 
566
- const cchunkData = await encodeColumnChunk(field, data, body.length, opts);
1564
+ const cchunkData = await encodeColumnChunk(
1565
+ field,
1566
+ data,
1567
+ body.length,
1568
+ opts,
1569
+ opts.rowGroupOrdinal ?? 0,
1570
+ metadata.columns.length
1571
+ );
567
1572
 
568
1573
  const cchunk = new ColumnChunk({
569
- file_offset: cchunkData.metadataOffset,
570
- meta_data: cchunkData.metadata
1574
+ file_offset: int64(cchunkData.metadataOffset),
1575
+ meta_data: cchunkData.encryptedColumnMetadata ? undefined : cchunkData.metadata,
1576
+ offset_index_offset:
1577
+ cchunkData.offsetIndexOffset === undefined
1578
+ ? undefined
1579
+ : int64(cchunkData.offsetIndexOffset),
1580
+ offset_index_length: cchunkData.offsetIndexLength,
1581
+ column_index_offset:
1582
+ cchunkData.columnIndexOffset === undefined
1583
+ ? undefined
1584
+ : int64(cchunkData.columnIndexOffset),
1585
+ column_index_length: cchunkData.columnIndexLength,
1586
+ crypto_metadata: cchunkData.cryptoMetadata,
1587
+ encrypted_column_metadata: cchunkData.encryptedColumnMetadata
571
1588
  });
572
1589
 
573
1590
  metadata.columns.push(cchunk);
574
- metadata.total_byte_size = new Int64(Number(metadata.total_byte_size) + cchunkData.body.length);
1591
+ metadata.total_byte_size = int64(Number(metadata.total_byte_size) + cchunkData.body.length);
575
1592
 
576
- body = Buffer.concat([body, cchunkData.body] as Uint8Array[]);
1593
+ body = concatUint8Arrays([body, cchunkData.body]);
577
1594
  }
578
1595
 
579
1596
  return {body, metadata};
@@ -582,19 +1599,44 @@ async function encodeRowGroup(
582
1599
  /**
583
1600
  * Encode a parquet file metadata footer
584
1601
  */
585
- function encodeFooter(
1602
+ function encodeFooterMetadata(
586
1603
  schema: ParquetSchema,
587
1604
  rowCount: number,
588
1605
  rowGroups: RowGroup[],
589
- userMetadata: Record<string, string>
590
- ): Buffer {
1606
+ userMetadata: Record<string, string>,
1607
+ footerSignature?: ParquetWriterFooterSignatureOptions
1608
+ ): Uint8Array {
1609
+ const signatureAlgorithm = footerSignature?.algorithm ?? 'AES_GCM_V1';
1610
+ const signatureFileUnique = footerSignature?.fileUnique
1611
+ ? new Uint8Array(footerSignature.fileUnique)
1612
+ : undefined;
591
1613
  const metadata = new FileMetaData({
592
1614
  version: PARQUET_VERSION,
593
1615
  created_by: 'parquets',
594
- num_rows: rowCount,
1616
+ num_rows: int64(rowCount),
595
1617
  row_groups: rowGroups,
596
1618
  schema: [],
597
- key_value_metadata: []
1619
+ key_value_metadata: [],
1620
+ encryption_algorithm: footerSignature
1621
+ ? new EncryptionAlgorithm({
1622
+ ...(signatureAlgorithm === 'AES_GCM_CTR_V1'
1623
+ ? {
1624
+ AES_GCM_CTR_V1: {
1625
+ aad_prefix: footerSignature.aadPrefix,
1626
+ aad_file_unique: signatureFileUnique
1627
+ }
1628
+ }
1629
+ : {
1630
+ AES_GCM_V1: {
1631
+ aad_prefix: footerSignature.aadPrefix,
1632
+ aad_file_unique: signatureFileUnique
1633
+ }
1634
+ })
1635
+ })
1636
+ : undefined,
1637
+ footer_signing_key_metadata: footerSignature
1638
+ ? new Uint8Array(footerSignature.keyMetadata)
1639
+ : undefined
598
1640
  });
599
1641
 
600
1642
  for (const key in userMetadata) {
@@ -627,19 +1669,256 @@ function encodeFooter(
627
1669
  }
628
1670
 
629
1671
  if (field.originalType) {
630
- schemaElem.converted_type = ConvertedType[field.originalType] as ConvertedType;
1672
+ const convertedType = ConvertedType[field.originalType as keyof typeof ConvertedType];
1673
+ if (typeof convertedType === 'number') {
1674
+ schemaElem.converted_type = convertedType;
1675
+ }
631
1676
  }
632
1677
 
633
1678
  schemaElem.type_length = field.typeLength;
1679
+ schemaElem.precision = field.precision ?? field.presision;
1680
+ schemaElem.scale = field.scale;
1681
+ schemaElem.field_id = field.fieldId;
1682
+ const logicalType = getFieldLogicalType(field);
1683
+ if (logicalType) {
1684
+ schemaElem.logicalType = encodeParquetLogicalType(logicalType);
1685
+ }
634
1686
 
635
1687
  metadata.schema.push(schemaElem);
636
1688
  }
637
1689
 
638
- const metadataEncoded = serializeThrift(metadata);
639
- const footerEncoded = Buffer.alloc(metadataEncoded.length + 8);
1690
+ return serializeThrift(metadata);
1691
+ }
640
1692
 
641
- metadataEncoded.copy(footerEncoded as Uint8Array);
642
- footerEncoded.writeUInt32LE(metadataEncoded.length, metadataEncoded.length);
643
- footerEncoded.write(PARQUET_MAGIC, metadataEncoded.length + 4);
1693
+ /** Encodes an unsigned plaintext footer with its standard Parquet trailer. */
1694
+ function encodeFooter(
1695
+ schema: ParquetSchema,
1696
+ rowCount: number,
1697
+ rowGroups: RowGroup[],
1698
+ userMetadata: Record<string, string>
1699
+ ): Uint8Array {
1700
+ const metadataEncoded = encodeFooterMetadata(schema, rowCount, rowGroups, userMetadata);
1701
+ const footerEncoded = new Uint8Array(metadataEncoded.length + 8);
1702
+
1703
+ footerEncoded.set(metadataEncoded);
1704
+ writeUInt32LE(footerEncoded, metadataEncoded.length, metadataEncoded.length);
1705
+ footerEncoded.set(PARQUET_MAGIC_BYTES, metadataEncoded.length + 4);
644
1706
  return footerEncoded;
645
1707
  }
1708
+
1709
+ /** Encodes a plaintext footer with its trailing 28-byte authenticated signature. */
1710
+ async function encodeSignedFooter(
1711
+ schema: ParquetSchema,
1712
+ rowCount: number,
1713
+ rowGroups: RowGroup[],
1714
+ userMetadata: Record<string, string>,
1715
+ options: ParquetWriterFooterSignatureOptions
1716
+ ): Promise<Uint8Array> {
1717
+ const metadata = encodeFooterMetadata(schema, rowCount, rowGroups, userMetadata, options);
1718
+ const signature = await createParquetFooterSignature(metadata, options);
1719
+ const footerEncoded = new Uint8Array(metadata.length + signature.length + 8);
1720
+ footerEncoded.set(metadata);
1721
+ footerEncoded.set(signature, metadata.length);
1722
+ writeUInt32LE(
1723
+ footerEncoded,
1724
+ metadata.length + signature.length,
1725
+ metadata.length + signature.length
1726
+ );
1727
+ footerEncoded.set(PARQUET_MAGIC_BYTES, metadata.length + signature.length + 4);
1728
+ return footerEncoded;
1729
+ }
1730
+
1731
+ /** Wraps a serialized footer in Parquet modular-encryption metadata and AES-GCM. */
1732
+ async function encodeEncryptedFooter(
1733
+ footer: Uint8Array,
1734
+ options: ParquetWriterEncryptionOptions
1735
+ ): Promise<Uint8Array> {
1736
+ const algorithm = options.algorithm ?? 'AES_GCM_V1';
1737
+ const fileUnique = options.fileUnique ? new Uint8Array(options.fileUnique) : createFileUnique();
1738
+ if (fileUnique.byteLength !== 8) {
1739
+ throw new Error(`Parquet encrypted footer file_unique must be 8 bytes`);
1740
+ }
1741
+ const algorithmParameters = {
1742
+ aad_prefix: options.aadPrefix && new Uint8Array(options.aadPrefix),
1743
+ aad_file_unique: fileUnique,
1744
+ supply_aad_prefix: undefined
1745
+ };
1746
+ const encryptionAlgorithm =
1747
+ algorithm === 'AES_GCM_CTR_V1'
1748
+ ? new EncryptionAlgorithm({AES_GCM_CTR_V1: algorithmParameters})
1749
+ : new EncryptionAlgorithm({AES_GCM_V1: algorithmParameters});
1750
+ const cryptoMetadata = serializeThrift(
1751
+ new FileCryptoMetaData({
1752
+ encryption_algorithm: encryptionAlgorithm,
1753
+ key_metadata: options.keyMetadata && new Uint8Array(options.keyMetadata)
1754
+ })
1755
+ );
1756
+ const footerMetadata = footer.subarray(0, footer.byteLength - 8);
1757
+ const encryptedFooter = await encryptParquetModule(footerMetadata, {
1758
+ algorithm,
1759
+ aad: createParquetModuleAad(options.aadPrefix, fileUnique, 'footer'),
1760
+ keyMetadata: options.keyMetadata,
1761
+ keyRetriever: options.keyRetriever
1762
+ });
1763
+ const metadataSize = cryptoMetadata.byteLength + encryptedFooter.byteLength;
1764
+ const trailer = new Uint8Array(8);
1765
+ writeUInt32LE(trailer, metadataSize, 0);
1766
+ trailer.set(encodeUtf8(PARQUET_MAGIC_ENCRYPTED), 4);
1767
+ return concatUint8Arrays([cryptoMetadata, encryptedFooter, trailer]);
1768
+ }
1769
+
1770
+ /** Generates the eight-byte file identifier required by Parquet module AAD. */
1771
+ function createFileUnique(): Uint8Array {
1772
+ const fileUnique = new Uint8Array(8);
1773
+ const cryptoProvider = globalThis.crypto;
1774
+ if (!cryptoProvider?.getRandomValues) {
1775
+ throw new Error('Parquet encrypted footer requires Web Crypto random values');
1776
+ }
1777
+ cryptoProvider.getRandomValues(fileUnique);
1778
+ return fileUnique;
1779
+ }
1780
+
1781
+ /** Returns an explicit or inferred modern logical annotation for a writer field. */
1782
+ function getFieldLogicalType(field: ParquetField): ParquetLogicalType | undefined {
1783
+ if (field.logicalType) return field.logicalType;
1784
+ const originalType = field.originalType;
1785
+ if (!originalType) return undefined;
1786
+ if (originalType === 'UTF8') return {type: 'STRING'};
1787
+ if (originalType === 'ENUM') return {type: 'ENUM'};
1788
+ if (originalType.startsWith('DECIMAL_')) {
1789
+ return {
1790
+ type: 'DECIMAL',
1791
+ precision: field.precision ?? field.presision,
1792
+ scale: field.scale
1793
+ };
1794
+ }
1795
+ if (originalType === 'DATE') return {type: 'DATE'};
1796
+ if (originalType.startsWith('TIME_')) {
1797
+ return {
1798
+ type: 'TIME',
1799
+ unit: originalType.slice(5) as ParquetLogicalType['unit'],
1800
+ isAdjustedToUTC: true
1801
+ };
1802
+ }
1803
+ if (originalType.startsWith('TIMESTAMP_')) {
1804
+ return {
1805
+ type: 'TIMESTAMP',
1806
+ unit: originalType.slice(10) as ParquetLogicalType['unit'],
1807
+ isAdjustedToUTC: true
1808
+ };
1809
+ }
1810
+ if (originalType.startsWith('UINT_') || originalType.startsWith('INT_')) {
1811
+ return {
1812
+ type: 'INTEGER',
1813
+ bitWidth: Number(originalType.slice(originalType.indexOf('_') + 1)) as 8 | 16 | 32 | 64,
1814
+ isSigned: originalType.startsWith('INT_')
1815
+ };
1816
+ }
1817
+ if (originalType === 'JSON' || originalType === 'BSON') return {type: originalType};
1818
+ if (
1819
+ originalType === 'UUID' ||
1820
+ originalType === 'FLOAT16' ||
1821
+ originalType === 'UNKNOWN' ||
1822
+ originalType === 'VARIANT' ||
1823
+ originalType === 'GEOMETRY' ||
1824
+ originalType === 'GEOGRAPHY'
1825
+ ) {
1826
+ return {type: originalType};
1827
+ }
1828
+ return undefined;
1829
+ }
1830
+
1831
+ /** Converts the internal logical annotation into the Parquet 2.13 Thrift union. */
1832
+ function encodeParquetLogicalType(logicalType: ParquetLogicalType): LogicalType {
1833
+ switch (logicalType.type) {
1834
+ case 'STRING':
1835
+ return LogicalType.fromSTRING(new StringType());
1836
+ case 'MAP':
1837
+ return LogicalType.fromMAP(new MapType());
1838
+ case 'LIST':
1839
+ return LogicalType.fromLIST(new ListType());
1840
+ case 'ENUM':
1841
+ return LogicalType.fromENUM(new EnumType());
1842
+ case 'DECIMAL':
1843
+ if (logicalType.precision === undefined || logicalType.scale === undefined) {
1844
+ throw new Error('Parquet DECIMAL requires precision and scale');
1845
+ }
1846
+ return LogicalType.fromDECIMAL(
1847
+ new DecimalType({precision: logicalType.precision, scale: logicalType.scale})
1848
+ );
1849
+ case 'DATE':
1850
+ return LogicalType.fromDATE(new DateType());
1851
+ case 'TIME':
1852
+ return LogicalType.fromTIME(
1853
+ new TimeType({
1854
+ isAdjustedToUTC: logicalType.isAdjustedToUTC ?? false,
1855
+ unit: encodeParquetTimeUnit(logicalType.unit)
1856
+ })
1857
+ );
1858
+ case 'TIMESTAMP':
1859
+ return LogicalType.fromTIMESTAMP(
1860
+ new TimestampType({
1861
+ isAdjustedToUTC: logicalType.isAdjustedToUTC ?? false,
1862
+ unit: encodeParquetTimeUnit(logicalType.unit)
1863
+ })
1864
+ );
1865
+ case 'INTEGER':
1866
+ if (logicalType.bitWidth === undefined || logicalType.isSigned === undefined) {
1867
+ throw new Error('Parquet INTEGER requires bitWidth and isSigned');
1868
+ }
1869
+ return LogicalType.fromINTEGER(
1870
+ new IntType({bitWidth: logicalType.bitWidth, isSigned: logicalType.isSigned})
1871
+ );
1872
+ case 'UNKNOWN':
1873
+ return LogicalType.fromUNKNOWN(new NullType());
1874
+ case 'JSON':
1875
+ return LogicalType.fromJSON(new JsonType());
1876
+ case 'BSON':
1877
+ return LogicalType.fromBSON(new BsonType());
1878
+ case 'UUID':
1879
+ return LogicalType.fromUUID(new UUIDType());
1880
+ case 'FLOAT16':
1881
+ return LogicalType.fromFLOAT16(new Float16Type());
1882
+ case 'VARIANT':
1883
+ return LogicalType.fromVARIANT(
1884
+ new VariantType({specification_version: logicalType.specificationVersion})
1885
+ );
1886
+ case 'GEOMETRY':
1887
+ return LogicalType.fromGEOMETRY(new GeometryType({crs: logicalType.crs}));
1888
+ case 'GEOGRAPHY':
1889
+ return LogicalType.fromGEOGRAPHY(
1890
+ new GeographyType({
1891
+ crs: logicalType.crs,
1892
+ algorithm:
1893
+ logicalType.algorithm === undefined
1894
+ ? undefined
1895
+ : EdgeInterpolationAlgorithm[
1896
+ logicalType.algorithm as keyof typeof EdgeInterpolationAlgorithm
1897
+ ]
1898
+ })
1899
+ );
1900
+ default:
1901
+ throw new Error(
1902
+ `Unsupported Parquet logical type ${(logicalType as ParquetLogicalType).type}`
1903
+ );
1904
+ }
1905
+ }
1906
+
1907
+ /** Encodes one Parquet logical time unit. */
1908
+ function encodeParquetTimeUnit(unit: ParquetLogicalType['unit']): TimeUnit {
1909
+ switch (unit) {
1910
+ case 'MILLIS':
1911
+ return TimeUnit.fromMILLIS(new MilliSeconds());
1912
+ case 'MICROS':
1913
+ return TimeUnit.fromMICROS(new MicroSeconds());
1914
+ case 'NANOS':
1915
+ return TimeUnit.fromNANOS(new NanoSeconds());
1916
+ default:
1917
+ throw new Error('Parquet TIME and TIMESTAMP require a unit');
1918
+ }
1919
+ }
1920
+
1921
+ /** Wrap a writer metadata number in the thrift int64 compatibility object. */
1922
+ function int64(value: number): any {
1923
+ return new CompactInt64(BigInt(value));
1924
+ }