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,200 @@
1
+ 'use strict';
2
+
3
+ // src/sources/csv.ts
4
+ var CsvSourceError = class extends Error {
5
+ constructor(message) {
6
+ super(message);
7
+ this.name = "CsvSourceError";
8
+ }
9
+ };
10
+ function parseCsv(input, delimiter = ",") {
11
+ const rows = [];
12
+ let row = [];
13
+ let field = "";
14
+ let inQuotes = false;
15
+ const delim = delimiter.charCodeAt(0);
16
+ for (let i = 0; i < input.length; i++) {
17
+ const c = input.charCodeAt(i);
18
+ if (inQuotes) {
19
+ if (c === 34) {
20
+ if (input.charCodeAt(i + 1) === 34) {
21
+ field += '"';
22
+ i++;
23
+ } else {
24
+ inQuotes = false;
25
+ }
26
+ } else {
27
+ field += input[i];
28
+ }
29
+ } else {
30
+ if (c === 34) {
31
+ inQuotes = true;
32
+ } else if (c === delim) {
33
+ row.push(field);
34
+ field = "";
35
+ } else if (c === 13) {
36
+ if (input.charCodeAt(i + 1) === 10) i++;
37
+ row.push(field);
38
+ rows.push(row);
39
+ row = [];
40
+ field = "";
41
+ } else if (c === 10) {
42
+ row.push(field);
43
+ rows.push(row);
44
+ row = [];
45
+ field = "";
46
+ } else {
47
+ field += input[i];
48
+ }
49
+ }
50
+ }
51
+ if (field.length > 0 || row.length > 0) {
52
+ row.push(field);
53
+ rows.push(row);
54
+ }
55
+ return rows;
56
+ }
57
+ function isAllNumeric(values) {
58
+ let any = false;
59
+ for (const v of values) {
60
+ if (v === "") continue;
61
+ any = true;
62
+ const n = Number(v);
63
+ if (!Number.isFinite(n)) return false;
64
+ }
65
+ return any;
66
+ }
67
+ function csvToCube(text, options = {}) {
68
+ const delimiter = options.delimiter ?? ",";
69
+ const header = options.header ?? true;
70
+ const rows = parseCsv(text, delimiter);
71
+ if (rows.length === 0) throw new CsvSourceError("CSV has no rows");
72
+ let headerNames;
73
+ let dataRows;
74
+ if (header) {
75
+ headerNames = rows[0];
76
+ dataRows = rows.slice(1);
77
+ } else {
78
+ headerNames = rows[0].map((_, i) => `col${i}`);
79
+ dataRows = rows;
80
+ }
81
+ if (headerNames.length === 0) throw new CsvSourceError("CSV header is empty");
82
+ if (dataRows.length === 0) throw new CsvSourceError("CSV has no data rows");
83
+ const colCount = headerNames.length;
84
+ const columns = headerNames.map(() => []);
85
+ for (const r of dataRows) {
86
+ for (let c = 0; c < colCount; c++) columns[c].push(r[c] ?? "");
87
+ }
88
+ let measureIdx = headerNames.findIndex((n2) => n2 === options.measure);
89
+ if (measureIdx === -1) {
90
+ measureIdx = columns.findIndex((col) => isAllNumeric(col));
91
+ if (measureIdx === -1) {
92
+ throw new CsvSourceError(
93
+ "No measure column found: no column is fully numeric. Pass options.measure to name one (values will be parsed with Number())."
94
+ );
95
+ }
96
+ }
97
+ let statusIdx = headerNames.findIndex((n2) => n2 === options.status);
98
+ if (statusIdx === -1 && !options.status) {
99
+ statusIdx = headerNames.findIndex((n2) => n2.toLowerCase() === "status");
100
+ if (statusIdx === measureIdx) statusIdx = -1;
101
+ }
102
+ let dimensionIdxs;
103
+ if (options.dimensions) {
104
+ dimensionIdxs = options.dimensions.map((n2) => {
105
+ const idx = headerNames.indexOf(n2);
106
+ if (idx === -1) throw new CsvSourceError(`Dimension column "${n2}" not found in CSV header`);
107
+ return idx;
108
+ });
109
+ } else {
110
+ dimensionIdxs = headerNames.map((_, i) => i).filter((i) => i !== measureIdx && i !== statusIdx);
111
+ if (dimensionIdxs.length === 0) {
112
+ throw new CsvSourceError("No dimension columns remain after measure/status selection");
113
+ }
114
+ }
115
+ dataRows.length;
116
+ const dimensions = {};
117
+ for (const idx of dimensionIdxs) {
118
+ const name = headerNames[idx];
119
+ dimensions[name] = {
120
+ id: name,
121
+ values: columns[idx].map((v) => v === "" ? "" : v)
122
+ };
123
+ }
124
+ const measure = {
125
+ name: headerNames[measureIdx],
126
+ values: columns[measureIdx].map((v) => v === "" ? null : Number(v))
127
+ };
128
+ let status;
129
+ if (statusIdx !== -1) {
130
+ status = { values: columns[statusIdx].map((v) => v) };
131
+ }
132
+ const observations = {
133
+ dimensions,
134
+ measure,
135
+ status,
136
+ model: {
137
+ dimensionIds: dimensionIdxs.map((i) => headerNames[i]),
138
+ roles: options.roles,
139
+ valueForm: options.valueForm ?? "auto",
140
+ statusForm: status ? "auto" : "none"
141
+ }
142
+ };
143
+ return observations;
144
+ }
145
+ async function csvToDataset(text, options) {
146
+ const { toDataset } = await import('./cubeBuilder-ZU76VVEX.cjs');
147
+ return toDataset(csvToCube(text, options));
148
+ }
149
+ function cubeColumns(obs) {
150
+ const dimIds = obs.model.dimensionIds;
151
+ const measureName = obs.measure.name ?? "value";
152
+ const hasStatus = obs.status !== void 0;
153
+ const header = [...dimIds, measureName];
154
+ if (hasStatus) header.push("status");
155
+ const n = obs.measure.values.length;
156
+ const rows = new Array(n);
157
+ for (let i = 0; i < n; i++) {
158
+ const cells = [];
159
+ for (const id of dimIds) {
160
+ const col = obs.dimensions[id];
161
+ cells.push(col ? col.values[i] : "");
162
+ }
163
+ const v = obs.measure.values[i];
164
+ cells.push(v === null ? "" : String(v));
165
+ if (hasStatus) cells.push(obs.status.values[i] ?? "");
166
+ rows[i] = cells.join("\0");
167
+ }
168
+ return { header, rows };
169
+ }
170
+ function quoteCsvField(value, delimiter, force = false) {
171
+ const needsQuoting = force || value.includes(delimiter) || value.includes('"') || value.includes("\r") || value.includes("\n");
172
+ if (!needsQuoting) return value;
173
+ return `"${value.replace(/"/g, '""')}"`;
174
+ }
175
+ function serializeRows(header, rows, options = {}) {
176
+ const delimiter = options.delimiter ?? ",";
177
+ const eol = options.lineTerminator ?? "\r\n";
178
+ const parts = [];
179
+ parts.push(header.map((h) => quoteCsvField(h, delimiter)).join(delimiter));
180
+ for (const row of rows) {
181
+ const cells = row.split("\0");
182
+ parts.push(cells.map((c) => quoteCsvField(c, delimiter)).join(delimiter));
183
+ }
184
+ return parts.join(eol) + eol;
185
+ }
186
+ function cubeToCsv(obs, options = {}) {
187
+ const { header, rows } = cubeColumns(obs);
188
+ return serializeRows(header, rows, options);
189
+ }
190
+
191
+ exports.CsvSourceError = CsvSourceError;
192
+ exports.csvToCube = csvToCube;
193
+ exports.csvToDataset = csvToDataset;
194
+ exports.cubeColumns = cubeColumns;
195
+ exports.cubeToCsv = cubeToCsv;
196
+ exports.parseCsv = parseCsv;
197
+ exports.quoteCsvField = quoteCsvField;
198
+ exports.serializeRows = serializeRows;
199
+ //# sourceMappingURL=chunk-KFIKGBIJ.cjs.map
200
+ //# sourceMappingURL=chunk-KFIKGBIJ.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/sources/csv.ts"],"names":["n"],"mappings":"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","file":"chunk-KFIKGBIJ.cjs","sourcesContent":["/**\n * CSV adapter — `jsonstat-io/csv`.\n *\n * **Import:** unlike Parquet/DuckDB/Polars (which funnel through the Arrow\n * hub), plain CSV has no native Arrow producer in this package, so this adapter\n * builds the [`Observations`](../model/ir.ts) IR **directly** from parsed rows.\n *\n * **Export:** [`cubeToCsv`] writes the [`Observations`] IR to RFC-4180-style\n * CSV text (dimension columns + measure column + optional status column).\n *\n * ## Mapping heuristics (no metadata)\n *\n * Without a schema, the adapter infers:\n * - The **measure** is the first column that parses as a number for every\n * non-empty row (or the one named by `options.measure`).\n * - Every other column is a **dimension** (string-typed), in file order.\n * - A column named `status` (case-insensitive) is treated as the status\n * column unless `options.status` overrides it.\n *\n * Empty cells become `null` in the measure and `\"\"` in dimensions (then\n * rejected by the builder; sources should pre-clean). For richer, lossless\n * mapping with declared types/labels/roles, use CSVW (see [`csvw`](./csvw.ts)).\n */\n\nimport type {\n DimensionColumn,\n MeasureColumn,\n Observations,\n RoleMap,\n StatusColumn,\n} from \"../model/ir\";\nimport type { JsonStatDataset } from \"../model/jsonstat\";\n\n// ---------------------------------------------------------------------------\n// Errors & options\n// ---------------------------------------------------------------------------\n\nexport class CsvSourceError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"CsvSourceError\";\n }\n}\n\nexport interface CsvToCubeOptions {\n /** Explicit measure column name. Overrides inference. */\n measure?: string;\n /** Explicit dimension column names, in order. */\n dimensions?: string[];\n /** Explicit status column name. Overrides the `status`-name heuristic. */\n status?: string;\n /** Role assignments. */\n roles?: RoleMap;\n /** Value-form hint. */\n valueForm?: \"auto\" | \"dense\" | \"sparse\";\n /** CSV delimiter (default \",\"). */\n delimiter?: string;\n /** Treat the first row as a header (default true). */\n header?: boolean;\n}\n\n// ---------------------------------------------------------------------------\n// Minimal CSV parser\n// ---------------------------------------------------------------------------\n\n/**\n * A tiny, dependency-free RFC-4180-ish CSV parser. Handles quoted fields,\n * embedded quotes (doubled), and embedded newlines. Sufficient for the\n * importer's needs; for heavy CSV work, prefer piping through DuckDB/Polars.\n */\nexport function parseCsv(input: string, delimiter = \",\"): string[][] {\n const rows: string[][] = [];\n let row: string[] = [];\n let field = \"\";\n let inQuotes = false;\n const delim = delimiter.charCodeAt(0);\n\n for (let i = 0; i < input.length; i++) {\n const c = input.charCodeAt(i);\n if (inQuotes) {\n if (c === 34 /* \" */) {\n // Doubled quote → literal quote; otherwise end quote.\n if (input.charCodeAt(i + 1) === 34) {\n field += '\"';\n i++;\n } else {\n inQuotes = false;\n }\n } else {\n field += input[i];\n }\n } else {\n if (c === 34) {\n inQuotes = true;\n } else if (c === delim) {\n row.push(field);\n field = \"\";\n } else if (c === 13 /* \\r */) {\n // swallow; handle \\n\n if (input.charCodeAt(i + 1) === 10) i++;\n row.push(field);\n rows.push(row);\n row = [];\n field = \"\";\n } else if (c === 10 /* \\n */) {\n row.push(field);\n rows.push(row);\n row = [];\n field = \"\";\n } else {\n field += input[i];\n }\n }\n }\n // Flush trailing field/row.\n if (field.length > 0 || row.length > 0) {\n row.push(field);\n rows.push(row);\n }\n return rows;\n}\n\n// ---------------------------------------------------------------------------\n// Type inference\n// ---------------------------------------------------------------------------\n\n/** Does every non-empty value parse as a finite number? */\nfunction isAllNumeric(values: string[]): boolean {\n let any = false;\n for (const v of values) {\n if (v === \"\") continue;\n any = true;\n const n = Number(v);\n if (!Number.isFinite(n)) return false;\n }\n return any;\n}\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\n/**\n * Parse CSV text into the [`Observations`] IR.\n *\n * @throws [`CsvSourceError`] on structural problems.\n */\nexport function csvToCube(text: string, options: CsvToCubeOptions = {}): Observations {\n const delimiter = options.delimiter ?? \",\";\n const header = options.header ?? true;\n const rows = parseCsv(text, delimiter);\n if (rows.length === 0) throw new CsvSourceError(\"CSV has no rows\");\n\n let headerNames: string[];\n let dataRows: string[][];\n if (header) {\n headerNames = rows[0];\n dataRows = rows.slice(1);\n } else {\n headerNames = rows[0].map((_, i) => `col${i}`);\n dataRows = rows;\n }\n if (headerNames.length === 0) throw new CsvSourceError(\"CSV header is empty\");\n if (dataRows.length === 0) throw new CsvSourceError(\"CSV has no data rows\");\n\n const colCount = headerNames.length;\n\n // Transpose into columnar arrays for inference.\n const columns: string[][] = headerNames.map(() => []);\n for (const r of dataRows) {\n // Pad/truncate to header length.\n for (let c = 0; c < colCount; c++) columns[c].push(r[c] ?? \"\");\n }\n\n // Resolve measure column.\n let measureIdx = headerNames.findIndex((n) => n === options.measure);\n if (measureIdx === -1) {\n // Inference: first all-numeric column.\n measureIdx = columns.findIndex((col) => isAllNumeric(col));\n if (measureIdx === -1) {\n throw new CsvSourceError(\n \"No measure column found: no column is fully numeric. \" +\n \"Pass options.measure to name one (values will be parsed with Number()).\",\n );\n }\n }\n\n // Resolve status column.\n let statusIdx = headerNames.findIndex((n) => n === options.status);\n if (statusIdx === -1 && !options.status) {\n statusIdx = headerNames.findIndex((n) => n.toLowerCase() === \"status\");\n if (statusIdx === measureIdx) statusIdx = -1;\n }\n\n // Resolve dimensions: everything that's not measure or status.\n let dimensionIdxs: number[];\n if (options.dimensions) {\n dimensionIdxs = options.dimensions.map((n) => {\n const idx = headerNames.indexOf(n);\n if (idx === -1) throw new CsvSourceError(`Dimension column \"${n}\" not found in CSV header`);\n return idx;\n });\n } else {\n dimensionIdxs = headerNames\n .map((_, i) => i)\n .filter((i) => i !== measureIdx && i !== statusIdx);\n if (dimensionIdxs.length === 0) {\n throw new CsvSourceError(\"No dimension columns remain after measure/status selection\");\n }\n }\n\n const n = dataRows.length;\n const dimensions: Record<string, DimensionColumn> = {};\n for (const idx of dimensionIdxs) {\n const name = headerNames[idx];\n dimensions[name] = {\n id: name,\n values: columns[idx].map((v) => (v === \"\" ? \"\" : v)),\n };\n }\n\n const measure: MeasureColumn = {\n name: headerNames[measureIdx],\n values: columns[measureIdx].map((v) => (v === \"\" ? null : Number(v))),\n };\n\n let status: StatusColumn | undefined;\n if (statusIdx !== -1) {\n status = { values: columns[statusIdx].map((v) => v) };\n }\n\n const observations: Observations = {\n dimensions,\n measure,\n status,\n model: {\n dimensionIds: dimensionIdxs.map((i) => headerNames[i]),\n roles: options.roles,\n valueForm: options.valueForm ?? \"auto\",\n statusForm: status ? \"auto\" : \"none\",\n },\n };\n // n unused guard (kept for clarity; columns already encode length).\n void n;\n\n return observations;\n}\n\n/** Convenience: CSV text → JSON-stat [`Dataset`]. */\nexport async function csvToDataset(\n text: string,\n options?: CsvToCubeOptions,\n): Promise<JsonStatDataset> {\n const { toDataset } = await import(\"../core/cubeBuilder\");\n return toDataset(csvToCube(text, options));\n}\n\n// ---------------------------------------------------------------------------\n// Export: Observations IR → CSV text\n// ---------------------------------------------------------------------------\n\nexport interface CubeToCsvOptions {\n /** CSV delimiter (default \",\"). */\n delimiter?: string;\n /** Line terminator: \"\\r\\n\" (RFC-4180 default) or \"\\n\". */\n lineTerminator?: string;\n}\n\n/**\n * Resolve the ordered list of (column name, row value extractor) pairs for a\n * dataset IR. Shared by [`cubeToCsv`] and [`cubeToCsvw`] so the two writers\n * agree on column layout.\n *\n * Column order: dimension IDs (in `model.dimensionIds` order) → measure\n * (named `measure.name ?? \"value\"`) → status (`\"status\"`, if present).\n *\n * @internal\n */\nexport function cubeColumns(obs: Observations): {\n header: string[];\n rows: string[];\n} {\n const dimIds = obs.model.dimensionIds;\n const measureName = obs.measure.name ?? \"value\";\n const hasStatus = obs.status !== undefined;\n\n const header: string[] = [...dimIds, measureName];\n if (hasStatus) header.push(\"status\");\n\n const n = obs.measure.values.length;\n const rows: string[] = new Array(n);\n for (let i = 0; i < n; i++) {\n const cells: string[] = [];\n for (const id of dimIds) {\n const col = obs.dimensions[id];\n cells.push(col ? col.values[i] : \"\");\n }\n const v = obs.measure.values[i];\n cells.push(v === null ? \"\" : String(v));\n if (hasStatus) cells.push(obs.status!.values[i] ?? \"\");\n rows[i] = cells.join(\"\\u0000\"); // placeholder; real delimiter applied later\n }\n return { header, rows };\n}\n\n/**\n * Quote a single CSV field per RFC-4180. A field needs quoting if it contains\n * the delimiter, a double quote, a CR, or an LF. Embedded quotes are doubled.\n */\nexport function quoteCsvField(\n value: string,\n delimiter: string,\n force = false,\n): string {\n const needsQuoting =\n force ||\n value.includes(delimiter) ||\n value.includes('\"') ||\n value.includes(\"\\r\") ||\n value.includes(\"\\n\");\n if (!needsQuoting) return value;\n return `\"${value.replace(/\"/g, '\"\"')}\"`;\n}\n\n/**\n * Serialize a CSV header + rows (as produced by [`cubeColumns`]) into RFC-4180\n * text. Extracted so CSV and CSVW writers share one escaping path.\n *\n * @internal\n */\nexport function serializeRows(\n header: string[],\n rows: string[],\n options: CubeToCsvOptions = {},\n): string {\n const delimiter = options.delimiter ?? \",\";\n const eol = options.lineTerminator ?? \"\\r\\n\";\n const parts: string[] = [];\n parts.push(header.map((h) => quoteCsvField(h, delimiter)).join(delimiter));\n for (const row of rows) {\n const cells = row.split(\"\\u0000\");\n parts.push(cells.map((c) => quoteCsvField(c, delimiter)).join(delimiter));\n }\n return parts.join(eol) + eol;\n}\n\n/**\n * Write the [`Observations`](../model/ir.ts) IR to RFC-4180-style CSV text.\n *\n * The output is a tidy long table: one header row + one row per observation.\n * Columns are the dimension IDs (in `model.dimensionIds` order), then the\n * measure column (`measure.name ?? \"value\"`), then an optional `status`\n * column. Null measures are emitted as empty cells.\n *\n * This is the lossy inverse of [`csvToCube`]: labels, roles, and category\n * metadata are not preserved (use [`cubeToCsvw`] for a lossless export).\n *\n * @example\n * ```ts\n * import { cubeToCsv } from \"jsonstat-io/csv\";\n * const csv = cubeToCsv(observations);\n * ```\n */\nexport function cubeToCsv(\n obs: Observations,\n options: CubeToCsvOptions = {},\n): string {\n const { header, rows } = cubeColumns(obs);\n return serializeRows(header, rows, options);\n}\n\n"]}
@@ -0,0 +1,64 @@
1
+ // src/core/strides.ts
2
+ function strides(size) {
3
+ const out = new Array(size.length);
4
+ let acc = 1;
5
+ for (let d = size.length - 1; d >= 0; d--) {
6
+ out[d] = acc;
7
+ acc *= size[d];
8
+ }
9
+ return out;
10
+ }
11
+ function totalCells(size) {
12
+ return size.reduce((a, b) => a * b, 1);
13
+ }
14
+ function flatPosition(indices, size) {
15
+ if (indices.length !== size.length) {
16
+ throw new Error(
17
+ `flatPosition: indices length ${indices.length} !== size length ${size.length}`
18
+ );
19
+ }
20
+ const s = strides(size);
21
+ let pos = 0;
22
+ for (let d = 0; d < indices.length; d++) {
23
+ const i = indices[d];
24
+ if (i < 0 || i >= size[d]) {
25
+ throw new Error(
26
+ `flatPosition: index ${i} out of range [0,${size[d]}) for dimension ${d}`
27
+ );
28
+ }
29
+ pos += i * s[d];
30
+ }
31
+ return pos;
32
+ }
33
+ function multiIndex(pos, size) {
34
+ const s = strides(size);
35
+ const total = totalCells(size);
36
+ if (pos < 0 || pos >= total) {
37
+ throw new Error(`multiIndex: position ${pos} out of range [0,${total})`);
38
+ }
39
+ const out = new Array(size.length);
40
+ let rem = pos;
41
+ for (let d = 0; d < size.length; d++) {
42
+ out[d] = Math.floor(rem / s[d]);
43
+ rem = rem % s[d];
44
+ }
45
+ return out;
46
+ }
47
+ function enumerateCells(size, visit) {
48
+ const k = size.length;
49
+ const total = totalCells(size);
50
+ if (total === 0) return;
51
+ const idx = new Array(k).fill(0);
52
+ for (let pos = 0; pos < total; pos++) {
53
+ visit(idx.slice(), pos);
54
+ for (let d = k - 1; d >= 0; d--) {
55
+ idx[d]++;
56
+ if (idx[d] < size[d]) break;
57
+ idx[d] = 0;
58
+ }
59
+ }
60
+ }
61
+
62
+ export { enumerateCells, flatPosition, multiIndex, strides, totalCells };
63
+ //# sourceMappingURL=chunk-L65CRXTV.js.map
64
+ //# sourceMappingURL=chunk-L65CRXTV.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/core/strides.ts"],"names":[],"mappings":"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","file":"chunk-L65CRXTV.js","sourcesContent":["/**\n * Row-major index math — the correctness backbone of the importer.\n *\n * JSON-stat stores cell values in a flat array laid out in **row-major order**,\n * described by the spec as \"what does not change, first\": the *last* dimension\n * in `id` changes fastest (see wiki/format-specification.md and\n * wiki/sparse-cubes.md).\n *\n * Concretely, for dimensions with sizes `s₀, s₁, …, sₖ₋₁`, the flat position of\n * a cell at multi-index `(i₀, i₁, …, iₖ₋₁)` is:\n *\n * pos = i₀·(s₁·…·sₖ₋₁) + i₁·(s₂·…·sₖ₋₁) + … + iₖ₋₂·sₖ₋₁ + iₖ₋₁\n *\n * i.e. the stride of dimension `d` is the product of the sizes of all\n * dimensions that come *after* it. This module centralizes that arithmetic so\n * every converter and the round-trip tests share one source of truth.\n */\n\n/** Compute row-major strides from a `size` array. strides[d] = ∏ size[d+1..]. */\nexport function strides(size: number[]): number[] {\n const out = new Array<number>(size.length);\n let acc = 1;\n // Walk right-to-left so each stride is the running product of later sizes.\n for (let d = size.length - 1; d >= 0; d--) {\n out[d] = acc;\n acc *= size[d];\n }\n return out;\n}\n\n/** Total number of cells = product of all sizes (the dense cell count). */\nexport function totalCells(size: number[]): number {\n return size.reduce((a, b) => a * b, 1);\n}\n\n/**\n * Convert a multi-index to a flat row-major position.\n *\n * `indices` length must equal `size` length; each `indices[d]` must satisfy\n * `0 ≤ indices[d] < size[d]`.\n */\nexport function flatPosition(indices: number[], size: number[]): number {\n if (indices.length !== size.length) {\n throw new Error(\n `flatPosition: indices length ${indices.length} !== size length ${size.length}`,\n );\n }\n const s = strides(size);\n let pos = 0;\n for (let d = 0; d < indices.length; d++) {\n const i = indices[d];\n if (i < 0 || i >= size[d]) {\n throw new Error(\n `flatPosition: index ${i} out of range [0,${size[d]}) for dimension ${d}`,\n );\n }\n pos += i * s[d];\n }\n return pos;\n}\n\n/**\n * Inverse of [`flatPosition`](#flatposition): decompose a flat position back\n * into a multi-index. Used by the cube reader (Phase-2 seam) and by tests.\n */\nexport function multiIndex(pos: number, size: number[]): number[] {\n const s = strides(size);\n const total = totalCells(size);\n if (pos < 0 || pos >= total) {\n throw new Error(`multiIndex: position ${pos} out of range [0,${total})`);\n }\n const out = new Array<number>(size.length);\n let rem = pos;\n for (let d = 0; d < size.length; d++) {\n out[d] = Math.floor(rem / s[d]);\n rem = rem % s[d];\n }\n return out;\n}\n\n/**\n * Enumerate every multi-index in row-major order, yielding flat positions\n * 0..(total-1). Useful for materializing a dense value array from a sparse map.\n *\n * Calls `visit(indices, flatPos)` for each cell. Iterative (no recursion) to\n * avoid stack overflow on large cubes.\n */\nexport function enumerateCells(\n size: number[],\n visit: (indices: number[], flatPos: number) => void,\n): void {\n const k = size.length;\n const total = totalCells(size);\n if (total === 0) return;\n const idx = new Array<number>(k).fill(0);\n for (let pos = 0; pos < total; pos++) {\n visit(idx.slice(), pos);\n // Increment the last dimension first (it changes fastest), carrying over.\n for (let d = k - 1; d >= 0; d--) {\n idx[d]++;\n if (idx[d] < size[d]) break;\n idx[d] = 0; // carry\n }\n }\n}\n"]}