@milaboratories/pf-driver 1.8.4 → 1.9.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.
- package/dist/csv_writer.js +7 -5
- package/dist/csv_writer.js.map +1 -1
- package/dist/driver_decl.d.ts +3 -2
- package/dist/driver_decl.d.ts.map +1 -1
- package/dist/driver_impl.d.ts.map +1 -1
- package/dist/driver_impl.js +14 -5
- package/dist/driver_impl.js.map +1 -1
- package/dist/pframe_pool.js +1 -1
- package/dist/pframe_pool.js.map +1 -1
- package/dist/ptable_def_pool.js.map +1 -1
- package/dist/ptable_pool.js.map +1 -1
- package/dist/ptable_shared.d.ts +1 -2
- package/dist/ptable_shared.js.map +1 -1
- package/package.json +11 -12
- package/src/__tests__/csv_writer.test.ts +20 -0
- package/src/csv_writer.ts +25 -5
- package/src/driver_decl.ts +3 -2
- package/src/driver_double.test.ts +111 -1
- package/src/driver_impl.ts +22 -8
- package/src/pframe_pool.ts +2 -2
- package/src/ptable_def_pool.ts +2 -1
- package/src/ptable_pool.ts +2 -1
- package/src/ptable_shared.ts +4 -3
- package/dist/_virtual/_@oxc-project_runtime@0.137.0/helpers/esm/usingCtx.cjs +0 -57
- package/dist/_virtual/_rolldown/runtime.cjs +0 -23
- package/dist/csv_writer.cjs +0 -81
- package/dist/csv_writer.cjs.map +0 -1
- package/dist/data_info_helpers.cjs +0 -23
- package/dist/data_info_helpers.cjs.map +0 -1
- package/dist/driver_double.cjs +0 -114
- package/dist/driver_double.cjs.map +0 -1
- package/dist/driver_impl.cjs +0 -446
- package/dist/driver_impl.cjs.map +0 -1
- package/dist/index.cjs +0 -9
- package/dist/logging.cjs +0 -9
- package/dist/logging.cjs.map +0 -1
- package/dist/pframe_pool.cjs +0 -215
- package/dist/pframe_pool.cjs.map +0 -1
- package/dist/ptable_cache_per_frame.cjs +0 -58
- package/dist/ptable_cache_per_frame.cjs.map +0 -1
- package/dist/ptable_cache_plain.cjs +0 -45
- package/dist/ptable_cache_plain.cjs.map +0 -1
- package/dist/ptable_def_pool.cjs +0 -66
- package/dist/ptable_def_pool.cjs.map +0 -1
- package/dist/ptable_pool.cjs +0 -84
- package/dist/ptable_pool.cjs.map +0 -1
- package/dist/ptable_shared.cjs +0 -63
- package/dist/ptable_shared.cjs.map +0 -1
package/src/csv_writer.ts
CHANGED
|
@@ -21,7 +21,9 @@ export interface PTableDataSource {
|
|
|
21
21
|
|
|
22
22
|
/** Format a CSV/TSV header row from column specs. Line ending is CRLF. */
|
|
23
23
|
export function formatHeader(specs: PTableColumnSpec[], separator: string): string {
|
|
24
|
-
return
|
|
24
|
+
return (
|
|
25
|
+
specs.map((spec) => escapeField(getNativeColumnLabel(spec), separator)).join(separator) + "\r\n"
|
|
26
|
+
);
|
|
25
27
|
}
|
|
26
28
|
|
|
27
29
|
/** Format a single data row from parallel vectors. Line ending is CRLF. */
|
|
@@ -44,6 +46,13 @@ export interface StreamPTableRowsOptions {
|
|
|
44
46
|
pTable: PTableDataSource;
|
|
45
47
|
specs: PTableColumnSpec[];
|
|
46
48
|
columnIndices: number[];
|
|
49
|
+
/**
|
|
50
|
+
* Header names aligned 1:1 with `columnIndices`. When provided they are
|
|
51
|
+
* written verbatim, letting callers supply disambiguated labels the spec's
|
|
52
|
+
* intrinsic `pl7.app/label` may not carry. When omitted the header is derived
|
|
53
|
+
* from `specs` via {@link getNativeColumnLabel}.
|
|
54
|
+
*/
|
|
55
|
+
headerNames?: string[];
|
|
47
56
|
range?: TableRange;
|
|
48
57
|
chunkSize: number;
|
|
49
58
|
separator: string;
|
|
@@ -52,16 +61,27 @@ export interface StreamPTableRowsOptions {
|
|
|
52
61
|
signal?: AbortSignal;
|
|
53
62
|
}
|
|
54
63
|
export async function* streamPTableRows(options: StreamPTableRowsOptions): AsyncIterable<string> {
|
|
55
|
-
const { pTable, columnIndices, range, chunkSize, separator, signal, specs
|
|
64
|
+
const { pTable, columnIndices, headerNames, range, chunkSize, separator, signal, specs } =
|
|
56
65
|
options;
|
|
66
|
+
const { includeHeader, bom } = options;
|
|
67
|
+
|
|
68
|
+
if (headerNames !== undefined && headerNames.length !== columnIndices.length) {
|
|
69
|
+
throw new Error(
|
|
70
|
+
`headerNames length (${headerNames.length}) must match columnIndices length (${columnIndices.length})`,
|
|
71
|
+
);
|
|
72
|
+
}
|
|
57
73
|
|
|
58
74
|
if (bom) {
|
|
59
75
|
yield "\uFEFF";
|
|
60
76
|
}
|
|
61
77
|
|
|
62
78
|
if (includeHeader) {
|
|
63
|
-
|
|
64
|
-
|
|
79
|
+
yield headerNames !== undefined
|
|
80
|
+
? headerNames.map((name) => escapeField(name, separator)).join(separator) + "\r\n"
|
|
81
|
+
: formatHeader(
|
|
82
|
+
columnIndices.map((index) => specs[index]),
|
|
83
|
+
separator,
|
|
84
|
+
);
|
|
65
85
|
}
|
|
66
86
|
|
|
67
87
|
if (isNil(range)) {
|
|
@@ -89,7 +109,7 @@ export async function* streamPTableRows(options: StreamPTableRowsOptions): Async
|
|
|
89
109
|
// ── Helpers (low-level) ──────────────────────────────────────────────
|
|
90
110
|
|
|
91
111
|
/** Extract a human-readable label from a PTableColumnSpec. */
|
|
92
|
-
export function
|
|
112
|
+
export function getNativeColumnLabel(spec: PTableColumnSpec): string {
|
|
93
113
|
const annotation = readAnnotation(spec.spec, Annotation.Label);
|
|
94
114
|
return isNil(annotation) ? spec.spec.name : annotation.trim();
|
|
95
115
|
}
|
package/src/driver_decl.ts
CHANGED
|
@@ -105,8 +105,9 @@ export interface AbstractInternalPFrameDriver<PColumnData> extends PFrameDriver,
|
|
|
105
105
|
/**
|
|
106
106
|
* Export the table to a file, selecting the format from the extension of
|
|
107
107
|
* `options.path` (`csv`/`tsv`/`parquet`/`xlsx`). `options.columnIndices`
|
|
108
|
-
* selects the columns to export (output order);
|
|
109
|
-
*
|
|
108
|
+
* selects the columns to export (output order); optional
|
|
109
|
+
* `options.headerNames` (aligned 1:1) names them, with a missing or empty
|
|
110
|
+
* entry falling back to the field's label annotation (then the spec name).
|
|
110
111
|
*/
|
|
111
112
|
exportPTable(
|
|
112
113
|
handle: PTableHandle,
|
|
@@ -527,7 +527,11 @@ test("exportPTable writes a CSV with derived headers", async ({ expect }) => {
|
|
|
527
527
|
try {
|
|
528
528
|
const filePath = join(tempDir, "table.csv");
|
|
529
529
|
|
|
530
|
-
|
|
530
|
+
// Omitted headerNames fall back to labels derived from the column/axis specs.
|
|
531
|
+
await uiDriver.exportPTable(pTable.key, {
|
|
532
|
+
path: filePath,
|
|
533
|
+
columnIndices: [0, 1],
|
|
534
|
+
});
|
|
531
535
|
|
|
532
536
|
const content = fs.readFileSync(filePath, "utf-8");
|
|
533
537
|
const lines = content.split(/\r?\n/).filter((l) => l.length > 0);
|
|
@@ -540,3 +544,109 @@ test("exportPTable writes a CSV with derived headers", async ({ expect }) => {
|
|
|
540
544
|
fs.rmSync(tempDir, { recursive: true, force: true });
|
|
541
545
|
}
|
|
542
546
|
});
|
|
547
|
+
|
|
548
|
+
test("exportPTable writes caller-supplied header names verbatim", async ({ expect }) => {
|
|
549
|
+
await using driver = await createPFrameDriverDouble({});
|
|
550
|
+
|
|
551
|
+
const columnId = "column1" as PObjectId;
|
|
552
|
+
const column = {
|
|
553
|
+
id: columnId,
|
|
554
|
+
spec: {
|
|
555
|
+
kind: "PColumn" as const,
|
|
556
|
+
axesSpec: [
|
|
557
|
+
{
|
|
558
|
+
name: "axis1",
|
|
559
|
+
type: "String" as const,
|
|
560
|
+
annotations: { "pl7.app/label": "Sample" },
|
|
561
|
+
},
|
|
562
|
+
],
|
|
563
|
+
name: "column1",
|
|
564
|
+
valueType: "Int" as const,
|
|
565
|
+
annotations: { "pl7.app/label": "Score" },
|
|
566
|
+
},
|
|
567
|
+
data: [
|
|
568
|
+
{ key: ["a"], val: 10 },
|
|
569
|
+
{ key: ["b"], val: 20 },
|
|
570
|
+
{ key: ["c"], val: 30 },
|
|
571
|
+
],
|
|
572
|
+
};
|
|
573
|
+
|
|
574
|
+
const uiDriver: PFrameDriver = driver;
|
|
575
|
+
|
|
576
|
+
using pTable = driver.createPTableV2({ query: { type: "column", column } });
|
|
577
|
+
|
|
578
|
+
const tempDir = fs.mkdtempSync(join(os.tmpdir(), "pf-export-"));
|
|
579
|
+
try {
|
|
580
|
+
const filePath = join(tempDir, "table.csv");
|
|
581
|
+
|
|
582
|
+
// Caller-supplied names (the disambiguated labels the UI shows) win over the
|
|
583
|
+
// spec's intrinsic `pl7.app/label`.
|
|
584
|
+
await uiDriver.exportPTable(pTable.key, {
|
|
585
|
+
path: filePath,
|
|
586
|
+
columnIndices: [0, 1],
|
|
587
|
+
headerNames: ["Sample id", "Score (disambiguated)"],
|
|
588
|
+
});
|
|
589
|
+
|
|
590
|
+
const content = fs.readFileSync(filePath, "utf-8");
|
|
591
|
+
const lines = content.split(/\r?\n/).filter((l) => l.length > 0);
|
|
592
|
+
|
|
593
|
+
expect(lines[0]).toBe("Sample id,Score (disambiguated)");
|
|
594
|
+
expect(lines.slice(1)).toEqual(["a,10", "b,20", "c,30"]);
|
|
595
|
+
} finally {
|
|
596
|
+
fs.rmSync(tempDir, { recursive: true, force: true });
|
|
597
|
+
}
|
|
598
|
+
});
|
|
599
|
+
|
|
600
|
+
test("writePTableToFs writes caller-supplied header names verbatim", async ({ expect }) => {
|
|
601
|
+
await using driver = await createPFrameDriverDouble({});
|
|
602
|
+
|
|
603
|
+
const columnId = "column1" as PObjectId;
|
|
604
|
+
const column = {
|
|
605
|
+
id: columnId,
|
|
606
|
+
spec: {
|
|
607
|
+
kind: "PColumn" as const,
|
|
608
|
+
axesSpec: [
|
|
609
|
+
{
|
|
610
|
+
name: "axis1",
|
|
611
|
+
type: "String" as const,
|
|
612
|
+
annotations: { "pl7.app/label": "Sample" },
|
|
613
|
+
},
|
|
614
|
+
],
|
|
615
|
+
name: "column1",
|
|
616
|
+
valueType: "Int" as const,
|
|
617
|
+
annotations: { "pl7.app/label": "Score" },
|
|
618
|
+
},
|
|
619
|
+
data: [
|
|
620
|
+
{ key: ["a"], val: 10 },
|
|
621
|
+
{ key: ["b"], val: 20 },
|
|
622
|
+
{ key: ["c"], val: 30 },
|
|
623
|
+
],
|
|
624
|
+
};
|
|
625
|
+
|
|
626
|
+
const uiDriver: PFrameDriver = driver;
|
|
627
|
+
|
|
628
|
+
using pTable = driver.createPTableV2({ query: { type: "column", column } });
|
|
629
|
+
|
|
630
|
+
const tempDir = fs.mkdtempSync(join(os.tmpdir(), "pf-export-"));
|
|
631
|
+
try {
|
|
632
|
+
const filePath = join(tempDir, "table.csv");
|
|
633
|
+
|
|
634
|
+
// Fallback path (writePTableToFs): caller-supplied names win over the spec
|
|
635
|
+
// labels, mirroring the native exportPTable path.
|
|
636
|
+
await uiDriver.writePTableToFs(pTable.key, {
|
|
637
|
+
path: filePath,
|
|
638
|
+
format: "csv",
|
|
639
|
+
columnIndices: [0, 1],
|
|
640
|
+
headerNames: ["Sample id", "Score (disambiguated)"],
|
|
641
|
+
});
|
|
642
|
+
|
|
643
|
+
// Strip the leading BOM the CSV writer emits by default.
|
|
644
|
+
const content = fs.readFileSync(filePath, "utf-8").replace(/^/, "");
|
|
645
|
+
const lines = content.split(/\r?\n/).filter((l) => l.length > 0);
|
|
646
|
+
|
|
647
|
+
expect(lines[0]).toBe("Sample id,Score (disambiguated)");
|
|
648
|
+
expect(lines.slice(1)).toEqual(["a,10", "b,20", "c,30"]);
|
|
649
|
+
} finally {
|
|
650
|
+
fs.rmSync(tempDir, { recursive: true, force: true });
|
|
651
|
+
}
|
|
652
|
+
});
|
package/src/driver_impl.ts
CHANGED
|
@@ -44,13 +44,13 @@ import {
|
|
|
44
44
|
} from "@milaboratories/ts-helpers";
|
|
45
45
|
import { isNil, PoolEntryGuard, type PoolEntry } from "@milaboratories/helpers";
|
|
46
46
|
import { PFrameFactory } from "@milaboratories/pframes-rs-node";
|
|
47
|
-
import { expandAxes, findTableColumn, rewriteLegacyFilters } from "@milaboratories/
|
|
47
|
+
import { expandAxes, findTableColumn, rewriteLegacyFilters } from "@milaboratories/pf-spec";
|
|
48
48
|
import { tmpdir } from "node:os";
|
|
49
49
|
import * as fs from "node:fs";
|
|
50
50
|
import { Readable } from "node:stream";
|
|
51
51
|
import { pipeline } from "node:stream/promises";
|
|
52
52
|
import * as zlib from "node:zlib";
|
|
53
|
-
import {
|
|
53
|
+
import { getNativeColumnLabel, streamPTableRows } from "./csv_writer";
|
|
54
54
|
import type {
|
|
55
55
|
AbstractInternalPFrameDriver,
|
|
56
56
|
WritePTableToFsOptions,
|
|
@@ -297,6 +297,14 @@ export class AbstractPFrameDriver<
|
|
|
297
297
|
const { pTablePromise, disposeSignal } = tableGuard.resource;
|
|
298
298
|
const pTable = await pTablePromise;
|
|
299
299
|
|
|
300
|
+
// Caller-supplied header names win (they carry the disambiguated labels the
|
|
301
|
+
// UI shows); a missing or empty name falls back to the spec-derived label.
|
|
302
|
+
const { columnIndices } = options;
|
|
303
|
+
const headerNames = columnIndices.map((index, i) => {
|
|
304
|
+
const name = options.headerNames?.[i];
|
|
305
|
+
return name !== undefined && name !== "" ? name : getNativeColumnLabel(def.tableSpec[index]);
|
|
306
|
+
});
|
|
307
|
+
|
|
300
308
|
const combinedSignal = AbortSignal.any(
|
|
301
309
|
[options.signal, disposeSignal].filter((s): s is AbortSignal => !isNil(s)),
|
|
302
310
|
);
|
|
@@ -316,7 +324,8 @@ export class AbstractPFrameDriver<
|
|
|
316
324
|
const iterable = streamPTableRows({
|
|
317
325
|
pTable,
|
|
318
326
|
specs,
|
|
319
|
-
columnIndices
|
|
327
|
+
columnIndices,
|
|
328
|
+
headerNames,
|
|
320
329
|
range: clippedRange,
|
|
321
330
|
chunkSize: options.chunkSize ?? 50_000,
|
|
322
331
|
separator,
|
|
@@ -354,7 +363,7 @@ export class AbstractPFrameDriver<
|
|
|
354
363
|
const durationMs = Math.round(performance.now() - startTime);
|
|
355
364
|
this.logger(
|
|
356
365
|
"info",
|
|
357
|
-
`[WritePTableToFs] complete (handle = ${handle}, columns = ${
|
|
366
|
+
`[WritePTableToFs] complete (handle = ${handle}, columns = ${columnIndices.length}, rows = ${rowsWritten}, bytes = ${bytesWritten}, duration = ${durationMs}ms)`,
|
|
358
367
|
);
|
|
359
368
|
}
|
|
360
369
|
|
|
@@ -384,10 +393,15 @@ export class AbstractPFrameDriver<
|
|
|
384
393
|
[signal, disposeSignal].filter((s): s is AbortSignal => !isNil(s)),
|
|
385
394
|
);
|
|
386
395
|
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
396
|
+
// Caller-supplied header names win (they carry the disambiguated labels the
|
|
397
|
+
// UI shows); a missing or empty name falls back to the spec-derived label.
|
|
398
|
+
const headers: [number, string][] = columnIndices.map((index, i): [number, string] => {
|
|
399
|
+
const name = options.headerNames?.[i];
|
|
400
|
+
return [
|
|
401
|
+
index,
|
|
402
|
+
name !== undefined && name !== "" ? name : getNativeColumnLabel(def.tableSpec[index]),
|
|
403
|
+
];
|
|
404
|
+
});
|
|
391
405
|
|
|
392
406
|
// keep()/cache live outside the limiter task: on abort `run` rejects and they are
|
|
393
407
|
// skipped, so an abandoned (detached) operation never caches its result nor touches
|
package/src/pframe_pool.ts
CHANGED
|
@@ -16,7 +16,7 @@ import {
|
|
|
16
16
|
import { hashJson, PFrameInternal } from "@milaboratories/pl-model-middle-layer";
|
|
17
17
|
import { RefCountPoolBase, type PoolEntry } from "@milaboratories/helpers";
|
|
18
18
|
import { PFrameFactory } from "@milaboratories/pframes-rs-node";
|
|
19
|
-
import { createPFrame as createPFrameSpec } from "@milaboratories/
|
|
19
|
+
import { createPFrame as createPFrameSpec, type PFrame } from "@milaboratories/pf-spec";
|
|
20
20
|
import { mapValues } from "es-toolkit";
|
|
21
21
|
import { logPFrames } from "./logging";
|
|
22
22
|
|
|
@@ -37,7 +37,7 @@ export class PFrameHolder<TreeEntry extends JsonSerializable> implements Disposa
|
|
|
37
37
|
* for spec-side operations: column discovery, selector resolution,
|
|
38
38
|
* legacy-query lowering.
|
|
39
39
|
*/
|
|
40
|
-
public readonly pFrameSpec:
|
|
40
|
+
public readonly pFrameSpec: PFrame;
|
|
41
41
|
public readonly columnSpecs: Readonly<Record<PObjectId, PColumnSpec>>;
|
|
42
42
|
private readonly abortController = new AbortController();
|
|
43
43
|
|
package/src/ptable_def_pool.ts
CHANGED
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
type PTableHandle,
|
|
7
7
|
} from "@milaboratories/pl-model-common";
|
|
8
8
|
import type { PFrameInternal } from "@milaboratories/pl-model-middle-layer";
|
|
9
|
+
import type { PFrame } from "@milaboratories/pf-spec";
|
|
9
10
|
import { RefCountPoolBase, type PoolEntry } from "@milaboratories/helpers";
|
|
10
11
|
import { logPFrames } from "./logging";
|
|
11
12
|
import type { FullPTableDef } from "./ptable_shared";
|
|
@@ -70,7 +71,7 @@ export class PTableDefPool extends RefCountPoolBase<FullPTableDef, PTableHandle,
|
|
|
70
71
|
public acquireFromLegacy(opts: {
|
|
71
72
|
pFrameHandle: PFrameHandle;
|
|
72
73
|
def: PTableDef<PObjectId>;
|
|
73
|
-
pFrameSpec:
|
|
74
|
+
pFrameSpec: PFrame;
|
|
74
75
|
}): { def: FullPTableDef; entry: PoolEntry<PTableHandle> } {
|
|
75
76
|
const def = buildFullPTableDefFromLegacy(opts);
|
|
76
77
|
return { def, entry: this.acquire(def) };
|
package/src/ptable_pool.ts
CHANGED
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
type PObjectId,
|
|
9
9
|
} from "@milaboratories/pl-model-common";
|
|
10
10
|
import type { PFrameInternal } from "@milaboratories/pl-model-middle-layer";
|
|
11
|
+
import type { PFrame } from "@milaboratories/pf-spec";
|
|
11
12
|
import { RefCountPoolBase, type PoolEntry } from "@milaboratories/helpers";
|
|
12
13
|
import { logPFrames } from "./logging";
|
|
13
14
|
import type { PFramePool } from "./pframe_pool";
|
|
@@ -117,7 +118,7 @@ export class PTablePool<TreeEntry extends JsonSerializable> extends RefCountPool
|
|
|
117
118
|
public acquireFromLegacy(opts: {
|
|
118
119
|
pFrameHandle: PFrameHandle;
|
|
119
120
|
def: PTableDef<PObjectId>;
|
|
120
|
-
pFrameSpec:
|
|
121
|
+
pFrameSpec: PFrame;
|
|
121
122
|
}): { def: FullPTableDef; entry: PoolEntry<PTableHandle, PTableHolder> } {
|
|
122
123
|
const def = buildFullPTableDefFromLegacy(opts);
|
|
123
124
|
return { def, entry: this.acquire(def) };
|
package/src/ptable_shared.ts
CHANGED
|
@@ -10,6 +10,7 @@ import type {
|
|
|
10
10
|
} from "@milaboratories/pl-model-common";
|
|
11
11
|
import { traverseQuerySpec } from "@milaboratories/pl-model-common";
|
|
12
12
|
import { hashJson, PFrameInternal } from "@milaboratories/pl-model-middle-layer";
|
|
13
|
+
import type { LegacyQuery, PFrame } from "@milaboratories/pf-spec";
|
|
13
14
|
|
|
14
15
|
/**
|
|
15
16
|
* Make every inline column in a `SpecQuery` self-contained by embedding its
|
|
@@ -54,10 +55,10 @@ export function stableKeyFromFullPTableDef(data: FullPTableDef): PTableHandle {
|
|
|
54
55
|
* WASM-spec frame.
|
|
55
56
|
*/
|
|
56
57
|
export function lowerLegacyPTableDef(
|
|
57
|
-
pFrameSpec:
|
|
58
|
+
pFrameSpec: PFrame,
|
|
58
59
|
legacyDef: PTableDef<PObjectId>,
|
|
59
60
|
): { tableSpec: PTableColumnSpec[]; dataQuery: DataQuery } {
|
|
60
|
-
const legacy:
|
|
61
|
+
const legacy: LegacyQuery = {
|
|
61
62
|
src: legacyDef.src,
|
|
62
63
|
filters: [...legacyDef.partitionFilters, ...legacyDef.filters],
|
|
63
64
|
sorting: legacyDef.sorting,
|
|
@@ -75,7 +76,7 @@ export function lowerLegacyPTableDef(
|
|
|
75
76
|
export function buildFullPTableDefFromLegacy(opts: {
|
|
76
77
|
pFrameHandle: PFrameHandle;
|
|
77
78
|
def: PTableDef<PObjectId>;
|
|
78
|
-
pFrameSpec:
|
|
79
|
+
pFrameSpec: PFrame;
|
|
79
80
|
}): FullPTableDef {
|
|
80
81
|
return {
|
|
81
82
|
pFrameHandle: opts.pFrameHandle,
|
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
//#region \0@oxc-project+runtime@0.137.0/helpers/esm/usingCtx.js
|
|
2
|
-
function _usingCtx() {
|
|
3
|
-
var r = "function" == typeof SuppressedError ? SuppressedError : function(r, e) {
|
|
4
|
-
var n = Error();
|
|
5
|
-
return n.name = "SuppressedError", n.error = r, n.suppressed = e, n;
|
|
6
|
-
}, e = {}, n = [];
|
|
7
|
-
function using(r, e) {
|
|
8
|
-
if (null != e) {
|
|
9
|
-
if (Object(e) !== e) throw new TypeError("using declarations can only be used with objects, functions, null, or undefined.");
|
|
10
|
-
if (r) var o = e[Symbol.asyncDispose || Symbol["for"]("Symbol.asyncDispose")];
|
|
11
|
-
if (void 0 === o && (o = e[Symbol.dispose || Symbol["for"]("Symbol.dispose")], r)) var t = o;
|
|
12
|
-
if ("function" != typeof o) throw new TypeError("Object is not disposable.");
|
|
13
|
-
t && (o = function o() {
|
|
14
|
-
try {
|
|
15
|
-
t.call(e);
|
|
16
|
-
} catch (r) {
|
|
17
|
-
return Promise.reject(r);
|
|
18
|
-
}
|
|
19
|
-
}), n.push({
|
|
20
|
-
v: e,
|
|
21
|
-
d: o,
|
|
22
|
-
a: r
|
|
23
|
-
});
|
|
24
|
-
} else r && n.push({
|
|
25
|
-
d: e,
|
|
26
|
-
a: r
|
|
27
|
-
});
|
|
28
|
-
return e;
|
|
29
|
-
}
|
|
30
|
-
return {
|
|
31
|
-
e,
|
|
32
|
-
u: using.bind(null, !1),
|
|
33
|
-
a: using.bind(null, !0),
|
|
34
|
-
d: function d() {
|
|
35
|
-
var o, t = this.e, s = 0;
|
|
36
|
-
function next() {
|
|
37
|
-
for (; o = n.pop();) try {
|
|
38
|
-
if (!o.a && 1 === s) return s = 0, n.push(o), Promise.resolve().then(next);
|
|
39
|
-
if (o.d) {
|
|
40
|
-
var r = o.d.call(o.v);
|
|
41
|
-
if (o.a) return s |= 2, Promise.resolve(r).then(next, err);
|
|
42
|
-
} else s |= 1;
|
|
43
|
-
} catch (r) {
|
|
44
|
-
return err(r);
|
|
45
|
-
}
|
|
46
|
-
if (1 === s) return t !== e ? Promise.reject(t) : Promise.resolve();
|
|
47
|
-
if (t !== e) throw t;
|
|
48
|
-
}
|
|
49
|
-
function err(n) {
|
|
50
|
-
return t = t !== e ? new r(n, t) : n, next();
|
|
51
|
-
}
|
|
52
|
-
return next();
|
|
53
|
-
}
|
|
54
|
-
};
|
|
55
|
-
}
|
|
56
|
-
//#endregion
|
|
57
|
-
exports.default = _usingCtx;
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
//#region \0rolldown/runtime.js
|
|
2
|
-
var __create = Object.create;
|
|
3
|
-
var __defProp = Object.defineProperty;
|
|
4
|
-
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
-
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
-
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
-
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
-
var __copyProps = (to, from, except, desc) => {
|
|
9
|
-
if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
|
|
10
|
-
key = keys[i];
|
|
11
|
-
if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
|
|
12
|
-
get: ((k) => from[k]).bind(null, key),
|
|
13
|
-
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
14
|
-
});
|
|
15
|
-
}
|
|
16
|
-
return to;
|
|
17
|
-
};
|
|
18
|
-
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
|
|
19
|
-
value: mod,
|
|
20
|
-
enumerable: true
|
|
21
|
-
}) : target, mod));
|
|
22
|
-
//#endregion
|
|
23
|
-
exports.__toESM = __toESM;
|
package/dist/csv_writer.cjs
DELETED
|
@@ -1,81 +0,0 @@
|
|
|
1
|
-
let _milaboratories_pl_model_common = require("@milaboratories/pl-model-common");
|
|
2
|
-
let _milaboratories_helpers = require("@milaboratories/helpers");
|
|
3
|
-
//#region src/csv_writer.ts
|
|
4
|
-
/** Format a CSV/TSV header row from column specs. Line ending is CRLF. */
|
|
5
|
-
function formatHeader(specs, separator) {
|
|
6
|
-
return specs.map((spec) => escapeField(columnLabel(spec), separator)).join(separator) + "\r\n";
|
|
7
|
-
}
|
|
8
|
-
/** Format a single data row from parallel vectors. Line ending is CRLF. */
|
|
9
|
-
function formatRow(vectors, rowIndex, separator) {
|
|
10
|
-
return vectors.map((vector) => escapeField(serializeValue(vector, rowIndex), separator)).join(separator) + "\r\n";
|
|
11
|
-
}
|
|
12
|
-
async function* streamPTableRows(options) {
|
|
13
|
-
const { pTable, columnIndices, range, chunkSize, separator, signal, specs, includeHeader, bom } = options;
|
|
14
|
-
if (bom) yield "";
|
|
15
|
-
if (includeHeader) yield formatHeader(columnIndices.map((index) => specs[index]), separator);
|
|
16
|
-
if ((0, _milaboratories_helpers.isNil)(range)) return;
|
|
17
|
-
const end = range.offset + range.length;
|
|
18
|
-
for (let from = range.offset; from < end; from += chunkSize) {
|
|
19
|
-
signal?.throwIfAborted();
|
|
20
|
-
const length = Math.min(chunkSize, end - from);
|
|
21
|
-
const subRange = {
|
|
22
|
-
offset: from,
|
|
23
|
-
length
|
|
24
|
-
};
|
|
25
|
-
const vectors = await pTable.getData(columnIndices, {
|
|
26
|
-
range: subRange,
|
|
27
|
-
signal
|
|
28
|
-
});
|
|
29
|
-
const rows = [];
|
|
30
|
-
for (let rowIndex = 0; rowIndex < length; rowIndex++) rows.push(formatRow(vectors, rowIndex, separator));
|
|
31
|
-
yield rows.join("");
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
/** Extract a human-readable label from a PTableColumnSpec. */
|
|
35
|
-
function columnLabel(spec) {
|
|
36
|
-
const annotation = (0, _milaboratories_pl_model_common.readAnnotation)(spec.spec, _milaboratories_pl_model_common.Annotation.Label);
|
|
37
|
-
return (0, _milaboratories_helpers.isNil)(annotation) ? spec.spec.name : annotation.trim();
|
|
38
|
-
}
|
|
39
|
-
/**
|
|
40
|
-
* RFC 4180 field escaping.
|
|
41
|
-
* Quote if the field contains the separator, a double-quote, CR, or LF.
|
|
42
|
-
* Embedded `"` are doubled.
|
|
43
|
-
*/
|
|
44
|
-
function escapeField(value, separator) {
|
|
45
|
-
return needsQuoting(value, separator) ? "\"" + value.replace(/"/g, "\"\"") + "\"" : value;
|
|
46
|
-
}
|
|
47
|
-
/** Returns true when the value must be wrapped in double-quotes. */
|
|
48
|
-
function needsQuoting(value, separator) {
|
|
49
|
-
return value.includes(separator) || value.includes("\"") || value.includes("\r") || value.includes("\n");
|
|
50
|
-
}
|
|
51
|
-
/**
|
|
52
|
-
* Serialize one cell value from a typed vector to its string representation.
|
|
53
|
-
*
|
|
54
|
-
* - `null` / `undefined` -> `""`
|
|
55
|
-
* - `bigint` -> `String(x)`
|
|
56
|
-
* - `NaN` / `+Inf` / `-Inf` -> `""`
|
|
57
|
-
*/
|
|
58
|
-
function serializeValue(vector, rowIndex) {
|
|
59
|
-
const rawValue = vector.data[rowIndex];
|
|
60
|
-
if ((0, _milaboratories_helpers.isNil)(rawValue)) return "";
|
|
61
|
-
if ((0, _milaboratories_pl_model_common.isValueNA)(vector, rowIndex)) return "";
|
|
62
|
-
switch (vector.type) {
|
|
63
|
-
case _milaboratories_pl_model_common.ValueType.Long: return String(rawValue);
|
|
64
|
-
case _milaboratories_pl_model_common.ValueType.Float:
|
|
65
|
-
case _milaboratories_pl_model_common.ValueType.Double: {
|
|
66
|
-
const numeric = rawValue;
|
|
67
|
-
return Number.isNaN(numeric) || !Number.isFinite(numeric) ? "" : String(numeric);
|
|
68
|
-
}
|
|
69
|
-
case _milaboratories_pl_model_common.ValueType.Int: return String(rawValue);
|
|
70
|
-
case _milaboratories_pl_model_common.ValueType.String: return rawValue;
|
|
71
|
-
case _milaboratories_pl_model_common.ValueType.Bytes: return "";
|
|
72
|
-
default: return String(rawValue);
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
//#endregion
|
|
76
|
-
exports.columnLabel = columnLabel;
|
|
77
|
-
exports.formatHeader = formatHeader;
|
|
78
|
-
exports.formatRow = formatRow;
|
|
79
|
-
exports.streamPTableRows = streamPTableRows;
|
|
80
|
-
|
|
81
|
-
//# sourceMappingURL=csv_writer.cjs.map
|
package/dist/csv_writer.cjs.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"csv_writer.cjs","names":["Annotation","ValueType"],"sources":["../src/csv_writer.ts"],"sourcesContent":["import {\n Annotation,\n isValueNA,\n readAnnotation,\n ValueType,\n type PTableColumnSpec,\n type PTableVector,\n type TableRange,\n} from \"@milaboratories/pl-model-common\";\nimport { isNil } from \"@milaboratories/helpers\";\n\n/** Minimal subset of PTable required by streamPTableRows. */\nexport interface PTableDataSource {\n getData(\n columnIndices: number[],\n options?: { range?: TableRange; signal?: AbortSignal },\n ): Promise<PTableVector[]>;\n}\n\n// ── Public API (high-level → low-level) ──────────────────────────────\n\n/** Format a CSV/TSV header row from column specs. Line ending is CRLF. */\nexport function formatHeader(specs: PTableColumnSpec[], separator: string): string {\n return specs.map((spec) => escapeField(columnLabel(spec), separator)).join(separator) + \"\\r\\n\";\n}\n\n/** Format a single data row from parallel vectors. Line ending is CRLF. */\nexport function formatRow(vectors: PTableVector[], rowIndex: number, separator: string): string {\n return (\n vectors\n .map((vector) => escapeField(serializeValue(vector, rowIndex), separator))\n .join(separator) + \"\\r\\n\"\n );\n}\n\n/**\n * Async generator that streams CSV/TSV content chunk by chunk.\n *\n * The caller is responsible for providing a concrete `range` (already clipped\n * to the table shape). When `range` is undefined the generator does nothing\n * beyond emitting an optional BOM and header.\n */\nexport interface StreamPTableRowsOptions {\n pTable: PTableDataSource;\n specs: PTableColumnSpec[];\n columnIndices: number[];\n range?: TableRange;\n chunkSize: number;\n separator: string;\n includeHeader: boolean;\n bom: boolean;\n signal?: AbortSignal;\n}\nexport async function* streamPTableRows(options: StreamPTableRowsOptions): AsyncIterable<string> {\n const { pTable, columnIndices, range, chunkSize, separator, signal, specs, includeHeader, bom } =\n options;\n\n if (bom) {\n yield \"\\uFEFF\";\n }\n\n if (includeHeader) {\n const selectedSpecs = columnIndices.map((index) => specs[index]);\n yield formatHeader(selectedSpecs, separator);\n }\n\n if (isNil(range)) {\n return;\n }\n\n const end = range.offset + range.length;\n\n for (let from = range.offset; from < end; from += chunkSize) {\n signal?.throwIfAborted();\n\n const length = Math.min(chunkSize, end - from);\n const subRange: TableRange = { offset: from, length };\n\n const vectors = await pTable.getData(columnIndices, { range: subRange, signal });\n\n const rows: string[] = [];\n for (let rowIndex = 0; rowIndex < length; rowIndex++) {\n rows.push(formatRow(vectors, rowIndex, separator));\n }\n yield rows.join(\"\");\n }\n}\n\n// ── Helpers (low-level) ──────────────────────────────────────────────\n\n/** Extract a human-readable label from a PTableColumnSpec. */\nexport function columnLabel(spec: PTableColumnSpec): string {\n const annotation = readAnnotation(spec.spec, Annotation.Label);\n return isNil(annotation) ? spec.spec.name : annotation.trim();\n}\n\n/**\n * RFC 4180 field escaping.\n * Quote if the field contains the separator, a double-quote, CR, or LF.\n * Embedded `\"` are doubled.\n */\nfunction escapeField(value: string, separator: string): string {\n return needsQuoting(value, separator) ? '\"' + value.replace(/\"/g, '\"\"') + '\"' : value;\n}\n\n/** Returns true when the value must be wrapped in double-quotes. */\nfunction needsQuoting(value: string, separator: string): boolean {\n return (\n value.includes(separator) || value.includes('\"') || value.includes(\"\\r\") || value.includes(\"\\n\")\n );\n}\n\n/**\n * Serialize one cell value from a typed vector to its string representation.\n *\n * - `null` / `undefined` -> `\"\"`\n * - `bigint` -> `String(x)`\n * - `NaN` / `+Inf` / `-Inf` -> `\"\"`\n */\nfunction serializeValue(vector: PTableVector, rowIndex: number): string {\n const rawValue = vector.data[rowIndex];\n\n if (isNil(rawValue)) {\n return \"\";\n }\n\n if (isValueNA(vector, rowIndex)) {\n return \"\";\n }\n\n switch (vector.type) {\n case ValueType.Long: {\n // BigInt64Array element — may be stored as bigint\n return String(rawValue);\n }\n case ValueType.Float:\n case ValueType.Double: {\n const numeric = rawValue as number;\n return Number.isNaN(numeric) || !Number.isFinite(numeric) ? \"\" : String(numeric);\n }\n case ValueType.Int: {\n return String(rawValue);\n }\n case ValueType.String: {\n return rawValue as string;\n }\n case ValueType.Bytes: {\n return \"\";\n }\n default: {\n return String(rawValue);\n }\n }\n}\n"],"mappings":";;;;AAsBA,SAAgB,aAAa,OAA2B,WAA2B;CACjF,OAAO,MAAM,KAAK,SAAS,YAAY,YAAY,IAAI,GAAG,SAAS,CAAC,CAAC,CAAC,KAAK,SAAS,IAAI;AAC1F;;AAGA,SAAgB,UAAU,SAAyB,UAAkB,WAA2B;CAC9F,OACE,QACG,KAAK,WAAW,YAAY,eAAe,QAAQ,QAAQ,GAAG,SAAS,CAAC,CAAC,CACzE,KAAK,SAAS,IAAI;AAEzB;AAoBA,gBAAuB,iBAAiB,SAAyD;CAC/F,MAAM,EAAE,QAAQ,eAAe,OAAO,WAAW,WAAW,QAAQ,OAAO,eAAe,QACxF;CAEF,IAAI,KACF,MAAM;CAGR,IAAI,eAEF,MAAM,aADgB,cAAc,KAAK,UAAU,MAAM,MAC1B,GAAG,SAAS;CAG7C,KAAA,GAAA,wBAAA,MAAA,CAAU,KAAK,GACb;CAGF,MAAM,MAAM,MAAM,SAAS,MAAM;CAEjC,KAAK,IAAI,OAAO,MAAM,QAAQ,OAAO,KAAK,QAAQ,WAAW;EAC3D,QAAQ,eAAe;EAEvB,MAAM,SAAS,KAAK,IAAI,WAAW,MAAM,IAAI;EAC7C,MAAM,WAAuB;GAAE,QAAQ;GAAM;EAAO;EAEpD,MAAM,UAAU,MAAM,OAAO,QAAQ,eAAe;GAAE,OAAO;GAAU;EAAO,CAAC;EAE/E,MAAM,OAAiB,CAAC;EACxB,KAAK,IAAI,WAAW,GAAG,WAAW,QAAQ,YACxC,KAAK,KAAK,UAAU,SAAS,UAAU,SAAS,CAAC;EAEnD,MAAM,KAAK,KAAK,EAAE;CACpB;AACF;;AAKA,SAAgB,YAAY,MAAgC;CAC1D,MAAM,cAAA,GAAA,gCAAA,eAAA,CAA4B,KAAK,MAAMA,gCAAAA,WAAW,KAAK;CAC7D,QAAA,GAAA,wBAAA,MAAA,CAAa,UAAU,IAAI,KAAK,KAAK,OAAO,WAAW,KAAK;AAC9D;;;;;;AAOA,SAAS,YAAY,OAAe,WAA2B;CAC7D,OAAO,aAAa,OAAO,SAAS,IAAI,OAAM,MAAM,QAAQ,MAAM,MAAI,IAAI,OAAM;AAClF;;AAGA,SAAS,aAAa,OAAe,WAA4B;CAC/D,OACE,MAAM,SAAS,SAAS,KAAK,MAAM,SAAS,IAAG,KAAK,MAAM,SAAS,IAAI,KAAK,MAAM,SAAS,IAAI;AAEnG;;;;;;;;AASA,SAAS,eAAe,QAAsB,UAA0B;CACtE,MAAM,WAAW,OAAO,KAAK;CAE7B,KAAA,GAAA,wBAAA,MAAA,CAAU,QAAQ,GAChB,OAAO;CAGT,KAAA,GAAA,gCAAA,UAAA,CAAc,QAAQ,QAAQ,GAC5B,OAAO;CAGT,QAAQ,OAAO,MAAf;EACE,KAAKC,gCAAAA,UAAU,MAEb,OAAO,OAAO,QAAQ;EAExB,KAAKA,gCAAAA,UAAU;EACf,KAAKA,gCAAAA,UAAU,QAAQ;GACrB,MAAM,UAAU;GAChB,OAAO,OAAO,MAAM,OAAO,KAAK,CAAC,OAAO,SAAS,OAAO,IAAI,KAAK,OAAO,OAAO;EACjF;EACA,KAAKA,gCAAAA,UAAU,KACb,OAAO,OAAO,QAAQ;EAExB,KAAKA,gCAAAA,UAAU,QACb,OAAO;EAET,KAAKA,gCAAAA,UAAU,OACb,OAAO;EAET,SACE,OAAO,OAAO,QAAQ;CAE1B;AACF"}
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
let _milaboratories_pl_model_common = require("@milaboratories/pl-model-common");
|
|
2
|
-
//#region src/data_info_helpers.ts
|
|
3
|
-
function makeJsonDataInfo(spec, data) {
|
|
4
|
-
const keyLength = spec.axesSpec.length;
|
|
5
|
-
const jsonData = {};
|
|
6
|
-
for (const { key, val } of data) {
|
|
7
|
-
if (key.length !== keyLength) {
|
|
8
|
-
const error = new _milaboratories_pl_model_common.PFrameDriverError(`Inconsistent inline column key length`);
|
|
9
|
-
error.cause = /* @__PURE__ */ new Error(`Inline column key length ${key.length} differs from axes count ${keyLength}`);
|
|
10
|
-
throw error;
|
|
11
|
-
}
|
|
12
|
-
jsonData[JSON.stringify(key)] = val;
|
|
13
|
-
}
|
|
14
|
-
return {
|
|
15
|
-
type: "Json",
|
|
16
|
-
keyLength,
|
|
17
|
-
data: jsonData
|
|
18
|
-
};
|
|
19
|
-
}
|
|
20
|
-
//#endregion
|
|
21
|
-
exports.makeJsonDataInfo = makeJsonDataInfo;
|
|
22
|
-
|
|
23
|
-
//# sourceMappingURL=data_info_helpers.cjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"data_info_helpers.cjs","names":["PFrameDriverError"],"sources":["../src/data_info_helpers.ts"],"sourcesContent":["import {\n PFrameDriverError,\n type PColumnSpec,\n type PColumnValues,\n type JsonDataInfo,\n type PColumnValue,\n} from \"@milaboratories/pl-model-common\";\n\nexport function makeJsonDataInfo(spec: PColumnSpec, data: PColumnValues): JsonDataInfo {\n const keyLength = spec.axesSpec.length;\n const jsonData: Record<string, PColumnValue> = {};\n for (const { key, val } of data) {\n if (key.length !== keyLength) {\n const error = new PFrameDriverError(`Inconsistent inline column key length`);\n error.cause = new Error(\n `Inline column key length ${key.length} differs from axes count ${keyLength}`,\n );\n throw error;\n }\n jsonData[JSON.stringify(key)] = val;\n }\n\n return {\n type: \"Json\",\n keyLength,\n data: jsonData,\n };\n}\n"],"mappings":";;AAQA,SAAgB,iBAAiB,MAAmB,MAAmC;CACrF,MAAM,YAAY,KAAK,SAAS;CAChC,MAAM,WAAyC,CAAC;CAChD,KAAK,MAAM,EAAE,KAAK,SAAS,MAAM;EAC/B,IAAI,IAAI,WAAW,WAAW;GAC5B,MAAM,QAAQ,IAAIA,gCAAAA,kBAAkB,uCAAuC;GAC3E,MAAM,wBAAQ,IAAI,MAChB,4BAA4B,IAAI,OAAO,2BAA2B,WACpE;GACA,MAAM;EACR;EACA,SAAS,KAAK,UAAU,GAAG,KAAK;CAClC;CAEA,OAAO;EACL,MAAM;EACN;EACA,MAAM;CACR;AACF"}
|