jsonstat-io 0.1.0

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 (99) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +200 -0
  3. package/dist/arrow/index.cjs +60 -0
  4. package/dist/arrow/index.cjs.map +1 -0
  5. package/dist/arrow/index.d.cts +125 -0
  6. package/dist/arrow/index.d.ts +125 -0
  7. package/dist/arrow/index.js +3 -0
  8. package/dist/arrow/index.js.map +1 -0
  9. package/dist/arrowToCube-BdRWZ5oG.d.cts +29 -0
  10. package/dist/arrowToCube-Mjr79ZHM.d.ts +29 -0
  11. package/dist/chunk-2HY7NRKX.js +259 -0
  12. package/dist/chunk-2HY7NRKX.js.map +1 -0
  13. package/dist/chunk-62NIY7ML.js +191 -0
  14. package/dist/chunk-62NIY7ML.js.map +1 -0
  15. package/dist/chunk-AXUZF6O5.cjs +352 -0
  16. package/dist/chunk-AXUZF6O5.cjs.map +1 -0
  17. package/dist/chunk-EFRDFQOT.js +335 -0
  18. package/dist/chunk-EFRDFQOT.js.map +1 -0
  19. package/dist/chunk-GDKDYLZ4.cjs +263 -0
  20. package/dist/chunk-GDKDYLZ4.cjs.map +1 -0
  21. package/dist/chunk-IDTQZG4B.js +432 -0
  22. package/dist/chunk-IDTQZG4B.js.map +1 -0
  23. package/dist/chunk-KFIKGBIJ.cjs +200 -0
  24. package/dist/chunk-KFIKGBIJ.cjs.map +1 -0
  25. package/dist/chunk-L65CRXTV.js +64 -0
  26. package/dist/chunk-L65CRXTV.js.map +1 -0
  27. package/dist/chunk-PHLCWTHB.cjs +446 -0
  28. package/dist/chunk-PHLCWTHB.cjs.map +1 -0
  29. package/dist/chunk-VQCS4LVD.cjs +166 -0
  30. package/dist/chunk-VQCS4LVD.cjs.map +1 -0
  31. package/dist/chunk-VYS7DPB3.js +162 -0
  32. package/dist/chunk-VYS7DPB3.js.map +1 -0
  33. package/dist/chunk-ZEDTWRDN.cjs +70 -0
  34. package/dist/chunk-ZEDTWRDN.cjs.map +1 -0
  35. package/dist/cli/index.cjs +337 -0
  36. package/dist/cli/index.cjs.map +1 -0
  37. package/dist/cli/index.d.cts +90 -0
  38. package/dist/cli/index.d.ts +90 -0
  39. package/dist/cli/index.js +333 -0
  40. package/dist/cli/index.js.map +1 -0
  41. package/dist/cubeBuilder-BEGA4C5Y.js +4 -0
  42. package/dist/cubeBuilder-BEGA4C5Y.js.map +1 -0
  43. package/dist/cubeBuilder-ZU76VVEX.cjs +25 -0
  44. package/dist/cubeBuilder-ZU76VVEX.cjs.map +1 -0
  45. package/dist/cubeReader-7QVUF3TI.js +4 -0
  46. package/dist/cubeReader-7QVUF3TI.js.map +1 -0
  47. package/dist/cubeReader-UN2BHQWK.cjs +21 -0
  48. package/dist/cubeReader-UN2BHQWK.cjs.map +1 -0
  49. package/dist/index.cjs +172 -0
  50. package/dist/index.cjs.map +1 -0
  51. package/dist/index.d.cts +445 -0
  52. package/dist/index.d.ts +445 -0
  53. package/dist/index.js +7 -0
  54. package/dist/index.js.map +1 -0
  55. package/dist/ir-BArfNxo-.d.cts +143 -0
  56. package/dist/ir-Jae2ymov.d.ts +143 -0
  57. package/dist/jsonstat-BX4mBLci.d.cts +179 -0
  58. package/dist/jsonstat-BX4mBLci.d.ts +179 -0
  59. package/dist/sources/csv.cjs +40 -0
  60. package/dist/sources/csv.cjs.map +1 -0
  61. package/dist/sources/csv.d.cts +112 -0
  62. package/dist/sources/csv.d.ts +112 -0
  63. package/dist/sources/csv.js +3 -0
  64. package/dist/sources/csv.js.map +1 -0
  65. package/dist/sources/csvw.cjs +236 -0
  66. package/dist/sources/csvw.cjs.map +1 -0
  67. package/dist/sources/csvw.d.cts +143 -0
  68. package/dist/sources/csvw.d.ts +143 -0
  69. package/dist/sources/csvw.js +230 -0
  70. package/dist/sources/csvw.js.map +1 -0
  71. package/dist/sources/duckdb.cjs +121 -0
  72. package/dist/sources/duckdb.cjs.map +1 -0
  73. package/dist/sources/duckdb.d.cts +111 -0
  74. package/dist/sources/duckdb.d.ts +111 -0
  75. package/dist/sources/duckdb.js +115 -0
  76. package/dist/sources/duckdb.js.map +1 -0
  77. package/dist/sources/parquet.cjs +117 -0
  78. package/dist/sources/parquet.cjs.map +1 -0
  79. package/dist/sources/parquet.d.cts +83 -0
  80. package/dist/sources/parquet.d.ts +83 -0
  81. package/dist/sources/parquet.js +111 -0
  82. package/dist/sources/parquet.js.map +1 -0
  83. package/dist/sources/polars.cjs +94 -0
  84. package/dist/sources/polars.cjs.map +1 -0
  85. package/dist/sources/polars.d.cts +91 -0
  86. package/dist/sources/polars.d.ts +91 -0
  87. package/dist/sources/polars.js +87 -0
  88. package/dist/sources/polars.js.map +1 -0
  89. package/docs/api.md +322 -0
  90. package/docs/architecture.md +140 -0
  91. package/docs/cli.md +159 -0
  92. package/docs/formats/arrow.md +111 -0
  93. package/docs/formats/csv.md +115 -0
  94. package/docs/formats/csvw.md +152 -0
  95. package/docs/formats/duckdb.md +151 -0
  96. package/docs/formats/parquet.md +132 -0
  97. package/docs/formats/polars.md +111 -0
  98. package/docs/mapping.md +136 -0
  99. package/package.json +119 -0
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/model/jsonstat.ts","../src/model/ir.ts","../src/sink/serialize.ts","../src/util/detect.ts","../src/util/density.ts","../src/util/fetch.ts","../src/index.ts"],"names":["tableFromIPC","arrowToCube","readDataset","buildDataset","cubeToArrow"],"mappings":"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","file":"chunk-AXUZF6O5.cjs","sourcesContent":["/**\n * JSON-stat 2.0 type model.\n *\n * Faithful TypeScript representation of the JSON-stat 2.0 format, derived from\n * the official specification (raw/fullspec.html) and the vendored JSON Schemas\n * (raw/schema/{index,dataset,collection,dimension}.json).\n *\n * These are *structural* types: they describe the wire format. Cross-field\n * cube invariants (e.g. `value.length === size.reduce(product)`) live in the\n * core engine, mirroring how the official JSON Schemas defer semantic checks\n * to jsonstat-validator (see wiki/validator.md and wiki/schema.md).\n */\n\n// ---------------------------------------------------------------------------\n// Primitives & shared building blocks\n// ---------------------------------------------------------------------------\n\n/** Allowed JSON-stat response classes (see wiki/response-classes.md). */\nexport type JsonStatClass = \"dataset\" | \"dimension\" | \"collection\" | \"bundle\" | \"error\";\n\n/** Semantic role assigned to one or more dimension IDs (see wiki/dimensions.md). */\nexport interface JsonStatRole {\n /** Dimension IDs carrying temporal data (chronological order assumed). */\n time?: string[];\n /** Dimension IDs carrying spatial data (categories may have coordinates). */\n geo?: string[];\n /** Dimension IDs carrying different metrics/measures (units attached). */\n metric?: string[];\n}\n\n/** Unit of measure metadata for a metric-role category (see wiki/dimensions.md). */\nexport interface JsonStatUnit {\n /** Required when `unit` is present: number of decimals. */\n decimals: number;\n /** Unit text displayed after (or before) the value. */\n label?: string;\n /** Unit symbol, e.g. \"$\" or \"%\". */\n symbol?: string;\n /** Where the symbol goes relative to the value. Defaults to \"end\". */\n position?: \"start\" | \"end\";\n}\n\n/** [longitude, latitude] pair for geo-role categories. */\nexport type Coordinates = [longitude: number, latitude: number];\n\n/**\n * Category block of a dimension.\n *\n * `index` may be an array (IDs in order) or an object (ID→position map). For\n * constant (single-category) dimensions `index` is unnecessary provided a\n * `label` is given. `label` is optional and falls back to category IDs.\n */\nexport interface JsonStatCategory {\n /** Category ordering. Array form: `[\"M\",\"F\",\"T\"]`. Object form: `{\"M\":0,...}`. */\n index?: string[] | Record<string, number>;\n /** ID→human-readable label map. If omitted, IDs are used as labels. */\n label?: Record<string, string>;\n /** Parent ID → array of direct-child IDs (hierarchies; see wiki/dimensions.md). */\n child?: Record<string, string[]>;\n /** ID → [lon, lat] for geo-role categories. */\n coordinates?: Record<string, Coordinates>;\n /** ID → unit metadata for metric-role categories. */\n unit?: Record<string, JsonStatUnit>;\n}\n\n/**\n * A dimension object within `dimension`.\n *\n * `category` is the only required property in practice for the dimension\n * class; for dataset-class dimensions it is always present.\n */\nexport interface JsonStatDimension {\n /** Short descriptive text (lowercase recommended). */\n label?: string;\n /** The categories of this dimension. */\n category?: JsonStatCategory;\n /** URL to an external dimension definition. */\n href?: string;\n /** Provider-specific extension data. */\n extension?: Record<string, unknown>;\n /** Related resources (see wiki/links.md). */\n link?: JsonStatLink[];\n /** Annotations. */\n note?: string[];\n}\n\n// ---------------------------------------------------------------------------\n// Links (IANA link relations; see wiki/links.md)\n// ---------------------------------------------------------------------------\n\n/** A single link entry. `rel` is an IANA link relation (e.g. \"item\", \"self\"). */\nexport interface JsonStatLink {\n /** IANA link relation type. */\n rel?: string;\n /** Target URL. */\n href?: string;\n /** Type of the linked resource (e.g. a class value). */\n type?: string;\n /** Human-readable label. */\n label?: string;\n /** When present, the item is embedded inline rather than referenced by href. */\n [embedded: string]: unknown;\n}\n\n/** Container for links, indexed by relation. The \"item\" relation holds an array. */\nexport interface JsonStatLinkMap {\n [rel: string]: JsonStatLink[] | undefined;\n}\n\n// ---------------------------------------------------------------------------\n// status (observation-level metadata; see wiki/dataset-structure.md)\n// ---------------------------------------------------------------------------\n\n/**\n * Observation-level status. Three forms:\n * - array: one status code per cell (aligned with `value`)\n * - string: a single status applied to all cells\n * - object: position→status for specific cells (sparse status)\n *\n * Status has no standard vocabulary — it is provider-defined.\n */\nexport type JsonStatStatus = string[] | string | Record<string, string>;\n\n// ---------------------------------------------------------------------------\n// value (dense array or sparse object; see wiki/sparse-cubes.md)\n// ---------------------------------------------------------------------------\n\n/**\n * Cell values in row-major order (\"what does not change, first\"; the last\n * dimension in `id` changes fastest).\n *\n * - Array form (dense): missing values are `null`.\n * - Object form (sparse): maps flat position index → value, omitting nulls.\n */\nexport type JsonStatValue = (number | null)[] | Record<string, number>;\n\n// ---------------------------------------------------------------------------\n// Response classes\n// ---------------------------------------------------------------------------\n\n/** Top-level dataset response (see wiki/response-classes.md). */\nexport interface JsonStatDataset {\n version: \"2.0\";\n class: \"dataset\";\n /** Short descriptive text. */\n label?: string;\n /** Short text describing the dataset source. */\n source?: string;\n /** Update time in ISO 8601. */\n updated?: string;\n /** Ordered list of dimension IDs. */\n id: string[];\n /** Number of categories per dimension, same order as `id`. */\n size: number[];\n /** Per-dimension metadata, keyed by dimension ID. */\n dimension: Record<string, JsonStatDimension>;\n /** Cell values (dense or sparse). */\n value: JsonStatValue;\n /** Observation-level status metadata. */\n status?: JsonStatStatus;\n /** Semantic roles for dimensions. */\n role?: JsonStatRole;\n /** Provider-specific extension data. */\n extension?: Record<string, unknown>;\n /** Related resources. */\n link?: JsonStatLinkMap;\n /** Annotations. */\n note?: string[];\n /** URL of the dataset. */\n href?: string;\n /** Optional error array (suggested, not mandatory). */\n error?: JsonStatError[];\n}\n\n/** Dimension-only response. */\nexport interface JsonStatDimensionResponse {\n version: \"2.0\";\n class: \"dimension\";\n label?: string;\n category: JsonStatCategory;\n extension?: Record<string, unknown>;\n link?: JsonStatLinkMap;\n href?: string;\n}\n\n/** Collection response (2.0 replacement for bundles). */\nexport interface JsonStatCollection {\n version: \"2.0\";\n class: \"collection\";\n href?: string;\n label?: string;\n updated?: string;\n link: JsonStatLinkMap;\n extension?: Record<string, unknown>;\n}\n\n/** A proposed (not mandatory) error entry. */\nexport interface JsonStatError {\n status?: number;\n id?: string;\n href?: string;\n label?: string;\n}\n\n/** Any top-level JSON-stat 2.0 response. */\nexport type JsonStatResponse =\n | JsonStatDataset\n | JsonStatDimensionResponse\n | JsonStatCollection;\n\n/** Discriminated union guard helper: is this a dataset response? */\nexport function isDataset(r: JsonStatResponse): r is JsonStatDataset {\n return r.class === \"dataset\";\n}\n\n/** Discriminated union guard helper: is this a collection response? */\nexport function isCollection(r: JsonStatResponse): r is JsonStatCollection {\n return r.class === \"collection\";\n}\n\n/** Discriminated union guard helper: is this a dimension response? */\nexport function isDimensionResponse(r: JsonStatResponse): r is JsonStatDimensionResponse {\n return r.class === \"dimension\";\n}\n","/**\n * Observations IR — the format-agnostic intermediate representation.\n *\n * Every source (Arrow, Parquet, DuckDB, Polars, CSVW, CSV) converts its data\n * into this IR. The cube builder ([`CubeBuilder`](../core/cubeBuilder.ts))\n * then turns the IR into a JSON-stat [`Dataset`](./jsonstat.ts).\n *\n * The IR is deliberately a **long / tidy table**: one row per observation,\n * with one column per dimension and a single measure column. This is the\n * natural shape of the columnar stack and the dual of a JSON-stat cube.\n *\n * Because the IR is bidirectional, the same model also powers the Phase-2\n * export path ([`cubeReader`](../core/cubeReader.ts) → IR →\n * [`arrowFromCube`](../arrow/arrowFromCube.ts) → Arrow/Parquet/CSV). See\n * [docs/architecture.md](../../docs/architecture.md) §\"The two documented seams\".\n */\n\nimport type { JsonStatRole, JsonStatUnit } from \"./jsonstat\";\n\n// ---------------------------------------------------------------------------\n// Columns\n// ---------------------------------------------------------------------------\n\n/**\n * A dimension column: the observed category *labels or IDs* for each row.\n *\n * `values` is a parallel array (one entry per observation) of category IDs.\n * Category order is *not* implied here — it is resolved by the builder, which\n * enumerates distinct values and assigns indices. Use `categoryOrder` to pin\n * a specific order (e.g. chronological time) when the source knows it.\n */\nexport interface DimensionColumn {\n /** Dimension ID (becomes a key in JSON-stat `dimension`). */\n id: string;\n /** Human-readable dimension label. */\n label?: string;\n /** Per-row category ID. `null` is not permitted for dimensions. */\n values: string[];\n /**\n * Optional explicit category ordering. When omitted the builder enumerates\n * distinct values in first-seen order. Provide for time dimensions whose\n * chronological order is not lexicographic.\n */\n categoryOrder?: string[];\n /** ID → human-readable category label. Falls back to the ID. */\n categoryLabels?: Record<string, string>;\n /** ID → unit metadata (metric-role dimensions only). */\n categoryUnits?: Record<string, JsonStatUnit>;\n /** ID → [lon, lat] (geo-role dimensions only). */\n categoryCoordinates?: Record<string, [number, number]>;\n /** Parent ID → direct child IDs (hierarchical dimensions only). */\n categoryChild?: Record<string, string[]>;\n /** Optional href to an external dimension definition. */\n href?: string;\n}\n\n/**\n * The measure column: the numeric observation values, one per row.\n *\n * `null` represents a missing observation. The importer supports a single\n * measure column in v0.1 — multi-measure datasets should be split across\n * multiple datasets (one per metric) or modeled as a metric *dimension*,\n * matching how JSON-stat treats measures as a dimension with the `metric`\n * role rather than as separate value arrays.\n */\nexport interface MeasureColumn {\n /** Column name in the source (kept for round-trip fidelity, not emitted). */\n name?: string;\n /** Numeric values; `null` = missing. */\n values: (number | null)[];\n}\n\n/**\n * Optional per-row status column. Provider-defined vocabulary; emitted verbatim\n * as JSON-stat `status` (array form) by the builder.\n */\nexport interface StatusColumn {\n /** Per-row status code. */\n values: string[];\n}\n\n// ---------------------------------------------------------------------------\n// Roles & cube model\n// ---------------------------------------------------------------------------\n\n/**\n * Role assignment. Mirrors JSON-stat's `role` object: each role maps to a list\n * of dimension IDs. The importer assigns roles from source metadata or CLI\n * hints; unassigned dimensions default to the implicit \"classification\" role.\n */\nexport type RoleMap = JsonStatRole;\n\n/**\n * Dataset-level metadata carried alongside the observations.\n *\n * These fields map directly to top-level JSON-stat dataset properties\n * (`label`, `source`, `updated`, `extension`).\n */\nexport interface DatasetMeta {\n label?: string;\n source?: string;\n updated?: string;\n extension?: Record<string, unknown>;\n}\n\n/**\n * The complete cube model: everything the builder needs besides the raw\n * observation columns to produce a fully-specified JSON-stat dataset.\n */\nexport interface CubeModel {\n /** Ordered dimension IDs. Determines row-major stride order. */\n dimensionIds: string[];\n /** Role assignments (time/geo/metric). */\n roles?: RoleMap;\n /** Dataset-level metadata. */\n meta?: DatasetMeta;\n /**\n * How to emit `value`.\n * - \"auto\": dense unless null-ratio exceeds `sparseThreshold`.\n * - \"dense\": always array form.\n * - \"sparse\": always object form.\n */\n valueForm?: \"auto\" | \"dense\" | \"sparse\";\n /** Null ratio above which \"auto\" chooses sparse. Default 0.5. */\n sparseThreshold?: number;\n /**\n * Whether to emit a status array. When a StatusColumn is present and this is\n * not \"none\", the builder emits status. \"object\" emits the sparse object form\n * when there are few non-default statuses; \"string\" collapses a uniform\n * status to a single string.\n */\n statusForm?: \"auto\" | \"array\" | \"string\" | \"object\" | \"none\";\n}\n\n// ---------------------------------------------------------------------------\n// The IR\n// ---------------------------------------------------------------------------\n\n/**\n * The Observations IR: a tidy long table plus its cube model.\n *\n * Invariants the builder assumes (validated in [`CubeBuilder.build`]):\n * - All columns in `dimensions` and `measure` share the same length `n`.\n * - `status`, if present, has length `n`.\n * - `model.dimensionIds` lists every dimension column id, and no others.\n * - No dimension value is `null` or `undefined`.\n */\nexport interface Observations {\n /** Dimension columns, keyed by dimension ID. */\n dimensions: Record<string, DimensionColumn>;\n /** The single measure column. */\n measure: MeasureColumn;\n /** Optional per-row status. */\n status?: StatusColumn;\n /** The cube model governing how to build the JSON-stat dataset. */\n model: CubeModel;\n}\n\n/** Number of observations (rows). */\nexport function observationCount(obs: Observations): number {\n return obs.measure.values.length;\n}\n","/**\n * Serialize — turn a JSON-stat [`Dataset`](../model/jsonstat.ts) into a JSON\n * string or Buffer for writing to stdout/file.\n *\n * Also offers a lightweight \"pretty\" mode that sorts top-level keys in the\n * canonical JSON-stat order (version, class, label, href, source, updated,\n * id, size, dimension, role, value, status, …) so output is stable and\n * diff-friendly — useful for fixtures and round-trip tests.\n */\n\nimport type { JsonStatDataset, JsonStatResponse } from \"../model/jsonstat\";\n\n// Canonical top-level key order for pretty output.\nconst KEY_ORDER = [\n \"version\",\n \"class\",\n \"label\",\n \"href\",\n \"source\",\n \"updated\",\n \"id\",\n \"size\",\n \"dimension\",\n \"role\",\n \"value\",\n \"status\",\n \"extension\",\n \"link\",\n \"note\",\n \"error\",\n];\n\nexport interface SerializeOptions {\n /** Pretty-print with 2-space indentation (default true). */\n pretty?: boolean;\n /** Reorder top-level keys canonically (default true, only with pretty). */\n canonicalKeys?: boolean;\n}\n\n/**\n * Serialize a JSON-stat response to a JSON string.\n *\n * @param response The dataset (or any JSON-stat response).\n */\nexport function serialize(\n response: JsonStatResponse | JsonStatDataset,\n options: SerializeOptions = {},\n): string {\n const pretty = options.pretty ?? true;\n const canonical = options.canonicalKeys ?? true;\n\n let value: unknown = response;\n if (pretty && canonical) {\n value = reorderKeys(response as object, KEY_ORDER);\n }\n\n return pretty ? JSON.stringify(value, null, 2) : JSON.stringify(value);\n}\n\n/**\n * Serialize and encode as UTF-8 bytes (Uint8Array). Handy for file writing\n * without pulling in Node's Buffer in browser contexts.\n */\nexport function serializeToBytes(\n response: JsonStatResponse | JsonStatDataset,\n options?: SerializeOptions,\n): Uint8Array {\n return new TextEncoder().encode(serialize(response, options));\n}\n\n/**\n * Reorder an object's keys so that known keys appear in `order`, and any\n * unknown keys follow alphabetically. Only reshuffles top-level keys (does not\n * recurse), to avoid disturbing category/index ordering.\n */\nfunction reorderKeys(obj: object, order: string[]): Record<string, unknown> {\n const source = obj as Record<string, unknown>;\n const out: Record<string, unknown> = {};\n const known = new Set<string>();\n for (const k of order) {\n if (k in source) {\n out[k] = source[k];\n known.add(k);\n }\n }\n const rest = Object.keys(source)\n .filter((k) => !known.has(k))\n .sort();\n for (const k of rest) out[k] = source[k];\n return out;\n}\n","/**\n * Format detection — sniffs the input source to pick the right adapter.\n *\n * Two signals are used, in order of reliability:\n * 1. **Magic bytes** (content sniffing) — authoritative for binary formats\n * (Parquet `PAR1`, Arrow IPC `ARROW1`).\n * 2. **File extension** — used when content is not yet available (e.g. a path\n * or URL) or for text formats (`.csv`, `.csvw`).\n */\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport type SourceFormat =\n | \"parquet\"\n | \"arrow\"\n | \"csv\"\n | \"csvw\"\n | \"jsonstat\"\n | \"json\"\n | \"unknown\";\n\n// ---------------------------------------------------------------------------\n// Magic bytes\n// ---------------------------------------------------------------------------\n\n/** Parquet files begin with `PAR1`. */\nconst PARQUET_MAGIC = [0x50, 0x41, 0x52, 0x31]; // \"PAR1\"\n/** Arrow IPC files begin with `ARROW1\\0\\0`. */\nconst ARROW_MAGIC = [0x41, 0x52, 0x52, 0x4f, 0x57, 0x31]; // \"ARROW1\"\n\nfunction bytesStartWith(bytes: Uint8Array, magic: number[]): boolean {\n if (bytes.length < magic.length) return false;\n for (let i = 0; i < magic.length; i++) {\n if (bytes[i] !== magic[i]) return false;\n }\n return true;\n}\n\n/**\n * Sniff the format from the leading bytes of a binary/text input.\n *\n * @returns The detected format, or \"unknown\" if no magic matches.\n */\nexport function detectFromBytes(bytes: Uint8Array | ArrayBuffer): SourceFormat {\n const view = bytes instanceof ArrayBuffer ? new Uint8Array(bytes) : bytes;\n if (bytesStartWith(view, PARQUET_MAGIC)) return \"parquet\";\n if (bytesStartWith(view, ARROW_MAGIC)) return \"arrow\";\n // JSON-stat / JSON text: starts with `{` (after optional BOM/whitespace).\n const head = view.slice(0, Math.min(64, view.length));\n const text = new TextDecoder().decode(head).trimStart();\n if (text.startsWith(\"{\") || text.startsWith(\"[\")) {\n // Heuristic: if it mentions \"json-stat\" or `\"class\"` + `\"version\"`, JSON-stat.\n if (text.includes(\"json-stat\") || (text.includes('\"class\"') && text.includes('\"version\"'))) {\n return \"jsonstat\";\n }\n return \"json\";\n }\n // CSV: presence of a delimiter and typical header-ish content is weak; defer\n // to extension if available. Mark as \"csv\" only as a fallback guess.\n if (text.includes(\",\") || text.includes(\";\") || text.includes(\"\\t\")) return \"csv\";\n return \"unknown\";\n}\n\n// ---------------------------------------------------------------------------\n// Extension\n// ---------------------------------------------------------------------------\n\n/** Map a file extension (without dot) to a source format. */\nexport function detectFromExtension(ext: string): SourceFormat {\n const e = ext.toLowerCase().replace(/^\\./, \"\");\n switch (e) {\n case \"parquet\":\n return \"parquet\";\n case \"arrow\":\n case \"ipc\":\n case \"feather\":\n return \"arrow\";\n case \"csv\":\n return \"csv\";\n case \"csvw\":\n case \"csv-metadata\":\n return \"csvw\";\n case \"json-stat\":\n case \"jsonstat\":\n return \"jsonstat\";\n case \"json\":\n return \"jsonstat\"; // assume JSON-stat by default for .json\n default:\n return \"unknown\";\n }\n}\n\n/** Extract the extension from a path/URL (without the dot), or undefined. */\nexport function extensionOf(path: string): string | undefined {\n // Strip query string / fragment.\n const clean = path.split(\"?\")[0]?.split(\"#\")[0] ?? path;\n const dot = clean.lastIndexOf(\".\");\n const slash = Math.max(clean.lastIndexOf(\"/\"), clean.lastIndexOf(\"\\\\\"));\n if (dot === -1 || dot < slash) return undefined;\n return clean.slice(dot + 1);\n}\n\n/**\n * Detect a format from a filename/URL, falling back to bytes if the extension\n * is unknown or ambiguous.\n */\nexport function detectFormat(\n pathOrUrl: string | undefined,\n bytes?: Uint8Array | ArrayBuffer,\n): SourceFormat {\n if (pathOrUrl) {\n const ext = extensionOf(pathOrUrl);\n if (ext) {\n const byExt = detectFromExtension(ext);\n if (byExt !== \"unknown\") return byExt;\n }\n }\n if (bytes) return detectFromBytes(bytes);\n return \"unknown\";\n}\n","/**\n * Density helper — sparse vs dense `value` decision.\n *\n * Centralizes the null-ratio threshold logic so the cube builder and tests\n * share one definition. See wiki/sparse-cubes.md for the rationale: the object\n * (sparse) form is preferred when many cells are null.\n */\n\nexport interface DensityResult {\n form: \"dense\" | \"sparse\";\n nullRatio: number;\n nullCount: number;\n total: number;\n}\n\nexport interface DensityOptions {\n /** Null ratio above which \"auto\" chooses sparse. Default 0.5. */\n threshold?: number;\n}\n\n/**\n * Decide dense vs sparse for a flat value array.\n *\n * @param values The dense row-major array (with nulls).\n * @param requested \"auto\" | \"dense\" | \"sparse\".\n */\nexport function decideDensity(\n values: (number | null)[],\n requested: \"auto\" | \"dense\" | \"sparse\" = \"auto\",\n options: DensityOptions = {},\n): DensityResult {\n const total = values.length;\n const threshold = options.threshold ?? 0.5;\n let nullCount = 0;\n for (const v of values) {\n if (v === null) nullCount++;\n }\n const nullRatio = total === 0 ? 0 : nullCount / total;\n\n let form: \"dense\" | \"sparse\";\n if (requested === \"dense\") form = \"dense\";\n else if (requested === \"sparse\") form = \"sparse\";\n else form = nullRatio > threshold ? \"sparse\" : \"dense\";\n\n return { form, nullRatio, nullCount, total };\n}\n","/**\n * Isomorphic input loader — works in both Node and the browser.\n *\n * Resolves a source specifier (file path, URL, or raw bytes/text) into a\n * `Uint8Array` of bytes and, where possible, a hint about its origin (path/URL)\n * so format detection can use the extension.\n *\n * - **Node**: reads from `node:fs` for local paths; uses `fetch` for URLs and\n * for the browser. Reading from stdin is supported via the special source\n * `\"-\"`.\n * - **Browser**: uses the global `fetch` for URLs and `File`/`Blob` inputs;\n * local paths are not available.\n *\n * The Node-specific `fs`/`stdin` imports are guarded by a runtime check so the\n * same code path works in browsers without bundling `node:fs`.\n */\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport interface LoadedInput {\n bytes: Uint8Array;\n /** The original source path/URL, if known (used for extension detection). */\n source?: string;\n /** True if the source was stdin (\"-\"). */\n fromStdin?: boolean;\n}\n\n// ---------------------------------------------------------------------------\n// Environment detection\n// ---------------------------------------------------------------------------\n\nfunction isNode(): boolean {\n return (\n typeof process !== \"undefined\" &&\n typeof process.versions !== \"undefined\" &&\n typeof process.versions.node === \"string\"\n );\n}\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\n/**\n * Load input bytes from a path, URL, `\"-\"` (stdin), `Uint8Array`, `Blob`, or\n * string.\n *\n * @param source A file path, `http(s)://` URL, `\"-\"` for stdin, or raw data.\n * @throws If the source cannot be read.\n */\nexport async function loadInput(source: string | Uint8Array | Blob): Promise<LoadedInput> {\n // Already bytes.\n if (source instanceof Uint8Array) {\n return { bytes: source };\n }\n // Blob / File (browser & undici).\n if (typeof Blob !== \"undefined\" && source instanceof Blob) {\n const buf = await source.arrayBuffer();\n return { bytes: new Uint8Array(buf), source: (source as File).name };\n }\n if (typeof source !== \"string\") {\n throw new TypeError(`loadInput: unsupported source type ${typeof source}`);\n }\n\n // stdin.\n if (source === \"-\") {\n if (!isNode()) {\n throw new Error('loadInput: stdin (\"-\") is only supported in Node');\n }\n const bytes = await readStdin();\n return { bytes, fromStdin: true };\n }\n\n // URL.\n if (/^https?:\\/\\//i.test(source) || /^file:\\/\\//i.test(source)) {\n const res = await fetch(source);\n if (!res.ok) {\n throw new Error(`loadInput: fetch ${source} failed: ${res.status} ${res.statusText}`);\n }\n const buf = await res.arrayBuffer();\n return { bytes: new Uint8Array(buf), source };\n }\n\n // Local file path (Node only).\n if (isNode()) {\n const bytes = await readFileNode(source);\n return { bytes, source };\n }\n\n throw new Error(\n `loadInput: cannot resolve \"${source}\" in a browser without a URL scheme`,\n );\n}\n\n// ---------------------------------------------------------------------------\n// Node helpers (lazy imports)\n// ---------------------------------------------------------------------------\n\nasync function readStdin(): Promise<Uint8Array> {\n // Use process.stdin directly — avoids the PathLike type mismatch that\n // createReadStream(0) triggers under @types/node, and is more idiomatic.\n const chunks: Uint8Array[] = [];\n for await (const chunk of process.stdin) {\n chunks.push(chunk instanceof Uint8Array ? chunk : new Uint8Array(chunk));\n }\n const total = chunks.reduce((n, c) => n + c.length, 0);\n const out = new Uint8Array(total);\n let off = 0;\n for (const c of chunks) {\n out.set(c, off);\n off += c.length;\n }\n return out;\n}\n\nasync function readFileNode(path: string): Promise<Uint8Array> {\n const { readFile } = await import(\"node:fs/promises\");\n const buf = await readFile(path);\n return new Uint8Array(buf.buffer, buf.byteOffset, buf.byteLength);\n}\n","/**\n * jsonstat-io — public API surface (main entry).\n *\n * This package is a **bidirectional** bridge between JSON-stat 2.0 and the\n * columnar stack (Arrow, Parquet, DuckDB, Polars, CSVW, CSV):\n *\n * - **Import** (columnar → JSON-stat): [`importToCube`](#importtocube) /\n * [`importToDataset`](#importtodataset) — load, auto-detect, convert, build.\n * - **Export** (JSON-stat → columnar): [`exportDataset`](#exportdataset) — read\n * the JSON-stat cube into the IR and route to the requested sink writer.\n *\n * This barrel re-exports the **always loaded** layers: the model types, the\n * core cube engine (strides, builder, reader), the Arrow hub, the sink, and the\n * isomorphic utilities (format detection, input loading, density decision).\n *\n * Source/sink adapters that pull in optional peer dependencies live behind\n * **subpath exports** so that browser bundles stay lean and tree-shakeable:\n *\n * - `jsonstat-io/arrow` — Arrow hub (re-exported here too, since\n * `apache-arrow` is a hard dependency, not an optional peer).\n * - `jsonstat-io/parquet` — Parquet ↔ Arrow ↔ cube (needs `parquet-wasm`).\n * - `jsonstat-io/duckdb` — DuckDB ↔ Arrow ↔ cube (needs duckdb).\n * - `jsonstat-io/polars` — Polars ↔ Arrow ↔ cube (Node-only).\n * - `jsonstat-io/csvw` — CSVW + CSV metadata-aware path (no deps).\n * - `jsonstat-io/csv` — plain CSV inference path (no deps).\n *\n * For a one-call convenience, see the dispatchers:\n * - Import: [`importToCube`](#importtocube) / [`importToDataset`](#importtodataset).\n * - Export: [`exportDataset`](#exportdataset).\n *\n * @packageDocumentation\n */\n\n// ---------------------------------------------------------------------------\n// Model layer (pure types + guards, zero runtime cost)\n// ---------------------------------------------------------------------------\nexport type {\n JsonStatClass,\n JsonStatRole,\n JsonStatUnit,\n Coordinates,\n JsonStatCategory,\n JsonStatDimension,\n JsonStatLink,\n JsonStatLinkMap,\n JsonStatStatus,\n JsonStatValue,\n JsonStatDataset,\n JsonStatDimensionResponse,\n JsonStatCollection,\n JsonStatError,\n JsonStatResponse,\n} from \"./model/jsonstat\";\nexport { isDataset, isCollection, isDimensionResponse } from \"./model/jsonstat\";\n\nexport type {\n DimensionColumn,\n MeasureColumn,\n StatusColumn,\n RoleMap,\n DatasetMeta,\n CubeModel,\n Observations,\n} from \"./model/ir\";\nexport { observationCount } from \"./model/ir\";\n\n// ---------------------------------------------------------------------------\n// Core engine (strides, builder, reader)\n// ---------------------------------------------------------------------------\nexport {\n strides,\n totalCells,\n flatPosition,\n multiIndex,\n enumerateCells,\n} from \"./core/strides\";\n\nexport { CubeBuilderError } from \"./core/cubeBuilder\";\nexport type { BuildOptions, BuildResult } from \"./core/cubeBuilder\";\nexport { buildDataset, toDataset } from \"./core/cubeBuilder\";\n\nexport { CubeReaderError } from \"./core/cubeReader\";\nexport type { ReadOptions } from \"./core/cubeReader\";\nexport { readDataset, readResponse } from \"./core/cubeReader\";\n\n// ---------------------------------------------------------------------------\n// Arrow hub (hard dependency, so safe to re-export from the main entry)\n// ---------------------------------------------------------------------------\nexport {\n META_PREFIX,\n getFieldMeta,\n getFieldMetaJson,\n isMeasureField,\n isStatusField,\n getFieldRole,\n buildFieldMeta,\n readSchemaMeta,\n buildSchemaMeta,\n} from \"./arrow/schemaMeta\";\n\nexport { ArrowConversionError } from \"./arrow/arrowToCube\";\nexport type { ArrowToCubeOptions } from \"./arrow/arrowToCube\";\nexport { arrowToCube, arrowToDataset } from \"./arrow/arrowToCube\";\n\nexport { cubeToArrow } from \"./arrow/arrowFromCube\";\n\n// ---------------------------------------------------------------------------\n// Sink (serialization)\n// ---------------------------------------------------------------------------\nexport type { SerializeOptions } from \"./sink/serialize\";\nexport { serialize, serializeToBytes } from \"./sink/serialize\";\n\n// ---------------------------------------------------------------------------\n// Isomorphic utilities\n// ---------------------------------------------------------------------------\nexport type { SourceFormat } from \"./util/detect\";\nexport {\n detectFromBytes,\n detectFromExtension,\n extensionOf,\n detectFormat,\n} from \"./util/detect\";\n\nexport type { DensityResult, DensityOptions } from \"./util/density\";\nexport { decideDensity } from \"./util/density\";\n\nexport type { LoadedInput } from \"./util/fetch\";\nexport { loadInput } from \"./util/fetch\";\n\n// ---------------------------------------------------------------------------\n// High-level convenience: load → detect → convert → build\n// ---------------------------------------------------------------------------\n\nimport { tableFromIPC } from \"apache-arrow\";\nimport type { Table } from \"apache-arrow\";\nimport { buildDataset } from \"./core/cubeBuilder\";\nimport type { BuildOptions, BuildResult } from \"./core/cubeBuilder\";\nimport type { JsonStatDataset } from \"./model/jsonstat\";\nimport type { Observations } from \"./model/ir\";\nimport { detectFormat, detectFromBytes } from \"./util/detect\";\nimport type { SourceFormat } from \"./util/detect\";\nimport { loadInput } from \"./util/fetch\";\nimport type { LoadedInput } from \"./util/fetch\";\nimport { arrowToCube } from \"./arrow/arrowToCube\";\nimport { cubeToArrow } from \"./arrow/arrowFromCube\";\nimport type { ArrowToCubeOptions } from \"./arrow/arrowToCube\";\nimport type { CsvwMetadata } from \"./sources/csvw\";\n\n/**\n * Options for the high-level [`importToCube`](#importtocube) /\n * [`importToDataset`](#importtodataset) dispatchers.\n *\n * These combine format detection, source loading, and Arrow-hub conversion\n * hints into a single call.\n */\nexport interface ImportOptions extends ArrowToCubeOptions {\n /**\n * Force a source format instead of auto-detecting. One of `\"parquet\"`,\n * `\"arrow\"`, `\"csv\"`, `\"csvw\"`, `\"jsonstat\"`, `\"json\"`. When omitted the\n * format is sniffed from magic bytes (Parquet `PAR1`, Arrow IPC `ARROW1`)\n * and then from the file extension.\n */\n from?: SourceFormat | \"auto\";\n /** Passed through to [`buildDataset`](./core/cubeBuilder.ts) as `BuildOptions`. */\n build?: BuildOptions;\n /**\n * CSVW metadata, when the source is CSVW. If `from === \"csvw\"` and this is\n * omitted, the dispatcher attempts to load a sibling `*-metadata.json` next\n * to the CSV path (Node only). In the browser the caller must supply it.\n */\n csvwMetadata?: unknown;\n /**\n * CSV delimiter (default `,`). Only used when the source is plain CSV.\n */\n delimiter?: string;\n}\n\n/**\n * Error thrown by the high-level dispatchers when a format cannot be detected,\n * a required peer dependency is missing, or the source produces no data.\n */\nexport class ImporterError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"ImporterError\";\n }\n}\n\n/**\n * Load a columnar source (file path, URL, stdin `\"-\"`, `Uint8Array`, or `Blob`)\n * and convert it to the [`Observations`](./model/ir.ts) IR.\n *\n * This is the one-call convenience that ties together\n * [`loadInput`](./util/fetch.ts), [`detectFormat`](./util/detect.ts), the\n * appropriate source adapter, and the Arrow hub. It auto-detects the format\n * unless [`options.from`](#ImportOptions.from) is set.\n *\n * Parquet/DuckDB/Polars adapters are imported lazily, so they are only pulled\n * into the bundle when actually needed.\n *\n * @param source A file path, `http(s)://` URL, `\"-\"` for stdin (Node),\n * `Uint8Array`, or `Blob`.\n * @returns The [`Observations`](./model/ir.ts) IR, ready for\n * [`buildDataset`](./core/cubeBuilder.ts).\n *\n * @example\n * ```ts\n * import { importToDataset } from \"jsonstat-io\";\n * const dataset = await importToDataset(\"./sales.parquet\");\n * console.log(JSON.stringify(dataset, null, 2));\n * ```\n */\nexport async function importToCube(\n source: string | Uint8Array | Blob,\n options: ImportOptions = {},\n): Promise<Observations> {\n const loaded: LoadedInput = await loadInput(source);\n\n // --- Resolve the format -------------------------------------------------\n let format: SourceFormat;\n if (options.from && options.from !== \"auto\") {\n format = options.from;\n } else if (loaded.source) {\n format = detectFormat(loaded.source, loaded.bytes);\n } else {\n format = detectFromBytes(loaded.bytes);\n }\n\n // --- Dispatch to the right adapter --------------------------------------\n switch (format) {\n case \"arrow\": {\n const table = tableFromIPC(loaded.bytes);\n return arrowToCube(table, options);\n }\n\n case \"parquet\": {\n // Lazy import keeps parquet-wasm out of browser bundles that don't use it.\n const { parquetToCube } = await import(\"./sources/parquet\");\n return parquetToCube(loaded.bytes, options);\n }\n\n case \"csv\": {\n const { csvToCube } = await import(\"./sources/csv\");\n const text = new TextDecoder().decode(loaded.bytes);\n return csvToCube(text, {\n measure: options.measure,\n dimensions: options.dimensions,\n status: options.status,\n roles: options.roles,\n delimiter: options.delimiter,\n });\n }\n\n case \"csvw\": {\n const { csvwToCube, parseCsvwMetadata } = await import(\"./sources/csvw\");\n const text = new TextDecoder().decode(loaded.bytes);\n let metadata: CsvwMetadata | undefined;\n if (options.csvwMetadata !== undefined) {\n // Validate caller-supplied metadata through the same parser.\n metadata = parseCsvwMetadata(options.csvwMetadata);\n } else if (loaded.source) {\n // Try a sibling *-metadata.json (common CSVW convention), Node only.\n try {\n const metaPath = siblingMetadataPath(loaded.source);\n const metaBytes = await loadInput(metaPath);\n metadata = parseCsvwMetadata(\n JSON.parse(new TextDecoder().decode(metaBytes.bytes)),\n );\n } catch {\n throw new ImporterError(\n `CSVW metadata not supplied and could not be auto-loaded next to \"${loaded.source}\". ` +\n 'Pass options.csvwMetadata or set options.from = \"csv\".',\n );\n }\n }\n if (metadata === undefined) {\n throw new ImporterError(\n \"CSVW source requires metadata. Pass options.csvwMetadata.\",\n );\n }\n return csvwToCube(text, metadata, {\n measure: options.measure,\n dimensions: options.dimensions,\n status: options.status,\n roles: options.roles,\n });\n }\n\n case \"jsonstat\":\n case \"json\": {\n // A JSON-stat input: read it back into the IR via the cube reader.\n // This supports the round-trip / re-emission use case.\n const { readDataset } = await import(\"./core/cubeReader\");\n const parsed = JSON.parse(new TextDecoder().decode(loaded.bytes));\n return readDataset(parsed, { dropNulls: false });\n }\n\n default:\n throw new ImporterError(\n `Could not detect the source format. Set options.from explicitly ` +\n '(e.g. \"parquet\", \"arrow\", \"csv\", \"csvw\", \"jsonstat\"). ' +\n \"DuckDB and Polars require a live connection/DataFrame — use the \" +\n 'dedicated subpath import (e.g. import { duckdbToCube } from \"jsonstat-io/duckdb\").',\n );\n }\n}\n\n/**\n * Like [`importToCube`](#importtocube) but also builds the final\n * [`JsonStatDataset`](./model/jsonstat.ts) via [`buildDataset`](./core/cubeBuilder.ts).\n *\n * @returns The serialized-ready [`JsonStatDataset`](./model/jsonstat.ts).\n */\nexport async function importToDataset(\n source: string | Uint8Array | Blob,\n options: ImportOptions = {},\n): Promise<JsonStatDataset> {\n const obs = await importToCube(source, options);\n const result: BuildResult = buildDataset(obs, options.build);\n return result.dataset;\n}\n\n/**\n * Build the conventional sibling metadata path for a CSVW CSV file.\n * `data/sales.csv` → `data/sales-metadata.json`.\n */\nfunction siblingMetadataPath(csvPath: string): string {\n const dot = csvPath.lastIndexOf(\".\");\n const base = dot > 0 ? csvPath.slice(0, dot) : csvPath;\n return `${base}-metadata.json`;\n}\n\n// ---------------------------------------------------------------------------\n// High-level convenience: JSON-stat → columnar (export / Phase 2)\n// ---------------------------------------------------------------------------\n\n/** Supported export sinks for [`exportDataset`](#exportdataset). */\nexport type ExportTarget = \"arrow\" | \"parquet\" | \"csv\" | \"csvw\";\n\n/**\n * Options for [`exportDataset`](#exportdataset).\n *\n * Format-specific knobs are passed through to the underlying writer. For\n * Parquet, `init` is forwarded to `parquet-wasm`; for CSVW, `url` sets the\n * metadata `url`. DuckDB and Polars are intentionally not exposed here because\n * they require a live connection / native module — use the dedicated subpath\n * exports (`jsonstat-io/duckdb`, `jsonstat-io/polars`) for those.\n */\nexport interface ExportOptions {\n to: ExportTarget;\n /** Parquet compression codec (forwarded to `parquet-wasm` `writeParquet`). */\n compression?: string;\n /** CSV / CSVW delimiter (default \",\"). */\n delimiter?: string;\n /** CSVW metadata `url` field. */\n url?: string;\n /** Line terminator for CSV / CSVW output (default \"\\r\\n\"). */\n lineTerminator?: string;\n /** Async initializer for parquet-wasm (browser base URL, etc.). */\n init?: () => Promise<void>;\n}\n\n/**\n * The polymorphic result of [`exportDataset`](#exportdataset). The concrete\n * shape depends on [`ExportOptions.to`](#ExportOptions.to):\n *\n * - `\"arrow\"` → an Apache Arrow [`Table`](https://arrow.apache.org/docs/js/).\n * - `\"parquet\"` → a `Uint8Array` of the Parquet file bytes.\n * - `\"csv\"` → the CSV text (`string`).\n * - `\"csvw\"` → `{ csv: string; metadata: CsvwMetadata }`.\n */\nexport type ExportResult = Table | Uint8Array | string | CsvwExportShape;\n\n/** Shape of the `\"csvw\"` export result (mirrors `csvw.CsvwExport`). */\nexport interface CsvwExportShape {\n csv: string;\n metadata: unknown;\n}\n\n/**\n * Export a JSON-stat [`Dataset`](./model/jsonstat.ts) to a columnar format.\n *\n * This is the high-level export dispatcher, the mirror of\n * [`importToDataset`](#importtodataset). It reads the dataset into the\n * [`Observations`](./model/ir.ts) IR via [`readDataset`](./core/cubeReader.ts),\n * then routes to the appropriate writer:\n *\n * - `\"arrow\"` → [`cubeToArrow`](./arrow/arrowFromCube.ts) → Arrow `Table`.\n * - `\"parquet\"` → Arrow → `parquet-wasm` `writeParquet` → `Uint8Array`.\n * - `\"csv\"` → [`cubeToCsv`](./sources/csv.ts) → CSV text.\n * - `\"csvw\"` → [`cubeToCsvw`](./sources/csvw.ts) → `{ csv, metadata }`.\n *\n * DuckDB and Polars require a live connection / native module and are not\n * reachable from this dispatcher — use the dedicated subpath writers\n * (`cubeToDuckdb`, `cubeToPolars`) directly.\n *\n * @example\n * ```ts\n * import { exportDataset } from \"jsonstat-io\";\n * const bytes = await exportDataset(dataset, { to: \"parquet\" });\n * ```\n */\nexport async function exportDataset(\n dataset: JsonStatDataset,\n options: ExportOptions,\n): Promise<ExportResult> {\n // Lazy import keeps the reader out of the import-only bundle path.\n const { readDataset } = await import(\"./core/cubeReader\");\n const obs = readDataset(dataset, { dropNulls: false });\n\n switch (options.to) {\n case \"arrow\": {\n return cubeToArrow(obs);\n }\n case \"parquet\": {\n const { cubeToParquet } = await import(\"./sources/parquet\");\n return cubeToParquet(obs, {\n init: options.init,\n compression: options.compression,\n });\n }\n case \"csv\": {\n const { cubeToCsv } = await import(\"./sources/csv\");\n return cubeToCsv(obs, {\n delimiter: options.delimiter,\n lineTerminator: options.lineTerminator,\n });\n }\n case \"csvw\": {\n const { cubeToCsvw } = await import(\"./sources/csvw\");\n return cubeToCsvw(obs, {\n delimiter: options.delimiter,\n lineTerminator: options.lineTerminator,\n url: options.url,\n });\n }\n default:\n throw new ImporterError(\n `Unsupported export target \"${(options as { to?: string }).to}\". ` +\n 'Use one of: \"arrow\", \"parquet\", \"csv\", \"csvw\". ' +\n \"For DuckDB/Polars, use the dedicated subpath writers.\",\n );\n }\n}\n"]}
@@ -0,0 +1,335 @@
1
+ import { buildDataset } from './chunk-2HY7NRKX.js';
2
+ import { arrowToCube, cubeToArrow } from './chunk-IDTQZG4B.js';
3
+ import { tableFromIPC } from 'apache-arrow';
4
+
5
+ // src/model/jsonstat.ts
6
+ function isDataset(r) {
7
+ return r.class === "dataset";
8
+ }
9
+ function isCollection(r) {
10
+ return r.class === "collection";
11
+ }
12
+ function isDimensionResponse(r) {
13
+ return r.class === "dimension";
14
+ }
15
+
16
+ // src/model/ir.ts
17
+ function observationCount(obs) {
18
+ return obs.measure.values.length;
19
+ }
20
+
21
+ // src/sink/serialize.ts
22
+ var KEY_ORDER = [
23
+ "version",
24
+ "class",
25
+ "label",
26
+ "href",
27
+ "source",
28
+ "updated",
29
+ "id",
30
+ "size",
31
+ "dimension",
32
+ "role",
33
+ "value",
34
+ "status",
35
+ "extension",
36
+ "link",
37
+ "note",
38
+ "error"
39
+ ];
40
+ function serialize(response, options = {}) {
41
+ const pretty = options.pretty ?? true;
42
+ const canonical = options.canonicalKeys ?? true;
43
+ let value = response;
44
+ if (pretty && canonical) {
45
+ value = reorderKeys(response, KEY_ORDER);
46
+ }
47
+ return pretty ? JSON.stringify(value, null, 2) : JSON.stringify(value);
48
+ }
49
+ function serializeToBytes(response, options) {
50
+ return new TextEncoder().encode(serialize(response, options));
51
+ }
52
+ function reorderKeys(obj, order) {
53
+ const source = obj;
54
+ const out = {};
55
+ const known = /* @__PURE__ */ new Set();
56
+ for (const k of order) {
57
+ if (k in source) {
58
+ out[k] = source[k];
59
+ known.add(k);
60
+ }
61
+ }
62
+ const rest = Object.keys(source).filter((k) => !known.has(k)).sort();
63
+ for (const k of rest) out[k] = source[k];
64
+ return out;
65
+ }
66
+
67
+ // src/util/detect.ts
68
+ var PARQUET_MAGIC = [80, 65, 82, 49];
69
+ var ARROW_MAGIC = [65, 82, 82, 79, 87, 49];
70
+ function bytesStartWith(bytes, magic) {
71
+ if (bytes.length < magic.length) return false;
72
+ for (let i = 0; i < magic.length; i++) {
73
+ if (bytes[i] !== magic[i]) return false;
74
+ }
75
+ return true;
76
+ }
77
+ function detectFromBytes(bytes) {
78
+ const view = bytes instanceof ArrayBuffer ? new Uint8Array(bytes) : bytes;
79
+ if (bytesStartWith(view, PARQUET_MAGIC)) return "parquet";
80
+ if (bytesStartWith(view, ARROW_MAGIC)) return "arrow";
81
+ const head = view.slice(0, Math.min(64, view.length));
82
+ const text = new TextDecoder().decode(head).trimStart();
83
+ if (text.startsWith("{") || text.startsWith("[")) {
84
+ if (text.includes("json-stat") || text.includes('"class"') && text.includes('"version"')) {
85
+ return "jsonstat";
86
+ }
87
+ return "json";
88
+ }
89
+ if (text.includes(",") || text.includes(";") || text.includes(" ")) return "csv";
90
+ return "unknown";
91
+ }
92
+ function detectFromExtension(ext) {
93
+ const e = ext.toLowerCase().replace(/^\./, "");
94
+ switch (e) {
95
+ case "parquet":
96
+ return "parquet";
97
+ case "arrow":
98
+ case "ipc":
99
+ case "feather":
100
+ return "arrow";
101
+ case "csv":
102
+ return "csv";
103
+ case "csvw":
104
+ case "csv-metadata":
105
+ return "csvw";
106
+ case "json-stat":
107
+ case "jsonstat":
108
+ return "jsonstat";
109
+ case "json":
110
+ return "jsonstat";
111
+ // assume JSON-stat by default for .json
112
+ default:
113
+ return "unknown";
114
+ }
115
+ }
116
+ function extensionOf(path) {
117
+ const clean = path.split("?")[0]?.split("#")[0] ?? path;
118
+ const dot = clean.lastIndexOf(".");
119
+ const slash = Math.max(clean.lastIndexOf("/"), clean.lastIndexOf("\\"));
120
+ if (dot === -1 || dot < slash) return void 0;
121
+ return clean.slice(dot + 1);
122
+ }
123
+ function detectFormat(pathOrUrl, bytes) {
124
+ if (pathOrUrl) {
125
+ const ext = extensionOf(pathOrUrl);
126
+ if (ext) {
127
+ const byExt = detectFromExtension(ext);
128
+ if (byExt !== "unknown") return byExt;
129
+ }
130
+ }
131
+ if (bytes) return detectFromBytes(bytes);
132
+ return "unknown";
133
+ }
134
+
135
+ // src/util/density.ts
136
+ function decideDensity(values, requested = "auto", options = {}) {
137
+ const total = values.length;
138
+ const threshold = options.threshold ?? 0.5;
139
+ let nullCount = 0;
140
+ for (const v of values) {
141
+ if (v === null) nullCount++;
142
+ }
143
+ const nullRatio = total === 0 ? 0 : nullCount / total;
144
+ let form;
145
+ if (requested === "dense") form = "dense";
146
+ else if (requested === "sparse") form = "sparse";
147
+ else form = nullRatio > threshold ? "sparse" : "dense";
148
+ return { form, nullRatio, nullCount, total };
149
+ }
150
+
151
+ // src/util/fetch.ts
152
+ function isNode() {
153
+ return typeof process !== "undefined" && typeof process.versions !== "undefined" && typeof process.versions.node === "string";
154
+ }
155
+ async function loadInput(source) {
156
+ if (source instanceof Uint8Array) {
157
+ return { bytes: source };
158
+ }
159
+ if (typeof Blob !== "undefined" && source instanceof Blob) {
160
+ const buf = await source.arrayBuffer();
161
+ return { bytes: new Uint8Array(buf), source: source.name };
162
+ }
163
+ if (typeof source !== "string") {
164
+ throw new TypeError(`loadInput: unsupported source type ${typeof source}`);
165
+ }
166
+ if (source === "-") {
167
+ if (!isNode()) {
168
+ throw new Error('loadInput: stdin ("-") is only supported in Node');
169
+ }
170
+ const bytes = await readStdin();
171
+ return { bytes, fromStdin: true };
172
+ }
173
+ if (/^https?:\/\//i.test(source) || /^file:\/\//i.test(source)) {
174
+ const res = await fetch(source);
175
+ if (!res.ok) {
176
+ throw new Error(`loadInput: fetch ${source} failed: ${res.status} ${res.statusText}`);
177
+ }
178
+ const buf = await res.arrayBuffer();
179
+ return { bytes: new Uint8Array(buf), source };
180
+ }
181
+ if (isNode()) {
182
+ const bytes = await readFileNode(source);
183
+ return { bytes, source };
184
+ }
185
+ throw new Error(
186
+ `loadInput: cannot resolve "${source}" in a browser without a URL scheme`
187
+ );
188
+ }
189
+ async function readStdin() {
190
+ const chunks = [];
191
+ for await (const chunk of process.stdin) {
192
+ chunks.push(chunk instanceof Uint8Array ? chunk : new Uint8Array(chunk));
193
+ }
194
+ const total = chunks.reduce((n, c) => n + c.length, 0);
195
+ const out = new Uint8Array(total);
196
+ let off = 0;
197
+ for (const c of chunks) {
198
+ out.set(c, off);
199
+ off += c.length;
200
+ }
201
+ return out;
202
+ }
203
+ async function readFileNode(path) {
204
+ const { readFile } = await import('fs/promises');
205
+ const buf = await readFile(path);
206
+ return new Uint8Array(buf.buffer, buf.byteOffset, buf.byteLength);
207
+ }
208
+ var ImporterError = class extends Error {
209
+ constructor(message) {
210
+ super(message);
211
+ this.name = "ImporterError";
212
+ }
213
+ };
214
+ async function importToCube(source, options = {}) {
215
+ const loaded = await loadInput(source);
216
+ let format;
217
+ if (options.from && options.from !== "auto") {
218
+ format = options.from;
219
+ } else if (loaded.source) {
220
+ format = detectFormat(loaded.source, loaded.bytes);
221
+ } else {
222
+ format = detectFromBytes(loaded.bytes);
223
+ }
224
+ switch (format) {
225
+ case "arrow": {
226
+ const table = tableFromIPC(loaded.bytes);
227
+ return arrowToCube(table, options);
228
+ }
229
+ case "parquet": {
230
+ const { parquetToCube } = await import('./sources/parquet.js');
231
+ return parquetToCube(loaded.bytes, options);
232
+ }
233
+ case "csv": {
234
+ const { csvToCube } = await import('./sources/csv.js');
235
+ const text = new TextDecoder().decode(loaded.bytes);
236
+ return csvToCube(text, {
237
+ measure: options.measure,
238
+ dimensions: options.dimensions,
239
+ status: options.status,
240
+ roles: options.roles,
241
+ delimiter: options.delimiter
242
+ });
243
+ }
244
+ case "csvw": {
245
+ const { csvwToCube, parseCsvwMetadata } = await import('./sources/csvw.js');
246
+ const text = new TextDecoder().decode(loaded.bytes);
247
+ let metadata;
248
+ if (options.csvwMetadata !== void 0) {
249
+ metadata = parseCsvwMetadata(options.csvwMetadata);
250
+ } else if (loaded.source) {
251
+ try {
252
+ const metaPath = siblingMetadataPath(loaded.source);
253
+ const metaBytes = await loadInput(metaPath);
254
+ metadata = parseCsvwMetadata(
255
+ JSON.parse(new TextDecoder().decode(metaBytes.bytes))
256
+ );
257
+ } catch {
258
+ throw new ImporterError(
259
+ `CSVW metadata not supplied and could not be auto-loaded next to "${loaded.source}". Pass options.csvwMetadata or set options.from = "csv".`
260
+ );
261
+ }
262
+ }
263
+ if (metadata === void 0) {
264
+ throw new ImporterError(
265
+ "CSVW source requires metadata. Pass options.csvwMetadata."
266
+ );
267
+ }
268
+ return csvwToCube(text, metadata, {
269
+ measure: options.measure,
270
+ dimensions: options.dimensions,
271
+ status: options.status,
272
+ roles: options.roles
273
+ });
274
+ }
275
+ case "jsonstat":
276
+ case "json": {
277
+ const { readDataset: readDataset2 } = await import('./cubeReader-7QVUF3TI.js');
278
+ const parsed = JSON.parse(new TextDecoder().decode(loaded.bytes));
279
+ return readDataset2(parsed, { dropNulls: false });
280
+ }
281
+ default:
282
+ throw new ImporterError(
283
+ `Could not detect the source format. Set options.from explicitly (e.g. "parquet", "arrow", "csv", "csvw", "jsonstat"). DuckDB and Polars require a live connection/DataFrame \u2014 use the dedicated subpath import (e.g. import { duckdbToCube } from "jsonstat-io/duckdb").`
284
+ );
285
+ }
286
+ }
287
+ async function importToDataset(source, options = {}) {
288
+ const obs = await importToCube(source, options);
289
+ const result = buildDataset(obs, options.build);
290
+ return result.dataset;
291
+ }
292
+ function siblingMetadataPath(csvPath) {
293
+ const dot = csvPath.lastIndexOf(".");
294
+ const base = dot > 0 ? csvPath.slice(0, dot) : csvPath;
295
+ return `${base}-metadata.json`;
296
+ }
297
+ async function exportDataset(dataset, options) {
298
+ const { readDataset: readDataset2 } = await import('./cubeReader-7QVUF3TI.js');
299
+ const obs = readDataset2(dataset, { dropNulls: false });
300
+ switch (options.to) {
301
+ case "arrow": {
302
+ return cubeToArrow(obs);
303
+ }
304
+ case "parquet": {
305
+ const { cubeToParquet } = await import('./sources/parquet.js');
306
+ return cubeToParquet(obs, {
307
+ init: options.init,
308
+ compression: options.compression
309
+ });
310
+ }
311
+ case "csv": {
312
+ const { cubeToCsv } = await import('./sources/csv.js');
313
+ return cubeToCsv(obs, {
314
+ delimiter: options.delimiter,
315
+ lineTerminator: options.lineTerminator
316
+ });
317
+ }
318
+ case "csvw": {
319
+ const { cubeToCsvw } = await import('./sources/csvw.js');
320
+ return cubeToCsvw(obs, {
321
+ delimiter: options.delimiter,
322
+ lineTerminator: options.lineTerminator,
323
+ url: options.url
324
+ });
325
+ }
326
+ default:
327
+ throw new ImporterError(
328
+ `Unsupported export target "${options.to}". Use one of: "arrow", "parquet", "csv", "csvw". For DuckDB/Polars, use the dedicated subpath writers.`
329
+ );
330
+ }
331
+ }
332
+
333
+ export { ImporterError, decideDensity, detectFormat, detectFromBytes, detectFromExtension, exportDataset, extensionOf, importToCube, importToDataset, isCollection, isDataset, isDimensionResponse, loadInput, observationCount, serialize, serializeToBytes };
334
+ //# sourceMappingURL=chunk-EFRDFQOT.js.map
335
+ //# sourceMappingURL=chunk-EFRDFQOT.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/model/jsonstat.ts","../src/model/ir.ts","../src/sink/serialize.ts","../src/util/detect.ts","../src/util/density.ts","../src/util/fetch.ts","../src/index.ts"],"names":["readDataset"],"mappings":"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","file":"chunk-EFRDFQOT.js","sourcesContent":["/**\n * JSON-stat 2.0 type model.\n *\n * Faithful TypeScript representation of the JSON-stat 2.0 format, derived from\n * the official specification (raw/fullspec.html) and the vendored JSON Schemas\n * (raw/schema/{index,dataset,collection,dimension}.json).\n *\n * These are *structural* types: they describe the wire format. Cross-field\n * cube invariants (e.g. `value.length === size.reduce(product)`) live in the\n * core engine, mirroring how the official JSON Schemas defer semantic checks\n * to jsonstat-validator (see wiki/validator.md and wiki/schema.md).\n */\n\n// ---------------------------------------------------------------------------\n// Primitives & shared building blocks\n// ---------------------------------------------------------------------------\n\n/** Allowed JSON-stat response classes (see wiki/response-classes.md). */\nexport type JsonStatClass = \"dataset\" | \"dimension\" | \"collection\" | \"bundle\" | \"error\";\n\n/** Semantic role assigned to one or more dimension IDs (see wiki/dimensions.md). */\nexport interface JsonStatRole {\n /** Dimension IDs carrying temporal data (chronological order assumed). */\n time?: string[];\n /** Dimension IDs carrying spatial data (categories may have coordinates). */\n geo?: string[];\n /** Dimension IDs carrying different metrics/measures (units attached). */\n metric?: string[];\n}\n\n/** Unit of measure metadata for a metric-role category (see wiki/dimensions.md). */\nexport interface JsonStatUnit {\n /** Required when `unit` is present: number of decimals. */\n decimals: number;\n /** Unit text displayed after (or before) the value. */\n label?: string;\n /** Unit symbol, e.g. \"$\" or \"%\". */\n symbol?: string;\n /** Where the symbol goes relative to the value. Defaults to \"end\". */\n position?: \"start\" | \"end\";\n}\n\n/** [longitude, latitude] pair for geo-role categories. */\nexport type Coordinates = [longitude: number, latitude: number];\n\n/**\n * Category block of a dimension.\n *\n * `index` may be an array (IDs in order) or an object (ID→position map). For\n * constant (single-category) dimensions `index` is unnecessary provided a\n * `label` is given. `label` is optional and falls back to category IDs.\n */\nexport interface JsonStatCategory {\n /** Category ordering. Array form: `[\"M\",\"F\",\"T\"]`. Object form: `{\"M\":0,...}`. */\n index?: string[] | Record<string, number>;\n /** ID→human-readable label map. If omitted, IDs are used as labels. */\n label?: Record<string, string>;\n /** Parent ID → array of direct-child IDs (hierarchies; see wiki/dimensions.md). */\n child?: Record<string, string[]>;\n /** ID → [lon, lat] for geo-role categories. */\n coordinates?: Record<string, Coordinates>;\n /** ID → unit metadata for metric-role categories. */\n unit?: Record<string, JsonStatUnit>;\n}\n\n/**\n * A dimension object within `dimension`.\n *\n * `category` is the only required property in practice for the dimension\n * class; for dataset-class dimensions it is always present.\n */\nexport interface JsonStatDimension {\n /** Short descriptive text (lowercase recommended). */\n label?: string;\n /** The categories of this dimension. */\n category?: JsonStatCategory;\n /** URL to an external dimension definition. */\n href?: string;\n /** Provider-specific extension data. */\n extension?: Record<string, unknown>;\n /** Related resources (see wiki/links.md). */\n link?: JsonStatLink[];\n /** Annotations. */\n note?: string[];\n}\n\n// ---------------------------------------------------------------------------\n// Links (IANA link relations; see wiki/links.md)\n// ---------------------------------------------------------------------------\n\n/** A single link entry. `rel` is an IANA link relation (e.g. \"item\", \"self\"). */\nexport interface JsonStatLink {\n /** IANA link relation type. */\n rel?: string;\n /** Target URL. */\n href?: string;\n /** Type of the linked resource (e.g. a class value). */\n type?: string;\n /** Human-readable label. */\n label?: string;\n /** When present, the item is embedded inline rather than referenced by href. */\n [embedded: string]: unknown;\n}\n\n/** Container for links, indexed by relation. The \"item\" relation holds an array. */\nexport interface JsonStatLinkMap {\n [rel: string]: JsonStatLink[] | undefined;\n}\n\n// ---------------------------------------------------------------------------\n// status (observation-level metadata; see wiki/dataset-structure.md)\n// ---------------------------------------------------------------------------\n\n/**\n * Observation-level status. Three forms:\n * - array: one status code per cell (aligned with `value`)\n * - string: a single status applied to all cells\n * - object: position→status for specific cells (sparse status)\n *\n * Status has no standard vocabulary — it is provider-defined.\n */\nexport type JsonStatStatus = string[] | string | Record<string, string>;\n\n// ---------------------------------------------------------------------------\n// value (dense array or sparse object; see wiki/sparse-cubes.md)\n// ---------------------------------------------------------------------------\n\n/**\n * Cell values in row-major order (\"what does not change, first\"; the last\n * dimension in `id` changes fastest).\n *\n * - Array form (dense): missing values are `null`.\n * - Object form (sparse): maps flat position index → value, omitting nulls.\n */\nexport type JsonStatValue = (number | null)[] | Record<string, number>;\n\n// ---------------------------------------------------------------------------\n// Response classes\n// ---------------------------------------------------------------------------\n\n/** Top-level dataset response (see wiki/response-classes.md). */\nexport interface JsonStatDataset {\n version: \"2.0\";\n class: \"dataset\";\n /** Short descriptive text. */\n label?: string;\n /** Short text describing the dataset source. */\n source?: string;\n /** Update time in ISO 8601. */\n updated?: string;\n /** Ordered list of dimension IDs. */\n id: string[];\n /** Number of categories per dimension, same order as `id`. */\n size: number[];\n /** Per-dimension metadata, keyed by dimension ID. */\n dimension: Record<string, JsonStatDimension>;\n /** Cell values (dense or sparse). */\n value: JsonStatValue;\n /** Observation-level status metadata. */\n status?: JsonStatStatus;\n /** Semantic roles for dimensions. */\n role?: JsonStatRole;\n /** Provider-specific extension data. */\n extension?: Record<string, unknown>;\n /** Related resources. */\n link?: JsonStatLinkMap;\n /** Annotations. */\n note?: string[];\n /** URL of the dataset. */\n href?: string;\n /** Optional error array (suggested, not mandatory). */\n error?: JsonStatError[];\n}\n\n/** Dimension-only response. */\nexport interface JsonStatDimensionResponse {\n version: \"2.0\";\n class: \"dimension\";\n label?: string;\n category: JsonStatCategory;\n extension?: Record<string, unknown>;\n link?: JsonStatLinkMap;\n href?: string;\n}\n\n/** Collection response (2.0 replacement for bundles). */\nexport interface JsonStatCollection {\n version: \"2.0\";\n class: \"collection\";\n href?: string;\n label?: string;\n updated?: string;\n link: JsonStatLinkMap;\n extension?: Record<string, unknown>;\n}\n\n/** A proposed (not mandatory) error entry. */\nexport interface JsonStatError {\n status?: number;\n id?: string;\n href?: string;\n label?: string;\n}\n\n/** Any top-level JSON-stat 2.0 response. */\nexport type JsonStatResponse =\n | JsonStatDataset\n | JsonStatDimensionResponse\n | JsonStatCollection;\n\n/** Discriminated union guard helper: is this a dataset response? */\nexport function isDataset(r: JsonStatResponse): r is JsonStatDataset {\n return r.class === \"dataset\";\n}\n\n/** Discriminated union guard helper: is this a collection response? */\nexport function isCollection(r: JsonStatResponse): r is JsonStatCollection {\n return r.class === \"collection\";\n}\n\n/** Discriminated union guard helper: is this a dimension response? */\nexport function isDimensionResponse(r: JsonStatResponse): r is JsonStatDimensionResponse {\n return r.class === \"dimension\";\n}\n","/**\n * Observations IR — the format-agnostic intermediate representation.\n *\n * Every source (Arrow, Parquet, DuckDB, Polars, CSVW, CSV) converts its data\n * into this IR. The cube builder ([`CubeBuilder`](../core/cubeBuilder.ts))\n * then turns the IR into a JSON-stat [`Dataset`](./jsonstat.ts).\n *\n * The IR is deliberately a **long / tidy table**: one row per observation,\n * with one column per dimension and a single measure column. This is the\n * natural shape of the columnar stack and the dual of a JSON-stat cube.\n *\n * Because the IR is bidirectional, the same model also powers the Phase-2\n * export path ([`cubeReader`](../core/cubeReader.ts) → IR →\n * [`arrowFromCube`](../arrow/arrowFromCube.ts) → Arrow/Parquet/CSV). See\n * [docs/architecture.md](../../docs/architecture.md) §\"The two documented seams\".\n */\n\nimport type { JsonStatRole, JsonStatUnit } from \"./jsonstat\";\n\n// ---------------------------------------------------------------------------\n// Columns\n// ---------------------------------------------------------------------------\n\n/**\n * A dimension column: the observed category *labels or IDs* for each row.\n *\n * `values` is a parallel array (one entry per observation) of category IDs.\n * Category order is *not* implied here — it is resolved by the builder, which\n * enumerates distinct values and assigns indices. Use `categoryOrder` to pin\n * a specific order (e.g. chronological time) when the source knows it.\n */\nexport interface DimensionColumn {\n /** Dimension ID (becomes a key in JSON-stat `dimension`). */\n id: string;\n /** Human-readable dimension label. */\n label?: string;\n /** Per-row category ID. `null` is not permitted for dimensions. */\n values: string[];\n /**\n * Optional explicit category ordering. When omitted the builder enumerates\n * distinct values in first-seen order. Provide for time dimensions whose\n * chronological order is not lexicographic.\n */\n categoryOrder?: string[];\n /** ID → human-readable category label. Falls back to the ID. */\n categoryLabels?: Record<string, string>;\n /** ID → unit metadata (metric-role dimensions only). */\n categoryUnits?: Record<string, JsonStatUnit>;\n /** ID → [lon, lat] (geo-role dimensions only). */\n categoryCoordinates?: Record<string, [number, number]>;\n /** Parent ID → direct child IDs (hierarchical dimensions only). */\n categoryChild?: Record<string, string[]>;\n /** Optional href to an external dimension definition. */\n href?: string;\n}\n\n/**\n * The measure column: the numeric observation values, one per row.\n *\n * `null` represents a missing observation. The importer supports a single\n * measure column in v0.1 — multi-measure datasets should be split across\n * multiple datasets (one per metric) or modeled as a metric *dimension*,\n * matching how JSON-stat treats measures as a dimension with the `metric`\n * role rather than as separate value arrays.\n */\nexport interface MeasureColumn {\n /** Column name in the source (kept for round-trip fidelity, not emitted). */\n name?: string;\n /** Numeric values; `null` = missing. */\n values: (number | null)[];\n}\n\n/**\n * Optional per-row status column. Provider-defined vocabulary; emitted verbatim\n * as JSON-stat `status` (array form) by the builder.\n */\nexport interface StatusColumn {\n /** Per-row status code. */\n values: string[];\n}\n\n// ---------------------------------------------------------------------------\n// Roles & cube model\n// ---------------------------------------------------------------------------\n\n/**\n * Role assignment. Mirrors JSON-stat's `role` object: each role maps to a list\n * of dimension IDs. The importer assigns roles from source metadata or CLI\n * hints; unassigned dimensions default to the implicit \"classification\" role.\n */\nexport type RoleMap = JsonStatRole;\n\n/**\n * Dataset-level metadata carried alongside the observations.\n *\n * These fields map directly to top-level JSON-stat dataset properties\n * (`label`, `source`, `updated`, `extension`).\n */\nexport interface DatasetMeta {\n label?: string;\n source?: string;\n updated?: string;\n extension?: Record<string, unknown>;\n}\n\n/**\n * The complete cube model: everything the builder needs besides the raw\n * observation columns to produce a fully-specified JSON-stat dataset.\n */\nexport interface CubeModel {\n /** Ordered dimension IDs. Determines row-major stride order. */\n dimensionIds: string[];\n /** Role assignments (time/geo/metric). */\n roles?: RoleMap;\n /** Dataset-level metadata. */\n meta?: DatasetMeta;\n /**\n * How to emit `value`.\n * - \"auto\": dense unless null-ratio exceeds `sparseThreshold`.\n * - \"dense\": always array form.\n * - \"sparse\": always object form.\n */\n valueForm?: \"auto\" | \"dense\" | \"sparse\";\n /** Null ratio above which \"auto\" chooses sparse. Default 0.5. */\n sparseThreshold?: number;\n /**\n * Whether to emit a status array. When a StatusColumn is present and this is\n * not \"none\", the builder emits status. \"object\" emits the sparse object form\n * when there are few non-default statuses; \"string\" collapses a uniform\n * status to a single string.\n */\n statusForm?: \"auto\" | \"array\" | \"string\" | \"object\" | \"none\";\n}\n\n// ---------------------------------------------------------------------------\n// The IR\n// ---------------------------------------------------------------------------\n\n/**\n * The Observations IR: a tidy long table plus its cube model.\n *\n * Invariants the builder assumes (validated in [`CubeBuilder.build`]):\n * - All columns in `dimensions` and `measure` share the same length `n`.\n * - `status`, if present, has length `n`.\n * - `model.dimensionIds` lists every dimension column id, and no others.\n * - No dimension value is `null` or `undefined`.\n */\nexport interface Observations {\n /** Dimension columns, keyed by dimension ID. */\n dimensions: Record<string, DimensionColumn>;\n /** The single measure column. */\n measure: MeasureColumn;\n /** Optional per-row status. */\n status?: StatusColumn;\n /** The cube model governing how to build the JSON-stat dataset. */\n model: CubeModel;\n}\n\n/** Number of observations (rows). */\nexport function observationCount(obs: Observations): number {\n return obs.measure.values.length;\n}\n","/**\n * Serialize — turn a JSON-stat [`Dataset`](../model/jsonstat.ts) into a JSON\n * string or Buffer for writing to stdout/file.\n *\n * Also offers a lightweight \"pretty\" mode that sorts top-level keys in the\n * canonical JSON-stat order (version, class, label, href, source, updated,\n * id, size, dimension, role, value, status, …) so output is stable and\n * diff-friendly — useful for fixtures and round-trip tests.\n */\n\nimport type { JsonStatDataset, JsonStatResponse } from \"../model/jsonstat\";\n\n// Canonical top-level key order for pretty output.\nconst KEY_ORDER = [\n \"version\",\n \"class\",\n \"label\",\n \"href\",\n \"source\",\n \"updated\",\n \"id\",\n \"size\",\n \"dimension\",\n \"role\",\n \"value\",\n \"status\",\n \"extension\",\n \"link\",\n \"note\",\n \"error\",\n];\n\nexport interface SerializeOptions {\n /** Pretty-print with 2-space indentation (default true). */\n pretty?: boolean;\n /** Reorder top-level keys canonically (default true, only with pretty). */\n canonicalKeys?: boolean;\n}\n\n/**\n * Serialize a JSON-stat response to a JSON string.\n *\n * @param response The dataset (or any JSON-stat response).\n */\nexport function serialize(\n response: JsonStatResponse | JsonStatDataset,\n options: SerializeOptions = {},\n): string {\n const pretty = options.pretty ?? true;\n const canonical = options.canonicalKeys ?? true;\n\n let value: unknown = response;\n if (pretty && canonical) {\n value = reorderKeys(response as object, KEY_ORDER);\n }\n\n return pretty ? JSON.stringify(value, null, 2) : JSON.stringify(value);\n}\n\n/**\n * Serialize and encode as UTF-8 bytes (Uint8Array). Handy for file writing\n * without pulling in Node's Buffer in browser contexts.\n */\nexport function serializeToBytes(\n response: JsonStatResponse | JsonStatDataset,\n options?: SerializeOptions,\n): Uint8Array {\n return new TextEncoder().encode(serialize(response, options));\n}\n\n/**\n * Reorder an object's keys so that known keys appear in `order`, and any\n * unknown keys follow alphabetically. Only reshuffles top-level keys (does not\n * recurse), to avoid disturbing category/index ordering.\n */\nfunction reorderKeys(obj: object, order: string[]): Record<string, unknown> {\n const source = obj as Record<string, unknown>;\n const out: Record<string, unknown> = {};\n const known = new Set<string>();\n for (const k of order) {\n if (k in source) {\n out[k] = source[k];\n known.add(k);\n }\n }\n const rest = Object.keys(source)\n .filter((k) => !known.has(k))\n .sort();\n for (const k of rest) out[k] = source[k];\n return out;\n}\n","/**\n * Format detection — sniffs the input source to pick the right adapter.\n *\n * Two signals are used, in order of reliability:\n * 1. **Magic bytes** (content sniffing) — authoritative for binary formats\n * (Parquet `PAR1`, Arrow IPC `ARROW1`).\n * 2. **File extension** — used when content is not yet available (e.g. a path\n * or URL) or for text formats (`.csv`, `.csvw`).\n */\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport type SourceFormat =\n | \"parquet\"\n | \"arrow\"\n | \"csv\"\n | \"csvw\"\n | \"jsonstat\"\n | \"json\"\n | \"unknown\";\n\n// ---------------------------------------------------------------------------\n// Magic bytes\n// ---------------------------------------------------------------------------\n\n/** Parquet files begin with `PAR1`. */\nconst PARQUET_MAGIC = [0x50, 0x41, 0x52, 0x31]; // \"PAR1\"\n/** Arrow IPC files begin with `ARROW1\\0\\0`. */\nconst ARROW_MAGIC = [0x41, 0x52, 0x52, 0x4f, 0x57, 0x31]; // \"ARROW1\"\n\nfunction bytesStartWith(bytes: Uint8Array, magic: number[]): boolean {\n if (bytes.length < magic.length) return false;\n for (let i = 0; i < magic.length; i++) {\n if (bytes[i] !== magic[i]) return false;\n }\n return true;\n}\n\n/**\n * Sniff the format from the leading bytes of a binary/text input.\n *\n * @returns The detected format, or \"unknown\" if no magic matches.\n */\nexport function detectFromBytes(bytes: Uint8Array | ArrayBuffer): SourceFormat {\n const view = bytes instanceof ArrayBuffer ? new Uint8Array(bytes) : bytes;\n if (bytesStartWith(view, PARQUET_MAGIC)) return \"parquet\";\n if (bytesStartWith(view, ARROW_MAGIC)) return \"arrow\";\n // JSON-stat / JSON text: starts with `{` (after optional BOM/whitespace).\n const head = view.slice(0, Math.min(64, view.length));\n const text = new TextDecoder().decode(head).trimStart();\n if (text.startsWith(\"{\") || text.startsWith(\"[\")) {\n // Heuristic: if it mentions \"json-stat\" or `\"class\"` + `\"version\"`, JSON-stat.\n if (text.includes(\"json-stat\") || (text.includes('\"class\"') && text.includes('\"version\"'))) {\n return \"jsonstat\";\n }\n return \"json\";\n }\n // CSV: presence of a delimiter and typical header-ish content is weak; defer\n // to extension if available. Mark as \"csv\" only as a fallback guess.\n if (text.includes(\",\") || text.includes(\";\") || text.includes(\"\\t\")) return \"csv\";\n return \"unknown\";\n}\n\n// ---------------------------------------------------------------------------\n// Extension\n// ---------------------------------------------------------------------------\n\n/** Map a file extension (without dot) to a source format. */\nexport function detectFromExtension(ext: string): SourceFormat {\n const e = ext.toLowerCase().replace(/^\\./, \"\");\n switch (e) {\n case \"parquet\":\n return \"parquet\";\n case \"arrow\":\n case \"ipc\":\n case \"feather\":\n return \"arrow\";\n case \"csv\":\n return \"csv\";\n case \"csvw\":\n case \"csv-metadata\":\n return \"csvw\";\n case \"json-stat\":\n case \"jsonstat\":\n return \"jsonstat\";\n case \"json\":\n return \"jsonstat\"; // assume JSON-stat by default for .json\n default:\n return \"unknown\";\n }\n}\n\n/** Extract the extension from a path/URL (without the dot), or undefined. */\nexport function extensionOf(path: string): string | undefined {\n // Strip query string / fragment.\n const clean = path.split(\"?\")[0]?.split(\"#\")[0] ?? path;\n const dot = clean.lastIndexOf(\".\");\n const slash = Math.max(clean.lastIndexOf(\"/\"), clean.lastIndexOf(\"\\\\\"));\n if (dot === -1 || dot < slash) return undefined;\n return clean.slice(dot + 1);\n}\n\n/**\n * Detect a format from a filename/URL, falling back to bytes if the extension\n * is unknown or ambiguous.\n */\nexport function detectFormat(\n pathOrUrl: string | undefined,\n bytes?: Uint8Array | ArrayBuffer,\n): SourceFormat {\n if (pathOrUrl) {\n const ext = extensionOf(pathOrUrl);\n if (ext) {\n const byExt = detectFromExtension(ext);\n if (byExt !== \"unknown\") return byExt;\n }\n }\n if (bytes) return detectFromBytes(bytes);\n return \"unknown\";\n}\n","/**\n * Density helper — sparse vs dense `value` decision.\n *\n * Centralizes the null-ratio threshold logic so the cube builder and tests\n * share one definition. See wiki/sparse-cubes.md for the rationale: the object\n * (sparse) form is preferred when many cells are null.\n */\n\nexport interface DensityResult {\n form: \"dense\" | \"sparse\";\n nullRatio: number;\n nullCount: number;\n total: number;\n}\n\nexport interface DensityOptions {\n /** Null ratio above which \"auto\" chooses sparse. Default 0.5. */\n threshold?: number;\n}\n\n/**\n * Decide dense vs sparse for a flat value array.\n *\n * @param values The dense row-major array (with nulls).\n * @param requested \"auto\" | \"dense\" | \"sparse\".\n */\nexport function decideDensity(\n values: (number | null)[],\n requested: \"auto\" | \"dense\" | \"sparse\" = \"auto\",\n options: DensityOptions = {},\n): DensityResult {\n const total = values.length;\n const threshold = options.threshold ?? 0.5;\n let nullCount = 0;\n for (const v of values) {\n if (v === null) nullCount++;\n }\n const nullRatio = total === 0 ? 0 : nullCount / total;\n\n let form: \"dense\" | \"sparse\";\n if (requested === \"dense\") form = \"dense\";\n else if (requested === \"sparse\") form = \"sparse\";\n else form = nullRatio > threshold ? \"sparse\" : \"dense\";\n\n return { form, nullRatio, nullCount, total };\n}\n","/**\n * Isomorphic input loader — works in both Node and the browser.\n *\n * Resolves a source specifier (file path, URL, or raw bytes/text) into a\n * `Uint8Array` of bytes and, where possible, a hint about its origin (path/URL)\n * so format detection can use the extension.\n *\n * - **Node**: reads from `node:fs` for local paths; uses `fetch` for URLs and\n * for the browser. Reading from stdin is supported via the special source\n * `\"-\"`.\n * - **Browser**: uses the global `fetch` for URLs and `File`/`Blob` inputs;\n * local paths are not available.\n *\n * The Node-specific `fs`/`stdin` imports are guarded by a runtime check so the\n * same code path works in browsers without bundling `node:fs`.\n */\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport interface LoadedInput {\n bytes: Uint8Array;\n /** The original source path/URL, if known (used for extension detection). */\n source?: string;\n /** True if the source was stdin (\"-\"). */\n fromStdin?: boolean;\n}\n\n// ---------------------------------------------------------------------------\n// Environment detection\n// ---------------------------------------------------------------------------\n\nfunction isNode(): boolean {\n return (\n typeof process !== \"undefined\" &&\n typeof process.versions !== \"undefined\" &&\n typeof process.versions.node === \"string\"\n );\n}\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\n/**\n * Load input bytes from a path, URL, `\"-\"` (stdin), `Uint8Array`, `Blob`, or\n * string.\n *\n * @param source A file path, `http(s)://` URL, `\"-\"` for stdin, or raw data.\n * @throws If the source cannot be read.\n */\nexport async function loadInput(source: string | Uint8Array | Blob): Promise<LoadedInput> {\n // Already bytes.\n if (source instanceof Uint8Array) {\n return { bytes: source };\n }\n // Blob / File (browser & undici).\n if (typeof Blob !== \"undefined\" && source instanceof Blob) {\n const buf = await source.arrayBuffer();\n return { bytes: new Uint8Array(buf), source: (source as File).name };\n }\n if (typeof source !== \"string\") {\n throw new TypeError(`loadInput: unsupported source type ${typeof source}`);\n }\n\n // stdin.\n if (source === \"-\") {\n if (!isNode()) {\n throw new Error('loadInput: stdin (\"-\") is only supported in Node');\n }\n const bytes = await readStdin();\n return { bytes, fromStdin: true };\n }\n\n // URL.\n if (/^https?:\\/\\//i.test(source) || /^file:\\/\\//i.test(source)) {\n const res = await fetch(source);\n if (!res.ok) {\n throw new Error(`loadInput: fetch ${source} failed: ${res.status} ${res.statusText}`);\n }\n const buf = await res.arrayBuffer();\n return { bytes: new Uint8Array(buf), source };\n }\n\n // Local file path (Node only).\n if (isNode()) {\n const bytes = await readFileNode(source);\n return { bytes, source };\n }\n\n throw new Error(\n `loadInput: cannot resolve \"${source}\" in a browser without a URL scheme`,\n );\n}\n\n// ---------------------------------------------------------------------------\n// Node helpers (lazy imports)\n// ---------------------------------------------------------------------------\n\nasync function readStdin(): Promise<Uint8Array> {\n // Use process.stdin directly — avoids the PathLike type mismatch that\n // createReadStream(0) triggers under @types/node, and is more idiomatic.\n const chunks: Uint8Array[] = [];\n for await (const chunk of process.stdin) {\n chunks.push(chunk instanceof Uint8Array ? chunk : new Uint8Array(chunk));\n }\n const total = chunks.reduce((n, c) => n + c.length, 0);\n const out = new Uint8Array(total);\n let off = 0;\n for (const c of chunks) {\n out.set(c, off);\n off += c.length;\n }\n return out;\n}\n\nasync function readFileNode(path: string): Promise<Uint8Array> {\n const { readFile } = await import(\"node:fs/promises\");\n const buf = await readFile(path);\n return new Uint8Array(buf.buffer, buf.byteOffset, buf.byteLength);\n}\n","/**\n * jsonstat-io — public API surface (main entry).\n *\n * This package is a **bidirectional** bridge between JSON-stat 2.0 and the\n * columnar stack (Arrow, Parquet, DuckDB, Polars, CSVW, CSV):\n *\n * - **Import** (columnar → JSON-stat): [`importToCube`](#importtocube) /\n * [`importToDataset`](#importtodataset) — load, auto-detect, convert, build.\n * - **Export** (JSON-stat → columnar): [`exportDataset`](#exportdataset) — read\n * the JSON-stat cube into the IR and route to the requested sink writer.\n *\n * This barrel re-exports the **always loaded** layers: the model types, the\n * core cube engine (strides, builder, reader), the Arrow hub, the sink, and the\n * isomorphic utilities (format detection, input loading, density decision).\n *\n * Source/sink adapters that pull in optional peer dependencies live behind\n * **subpath exports** so that browser bundles stay lean and tree-shakeable:\n *\n * - `jsonstat-io/arrow` — Arrow hub (re-exported here too, since\n * `apache-arrow` is a hard dependency, not an optional peer).\n * - `jsonstat-io/parquet` — Parquet ↔ Arrow ↔ cube (needs `parquet-wasm`).\n * - `jsonstat-io/duckdb` — DuckDB ↔ Arrow ↔ cube (needs duckdb).\n * - `jsonstat-io/polars` — Polars ↔ Arrow ↔ cube (Node-only).\n * - `jsonstat-io/csvw` — CSVW + CSV metadata-aware path (no deps).\n * - `jsonstat-io/csv` — plain CSV inference path (no deps).\n *\n * For a one-call convenience, see the dispatchers:\n * - Import: [`importToCube`](#importtocube) / [`importToDataset`](#importtodataset).\n * - Export: [`exportDataset`](#exportdataset).\n *\n * @packageDocumentation\n */\n\n// ---------------------------------------------------------------------------\n// Model layer (pure types + guards, zero runtime cost)\n// ---------------------------------------------------------------------------\nexport type {\n JsonStatClass,\n JsonStatRole,\n JsonStatUnit,\n Coordinates,\n JsonStatCategory,\n JsonStatDimension,\n JsonStatLink,\n JsonStatLinkMap,\n JsonStatStatus,\n JsonStatValue,\n JsonStatDataset,\n JsonStatDimensionResponse,\n JsonStatCollection,\n JsonStatError,\n JsonStatResponse,\n} from \"./model/jsonstat\";\nexport { isDataset, isCollection, isDimensionResponse } from \"./model/jsonstat\";\n\nexport type {\n DimensionColumn,\n MeasureColumn,\n StatusColumn,\n RoleMap,\n DatasetMeta,\n CubeModel,\n Observations,\n} from \"./model/ir\";\nexport { observationCount } from \"./model/ir\";\n\n// ---------------------------------------------------------------------------\n// Core engine (strides, builder, reader)\n// ---------------------------------------------------------------------------\nexport {\n strides,\n totalCells,\n flatPosition,\n multiIndex,\n enumerateCells,\n} from \"./core/strides\";\n\nexport { CubeBuilderError } from \"./core/cubeBuilder\";\nexport type { BuildOptions, BuildResult } from \"./core/cubeBuilder\";\nexport { buildDataset, toDataset } from \"./core/cubeBuilder\";\n\nexport { CubeReaderError } from \"./core/cubeReader\";\nexport type { ReadOptions } from \"./core/cubeReader\";\nexport { readDataset, readResponse } from \"./core/cubeReader\";\n\n// ---------------------------------------------------------------------------\n// Arrow hub (hard dependency, so safe to re-export from the main entry)\n// ---------------------------------------------------------------------------\nexport {\n META_PREFIX,\n getFieldMeta,\n getFieldMetaJson,\n isMeasureField,\n isStatusField,\n getFieldRole,\n buildFieldMeta,\n readSchemaMeta,\n buildSchemaMeta,\n} from \"./arrow/schemaMeta\";\n\nexport { ArrowConversionError } from \"./arrow/arrowToCube\";\nexport type { ArrowToCubeOptions } from \"./arrow/arrowToCube\";\nexport { arrowToCube, arrowToDataset } from \"./arrow/arrowToCube\";\n\nexport { cubeToArrow } from \"./arrow/arrowFromCube\";\n\n// ---------------------------------------------------------------------------\n// Sink (serialization)\n// ---------------------------------------------------------------------------\nexport type { SerializeOptions } from \"./sink/serialize\";\nexport { serialize, serializeToBytes } from \"./sink/serialize\";\n\n// ---------------------------------------------------------------------------\n// Isomorphic utilities\n// ---------------------------------------------------------------------------\nexport type { SourceFormat } from \"./util/detect\";\nexport {\n detectFromBytes,\n detectFromExtension,\n extensionOf,\n detectFormat,\n} from \"./util/detect\";\n\nexport type { DensityResult, DensityOptions } from \"./util/density\";\nexport { decideDensity } from \"./util/density\";\n\nexport type { LoadedInput } from \"./util/fetch\";\nexport { loadInput } from \"./util/fetch\";\n\n// ---------------------------------------------------------------------------\n// High-level convenience: load → detect → convert → build\n// ---------------------------------------------------------------------------\n\nimport { tableFromIPC } from \"apache-arrow\";\nimport type { Table } from \"apache-arrow\";\nimport { buildDataset } from \"./core/cubeBuilder\";\nimport type { BuildOptions, BuildResult } from \"./core/cubeBuilder\";\nimport type { JsonStatDataset } from \"./model/jsonstat\";\nimport type { Observations } from \"./model/ir\";\nimport { detectFormat, detectFromBytes } from \"./util/detect\";\nimport type { SourceFormat } from \"./util/detect\";\nimport { loadInput } from \"./util/fetch\";\nimport type { LoadedInput } from \"./util/fetch\";\nimport { arrowToCube } from \"./arrow/arrowToCube\";\nimport { cubeToArrow } from \"./arrow/arrowFromCube\";\nimport type { ArrowToCubeOptions } from \"./arrow/arrowToCube\";\nimport type { CsvwMetadata } from \"./sources/csvw\";\n\n/**\n * Options for the high-level [`importToCube`](#importtocube) /\n * [`importToDataset`](#importtodataset) dispatchers.\n *\n * These combine format detection, source loading, and Arrow-hub conversion\n * hints into a single call.\n */\nexport interface ImportOptions extends ArrowToCubeOptions {\n /**\n * Force a source format instead of auto-detecting. One of `\"parquet\"`,\n * `\"arrow\"`, `\"csv\"`, `\"csvw\"`, `\"jsonstat\"`, `\"json\"`. When omitted the\n * format is sniffed from magic bytes (Parquet `PAR1`, Arrow IPC `ARROW1`)\n * and then from the file extension.\n */\n from?: SourceFormat | \"auto\";\n /** Passed through to [`buildDataset`](./core/cubeBuilder.ts) as `BuildOptions`. */\n build?: BuildOptions;\n /**\n * CSVW metadata, when the source is CSVW. If `from === \"csvw\"` and this is\n * omitted, the dispatcher attempts to load a sibling `*-metadata.json` next\n * to the CSV path (Node only). In the browser the caller must supply it.\n */\n csvwMetadata?: unknown;\n /**\n * CSV delimiter (default `,`). Only used when the source is plain CSV.\n */\n delimiter?: string;\n}\n\n/**\n * Error thrown by the high-level dispatchers when a format cannot be detected,\n * a required peer dependency is missing, or the source produces no data.\n */\nexport class ImporterError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"ImporterError\";\n }\n}\n\n/**\n * Load a columnar source (file path, URL, stdin `\"-\"`, `Uint8Array`, or `Blob`)\n * and convert it to the [`Observations`](./model/ir.ts) IR.\n *\n * This is the one-call convenience that ties together\n * [`loadInput`](./util/fetch.ts), [`detectFormat`](./util/detect.ts), the\n * appropriate source adapter, and the Arrow hub. It auto-detects the format\n * unless [`options.from`](#ImportOptions.from) is set.\n *\n * Parquet/DuckDB/Polars adapters are imported lazily, so they are only pulled\n * into the bundle when actually needed.\n *\n * @param source A file path, `http(s)://` URL, `\"-\"` for stdin (Node),\n * `Uint8Array`, or `Blob`.\n * @returns The [`Observations`](./model/ir.ts) IR, ready for\n * [`buildDataset`](./core/cubeBuilder.ts).\n *\n * @example\n * ```ts\n * import { importToDataset } from \"jsonstat-io\";\n * const dataset = await importToDataset(\"./sales.parquet\");\n * console.log(JSON.stringify(dataset, null, 2));\n * ```\n */\nexport async function importToCube(\n source: string | Uint8Array | Blob,\n options: ImportOptions = {},\n): Promise<Observations> {\n const loaded: LoadedInput = await loadInput(source);\n\n // --- Resolve the format -------------------------------------------------\n let format: SourceFormat;\n if (options.from && options.from !== \"auto\") {\n format = options.from;\n } else if (loaded.source) {\n format = detectFormat(loaded.source, loaded.bytes);\n } else {\n format = detectFromBytes(loaded.bytes);\n }\n\n // --- Dispatch to the right adapter --------------------------------------\n switch (format) {\n case \"arrow\": {\n const table = tableFromIPC(loaded.bytes);\n return arrowToCube(table, options);\n }\n\n case \"parquet\": {\n // Lazy import keeps parquet-wasm out of browser bundles that don't use it.\n const { parquetToCube } = await import(\"./sources/parquet\");\n return parquetToCube(loaded.bytes, options);\n }\n\n case \"csv\": {\n const { csvToCube } = await import(\"./sources/csv\");\n const text = new TextDecoder().decode(loaded.bytes);\n return csvToCube(text, {\n measure: options.measure,\n dimensions: options.dimensions,\n status: options.status,\n roles: options.roles,\n delimiter: options.delimiter,\n });\n }\n\n case \"csvw\": {\n const { csvwToCube, parseCsvwMetadata } = await import(\"./sources/csvw\");\n const text = new TextDecoder().decode(loaded.bytes);\n let metadata: CsvwMetadata | undefined;\n if (options.csvwMetadata !== undefined) {\n // Validate caller-supplied metadata through the same parser.\n metadata = parseCsvwMetadata(options.csvwMetadata);\n } else if (loaded.source) {\n // Try a sibling *-metadata.json (common CSVW convention), Node only.\n try {\n const metaPath = siblingMetadataPath(loaded.source);\n const metaBytes = await loadInput(metaPath);\n metadata = parseCsvwMetadata(\n JSON.parse(new TextDecoder().decode(metaBytes.bytes)),\n );\n } catch {\n throw new ImporterError(\n `CSVW metadata not supplied and could not be auto-loaded next to \"${loaded.source}\". ` +\n 'Pass options.csvwMetadata or set options.from = \"csv\".',\n );\n }\n }\n if (metadata === undefined) {\n throw new ImporterError(\n \"CSVW source requires metadata. Pass options.csvwMetadata.\",\n );\n }\n return csvwToCube(text, metadata, {\n measure: options.measure,\n dimensions: options.dimensions,\n status: options.status,\n roles: options.roles,\n });\n }\n\n case \"jsonstat\":\n case \"json\": {\n // A JSON-stat input: read it back into the IR via the cube reader.\n // This supports the round-trip / re-emission use case.\n const { readDataset } = await import(\"./core/cubeReader\");\n const parsed = JSON.parse(new TextDecoder().decode(loaded.bytes));\n return readDataset(parsed, { dropNulls: false });\n }\n\n default:\n throw new ImporterError(\n `Could not detect the source format. Set options.from explicitly ` +\n '(e.g. \"parquet\", \"arrow\", \"csv\", \"csvw\", \"jsonstat\"). ' +\n \"DuckDB and Polars require a live connection/DataFrame — use the \" +\n 'dedicated subpath import (e.g. import { duckdbToCube } from \"jsonstat-io/duckdb\").',\n );\n }\n}\n\n/**\n * Like [`importToCube`](#importtocube) but also builds the final\n * [`JsonStatDataset`](./model/jsonstat.ts) via [`buildDataset`](./core/cubeBuilder.ts).\n *\n * @returns The serialized-ready [`JsonStatDataset`](./model/jsonstat.ts).\n */\nexport async function importToDataset(\n source: string | Uint8Array | Blob,\n options: ImportOptions = {},\n): Promise<JsonStatDataset> {\n const obs = await importToCube(source, options);\n const result: BuildResult = buildDataset(obs, options.build);\n return result.dataset;\n}\n\n/**\n * Build the conventional sibling metadata path for a CSVW CSV file.\n * `data/sales.csv` → `data/sales-metadata.json`.\n */\nfunction siblingMetadataPath(csvPath: string): string {\n const dot = csvPath.lastIndexOf(\".\");\n const base = dot > 0 ? csvPath.slice(0, dot) : csvPath;\n return `${base}-metadata.json`;\n}\n\n// ---------------------------------------------------------------------------\n// High-level convenience: JSON-stat → columnar (export / Phase 2)\n// ---------------------------------------------------------------------------\n\n/** Supported export sinks for [`exportDataset`](#exportdataset). */\nexport type ExportTarget = \"arrow\" | \"parquet\" | \"csv\" | \"csvw\";\n\n/**\n * Options for [`exportDataset`](#exportdataset).\n *\n * Format-specific knobs are passed through to the underlying writer. For\n * Parquet, `init` is forwarded to `parquet-wasm`; for CSVW, `url` sets the\n * metadata `url`. DuckDB and Polars are intentionally not exposed here because\n * they require a live connection / native module — use the dedicated subpath\n * exports (`jsonstat-io/duckdb`, `jsonstat-io/polars`) for those.\n */\nexport interface ExportOptions {\n to: ExportTarget;\n /** Parquet compression codec (forwarded to `parquet-wasm` `writeParquet`). */\n compression?: string;\n /** CSV / CSVW delimiter (default \",\"). */\n delimiter?: string;\n /** CSVW metadata `url` field. */\n url?: string;\n /** Line terminator for CSV / CSVW output (default \"\\r\\n\"). */\n lineTerminator?: string;\n /** Async initializer for parquet-wasm (browser base URL, etc.). */\n init?: () => Promise<void>;\n}\n\n/**\n * The polymorphic result of [`exportDataset`](#exportdataset). The concrete\n * shape depends on [`ExportOptions.to`](#ExportOptions.to):\n *\n * - `\"arrow\"` → an Apache Arrow [`Table`](https://arrow.apache.org/docs/js/).\n * - `\"parquet\"` → a `Uint8Array` of the Parquet file bytes.\n * - `\"csv\"` → the CSV text (`string`).\n * - `\"csvw\"` → `{ csv: string; metadata: CsvwMetadata }`.\n */\nexport type ExportResult = Table | Uint8Array | string | CsvwExportShape;\n\n/** Shape of the `\"csvw\"` export result (mirrors `csvw.CsvwExport`). */\nexport interface CsvwExportShape {\n csv: string;\n metadata: unknown;\n}\n\n/**\n * Export a JSON-stat [`Dataset`](./model/jsonstat.ts) to a columnar format.\n *\n * This is the high-level export dispatcher, the mirror of\n * [`importToDataset`](#importtodataset). It reads the dataset into the\n * [`Observations`](./model/ir.ts) IR via [`readDataset`](./core/cubeReader.ts),\n * then routes to the appropriate writer:\n *\n * - `\"arrow\"` → [`cubeToArrow`](./arrow/arrowFromCube.ts) → Arrow `Table`.\n * - `\"parquet\"` → Arrow → `parquet-wasm` `writeParquet` → `Uint8Array`.\n * - `\"csv\"` → [`cubeToCsv`](./sources/csv.ts) → CSV text.\n * - `\"csvw\"` → [`cubeToCsvw`](./sources/csvw.ts) → `{ csv, metadata }`.\n *\n * DuckDB and Polars require a live connection / native module and are not\n * reachable from this dispatcher — use the dedicated subpath writers\n * (`cubeToDuckdb`, `cubeToPolars`) directly.\n *\n * @example\n * ```ts\n * import { exportDataset } from \"jsonstat-io\";\n * const bytes = await exportDataset(dataset, { to: \"parquet\" });\n * ```\n */\nexport async function exportDataset(\n dataset: JsonStatDataset,\n options: ExportOptions,\n): Promise<ExportResult> {\n // Lazy import keeps the reader out of the import-only bundle path.\n const { readDataset } = await import(\"./core/cubeReader\");\n const obs = readDataset(dataset, { dropNulls: false });\n\n switch (options.to) {\n case \"arrow\": {\n return cubeToArrow(obs);\n }\n case \"parquet\": {\n const { cubeToParquet } = await import(\"./sources/parquet\");\n return cubeToParquet(obs, {\n init: options.init,\n compression: options.compression,\n });\n }\n case \"csv\": {\n const { cubeToCsv } = await import(\"./sources/csv\");\n return cubeToCsv(obs, {\n delimiter: options.delimiter,\n lineTerminator: options.lineTerminator,\n });\n }\n case \"csvw\": {\n const { cubeToCsvw } = await import(\"./sources/csvw\");\n return cubeToCsvw(obs, {\n delimiter: options.delimiter,\n lineTerminator: options.lineTerminator,\n url: options.url,\n });\n }\n default:\n throw new ImporterError(\n `Unsupported export target \"${(options as { to?: string }).to}\". ` +\n 'Use one of: \"arrow\", \"parquet\", \"csv\", \"csvw\". ' +\n \"For DuckDB/Polars, use the dedicated subpath writers.\",\n );\n }\n}\n"]}