@gscdump/engine 0.36.4 → 0.37.1

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.
@@ -0,0 +1,15 @@
1
+ import { coerceBigIntToNumber } from "@gscdump/lakehouse";
2
+ function coerceRow(row) {
3
+ let mutated = null;
4
+ for (const [k, v] of Object.entries(row)) if (typeof v === "bigint") {
5
+ if (!mutated) mutated = { ...row };
6
+ mutated[k] = coerceBigIntToNumber(v);
7
+ }
8
+ return mutated ?? row;
9
+ }
10
+ function coerceRows(rows) {
11
+ const out = Array.from({ length: rows.length });
12
+ for (let i = 0; i < rows.length; i++) out[i] = coerceRow(rows[i]);
13
+ return out;
14
+ }
15
+ export { coerceRow, coerceRows };
@@ -1,9 +1,11 @@
1
+ import { coerceRows } from "./coerce.mjs";
1
2
  import { dayPartition, hourPartition, inferSearchType, objectKey, tenantPrefix } from "./layout.mjs";
2
3
  import { SCHEMAS, TABLE_METADATA, currentSchemaVersion, dateColumnsFor, dedupeByNaturalKey } from "./schema.mjs";
3
4
  import { compactTieredImpl, dedupeOverlappingTiers, splitOverlappingTiers } from "./compaction.mjs";
4
5
  import { dateReplaceClause as dateReplaceClause$1 } from "../sql-fragments.mjs";
5
6
  import { compileLogicalQueryPlan, substituteNamedFiles } from "./parquet-plan.mjs";
6
7
  import { sqlEscape } from "../sql-bind.mjs";
8
+ import { encodeJsonBigintSafe } from "@gscdump/lakehouse";
7
9
  import { buildLogicalPlan } from "gscdump/query/plan";
8
10
  import { normalizeUrl } from "gscdump/normalize";
9
11
  const BUFFER_READ_BATCH_SIZE = 8;
@@ -23,7 +25,7 @@ async function registerBufferedFiles(db, files, dataSource, registered, signal)
23
25
  async function encodeBytes(db, table, rows) {
24
26
  const inName = db.makeTempPath("json");
25
27
  const outName = db.makeTempPath("parquet");
26
- const jsonBytes = new TextEncoder().encode(JSON.stringify(rows));
28
+ const jsonBytes = encodeJsonBigintSafe(coerceRows(rows));
27
29
  const registered = [];
28
30
  await db.registerFileBuffer(inName, jsonBytes);
29
31
  registered.push(inName);
@@ -1,4 +1,5 @@
1
1
  import { decodeParquetToRows, encodeRowsToParquetFlex } from "../adapters/hyparquet.mjs";
2
+ import { encodeJsonBigintSafe } from "@gscdump/lakehouse";
2
3
  function isMissingKeyError(e) {
3
4
  if (typeof e !== "object" || e === null) return false;
4
5
  if (e.code === "ENOENT") return true;
@@ -90,7 +91,7 @@ function createQueryDimStore({ dataSource }) {
90
91
  normalizerVersion: records[0]?.normalizer_version ?? 0,
91
92
  intentVersion: records[0]?.intent_version ?? 0
92
93
  };
93
- await dataSource.write(queryDimMetaKey(ctx), new TextEncoder().encode(JSON.stringify(meta)));
94
+ await dataSource.write(queryDimMetaKey(ctx), encodeJsonBigintSafe(meta));
94
95
  return {
95
96
  parquetKey,
96
97
  rowCount: records.length
@@ -311,11 +312,10 @@ function createInspectionStore(opts) {
311
312
  byMonth.get(month).push(r);
312
313
  }
313
314
  for (const [yearMonth, batch] of byMonth) {
314
- const shard = {
315
+ const bytes = encodeJsonBigintSafe({
315
316
  version: 1,
316
317
  records: batch
317
- };
318
- const bytes = new TextEncoder().encode(JSON.stringify(shard));
318
+ });
319
319
  if (bytes.byteLength > 5242880) throw new Error(`inspection history shard exceeds ${INSPECTION_HISTORY_MAX_BYTES} bytes (got ${bytes.byteLength}); split the batch`);
320
320
  await ds.write(inspectionHistoryShardKey(ctx, yearMonth, batchId), bytes);
321
321
  }
@@ -581,7 +581,7 @@ function createSitemapStore(opts) {
581
581
  return JSON.parse(new TextDecoder().decode(bytes));
582
582
  }
583
583
  async function writeJson(key, value) {
584
- await ds.write(key, new TextEncoder().encode(JSON.stringify(value)));
584
+ await ds.write(key, encodeJsonBigintSafe(value));
585
585
  }
586
586
  return {
587
587
  async writeSnapshot(ctx, records) {
@@ -931,7 +931,7 @@ function createIndexingMetadataStore(opts) {
931
931
  const key = indexingMetadataIndexKey(ctx);
932
932
  const index = await readIndex(key);
933
933
  for (const r of records) index.records[hash(r.url)] = r;
934
- await ds.write(key, new TextEncoder().encode(JSON.stringify(index)));
934
+ await ds.write(key, encodeJsonBigintSafe(index));
935
935
  },
936
936
  async loadIndex(ctx) {
937
937
  return readIndex(indexingMetadataIndexKey(ctx));
@@ -954,7 +954,7 @@ function createEmptyTypesStore(opts) {
954
954
  return JSON.parse(new TextDecoder().decode(bytes));
955
955
  }
956
956
  async function writeDoc(key, doc) {
957
- await ds.write(key, new TextEncoder().encode(JSON.stringify(doc)));
957
+ await ds.write(key, encodeJsonBigintSafe(doc));
958
958
  }
959
959
  return {
960
960
  async load(ctx) {
@@ -1,8 +1,8 @@
1
1
  import { Row as Row$1, SearchType, TenantCtx as TenantCtx$1 } from "./storage.mjs";
2
2
  import { EngineError } from "./errors.mjs";
3
3
  import { icebergCreateTable } from "./libs/icebird.mjs";
4
- import { Result } from "gscdump/result";
5
4
  import { CatalogCache, CommitRetryOptions, ConnectIcebergOptions, DEFAULT_PARTITION_KEY_ENCODING as DEFAULT_PARTITION_KEY_ENCODING$1, IcebergCatalogConfig, IcebergColumn, IcebergColumn as IcebergColumn$1, IcebergColumnType, IcebergConnection, IcebergConnection as IcebergConnection$1, IcebergListedDataFile, IcebergListedDataFile as IcebergListedDataFile$1, IcebergPartitionField, IcebergPartitionField as IcebergPartitionField$1, IcebergPartitionSpec, IcebergPartitionSpecField, IcebergPartitionTransform, IcebergPrimitiveType, IcebergS3Config, IcebergSchema, IcebergSchemaField, IcebergSortOrder, IcebergSortOrderField, IcebergTableSpec, IcebergTableSpec as IcebergTableSpec$1, PartitionKeyEncoding, PartitionKeyEncoding as PartitionKeyEncoding$1, QueryProfiler, catalogCacheScope, connectIcebergCatalog, ensureIcebergNamespace, invalidateSnapshotRef, listIcebergTables } from "@gscdump/lakehouse";
5
+ import { Result } from "gscdump/result";
6
6
  import { icebergAppendRetrying, isCommitRateLimited } from "@gscdump/lakehouse/unsafe-raw";
7
7
  import { TableName } from "@gscdump/contracts";
8
8
  /** The 9 fact tables that exist as global Iceberg tables. */
@@ -1,20 +1,8 @@
1
+ import { coerceRows } from "./coerce.mjs";
1
2
  import "./layout.mjs";
2
3
  import { engineErrors } from "../errors.mjs";
3
4
  import { assertDimensionsSupported, getFilterDimensions, pgResolverAdapter, resolveToSQL } from "./resolver.mjs";
4
5
  import { runAnalyzerFromSource } from "./dispatch.mjs";
5
- function coerceRow(row) {
6
- let mutated = null;
7
- for (const [k, v] of Object.entries(row)) if (typeof v === "bigint") {
8
- if (!mutated) mutated = { ...row };
9
- mutated[k] = Number(v);
10
- }
11
- return mutated ?? row;
12
- }
13
- function coerceRows(rows) {
14
- const out = Array.from({ length: rows.length });
15
- for (let i = 0; i < rows.length; i++) out[i] = coerceRow(rows[i]);
16
- return out;
17
- }
18
6
  var AttachedTableMissingError = class extends Error {
19
7
  missing;
20
8
  engineError;
@@ -166,4 +154,4 @@ async function queryComparisonRows(source, current, previous) {
166
154
  previous: previousRows
167
155
  };
168
156
  }
169
- export { AttachedTableMissingError, ENGINE_QUERY_CAPABILITIES, coerceRow, coerceRows, createAttachedTableSource, createEngineQuerySource, createSqlQuerySource, queryComparisonRows, queryRows, rewriteForTableSource, runAnalyzerWithEngine, typedQuery };
157
+ export { AttachedTableMissingError, ENGINE_QUERY_CAPABILITIES, createAttachedTableSource, createEngineQuerySource, createSqlQuerySource, queryComparisonRows, queryRows, rewriteForTableSource, runAnalyzerWithEngine, typedQuery };
@@ -2,8 +2,8 @@ import { TABLE_METADATA } from "../_chunks/schema.mjs";
2
2
  import { engineErrors } from "../errors.mjs";
3
3
  import { DEFAULT_PARTITION_KEY_ENCODING, ICEBERG_FIELD_ID_BASE, ICEBERG_PARTITION_COLUMNS, ICEBERG_PARTITION_SPEC, ICEBERG_SCHEMAS, ICEBERG_SCHEMAS_INT, ICEBERG_SCHEMAS_STRING, ICEBERG_TABLES, INT_SEARCH_TYPE, SEARCH_TYPE_INT, assertIcebergTable, gscDataset, icebergPartitionColumns, icebergSchemasFor, icebergTableSpec, isIcebergTable } from "../_chunks/schema2.mjs";
4
4
  import { icebergCreateTable, icebergManifests, restCatalogLoadTable } from "../_chunks/libs/icebird.mjs";
5
- import { err, ok } from "gscdump/result";
6
5
  import { ICEBERG_TYPE_MAP, catalogCacheScope, connectIcebergCatalog, dropIcebergTables as dropIcebergTables$1, ensureIcebergNamespace, invalidateSnapshotRef, listIcebergTables, resolveIcebergDataFiles } from "@gscdump/lakehouse";
6
+ import { err, ok } from "gscdump/result";
7
7
  import { icebergAppendRetrying, isCommitRateLimited } from "@gscdump/lakehouse/unsafe-raw";
8
8
  function icebergSchemaFor(table, encoding = DEFAULT_PARTITION_KEY_ENCODING) {
9
9
  return {
package/dist/index.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { ENGINE_QUERY_CAPABILITIES, coerceRow, coerceRows, createSqlQuerySource } from "./_chunks/source.mjs";
1
+ import { coerceRow, coerceRows } from "./_chunks/coerce.mjs";
2
2
  import { DEFAULT_SEARCH_TYPE, dayPartition, hourPartition, inferLegacyTier, inferSearchType, objectKey } from "./_chunks/layout.mjs";
3
3
  import { SCHEMAS, TABLE_METADATA, allTables, countries, currentSchemaVersion, dates, dimensionToColumn, drizzleSchema, hourly_pages, inferTable, page_queries, pages, queries } from "./_chunks/schema.mjs";
4
4
  import { enumeratePartitions } from "./_chunks/compaction.mjs";
@@ -11,6 +11,7 @@ import "./planner.mjs";
11
11
  import { createIcebergResolverAdapter, createParquetResolverAdapter, createR2SqlResolverAdapter, pgResolverAdapter } from "./_chunks/resolver.mjs";
12
12
  import { rebuildDailyFromHourly } from "./rollups.mjs";
13
13
  import { fixedPolicy, inspectionPolicy, sitemapPolicy } from "./schedule.mjs";
14
+ import { ENGINE_QUERY_CAPABILITIES, createSqlQuerySource } from "./_chunks/source.mjs";
14
15
  import { err, ok, unwrapResult } from "gscdump/result";
15
16
  const NOOP_RESULT = {
16
17
  flushed: 0,
package/dist/rollups.mjs CHANGED
@@ -2,6 +2,7 @@ import "./_chunks/layout.mjs";
2
2
  import { engineErrors } from "./errors.mjs";
3
3
  import { encodeRowsToParquetFlex } from "./adapters/hyparquet.mjs";
4
4
  import { createIndexingMetadataStore, createQueryDimStore, createSitemapStore, inspectionParquetKey, sitemapUrlsIndexPrefix } from "./_chunks/entities.mjs";
5
+ import { encodeJsonBigintSafe } from "@gscdump/lakehouse";
5
6
  import { MS_PER_DAY } from "gscdump";
6
7
  function rollupPrefix(ctx, searchType) {
7
8
  const base = ctx.siteId ? `u_${ctx.userId}/${ctx.siteId}/rollups` : `u_${ctx.userId}/rollups`;
@@ -68,14 +69,13 @@ async function rebuildRollups(opts) {
68
69
  parquetKey,
69
70
  rowCount: rows.length
70
71
  };
71
- const envelope = {
72
+ const envelopeBytes = encodeJsonBigintSafe({
72
73
  version: 1,
73
74
  id: def.id,
74
75
  builtAt,
75
76
  windowDays: def.windowDays,
76
77
  payload: pointer
77
- };
78
- const envelopeBytes = new TextEncoder().encode(JSON.stringify(envelope));
78
+ });
79
79
  const key = rollupKey(opts.ctx, def.id, builtAt, defSearchType);
80
80
  await opts.dataSource.write(key, envelopeBytes);
81
81
  results.push({
@@ -88,15 +88,13 @@ async function rebuildRollups(opts) {
88
88
  });
89
89
  continue;
90
90
  }
91
- const envelope = {
91
+ const bytes = encodeJsonBigintSafe({
92
92
  version: 1,
93
93
  id: def.id,
94
94
  builtAt,
95
95
  windowDays: def.windowDays,
96
96
  payload
97
- };
98
- const json = JSON.stringify(envelope);
99
- const bytes = new TextEncoder().encode(json);
97
+ });
100
98
  const key = rollupKey(opts.ctx, def.id, builtAt, defSearchType);
101
99
  await opts.dataSource.write(key, bytes);
102
100
  results.push({
@@ -872,7 +870,7 @@ async function rebuildCanonicalDailyResumable(opts) {
872
870
  rowCount: 0
873
871
  }
874
872
  };
875
- await dataSource.write(rollupKey(ctx, CANONICAL_DAILY_ROLLUP_FINAL_ID, builtAt, searchType), new TextEncoder().encode(JSON.stringify(envelope)));
873
+ await dataSource.write(rollupKey(ctx, CANONICAL_DAILY_ROLLUP_FINAL_ID, builtAt, searchType), encodeJsonBigintSafe(envelope));
876
874
  return {
877
875
  done: true,
878
876
  nextWindowOffset,
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@gscdump/engine",
3
3
  "type": "module",
4
- "version": "0.36.4",
4
+ "version": "0.37.1",
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",
@@ -181,9 +181,9 @@
181
181
  "dependencies": {
182
182
  "drizzle-orm": "1.0.0-rc.3",
183
183
  "proper-lockfile": "^4.1.2",
184
- "@gscdump/contracts": "0.36.4",
185
- "@gscdump/lakehouse": "0.36.4",
186
- "gscdump": "0.36.4"
184
+ "@gscdump/contracts": "0.37.1",
185
+ "gscdump": "0.37.1",
186
+ "@gscdump/lakehouse": "0.37.1"
187
187
  },
188
188
  "devDependencies": {
189
189
  "@duckdb/duckdb-wasm": "^1.32.0",
@@ -191,8 +191,8 @@
191
191
  "aws4fetch": "^1.0.20",
192
192
  "hyparquet": "^1.26.2",
193
193
  "hyparquet-writer": "^0.16.1",
194
- "icebird": "^0.8.12",
195
- "tsx": "^4.22.5",
194
+ "icebird": "^0.8.13",
195
+ "tsx": "^4.23.0",
196
196
  "unstorage": "^1.17.5",
197
197
  "vitest": "^4.1.9"
198
198
  },