@gscdump/engine 7.3.1 → 7.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -20,6 +20,16 @@ npm install @gscdump/engine
20
20
  The CLI installs this peer. Browser consumers use `@gscdump/engine-duckdb-wasm`.
21
21
  The package includes `hyparquet` and `hyparquet-writer` for JavaScript Parquet encoding and decoding.
22
22
 
23
+ The `/hyparquet` encoders accept optional `fileMetadata: Record<string, string>`:
24
+ use `encodeRowsToParquet(table, rows, { fileMetadata })`, or include it in the
25
+ options for `encodeRowsToParquetFlex`. Metadata is stored in the file footer, not as fact
26
+ columns, including for empty files. Keys are sorted for deterministic encoding;
27
+ omitting metadata or passing an empty object preserves the existing bytes.
28
+ A publication coordinator can supply a unique identifier so identical rows from
29
+ different publications produce different bytes. It must retain and replay the
30
+ exact encoded bytes for retries; this option does not provide publication or
31
+ recovery coordination itself.
32
+
23
33
  ## Subpath exports
24
34
 
25
35
  | Subpath | Purpose |
@@ -2,8 +2,12 @@ import { ParquetQueryFilter } from "../node_modules/.pnpm/hyparquet@1.31.1/node_
2
2
  import "../node_modules/.pnpm/hyparquet@1.31.1/node_modules/hyparquet/types/node.mjs";
3
3
  import { CodecCtx, DataSource, ParquetCodec, Row, TableName } from "../storage.mjs";
4
4
  import { ColumnDef } from "../schema.mjs";
5
- export declare function encodeRowsToParquet(table: TableName, rows: readonly Row[]): Uint8Array;
6
- export interface EncodeFlexOptions {
5
+ export interface EncodeParquetOptions {
6
+ /** File footer metadata, without adding fact columns. Keys are encoded in sorted order. */
7
+ fileMetadata?: Readonly<Record<string, string>>;
8
+ }
9
+ export declare function encodeRowsToParquet(table: TableName, rows: readonly Row[], opts?: EncodeParquetOptions): Uint8Array;
10
+ export interface EncodeFlexOptions extends EncodeParquetOptions {
7
11
  /** Columns defining the output schema + order. */
8
12
  columns: readonly ColumnDef[];
9
13
  /** Sort key columns (subset of `columns` by name). Empty = preserve input order. */
@@ -157,7 +157,7 @@ function indexRow(index, key, row) {
157
157
  } else if (sameNaturalKey(key, existing, row)) index.set(hash, row);
158
158
  else index.set(hash, [existing, row]);
159
159
  }
160
- function encodeOrderedRows(rows, columns, rowGroupSize) {
160
+ function encodeOrderedRows(rows, columns, rowGroupSize, fileMetadata) {
161
161
  const schema = buildWriteSchema(columns);
162
162
  const codecs = columns.map((col) => ({
163
163
  name: col.name,
@@ -177,18 +177,24 @@ function encodeOrderedRows(rows, columns, rowGroupSize) {
177
177
  }
178
178
  }
179
179
  const writer = new ByteWriter();
180
+ const metadataKeys = Object.keys(fileMetadata ?? {}).sort();
181
+ const kvMetadata = metadataKeys.length > 0 ? metadataKeys.map((key) => ({
182
+ key,
183
+ value: fileMetadata[key]
184
+ })) : void 0;
180
185
  parquetWriteRows({
181
186
  writer,
182
187
  rows: coercedRows(),
183
188
  columns: columnSpecs,
184
189
  schema,
185
- rowGroupSize
190
+ rowGroupSize,
191
+ kvMetadata
186
192
  });
187
193
  return new Uint8Array(writer.getBuffer());
188
194
  }
189
- function encodeRowsToParquet(table, rows) {
195
+ function encodeRowsToParquet(table, rows, opts = {}) {
190
196
  const schema = SCHEMAS[table];
191
- return encodeOrderedRows(sortRowsByClusterKey(table, rows), schema.columns, ROW_GROUP_SIZE);
197
+ return encodeOrderedRows(sortRowsByClusterKey(table, rows), schema.columns, ROW_GROUP_SIZE, opts.fileMetadata);
192
198
  }
193
199
  function encodeRowsToParquetFlex(rows, opts) {
194
200
  const { columns, sortKey = [], rowGroupSize = ROW_GROUP_SIZE } = opts;
@@ -198,7 +204,7 @@ function encodeRowsToParquetFlex(rows, opts) {
198
204
  if (cmp !== 0) return cmp;
199
205
  }
200
206
  return 0;
201
- }), columns, rowGroupSize);
207
+ }), columns, rowGroupSize, opts.fileMetadata);
202
208
  }
203
209
  function asyncBufferFromBytes(bytes) {
204
210
  const base = bytes.byteOffset;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@gscdump/engine",
3
3
  "type": "module",
4
- "version": "7.3.1",
4
+ "version": "7.3.2",
5
5
  "description": "Append-only Parquet/DuckDB storage engine + planner + adapters for the gscdump pipeline. Node + edge runtimes; opt-in heavy peers.",
6
6
  "author": {
7
7
  "name": "Harlan Wilton",
@@ -189,10 +189,10 @@
189
189
  }
190
190
  },
191
191
  "dependencies": {
192
- "@gscdump/contracts": "^7.3.1",
193
- "@gscdump/lakehouse": "^7.3.1",
192
+ "@gscdump/contracts": "^7.3.2",
193
+ "@gscdump/lakehouse": "^7.3.2",
194
194
  "drizzle-orm": "1.0.0-rc.4",
195
- "gscdump": "^7.3.1",
195
+ "gscdump": "^7.3.2",
196
196
  "pathe": "^2.0.3",
197
197
  "proper-lockfile": "^4.1.2"
198
198
  },