@platforma-sdk/model 1.59.0 → 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 +8 -6
- 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
|
@@ -0,0 +1,455 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
AxisQualification,
|
|
3
|
+
ColumnAxesWithQualifications,
|
|
4
|
+
DiscoverColumnsConstraints,
|
|
5
|
+
MultiColumnSelector,
|
|
6
|
+
NativePObjectId,
|
|
7
|
+
PColumnSpec,
|
|
8
|
+
PlRef,
|
|
9
|
+
PObjectId,
|
|
10
|
+
SUniversalPColumnId,
|
|
11
|
+
} from "@milaboratories/pl-model-common";
|
|
12
|
+
import { AnchoredIdDeriver, deriveNativeId, isPlRef } from "@milaboratories/pl-model-common";
|
|
13
|
+
import type { ColumnSelectorInput } from "./column_selector";
|
|
14
|
+
import { normalizeSelectors } from "./column_selector";
|
|
15
|
+
import { TreeNodeAccessor } from "../render/accessor";
|
|
16
|
+
import type { ColumnSnapshot } from "./column_snapshot";
|
|
17
|
+
import { createColumnSnapshot } from "./column_snapshot";
|
|
18
|
+
import type { ColumnSnapshotProvider, ColumnSource } from "./column_snapshot_provider";
|
|
19
|
+
import { ArrayColumnProvider, toColumnSnapshotProvider } from "./column_snapshot_provider";
|
|
20
|
+
|
|
21
|
+
import type { GlobalCfgRenderCtxMethods } from "../render/internal";
|
|
22
|
+
|
|
23
|
+
/** Subset of render context methods needed for spec frame operations. */
|
|
24
|
+
type SpecFrameCtx = Pick<
|
|
25
|
+
GlobalCfgRenderCtxMethods,
|
|
26
|
+
"createSpecFrame" | "specFrameDiscoverColumns" | "specFrameDispose"
|
|
27
|
+
>;
|
|
28
|
+
|
|
29
|
+
// --- FindColumnsOptions ---
|
|
30
|
+
|
|
31
|
+
/** Options for plain collection findColumns. */
|
|
32
|
+
export interface FindColumnsOptions {
|
|
33
|
+
/** Include columns matching these selectors. If omitted, includes all columns. */
|
|
34
|
+
include?: ColumnSelectorInput;
|
|
35
|
+
/** Exclude columns matching these selectors. */
|
|
36
|
+
exclude?: ColumnSelectorInput;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// --- ColumnCollection ---
|
|
40
|
+
|
|
41
|
+
/** Plain collection — no axis context, selector-based filtering only. */
|
|
42
|
+
export interface ColumnCollection {
|
|
43
|
+
/** Point lookup by provider-native ID. */
|
|
44
|
+
getColumn(id: PObjectId): undefined | ColumnSnapshot<PObjectId>;
|
|
45
|
+
|
|
46
|
+
/** Find columns matching selectors. Returns flat list of snapshots.
|
|
47
|
+
* No axis compatibility matching, no linker traversal.
|
|
48
|
+
* Never returns undefined — the "not ready" state was absorbed by the builder. */
|
|
49
|
+
findColumns(options?: FindColumnsOptions): ColumnSnapshot<PObjectId>[];
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// --- AnchoredColumnCollection ---
|
|
53
|
+
|
|
54
|
+
/** Axis-aware column collection with anchored identity derivation. */
|
|
55
|
+
export interface AnchoredColumnCollection {
|
|
56
|
+
/** Point lookup by anchored ID. */
|
|
57
|
+
getColumn(id: SUniversalPColumnId): undefined | ColumnSnapshot<SUniversalPColumnId>;
|
|
58
|
+
|
|
59
|
+
/** Axis-aware column discovery. */
|
|
60
|
+
findColumns(options?: AnchoredFindColumnsOptions): ColumnMatch[];
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/** Controls axis matching behavior for anchored discovery. */
|
|
64
|
+
export type MatchingMode = "enrichment" | "related" | "exact";
|
|
65
|
+
|
|
66
|
+
/** Options for anchored collection findColumns. */
|
|
67
|
+
export interface AnchoredFindColumnsOptions extends FindColumnsOptions {
|
|
68
|
+
/** Controls axis matching behavior. Default: 'enrichment'. */
|
|
69
|
+
mode?: MatchingMode;
|
|
70
|
+
/** Maximum linker hops for cross-domain discovery (0 = direct only, default: 4). */
|
|
71
|
+
maxHops?: number;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/** Result of anchored discovery — column snapshot + routing info. */
|
|
75
|
+
export interface ColumnMatch {
|
|
76
|
+
/** Column snapshot with anchored SUniversalPColumnId. */
|
|
77
|
+
readonly column: ColumnSnapshot<SUniversalPColumnId>;
|
|
78
|
+
/** Provider-native ID — for lookups back to the source provider. */
|
|
79
|
+
readonly originalId: PObjectId;
|
|
80
|
+
/** Match variants — different paths/qualifications that reach this column. */
|
|
81
|
+
readonly variants: MatchVariant[];
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/** Qualifications needed for both query (already-integrated) columns and the hit column. */
|
|
85
|
+
export interface MatchQualifications {
|
|
86
|
+
/** Qualifications for each query (already-integrated) column set. */
|
|
87
|
+
readonly forQueries: AxisQualification[][];
|
|
88
|
+
/** Qualifications for the hit column. */
|
|
89
|
+
readonly forHit: AxisQualification[];
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/** A single mapping variant describing how a hit column can be integrated. */
|
|
93
|
+
export interface MatchVariant {
|
|
94
|
+
/** Full qualifications needed for integration. */
|
|
95
|
+
readonly qualifications: MatchQualifications;
|
|
96
|
+
/** Distinctive (minimal) qualifications needed for integration. */
|
|
97
|
+
readonly distinctiveQualifications: MatchQualifications;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// --- Build options ---
|
|
101
|
+
|
|
102
|
+
export interface BuildOptions {
|
|
103
|
+
allowPartialColumnList?: true;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export interface AnchoredBuildOptions extends BuildOptions {
|
|
107
|
+
anchors: Record<string, PlRef | PObjectId | PColumnSpec>;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// --- ColumnCollectionBuilder ---
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Mutable builder that accumulates column sources, then produces
|
|
114
|
+
* a ColumnCollection (plain) or AnchoredColumnCollection (with anchors).
|
|
115
|
+
*
|
|
116
|
+
* Each output lambda creates its own builder — a constraint of the
|
|
117
|
+
* computable framework where each output tracks its own dependencies.
|
|
118
|
+
*/
|
|
119
|
+
export class ColumnCollectionBuilder {
|
|
120
|
+
private readonly providers: ColumnSnapshotProvider[] = [];
|
|
121
|
+
|
|
122
|
+
constructor(private readonly specFrameCtx: SpecFrameCtx) {}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Register a column source. Sources added first take precedence for dedup.
|
|
126
|
+
* Does NOT accept undefined — if a source isn't available yet,
|
|
127
|
+
* the caller should return undefined from the output lambda.
|
|
128
|
+
*/
|
|
129
|
+
addSource(source: ColumnSource | TreeNodeAccessor): this {
|
|
130
|
+
if (source instanceof TreeNodeAccessor) {
|
|
131
|
+
const columns = source.getPColumns();
|
|
132
|
+
if (columns) this.providers.push(new ArrayColumnProvider(columns));
|
|
133
|
+
} else {
|
|
134
|
+
this.providers.push(toColumnSnapshotProvider(source));
|
|
135
|
+
}
|
|
136
|
+
return this;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
addSources(sources: (ColumnSource | TreeNodeAccessor)[]): this {
|
|
140
|
+
for (const source of sources) {
|
|
141
|
+
this.addSource(source);
|
|
142
|
+
}
|
|
143
|
+
return this;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/** Plain collection — selector-based filtering, PObjectId namespace. */
|
|
147
|
+
build(): undefined | ColumnCollection;
|
|
148
|
+
build(options: {
|
|
149
|
+
allowPartialColumnList: true;
|
|
150
|
+
}): ColumnCollection & { readonly columnListComplete: boolean };
|
|
151
|
+
/** Anchored collection — axis-aware discovery, SUniversalPColumnId namespace. */
|
|
152
|
+
build(
|
|
153
|
+
options: AnchoredBuildOptions & { allowPartialColumnList: true },
|
|
154
|
+
): AnchoredColumnCollection & { readonly columnListComplete: boolean };
|
|
155
|
+
build(options: AnchoredBuildOptions): undefined | AnchoredColumnCollection;
|
|
156
|
+
build(
|
|
157
|
+
options?: BuildOptions | AnchoredBuildOptions,
|
|
158
|
+
):
|
|
159
|
+
| undefined
|
|
160
|
+
| ColumnCollection
|
|
161
|
+
| AnchoredColumnCollection
|
|
162
|
+
| (ColumnCollection & { readonly columnListComplete: boolean })
|
|
163
|
+
| (AnchoredColumnCollection & { readonly columnListComplete: boolean }) {
|
|
164
|
+
const allowPartial = options?.allowPartialColumnList === true;
|
|
165
|
+
const hasAnchors = options !== undefined && "anchors" in options;
|
|
166
|
+
|
|
167
|
+
// Check column list completeness
|
|
168
|
+
const allComplete = this.providers.every((p) => p.isColumnListComplete());
|
|
169
|
+
if (!allComplete && !allowPartial) return undefined;
|
|
170
|
+
|
|
171
|
+
// Collect all columns, dedup by native ID (first source wins)
|
|
172
|
+
const columnMap = this.collectColumns();
|
|
173
|
+
|
|
174
|
+
if (hasAnchors) {
|
|
175
|
+
const anchorSpecs = resolveAnchorSpecs(options.anchors, columnMap);
|
|
176
|
+
const idDeriver = new AnchoredIdDeriver(anchorSpecs);
|
|
177
|
+
|
|
178
|
+
return new AnchoredColumnCollectionImpl(this.specFrameCtx, {
|
|
179
|
+
columns: columnMap,
|
|
180
|
+
idDeriver,
|
|
181
|
+
anchorSpecs,
|
|
182
|
+
columnListComplete: allowPartial ? allComplete : false,
|
|
183
|
+
});
|
|
184
|
+
} else {
|
|
185
|
+
return new ColumnCollectionImpl(this.specFrameCtx, {
|
|
186
|
+
columns: columnMap,
|
|
187
|
+
columnListComplete: allowPartial ? allComplete : false,
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Collect all columns from all providers, dedup by NativePObjectId.
|
|
194
|
+
* First source wins.
|
|
195
|
+
*/
|
|
196
|
+
private collectColumns(): Map<PObjectId, ColumnSnapshot<PObjectId>> {
|
|
197
|
+
const seen = new Set<NativePObjectId>();
|
|
198
|
+
const result = new Map<PObjectId, ColumnSnapshot<PObjectId>>();
|
|
199
|
+
|
|
200
|
+
for (const provider of this.providers) {
|
|
201
|
+
const columns = provider.getAllColumns();
|
|
202
|
+
for (const col of columns) {
|
|
203
|
+
const nativeId = deriveNativeId(col.spec);
|
|
204
|
+
if (seen.has(nativeId)) continue;
|
|
205
|
+
seen.add(nativeId);
|
|
206
|
+
result.set(col.id, col);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
return result;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// --- Permissive constraints for plain (non-anchored) filtering ---
|
|
215
|
+
|
|
216
|
+
const PLAIN_CONSTRAINTS: DiscoverColumnsConstraints = {
|
|
217
|
+
allowFloatingSourceAxes: true,
|
|
218
|
+
allowFloatingHitAxes: true,
|
|
219
|
+
allowSourceQualifications: false,
|
|
220
|
+
allowHitQualifications: false,
|
|
221
|
+
};
|
|
222
|
+
|
|
223
|
+
// --- ColumnCollectionImpl ---
|
|
224
|
+
|
|
225
|
+
interface ColumnCollectionImplOptions {
|
|
226
|
+
readonly columns: Map<PObjectId, ColumnSnapshot<PObjectId>>;
|
|
227
|
+
readonly columnListComplete?: boolean;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
class ColumnCollectionImpl implements ColumnCollection {
|
|
231
|
+
private readonly columns: Map<PObjectId, ColumnSnapshot<PObjectId>>;
|
|
232
|
+
private readonly specFrameHandle: string;
|
|
233
|
+
public readonly columnListComplete: boolean;
|
|
234
|
+
|
|
235
|
+
constructor(
|
|
236
|
+
private readonly ctx: SpecFrameCtx,
|
|
237
|
+
options: ColumnCollectionImplOptions,
|
|
238
|
+
) {
|
|
239
|
+
this.columns = options.columns;
|
|
240
|
+
this.columnListComplete = options.columnListComplete ?? false;
|
|
241
|
+
this.specFrameHandle = this.ctx.createSpecFrame(
|
|
242
|
+
Array.from(this.columns.entries()).reduce(
|
|
243
|
+
(acc, [id, col]) => ((acc[id] = col.spec), acc),
|
|
244
|
+
{} as Record<string, PColumnSpec>,
|
|
245
|
+
),
|
|
246
|
+
);
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
getColumn(id: PObjectId): undefined | ColumnSnapshot<PObjectId> {
|
|
250
|
+
const col = this.columns.get(id);
|
|
251
|
+
if (col === undefined) return undefined;
|
|
252
|
+
return this.toSnapshot(col);
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
findColumns(options?: FindColumnsOptions): ColumnSnapshot<PObjectId>[] {
|
|
256
|
+
const columnFilter = options?.include ? toMultiColumnSelectors(options.include) : [];
|
|
257
|
+
|
|
258
|
+
const response = this.ctx.specFrameDiscoverColumns(this.specFrameHandle, {
|
|
259
|
+
columnFilter,
|
|
260
|
+
axes: [],
|
|
261
|
+
constraints: PLAIN_CONSTRAINTS,
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
// Map hits back to snapshots
|
|
265
|
+
let results = response.hits
|
|
266
|
+
.map((hit) => this.columns.get(hit.hit.columnId as PObjectId))
|
|
267
|
+
.filter((col): col is ColumnSnapshot<PObjectId> => col !== undefined)
|
|
268
|
+
.map((col) => this.toSnapshot(col));
|
|
269
|
+
|
|
270
|
+
if (options?.exclude) {
|
|
271
|
+
throw new Error("Exclude filter is not yet implemented for plain ColumnCollection");
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
return results;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
private toSnapshot(col: ColumnSnapshot<PObjectId>): ColumnSnapshot<PObjectId> {
|
|
278
|
+
return remapSnapshot(col.id, col);
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
// --- AnchoredColumnCollectionImpl ---
|
|
283
|
+
|
|
284
|
+
interface AnchoredColumnCollectionImplOptions extends ColumnCollectionImplOptions {
|
|
285
|
+
readonly idDeriver: AnchoredIdDeriver;
|
|
286
|
+
readonly anchorSpecs: Record<string, PColumnSpec>;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
class AnchoredColumnCollectionImpl implements AnchoredColumnCollection {
|
|
290
|
+
private readonly columns: Map<PObjectId, ColumnSnapshot<PObjectId>>;
|
|
291
|
+
private readonly idDeriver: AnchoredIdDeriver;
|
|
292
|
+
private readonly specFrameHandle: string;
|
|
293
|
+
private readonly anchorAxes: ColumnAxesWithQualifications[];
|
|
294
|
+
/** Reverse lookup: SUniversalPColumnId → PObjectId */
|
|
295
|
+
private readonly idToOriginal: Map<SUniversalPColumnId, PObjectId>;
|
|
296
|
+
public readonly columnListComplete: boolean;
|
|
297
|
+
|
|
298
|
+
constructor(
|
|
299
|
+
private readonly ctx: SpecFrameCtx,
|
|
300
|
+
options: AnchoredColumnCollectionImplOptions,
|
|
301
|
+
) {
|
|
302
|
+
this.columns = options.columns;
|
|
303
|
+
this.idDeriver = options.idDeriver;
|
|
304
|
+
this.columnListComplete = options.columnListComplete ?? false;
|
|
305
|
+
|
|
306
|
+
// Create spec frame from all collected columns
|
|
307
|
+
this.specFrameHandle = this.ctx.createSpecFrame(
|
|
308
|
+
Array.from(this.columns.entries()).reduce(
|
|
309
|
+
(acc, [id, col]) => ((acc[id] = col.spec), acc),
|
|
310
|
+
{} as Record<string, PColumnSpec>,
|
|
311
|
+
),
|
|
312
|
+
);
|
|
313
|
+
|
|
314
|
+
// Build anchor axes for discovery requests
|
|
315
|
+
this.anchorAxes = Object.values(options.anchorSpecs).map((spec) => ({
|
|
316
|
+
axesSpec: spec.axesSpec,
|
|
317
|
+
qualifications: [],
|
|
318
|
+
}));
|
|
319
|
+
|
|
320
|
+
// Build reverse lookup map
|
|
321
|
+
this.idToOriginal = new Map(
|
|
322
|
+
Array.from(this.columns.entries()).map(
|
|
323
|
+
([id, col]) => [this.idDeriver.deriveS(col.spec), id] as const,
|
|
324
|
+
),
|
|
325
|
+
);
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
getColumn(id: SUniversalPColumnId): undefined | ColumnSnapshot<SUniversalPColumnId> {
|
|
329
|
+
const origId = this.idToOriginal.get(id);
|
|
330
|
+
if (origId === undefined) return undefined;
|
|
331
|
+
const col = this.columns.get(origId);
|
|
332
|
+
if (col === undefined) return undefined;
|
|
333
|
+
return this.toSnapshot(id, col);
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
findColumns(options?: AnchoredFindColumnsOptions): ColumnMatch[] {
|
|
337
|
+
const mode = options?.mode ?? "enrichment";
|
|
338
|
+
const constraints = matchingModeToConstraints(mode);
|
|
339
|
+
const columnFilter = options?.include ? toMultiColumnSelectors(options.include) : [];
|
|
340
|
+
|
|
341
|
+
const response = this.ctx.specFrameDiscoverColumns(this.specFrameHandle, {
|
|
342
|
+
columnFilter,
|
|
343
|
+
constraints,
|
|
344
|
+
axes: this.anchorAxes,
|
|
345
|
+
});
|
|
346
|
+
|
|
347
|
+
// Map hits back to ColumnMatch entries
|
|
348
|
+
let results = response.hits
|
|
349
|
+
.map((hit) => {
|
|
350
|
+
const origId = hit.hit.columnId as PObjectId;
|
|
351
|
+
const col = this.columns.get(origId);
|
|
352
|
+
if (!col) return undefined;
|
|
353
|
+
const universalId = this.idDeriver.deriveS(col.spec);
|
|
354
|
+
return {
|
|
355
|
+
column: this.toSnapshot(universalId, col),
|
|
356
|
+
originalId: origId,
|
|
357
|
+
variants: hit.mappingVariants.map(
|
|
358
|
+
(v): MatchVariant => ({
|
|
359
|
+
qualifications: v.qualifications,
|
|
360
|
+
distinctiveQualifications: v.distinctiveQualifications,
|
|
361
|
+
}),
|
|
362
|
+
),
|
|
363
|
+
} satisfies ColumnMatch;
|
|
364
|
+
})
|
|
365
|
+
.filter((m): m is ColumnMatch => m !== undefined);
|
|
366
|
+
|
|
367
|
+
if (options?.exclude) {
|
|
368
|
+
throw new Error("Exclude filter is not yet implemented for AnchoredColumnCollection");
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
return results;
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
private toSnapshot(
|
|
375
|
+
universalId: SUniversalPColumnId,
|
|
376
|
+
col: ColumnSnapshot<PObjectId>,
|
|
377
|
+
): ColumnSnapshot<SUniversalPColumnId> {
|
|
378
|
+
return remapSnapshot(universalId, col);
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
// --- Shared snapshot helpers ---
|
|
383
|
+
|
|
384
|
+
/** Create a new snapshot with a different ID, preserving data accessors. */
|
|
385
|
+
function remapSnapshot<Id extends PObjectId>(
|
|
386
|
+
id: Id,
|
|
387
|
+
col: ColumnSnapshot<PObjectId>,
|
|
388
|
+
): ColumnSnapshot<Id> {
|
|
389
|
+
return createColumnSnapshot(id, col.spec, col.dataStatus, col.data);
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
/** Normalize SDK ColumnSelectorInput to MultiColumnSelector[]. */
|
|
393
|
+
function toMultiColumnSelectors(input: ColumnSelectorInput): MultiColumnSelector[] {
|
|
394
|
+
return normalizeSelectors(input);
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
// --- Anchor resolution ---
|
|
398
|
+
|
|
399
|
+
/**
|
|
400
|
+
* Resolve each anchor value to a PColumnSpec.
|
|
401
|
+
* - PColumnSpec: used directly
|
|
402
|
+
* - PObjectId (string): looked up in the collected column map
|
|
403
|
+
* - PlRef: not supported at this level — caller must resolve before building
|
|
404
|
+
*/
|
|
405
|
+
function resolveAnchorSpecs(
|
|
406
|
+
anchors: Record<string, PlRef | PObjectId | PColumnSpec>,
|
|
407
|
+
columnMap: Map<PObjectId, ColumnSnapshot<PObjectId>>,
|
|
408
|
+
): Record<string, PColumnSpec> {
|
|
409
|
+
const result: Record<string, PColumnSpec> = {};
|
|
410
|
+
for (const [key, anchor] of Object.entries(anchors)) {
|
|
411
|
+
if (typeof anchor === "string") {
|
|
412
|
+
// PObjectId — look up in collected columns
|
|
413
|
+
const col = columnMap.get(anchor as PObjectId);
|
|
414
|
+
if (!col) throw new Error(`Anchor "${key}": column with id "${anchor}" not found in sources`);
|
|
415
|
+
result[key] = col.spec;
|
|
416
|
+
} else if (isPlRef(anchor)) {
|
|
417
|
+
throw new Error(
|
|
418
|
+
`Anchor "${key}": PlRef anchors must be resolved to PColumnSpec before building. ` +
|
|
419
|
+
`Use the column's spec directly or pass its PObjectId.`,
|
|
420
|
+
);
|
|
421
|
+
} else {
|
|
422
|
+
// PColumnSpec
|
|
423
|
+
result[key] = anchor;
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
return result;
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
// --- MatchingMode → DiscoverColumnsConstraints ---
|
|
430
|
+
|
|
431
|
+
function matchingModeToConstraints(mode: MatchingMode): DiscoverColumnsConstraints {
|
|
432
|
+
switch (mode) {
|
|
433
|
+
case "enrichment":
|
|
434
|
+
return {
|
|
435
|
+
allowFloatingSourceAxes: true,
|
|
436
|
+
allowFloatingHitAxes: true,
|
|
437
|
+
allowSourceQualifications: false,
|
|
438
|
+
allowHitQualifications: false,
|
|
439
|
+
};
|
|
440
|
+
case "related":
|
|
441
|
+
return {
|
|
442
|
+
allowFloatingSourceAxes: true,
|
|
443
|
+
allowFloatingHitAxes: true,
|
|
444
|
+
allowSourceQualifications: true,
|
|
445
|
+
allowHitQualifications: true,
|
|
446
|
+
};
|
|
447
|
+
case "exact":
|
|
448
|
+
return {
|
|
449
|
+
allowFloatingSourceAxes: false,
|
|
450
|
+
allowFloatingHitAxes: false,
|
|
451
|
+
allowSourceQualifications: false,
|
|
452
|
+
allowHitQualifications: false,
|
|
453
|
+
};
|
|
454
|
+
}
|
|
455
|
+
}
|