@platforma-sdk/model 1.59.3 → 1.60.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/block_storage.cjs.map +1 -1
- package/dist/block_storage.d.ts +1 -11
- package/dist/block_storage.js.map +1 -1
- package/dist/block_storage_callbacks.cjs.map +1 -1
- package/dist/block_storage_callbacks.js.map +1 -1
- package/dist/columns/column_collection_builder.cjs +215 -0
- package/dist/columns/column_collection_builder.cjs.map +1 -0
- package/dist/columns/column_collection_builder.d.ts +112 -0
- package/dist/columns/column_collection_builder.js +214 -0
- package/dist/columns/column_collection_builder.js.map +1 -0
- package/dist/columns/column_selector.cjs +122 -0
- package/dist/columns/column_selector.cjs.map +1 -0
- package/dist/columns/column_selector.d.ts +41 -0
- package/dist/columns/column_selector.js +118 -0
- package/dist/columns/column_selector.js.map +1 -0
- package/dist/columns/column_snapshot.cjs +20 -0
- package/dist/columns/column_snapshot.cjs.map +1 -0
- package/dist/columns/column_snapshot.d.ts +39 -0
- package/dist/columns/column_snapshot.js +18 -0
- package/dist/columns/column_snapshot.js.map +1 -0
- package/dist/columns/column_snapshot_provider.cjs +112 -0
- package/dist/columns/column_snapshot_provider.cjs.map +1 -0
- package/dist/columns/column_snapshot_provider.d.ts +73 -0
- package/dist/columns/column_snapshot_provider.js +107 -0
- package/dist/columns/column_snapshot_provider.js.map +1 -0
- package/dist/columns/ctx_column_sources.cjs +84 -0
- package/dist/columns/ctx_column_sources.cjs.map +1 -0
- package/dist/columns/ctx_column_sources.d.ts +33 -0
- package/dist/columns/ctx_column_sources.js +82 -0
- package/dist/columns/ctx_column_sources.js.map +1 -0
- package/dist/columns/index.cjs +5 -0
- package/dist/columns/index.d.ts +5 -0
- package/dist/columns/index.js +5 -0
- package/dist/components/PlDataTable/createPlDataTable/createPlDataTableV2.cjs +111 -0
- package/dist/components/PlDataTable/createPlDataTable/createPlDataTableV2.cjs.map +1 -0
- package/dist/components/PlDataTable/createPlDataTable/createPlDataTableV2.d.ts +25 -0
- package/dist/components/PlDataTable/createPlDataTable/createPlDataTableV2.js +110 -0
- package/dist/components/PlDataTable/createPlDataTable/createPlDataTableV2.js.map +1 -0
- package/dist/components/PlDataTable/{table.cjs → createPlDataTable/createPlDataTableV3.cjs} +54 -54
- package/dist/components/PlDataTable/createPlDataTable/createPlDataTableV3.cjs.map +1 -0
- package/dist/components/PlDataTable/createPlDataTable/createPlDataTableV3.d.ts +39 -0
- package/dist/components/PlDataTable/{table.js → createPlDataTable/createPlDataTableV3.js} +53 -53
- package/dist/components/PlDataTable/createPlDataTable/createPlDataTableV3.js.map +1 -0
- package/dist/components/PlDataTable/createPlDataTable/index.cjs +12 -0
- package/dist/components/PlDataTable/createPlDataTable/index.cjs.map +1 -0
- package/dist/components/PlDataTable/createPlDataTable/index.d.ts +15 -0
- package/dist/components/PlDataTable/createPlDataTable/index.js +12 -0
- package/dist/components/PlDataTable/createPlDataTable/index.js.map +1 -0
- package/dist/components/PlDataTable/createPlDataTableSheet.cjs +18 -0
- package/dist/components/PlDataTable/createPlDataTableSheet.cjs.map +1 -0
- package/dist/components/PlDataTable/createPlDataTableSheet.d.ts +11 -0
- package/dist/components/PlDataTable/createPlDataTableSheet.js +17 -0
- package/dist/components/PlDataTable/createPlDataTableSheet.js.map +1 -0
- package/dist/components/PlDataTable/index.cjs +4 -1
- package/dist/components/PlDataTable/index.d.ts +5 -2
- package/dist/components/PlDataTable/index.js +4 -1
- package/dist/components/PlDataTable/state-migration.cjs.map +1 -1
- package/dist/components/PlDataTable/state-migration.d.ts +2 -2
- package/dist/components/PlDataTable/state-migration.js.map +1 -1
- package/dist/components/PlDataTable/{v4.d.ts → typesV4.d.ts} +2 -2
- package/dist/components/PlDataTable/{v5.d.ts → typesV5.d.ts} +2 -2
- package/dist/components/index.cjs +4 -1
- package/dist/components/index.d.ts +5 -2
- package/dist/components/index.js +4 -1
- package/dist/index.cjs +44 -16
- package/dist/index.d.ts +17 -5
- package/dist/index.js +15 -3
- package/dist/labels/derive_distinct_labels.cjs +156 -0
- package/dist/labels/derive_distinct_labels.cjs.map +1 -0
- package/dist/labels/derive_distinct_labels.d.ts +29 -0
- package/dist/labels/derive_distinct_labels.js +155 -0
- package/dist/labels/derive_distinct_labels.js.map +1 -0
- package/dist/labels/index.cjs +2 -0
- package/dist/labels/index.d.ts +2 -0
- package/dist/labels/index.js +2 -0
- package/dist/labels/write_labels_to_specs.cjs +15 -0
- package/dist/labels/write_labels_to_specs.cjs.map +1 -0
- package/dist/labels/write_labels_to_specs.d.ts +9 -0
- package/dist/labels/write_labels_to_specs.js +14 -0
- package/dist/labels/write_labels_to_specs.js.map +1 -0
- package/dist/package.cjs +1 -1
- package/dist/package.js +1 -1
- package/dist/render/api.cjs +11 -2
- package/dist/render/api.cjs.map +1 -1
- package/dist/render/api.d.ts +9 -5
- package/dist/render/api.js +12 -3
- package/dist/render/api.js.map +1 -1
- package/dist/render/index.d.ts +2 -1
- package/dist/render/index.js +1 -1
- package/dist/render/internal.cjs.map +1 -1
- package/dist/render/internal.d.ts +5 -2
- package/dist/render/internal.js.map +1 -1
- package/dist/render/util/column_collection.cjs +3 -3
- package/dist/render/util/column_collection.cjs.map +1 -1
- package/dist/render/util/column_collection.d.ts +3 -2
- package/dist/render/util/column_collection.js +4 -4
- package/dist/render/util/column_collection.js.map +1 -1
- package/dist/render/util/index.d.ts +2 -1
- package/dist/render/util/index.js +1 -1
- package/dist/render/util/label.cjs +7 -134
- package/dist/render/util/label.cjs.map +1 -1
- package/dist/render/util/label.d.ts +5 -50
- package/dist/render/util/label.js +8 -132
- package/dist/render/util/label.js.map +1 -1
- package/dist/render/util/split_selectors.d.ts +2 -2
- package/package.json +9 -7
- package/src/block_storage.ts +0 -11
- package/src/block_storage_callbacks.ts +1 -1
- package/src/columns/column_collection_builder.test.ts +427 -0
- package/src/columns/column_collection_builder.ts +455 -0
- package/src/columns/column_selector.test.ts +472 -0
- package/src/columns/column_selector.ts +212 -0
- package/src/columns/column_snapshot.ts +55 -0
- package/src/columns/column_snapshot_provider.ts +177 -0
- package/src/columns/ctx_column_sources.ts +107 -0
- package/src/columns/expand_by_partition.test.ts +289 -0
- package/src/columns/expand_by_partition.ts +187 -0
- package/src/columns/index.ts +5 -0
- package/src/components/PlDataTable/createPlDataTable/createPlDataTableV2.ts +193 -0
- package/src/components/PlDataTable/{table.ts → createPlDataTable/createPlDataTableV3.ts} +134 -70
- package/src/components/PlDataTable/createPlDataTable/index.ts +27 -0
- package/src/components/PlDataTable/createPlDataTableSheet.ts +20 -0
- package/src/components/PlDataTable/index.ts +6 -4
- package/src/components/PlDataTable/state-migration.ts +2 -2
- package/src/index.ts +2 -1
- package/src/labels/derive_distinct_labels.test.ts +461 -0
- package/src/labels/derive_distinct_labels.ts +289 -0
- package/src/labels/index.ts +2 -0
- package/src/labels/write_labels_to_specs.ts +12 -0
- package/src/render/api.ts +25 -3
- package/src/render/internal.ts +20 -1
- package/src/render/util/column_collection.ts +9 -6
- package/src/render/util/label.test.ts +1 -1
- package/src/render/util/label.ts +19 -235
- package/src/render/util/split_selectors.ts +3 -3
- package/dist/components/PlDataTable/table.cjs.map +0 -1
- package/dist/components/PlDataTable/table.d.ts +0 -30
- package/dist/components/PlDataTable/table.js.map +0 -1
- /package/src/components/PlDataTable/{v4.ts → typesV4.ts} +0 -0
- /package/src/components/PlDataTable/{v5.ts → typesV5.ts} +0 -0
package/src/block_storage.ts
CHANGED
|
@@ -218,17 +218,6 @@ export function updateStorageData<TValue = unknown>(
|
|
|
218
218
|
}
|
|
219
219
|
}
|
|
220
220
|
|
|
221
|
-
/**
|
|
222
|
-
* Storage debug view returned by __pl_storage_debugView callback.
|
|
223
|
-
* Used by developer tools to display block storage info.
|
|
224
|
-
*/
|
|
225
|
-
export interface StorageDebugView {
|
|
226
|
-
/** Current data version key */
|
|
227
|
-
dataVersion: string;
|
|
228
|
-
/** Raw data payload stored in BlockStorage */
|
|
229
|
-
data: unknown;
|
|
230
|
-
}
|
|
231
|
-
|
|
232
221
|
// =============================================================================
|
|
233
222
|
// Atomic Migration
|
|
234
223
|
// =============================================================================
|
|
@@ -14,7 +14,6 @@ import {
|
|
|
14
14
|
BLOCK_STORAGE_SCHEMA_VERSION,
|
|
15
15
|
type BlockStorage,
|
|
16
16
|
type MutateStoragePayload,
|
|
17
|
-
type StorageDebugView,
|
|
18
17
|
type PluginRegistry,
|
|
19
18
|
type VersionedData,
|
|
20
19
|
createBlockStorage,
|
|
@@ -28,6 +27,7 @@ import type { PluginHandle } from "./plugin_handle";
|
|
|
28
27
|
|
|
29
28
|
import { stringifyJson, type StringifiedJson } from "@milaboratories/pl-model-common";
|
|
30
29
|
import type { DataVersioned, TransferRecord } from "./block_migrations";
|
|
30
|
+
import type { StorageDebugView } from "@milaboratories/pl-model-middle-layer";
|
|
31
31
|
|
|
32
32
|
// =============================================================================
|
|
33
33
|
// Hook interfaces for dependency injection
|
|
@@ -0,0 +1,427 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
AxisSpec,
|
|
3
|
+
PColumnSpec,
|
|
4
|
+
PObjectId,
|
|
5
|
+
SUniversalPColumnId,
|
|
6
|
+
} from "@milaboratories/pl-model-common";
|
|
7
|
+
import { AnchoredIdDeriver } from "@milaboratories/pl-model-common";
|
|
8
|
+
|
|
9
|
+
import { describe, expect, test } from "vitest";
|
|
10
|
+
import type { ColumnSnapshotProvider } from "./column_snapshot_provider";
|
|
11
|
+
import type { ColumnSnapshot } from "./column_snapshot";
|
|
12
|
+
import { ColumnCollectionBuilder } from "./column_collection_builder";
|
|
13
|
+
|
|
14
|
+
// --- Mock SpecFrameCtx ---
|
|
15
|
+
|
|
16
|
+
function createSpecFrameCtx() {
|
|
17
|
+
const frames = new Map<string, Record<string, PColumnSpec>>();
|
|
18
|
+
let nextId = 0;
|
|
19
|
+
|
|
20
|
+
return {
|
|
21
|
+
createSpecFrame: (specs: Record<string, PColumnSpec>) => {
|
|
22
|
+
const handle = `frame-${nextId++}`;
|
|
23
|
+
frames.set(handle, specs);
|
|
24
|
+
return handle;
|
|
25
|
+
},
|
|
26
|
+
specFrameDiscoverColumns: (handle: string, _request: any) => {
|
|
27
|
+
const specs = frames.get(handle)!;
|
|
28
|
+
return {
|
|
29
|
+
hits: Object.entries(specs).map(([columnId, spec]) => ({
|
|
30
|
+
hit: { columnId: columnId as PObjectId, spec },
|
|
31
|
+
mappingVariants: [
|
|
32
|
+
{
|
|
33
|
+
qualifications: { forQueries: [], forHit: [] },
|
|
34
|
+
distinctiveQualifications: { forQueries: [], forHit: [] },
|
|
35
|
+
},
|
|
36
|
+
],
|
|
37
|
+
})),
|
|
38
|
+
};
|
|
39
|
+
},
|
|
40
|
+
specFrameDispose: (handle: string) => {
|
|
41
|
+
frames.delete(handle);
|
|
42
|
+
},
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// --- Helpers ---
|
|
47
|
+
|
|
48
|
+
/** Default axis used when none specified — WASM requires at least one axis. */
|
|
49
|
+
const DEFAULT_AXIS: AxisSpec = { name: "id", type: "String" } as AxisSpec;
|
|
50
|
+
|
|
51
|
+
function createSpec(
|
|
52
|
+
name: string,
|
|
53
|
+
options?: { domain?: Record<string, string>; axesSpec?: AxisSpec[] },
|
|
54
|
+
): PColumnSpec {
|
|
55
|
+
return {
|
|
56
|
+
kind: "PColumn",
|
|
57
|
+
name,
|
|
58
|
+
valueType: "Int",
|
|
59
|
+
axesSpec: options?.axesSpec ?? [DEFAULT_AXIS],
|
|
60
|
+
annotations: {},
|
|
61
|
+
...(options?.domain !== undefined ? { domain: options.domain } : {}),
|
|
62
|
+
} as PColumnSpec;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function createSnapshot(
|
|
66
|
+
id: string,
|
|
67
|
+
spec: PColumnSpec,
|
|
68
|
+
dataStatus: "ready" | "computing" | "absent" = "ready",
|
|
69
|
+
): ColumnSnapshot<PObjectId> {
|
|
70
|
+
return {
|
|
71
|
+
id: id as PObjectId,
|
|
72
|
+
spec,
|
|
73
|
+
dataStatus,
|
|
74
|
+
data:
|
|
75
|
+
dataStatus === "absent"
|
|
76
|
+
? undefined
|
|
77
|
+
: { get: () => (dataStatus === "ready" ? ({} as never) : undefined) },
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function createProvider(
|
|
82
|
+
snapshots: ColumnSnapshot<PObjectId>[],
|
|
83
|
+
complete = true,
|
|
84
|
+
): ColumnSnapshotProvider {
|
|
85
|
+
return {
|
|
86
|
+
getAllColumns() {
|
|
87
|
+
return snapshots;
|
|
88
|
+
},
|
|
89
|
+
isColumnListComplete() {
|
|
90
|
+
return complete;
|
|
91
|
+
},
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function sampleAxis(name: string, type = "String"): AxisSpec {
|
|
96
|
+
return { name, type } as AxisSpec;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// --- Tests ---
|
|
100
|
+
|
|
101
|
+
describe("ColumnCollectionBuilder", () => {
|
|
102
|
+
test("empty builder returns empty collection", () => {
|
|
103
|
+
const builder = new ColumnCollectionBuilder(createSpecFrameCtx());
|
|
104
|
+
const collection = builder.build();
|
|
105
|
+
expect(collection).toBeDefined();
|
|
106
|
+
expect(collection!.findColumns()).toEqual([]);
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
test("addSource returns this for chaining", () => {
|
|
110
|
+
const builder = new ColumnCollectionBuilder(createSpecFrameCtx());
|
|
111
|
+
const result = builder.addSource([]);
|
|
112
|
+
expect(result).toBe(builder);
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
test("build returns undefined when providers are incomplete", () => {
|
|
116
|
+
const snap = createSnapshot("id1", createSpec("col1"));
|
|
117
|
+
const builder = new ColumnCollectionBuilder(createSpecFrameCtx());
|
|
118
|
+
builder.addSource(createProvider([snap], false));
|
|
119
|
+
expect(builder.build()).toBeUndefined();
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
test("build with allowPartialColumnList returns collection even when incomplete", () => {
|
|
123
|
+
const snap = createSnapshot("id1", createSpec("col1"));
|
|
124
|
+
const builder = new ColumnCollectionBuilder(createSpecFrameCtx());
|
|
125
|
+
builder.addSource(createProvider([snap], false));
|
|
126
|
+
|
|
127
|
+
const collection = builder.build({ allowPartialColumnList: true });
|
|
128
|
+
expect(collection).toBeDefined();
|
|
129
|
+
expect(collection.columnListComplete).toBe(false);
|
|
130
|
+
expect(collection.findColumns()).toHaveLength(1);
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
test("allowPartialColumnList with complete providers sets columnListComplete true", () => {
|
|
134
|
+
const snap = createSnapshot("id1", createSpec("col1"));
|
|
135
|
+
const builder = new ColumnCollectionBuilder(createSpecFrameCtx());
|
|
136
|
+
builder.addSource(createProvider([snap], true));
|
|
137
|
+
|
|
138
|
+
const collection = builder.build({ allowPartialColumnList: true });
|
|
139
|
+
expect(collection.columnListComplete).toBe(true);
|
|
140
|
+
});
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
describe("ColumnCollection.getColumn", () => {
|
|
144
|
+
test("returns snapshot for existing id", () => {
|
|
145
|
+
const spec = createSpec("col1");
|
|
146
|
+
const snap = createSnapshot("id1", spec);
|
|
147
|
+
const builder = new ColumnCollectionBuilder(createSpecFrameCtx());
|
|
148
|
+
builder.addSource([snap]);
|
|
149
|
+
|
|
150
|
+
const collection = builder.build()!;
|
|
151
|
+
const found = collection.getColumn("id1" as PObjectId);
|
|
152
|
+
expect(found).toBeDefined();
|
|
153
|
+
expect(found!.spec.name).toBe("col1");
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
test("returns undefined for missing id", () => {
|
|
157
|
+
const builder = new ColumnCollectionBuilder(createSpecFrameCtx());
|
|
158
|
+
builder.addSource([createSnapshot("id1", createSpec("col1"))]);
|
|
159
|
+
|
|
160
|
+
const collection = builder.build()!;
|
|
161
|
+
expect(collection.getColumn("missing" as PObjectId)).toBeUndefined();
|
|
162
|
+
});
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
describe("ColumnCollection.findColumns", () => {
|
|
166
|
+
test("no options returns all columns", () => {
|
|
167
|
+
const s1 = createSnapshot("id1", createSpec("col1"));
|
|
168
|
+
const s2 = createSnapshot("id2", createSpec("col2"));
|
|
169
|
+
const builder = new ColumnCollectionBuilder(createSpecFrameCtx());
|
|
170
|
+
builder.addSource([s1, s2]);
|
|
171
|
+
|
|
172
|
+
const collection = builder.build()!;
|
|
173
|
+
expect(collection.findColumns()).toHaveLength(2);
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
test("exclude throws not implemented", () => {
|
|
177
|
+
const s1 = createSnapshot("id1", createSpec("col1"));
|
|
178
|
+
const builder = new ColumnCollectionBuilder(createSpecFrameCtx());
|
|
179
|
+
builder.addSource([s1]);
|
|
180
|
+
|
|
181
|
+
const collection = builder.build()!;
|
|
182
|
+
expect(() => collection.findColumns({ exclude: { name: "col1" } })).toThrow(
|
|
183
|
+
/not yet implemented/i,
|
|
184
|
+
);
|
|
185
|
+
});
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
describe("dedup by native ID", () => {
|
|
189
|
+
test("first source wins when specs have same native ID", () => {
|
|
190
|
+
const spec = createSpec("col1");
|
|
191
|
+
const snap1 = createSnapshot("id-first", spec);
|
|
192
|
+
const snap2 = createSnapshot("id-second", spec);
|
|
193
|
+
|
|
194
|
+
const builder = new ColumnCollectionBuilder(createSpecFrameCtx());
|
|
195
|
+
builder.addSource([snap1]);
|
|
196
|
+
builder.addSource([snap2]);
|
|
197
|
+
|
|
198
|
+
const collection = builder.build()!;
|
|
199
|
+
const all = collection.findColumns();
|
|
200
|
+
expect(all).toHaveLength(1);
|
|
201
|
+
expect(all[0].id).toBe("id-first");
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
test("different specs are not deduped", () => {
|
|
205
|
+
const snap1 = createSnapshot("id1", createSpec("col1"));
|
|
206
|
+
const snap2 = createSnapshot("id2", createSpec("col2"));
|
|
207
|
+
|
|
208
|
+
const builder = new ColumnCollectionBuilder(createSpecFrameCtx());
|
|
209
|
+
builder.addSource([snap1]);
|
|
210
|
+
builder.addSource([snap2]);
|
|
211
|
+
|
|
212
|
+
const collection = builder.build()!;
|
|
213
|
+
expect(collection.findColumns()).toHaveLength(2);
|
|
214
|
+
});
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
describe("data status handling", () => {
|
|
218
|
+
test("ready column data is accessible", () => {
|
|
219
|
+
const data = { someKey: "someValue" };
|
|
220
|
+
const snap: ColumnSnapshot<PObjectId> = {
|
|
221
|
+
id: "id1" as PObjectId,
|
|
222
|
+
spec: createSpec("col1"),
|
|
223
|
+
dataStatus: "ready",
|
|
224
|
+
data: { get: () => data as never },
|
|
225
|
+
};
|
|
226
|
+
|
|
227
|
+
const builder = new ColumnCollectionBuilder(createSpecFrameCtx());
|
|
228
|
+
builder.addSource([snap]);
|
|
229
|
+
const collection = builder.build()!;
|
|
230
|
+
|
|
231
|
+
const found = collection.getColumn("id1" as PObjectId)!;
|
|
232
|
+
expect(found.dataStatus).toBe("ready");
|
|
233
|
+
expect(found.data).toBeDefined();
|
|
234
|
+
expect(found.data!.get()).toBe(data);
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
test("computing column data returns undefined", () => {
|
|
238
|
+
const snap = createSnapshot("id1", createSpec("col1"), "computing");
|
|
239
|
+
|
|
240
|
+
const builder = new ColumnCollectionBuilder(createSpecFrameCtx());
|
|
241
|
+
builder.addSource([snap]);
|
|
242
|
+
const collection = builder.build()!;
|
|
243
|
+
|
|
244
|
+
const found = collection.getColumn("id1" as PObjectId)!;
|
|
245
|
+
expect(found.dataStatus).toBe("computing");
|
|
246
|
+
expect(found.data).toBeDefined();
|
|
247
|
+
|
|
248
|
+
const result = found.data!.get();
|
|
249
|
+
expect(result).toBeUndefined();
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
test("absent column has no data accessor", () => {
|
|
253
|
+
const snap = createSnapshot("id1", createSpec("col1"), "absent");
|
|
254
|
+
|
|
255
|
+
const builder = new ColumnCollectionBuilder(createSpecFrameCtx());
|
|
256
|
+
builder.addSource([snap]);
|
|
257
|
+
const collection = builder.build()!;
|
|
258
|
+
|
|
259
|
+
const found = collection.getColumn("id1" as PObjectId)!;
|
|
260
|
+
expect(found.dataStatus).toBe("absent");
|
|
261
|
+
expect(found.data).toBeUndefined();
|
|
262
|
+
});
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
describe("multiple providers", () => {
|
|
266
|
+
test("columns from multiple sources are merged", () => {
|
|
267
|
+
const s1 = createSnapshot("id1", createSpec("col1"));
|
|
268
|
+
const s2 = createSnapshot("id2", createSpec("col2"));
|
|
269
|
+
|
|
270
|
+
const builder = new ColumnCollectionBuilder(createSpecFrameCtx());
|
|
271
|
+
builder.addSource([s1]);
|
|
272
|
+
builder.addSource([s2]);
|
|
273
|
+
|
|
274
|
+
const collection = builder.build()!;
|
|
275
|
+
expect(collection.findColumns()).toHaveLength(2);
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
test("build returns undefined if any provider is incomplete", () => {
|
|
279
|
+
const s1 = createSnapshot("id1", createSpec("col1"));
|
|
280
|
+
const s2 = createSnapshot("id2", createSpec("col2"));
|
|
281
|
+
|
|
282
|
+
const builder = new ColumnCollectionBuilder(createSpecFrameCtx());
|
|
283
|
+
builder.addSource(createProvider([s1], true));
|
|
284
|
+
builder.addSource(createProvider([s2], false));
|
|
285
|
+
|
|
286
|
+
expect(builder.build()).toBeUndefined();
|
|
287
|
+
});
|
|
288
|
+
|
|
289
|
+
test("allowPartialColumnList is false only when all providers complete", () => {
|
|
290
|
+
const builder = new ColumnCollectionBuilder(createSpecFrameCtx());
|
|
291
|
+
builder.addSource(createProvider([createSnapshot("id1", createSpec("col1"))], true));
|
|
292
|
+
builder.addSource(createProvider([createSnapshot("id2", createSpec("col2"))], false));
|
|
293
|
+
|
|
294
|
+
const collection = builder.build({ allowPartialColumnList: true });
|
|
295
|
+
expect(collection.columnListComplete).toBe(false);
|
|
296
|
+
});
|
|
297
|
+
});
|
|
298
|
+
|
|
299
|
+
describe("AnchoredColumnCollection", () => {
|
|
300
|
+
const anchorSpec = createSpec("anchor-col", {
|
|
301
|
+
axesSpec: [sampleAxis("sample"), sampleAxis("gene")],
|
|
302
|
+
});
|
|
303
|
+
|
|
304
|
+
test("build with PColumnSpec anchor returns anchored collection", () => {
|
|
305
|
+
const s1 = createSnapshot("id1", createSpec("col1", { axesSpec: [sampleAxis("sample")] }));
|
|
306
|
+
const builder = new ColumnCollectionBuilder(createSpecFrameCtx());
|
|
307
|
+
builder.addSource([s1]);
|
|
308
|
+
|
|
309
|
+
const collection = builder.build({ anchors: { main: anchorSpec } });
|
|
310
|
+
expect(collection).toBeDefined();
|
|
311
|
+
});
|
|
312
|
+
|
|
313
|
+
test("build with PObjectId anchor resolves from sources", () => {
|
|
314
|
+
const spec = createSpec("col1", { axesSpec: [sampleAxis("sample")] });
|
|
315
|
+
const snap = createSnapshot("anchor-id", spec);
|
|
316
|
+
const builder = new ColumnCollectionBuilder(createSpecFrameCtx());
|
|
317
|
+
builder.addSource([snap]);
|
|
318
|
+
|
|
319
|
+
const collection = builder.build({ anchors: { main: "anchor-id" as PObjectId } });
|
|
320
|
+
expect(collection).toBeDefined();
|
|
321
|
+
});
|
|
322
|
+
|
|
323
|
+
test("build with unknown PObjectId anchor throws", () => {
|
|
324
|
+
const builder = new ColumnCollectionBuilder(createSpecFrameCtx());
|
|
325
|
+
builder.addSource([createSnapshot("id1", createSpec("col1"))]);
|
|
326
|
+
|
|
327
|
+
expect(() => builder.build({ anchors: { main: "missing" as PObjectId } })).toThrow(
|
|
328
|
+
/not found in sources/,
|
|
329
|
+
);
|
|
330
|
+
});
|
|
331
|
+
|
|
332
|
+
test("build with PlRef anchor throws", () => {
|
|
333
|
+
const builder = new ColumnCollectionBuilder(createSpecFrameCtx());
|
|
334
|
+
const plRef = { __isRef: true as const, blockId: "b1", name: "out" };
|
|
335
|
+
|
|
336
|
+
expect(() => builder.build({ anchors: { main: plRef } })).toThrow(/PlRef/);
|
|
337
|
+
});
|
|
338
|
+
|
|
339
|
+
test("getColumn returns snapshot by SUniversalPColumnId", () => {
|
|
340
|
+
const spec = createSpec("col1", { axesSpec: [sampleAxis("sample")] });
|
|
341
|
+
const snap = createSnapshot("id1", spec);
|
|
342
|
+
const builder = new ColumnCollectionBuilder(createSpecFrameCtx());
|
|
343
|
+
builder.addSource([snap]);
|
|
344
|
+
|
|
345
|
+
const collection = builder.build({ anchors: { main: anchorSpec } })!;
|
|
346
|
+
|
|
347
|
+
const idDeriver = new AnchoredIdDeriver({ main: anchorSpec });
|
|
348
|
+
const expectedId = idDeriver.deriveS(spec);
|
|
349
|
+
|
|
350
|
+
const found = collection.getColumn(expectedId);
|
|
351
|
+
expect(found).toBeDefined();
|
|
352
|
+
expect(found!.spec.name).toBe("col1");
|
|
353
|
+
expect(found!.id).toBe(expectedId);
|
|
354
|
+
});
|
|
355
|
+
|
|
356
|
+
test("getColumn returns undefined for unknown id", () => {
|
|
357
|
+
const snap = createSnapshot("id1", createSpec("col1", { axesSpec: [sampleAxis("sample")] }));
|
|
358
|
+
const builder = new ColumnCollectionBuilder(createSpecFrameCtx());
|
|
359
|
+
builder.addSource([snap]);
|
|
360
|
+
|
|
361
|
+
const collection = builder.build({ anchors: { main: anchorSpec } })!;
|
|
362
|
+
expect(collection.getColumn("not-a-real-id" as SUniversalPColumnId)).toBeUndefined();
|
|
363
|
+
});
|
|
364
|
+
|
|
365
|
+
test("findColumns returns ColumnMatch with originalId and variants", () => {
|
|
366
|
+
const spec = createSpec("col1", { axesSpec: [sampleAxis("sample")] });
|
|
367
|
+
const snap = createSnapshot("id1", spec);
|
|
368
|
+
const builder = new ColumnCollectionBuilder(createSpecFrameCtx());
|
|
369
|
+
builder.addSource([snap]);
|
|
370
|
+
|
|
371
|
+
const collection = builder.build({ anchors: { main: anchorSpec } })!;
|
|
372
|
+
const matches = collection.findColumns();
|
|
373
|
+
|
|
374
|
+
expect(matches).toHaveLength(1);
|
|
375
|
+
expect(matches[0].originalId).toBe("id1");
|
|
376
|
+
expect(matches[0].column.spec.name).toBe("col1");
|
|
377
|
+
expect(matches[0].variants).toBeDefined();
|
|
378
|
+
});
|
|
379
|
+
|
|
380
|
+
test("findColumns exclude throws not implemented", () => {
|
|
381
|
+
const snap = createSnapshot("id1", createSpec("col1", { axesSpec: [sampleAxis("sample")] }));
|
|
382
|
+
const builder = new ColumnCollectionBuilder(createSpecFrameCtx());
|
|
383
|
+
builder.addSource([snap]);
|
|
384
|
+
|
|
385
|
+
const collection = builder.build({ anchors: { main: anchorSpec } })!;
|
|
386
|
+
expect(() => collection.findColumns({ exclude: { name: "col1" } })).toThrow(
|
|
387
|
+
/not yet implemented/i,
|
|
388
|
+
);
|
|
389
|
+
});
|
|
390
|
+
|
|
391
|
+
test("allowPartialColumnList with anchors tracks completeness", () => {
|
|
392
|
+
const snap = createSnapshot("id1", createSpec("col1", { axesSpec: [sampleAxis("sample")] }));
|
|
393
|
+
const builder = new ColumnCollectionBuilder(createSpecFrameCtx());
|
|
394
|
+
builder.addSource(createProvider([snap], false));
|
|
395
|
+
|
|
396
|
+
const collection = builder.build({
|
|
397
|
+
anchors: { main: anchorSpec },
|
|
398
|
+
allowPartialColumnList: true,
|
|
399
|
+
});
|
|
400
|
+
expect(collection).toBeDefined();
|
|
401
|
+
expect(collection.columnListComplete).toBe(false);
|
|
402
|
+
});
|
|
403
|
+
|
|
404
|
+
test("build returns undefined with anchors when incomplete and no allowPartial", () => {
|
|
405
|
+
const snap = createSnapshot("id1", createSpec("col1", { axesSpec: [sampleAxis("sample")] }));
|
|
406
|
+
const builder = new ColumnCollectionBuilder(createSpecFrameCtx());
|
|
407
|
+
builder.addSource(createProvider([snap], false));
|
|
408
|
+
|
|
409
|
+
const result = builder.build({ anchors: { main: anchorSpec } });
|
|
410
|
+
expect(result).toBeUndefined();
|
|
411
|
+
});
|
|
412
|
+
|
|
413
|
+
test("data status is preserved through anchored snapshots", () => {
|
|
414
|
+
const spec = createSpec("computing-col", { axesSpec: [sampleAxis("sample")] });
|
|
415
|
+
const snap = createSnapshot("id1", spec, "computing");
|
|
416
|
+
|
|
417
|
+
const builder = new ColumnCollectionBuilder(createSpecFrameCtx());
|
|
418
|
+
builder.addSource([snap]);
|
|
419
|
+
|
|
420
|
+
const collection = builder.build({ anchors: { main: anchorSpec } })!;
|
|
421
|
+
|
|
422
|
+
const idDeriver = new AnchoredIdDeriver({ main: anchorSpec });
|
|
423
|
+
const found = collection.getColumn(idDeriver.deriveS(spec))!;
|
|
424
|
+
expect(found.dataStatus).toBe("computing");
|
|
425
|
+
expect(found.data!.get()).toBeUndefined();
|
|
426
|
+
});
|
|
427
|
+
});
|