@platforma-sdk/model 1.61.1 → 1.62.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_model.cjs +17 -10
- package/dist/block_model.cjs.map +1 -1
- package/dist/block_model.d.ts +22 -5
- package/dist/block_model.js +16 -10
- package/dist/block_model.js.map +1 -1
- package/dist/columns/column_collection_builder.cjs +26 -14
- package/dist/columns/column_collection_builder.cjs.map +1 -1
- package/dist/columns/column_collection_builder.d.ts +9 -8
- package/dist/columns/column_collection_builder.js +26 -14
- package/dist/columns/column_collection_builder.js.map +1 -1
- package/dist/columns/ctx_column_sources.cjs.map +1 -1
- package/dist/columns/ctx_column_sources.d.ts +1 -1
- package/dist/columns/ctx_column_sources.js.map +1 -1
- package/dist/components/PlDataTable/createPlDataTable/createPlDataTableV3.cjs +93 -89
- package/dist/components/PlDataTable/createPlDataTable/createPlDataTableV3.cjs.map +1 -1
- package/dist/components/PlDataTable/createPlDataTable/createPlDataTableV3.d.ts +2 -2
- package/dist/components/PlDataTable/createPlDataTable/createPlDataTableV3.js +93 -89
- package/dist/components/PlDataTable/createPlDataTable/createPlDataTableV3.js.map +1 -1
- package/dist/components/PlDataTable/createPlDataTable/index.cjs.map +1 -1
- package/dist/components/PlDataTable/createPlDataTable/index.d.ts +2 -1
- package/dist/components/PlDataTable/createPlDataTable/index.js.map +1 -1
- package/dist/index.cjs +7 -0
- package/dist/index.d.ts +6 -2
- package/dist/index.js +4 -1
- package/dist/package.cjs +1 -1
- package/dist/package.js +1 -1
- package/dist/platforma.d.ts +8 -4
- package/dist/plugin_handle.cjs.map +1 -1
- package/dist/plugin_handle.d.ts +13 -7
- package/dist/plugin_handle.js.map +1 -1
- package/dist/plugin_model.cjs +37 -11
- package/dist/plugin_model.cjs.map +1 -1
- package/dist/plugin_model.d.ts +80 -39
- package/dist/plugin_model.js +37 -11
- package/dist/plugin_model.js.map +1 -1
- package/dist/render/api.cjs +13 -24
- package/dist/render/api.cjs.map +1 -1
- package/dist/render/api.d.ts +11 -14
- package/dist/render/api.js +13 -24
- package/dist/render/api.js.map +1 -1
- package/dist/render/internal.cjs.map +1 -1
- package/dist/render/internal.d.ts +3 -14
- package/dist/render/internal.js.map +1 -1
- package/dist/services/block_services.cjs +18 -0
- package/dist/services/block_services.cjs.map +1 -0
- package/dist/services/block_services.d.ts +18 -0
- package/dist/services/block_services.js +16 -0
- package/dist/services/block_services.js.map +1 -0
- package/dist/services/index.cjs +2 -0
- package/dist/services/index.d.ts +3 -0
- package/dist/services/index.js +2 -0
- package/dist/services/service_bridge.cjs +35 -0
- package/dist/services/service_bridge.cjs.map +1 -0
- package/dist/services/service_bridge.d.ts +18 -0
- package/dist/services/service_bridge.js +33 -0
- package/dist/services/service_bridge.js.map +1 -0
- package/dist/services/service_resolve.d.ts +13 -0
- package/package.json +9 -9
- package/src/block_model.ts +47 -14
- package/src/columns/column_collection_builder.test.ts +23 -2
- package/src/columns/column_collection_builder.ts +38 -30
- package/src/columns/ctx_column_sources.ts +2 -2
- package/src/components/PlDataTable/createPlDataTable/createPlDataTableV3.ts +159 -153
- package/src/components/PlDataTable/createPlDataTable/index.ts +5 -4
- package/src/index.ts +1 -0
- package/src/platforma.ts +14 -2
- package/src/plugin_handle.ts +24 -6
- package/src/plugin_model.ts +252 -84
- package/src/render/api.ts +50 -51
- package/src/render/internal.ts +3 -38
- package/src/services/block_services.ts +17 -0
- package/src/services/index.ts +3 -0
- package/src/services/service_bridge.ts +71 -0
- package/src/services/service_resolve.ts +71 -0
|
@@ -19,13 +19,7 @@ import { createColumnSnapshot } from "./column_snapshot";
|
|
|
19
19
|
import type { ColumnSnapshotProvider, ColumnSource } from "./column_snapshot_provider";
|
|
20
20
|
import { ArrayColumnProvider, toColumnSnapshotProvider } from "./column_snapshot_provider";
|
|
21
21
|
|
|
22
|
-
import type {
|
|
23
|
-
|
|
24
|
-
/** Subset of render context methods needed for spec frame operations. */
|
|
25
|
-
type SpecFrameCtx = Pick<
|
|
26
|
-
GlobalCfgRenderCtxMethods,
|
|
27
|
-
"createSpecFrame" | "specFrameDiscoverColumns" | "disposeSpecFrame"
|
|
28
|
-
>;
|
|
22
|
+
import type { PFrameSpecDriver, PoolEntry, SpecFrameHandle } from "@milaboratories/pl-model-common";
|
|
29
23
|
|
|
30
24
|
// --- FindColumnsOptions ---
|
|
31
25
|
|
|
@@ -40,7 +34,9 @@ export interface FindColumnsOptions {
|
|
|
40
34
|
// --- ColumnCollection ---
|
|
41
35
|
|
|
42
36
|
/** Plain collection — no axis context, selector-based filtering only. */
|
|
43
|
-
export interface ColumnCollection {
|
|
37
|
+
export interface ColumnCollection extends Disposable {
|
|
38
|
+
/** Release the underlying spec frame WASM resource. */
|
|
39
|
+
dispose(): void;
|
|
44
40
|
/** Point lookup by provider-native ID. */
|
|
45
41
|
getColumn(id: PObjectId): undefined | ColumnSnapshot<PObjectId>;
|
|
46
42
|
|
|
@@ -53,7 +49,9 @@ export interface ColumnCollection {
|
|
|
53
49
|
// --- AnchoredColumnCollection ---
|
|
54
50
|
|
|
55
51
|
/** Axis-aware column collection with anchored identity derivation. */
|
|
56
|
-
export interface AnchoredColumnCollection {
|
|
52
|
+
export interface AnchoredColumnCollection extends Disposable {
|
|
53
|
+
/** Release the underlying spec frame WASM resource. */
|
|
54
|
+
dispose(): void;
|
|
57
55
|
/** Point lookup by anchored ID. */
|
|
58
56
|
getColumn(id: SUniversalPColumnId): undefined | ColumnSnapshot<SUniversalPColumnId>;
|
|
59
57
|
|
|
@@ -122,7 +120,7 @@ export interface AnchoredBuildOptions extends BuildOptions {
|
|
|
122
120
|
export class ColumnCollectionBuilder {
|
|
123
121
|
private readonly providers: ColumnSnapshotProvider[] = [];
|
|
124
122
|
|
|
125
|
-
constructor(private readonly
|
|
123
|
+
constructor(private readonly specDriver: PFrameSpecDriver) {}
|
|
126
124
|
|
|
127
125
|
/**
|
|
128
126
|
* Register a column source. Sources added first take precedence for dedup.
|
|
@@ -178,14 +176,14 @@ export class ColumnCollectionBuilder {
|
|
|
178
176
|
const anchorSpecs = resolveAnchorSpecs(options.anchors, columnMap);
|
|
179
177
|
const idDeriver = new AnchoredIdDeriver(anchorSpecs);
|
|
180
178
|
|
|
181
|
-
return new AnchoredColumnCollectionImpl(this.
|
|
179
|
+
return new AnchoredColumnCollectionImpl(this.specDriver, {
|
|
182
180
|
columns: columnMap,
|
|
183
181
|
idDeriver,
|
|
184
182
|
anchorSpecs,
|
|
185
183
|
columnListComplete: allowPartial ? allComplete : false,
|
|
186
184
|
});
|
|
187
185
|
} else {
|
|
188
|
-
return new ColumnCollectionImpl(this.
|
|
186
|
+
return new ColumnCollectionImpl(this.specDriver, {
|
|
189
187
|
columns: columnMap,
|
|
190
188
|
columnListComplete: allowPartial ? allComplete : false,
|
|
191
189
|
});
|
|
@@ -230,25 +228,30 @@ interface ColumnCollectionImplOptions {
|
|
|
230
228
|
readonly columnListComplete?: boolean;
|
|
231
229
|
}
|
|
232
230
|
|
|
233
|
-
class ColumnCollectionImpl implements ColumnCollection {
|
|
231
|
+
class ColumnCollectionImpl implements ColumnCollection, Disposable {
|
|
234
232
|
private readonly columns: Map<PObjectId, ColumnSnapshot<PObjectId>>;
|
|
235
|
-
private readonly
|
|
233
|
+
private readonly specFrameEntry: PoolEntry<SpecFrameHandle>;
|
|
236
234
|
public readonly columnListComplete: boolean;
|
|
237
235
|
|
|
238
236
|
constructor(
|
|
239
|
-
private readonly
|
|
237
|
+
private readonly specDriver: PFrameSpecDriver,
|
|
240
238
|
options: ColumnCollectionImplOptions,
|
|
241
239
|
) {
|
|
242
240
|
this.columns = options.columns;
|
|
243
241
|
this.columnListComplete = options.columnListComplete ?? false;
|
|
244
|
-
this.
|
|
245
|
-
Array.from(this.columns.entries()).
|
|
246
|
-
(acc, [id, col]) => ((acc[id] = col.spec), acc),
|
|
247
|
-
{} as Record<string, PColumnSpec>,
|
|
248
|
-
),
|
|
242
|
+
this.specFrameEntry = this.specDriver.createSpecFrame(
|
|
243
|
+
Object.fromEntries(Array.from(this.columns.entries(), ([id, col]) => [id, col.spec])),
|
|
249
244
|
);
|
|
250
245
|
}
|
|
251
246
|
|
|
247
|
+
dispose(): void {
|
|
248
|
+
this.specFrameEntry.unref();
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
[Symbol.dispose](): void {
|
|
252
|
+
this.dispose();
|
|
253
|
+
}
|
|
254
|
+
|
|
252
255
|
getColumn(id: PObjectId): undefined | ColumnSnapshot<PObjectId> {
|
|
253
256
|
const col = this.columns.get(id);
|
|
254
257
|
if (col === undefined) return undefined;
|
|
@@ -259,7 +262,7 @@ class ColumnCollectionImpl implements ColumnCollection {
|
|
|
259
262
|
const includeColumns = options?.include ? toMultiColumnSelectors(options.include) : undefined;
|
|
260
263
|
const excludeColumns = options?.exclude ? toMultiColumnSelectors(options.exclude) : undefined;
|
|
261
264
|
|
|
262
|
-
const response = this.
|
|
265
|
+
const response = this.specDriver.discoverColumns(this.specFrameEntry.key, {
|
|
263
266
|
includeColumns,
|
|
264
267
|
excludeColumns,
|
|
265
268
|
axes: [],
|
|
@@ -288,17 +291,17 @@ interface AnchoredColumnCollectionImplOptions extends ColumnCollectionImplOption
|
|
|
288
291
|
readonly anchorSpecs: Record<string, PColumnSpec>;
|
|
289
292
|
}
|
|
290
293
|
|
|
291
|
-
class AnchoredColumnCollectionImpl implements AnchoredColumnCollection {
|
|
294
|
+
class AnchoredColumnCollectionImpl implements AnchoredColumnCollection, Disposable {
|
|
292
295
|
private readonly columns: Map<PObjectId, ColumnSnapshot<PObjectId>>;
|
|
293
296
|
private readonly idDeriver: AnchoredIdDeriver;
|
|
294
|
-
private readonly
|
|
297
|
+
private readonly specFrameEntry: PoolEntry<SpecFrameHandle>;
|
|
295
298
|
private readonly anchorAxes: ColumnAxesWithQualifications[];
|
|
296
299
|
/** Reverse lookup: SUniversalPColumnId → PObjectId */
|
|
297
300
|
private readonly idToOriginal: Map<SUniversalPColumnId, PObjectId>;
|
|
298
301
|
public readonly columnListComplete: boolean;
|
|
299
302
|
|
|
300
303
|
constructor(
|
|
301
|
-
private readonly
|
|
304
|
+
private readonly specDriver: PFrameSpecDriver,
|
|
302
305
|
options: AnchoredColumnCollectionImplOptions,
|
|
303
306
|
) {
|
|
304
307
|
this.columns = options.columns;
|
|
@@ -306,11 +309,8 @@ class AnchoredColumnCollectionImpl implements AnchoredColumnCollection {
|
|
|
306
309
|
this.columnListComplete = options.columnListComplete ?? false;
|
|
307
310
|
|
|
308
311
|
// Create spec frame from all collected columns
|
|
309
|
-
this.
|
|
310
|
-
Array.from(this.columns.entries()).
|
|
311
|
-
(acc, [id, col]) => ((acc[id] = col.spec), acc),
|
|
312
|
-
{} as Record<string, PColumnSpec>,
|
|
313
|
-
),
|
|
312
|
+
this.specFrameEntry = this.specDriver.createSpecFrame(
|
|
313
|
+
Object.fromEntries(Array.from(this.columns.entries(), ([id, col]) => [id, col.spec])),
|
|
314
314
|
);
|
|
315
315
|
|
|
316
316
|
// Build anchor axes for discovery requests
|
|
@@ -327,6 +327,14 @@ class AnchoredColumnCollectionImpl implements AnchoredColumnCollection {
|
|
|
327
327
|
);
|
|
328
328
|
}
|
|
329
329
|
|
|
330
|
+
dispose(): void {
|
|
331
|
+
this.specFrameEntry.unref();
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
[Symbol.dispose](): void {
|
|
335
|
+
this.dispose();
|
|
336
|
+
}
|
|
337
|
+
|
|
330
338
|
getColumn(id: SUniversalPColumnId): undefined | ColumnSnapshot<SUniversalPColumnId> {
|
|
331
339
|
const origId = this.idToOriginal.get(id);
|
|
332
340
|
if (origId === undefined) return undefined;
|
|
@@ -341,7 +349,7 @@ class AnchoredColumnCollectionImpl implements AnchoredColumnCollection {
|
|
|
341
349
|
const includeColumns = options?.include ? toMultiColumnSelectors(options.include) : undefined;
|
|
342
350
|
const excludeColumns = options?.exclude ? toMultiColumnSelectors(options.exclude) : undefined;
|
|
343
351
|
|
|
344
|
-
const response = this.
|
|
352
|
+
const response = this.specDriver.discoverColumns(this.specFrameEntry.key, {
|
|
345
353
|
includeColumns,
|
|
346
354
|
excludeColumns,
|
|
347
355
|
constraints,
|
|
@@ -17,8 +17,8 @@ import type { ValueOf } from "@milaboratories/helpers";
|
|
|
17
17
|
*
|
|
18
18
|
* Returns an array of providers suitable for `ColumnCollectionBuilder.addSource()`.
|
|
19
19
|
*/
|
|
20
|
-
export function collectCtxColumnSnapshotProviders<A, U>(
|
|
21
|
-
ctx: RenderCtxBase<A, U>,
|
|
20
|
+
export function collectCtxColumnSnapshotProviders<A, U, S>(
|
|
21
|
+
ctx: RenderCtxBase<A, U, S>,
|
|
22
22
|
): ColumnSnapshotProvider[] {
|
|
23
23
|
const providers: ColumnSnapshotProvider[] = [];
|
|
24
24
|
|
|
@@ -41,6 +41,7 @@ import type { PlDataTableFilters, PlDataTableModel } from "../typesV5";
|
|
|
41
41
|
import { upgradePlDataTableStateV2 } from "../state-migration";
|
|
42
42
|
import type { PlDataTableStateV2 } from "../state-migration";
|
|
43
43
|
import type { ColumnSource, MatchingMode } from "../../../columns";
|
|
44
|
+
import { Services, type RequireServices } from "@milaboratories/pl-model-common";
|
|
44
45
|
import { ColumnCollectionBuilder } from "../../../columns";
|
|
45
46
|
import { isColumnSnapshotProvider } from "../../../columns/column_snapshot_provider";
|
|
46
47
|
import { collectCtxColumnSnapshotProviders } from "../../../columns/ctx_column_sources";
|
|
@@ -184,8 +185,8 @@ export type createPlDataTableOptionsV3 = {
|
|
|
184
185
|
// | { annotation: Record<string, string> }
|
|
185
186
|
// | { ids: Set<string> };
|
|
186
187
|
|
|
187
|
-
export function createPlDataTableV3<A, U
|
|
188
|
-
ctx: RenderCtxBase<A, U>,
|
|
188
|
+
export function createPlDataTableV3<A, U, S extends RequireServices<typeof Services.PFrameSpec>>(
|
|
189
|
+
ctx: RenderCtxBase<A, U, S>,
|
|
189
190
|
options: createPlDataTableOptionsV3,
|
|
190
191
|
): PlDataTableModel | undefined {
|
|
191
192
|
const providers = options.source
|
|
@@ -195,170 +196,175 @@ export function createPlDataTableV3<A, U>(
|
|
|
195
196
|
if (providers.length === 0) return undefined;
|
|
196
197
|
|
|
197
198
|
// Step 1: Build collection from sources
|
|
198
|
-
const builder = new ColumnCollectionBuilder(ctx).addSources(providers);
|
|
199
|
+
const builder = new ColumnCollectionBuilder(ctx.services.pframeSpec).addSources(providers);
|
|
199
200
|
const anchors = options.columns.anchors;
|
|
200
201
|
const collection = isNil(anchors) ? builder.build() : builder.build({ anchors });
|
|
201
202
|
|
|
202
203
|
if (!collection) return undefined;
|
|
204
|
+
try {
|
|
205
|
+
// Step 2: Get data columns, excluding annotation-hidden ones
|
|
206
|
+
const findOptions = options.columns
|
|
207
|
+
? {
|
|
208
|
+
include: options.columns.include,
|
|
209
|
+
exclude: options.columns.exclude,
|
|
210
|
+
mode: options.columns.mode,
|
|
211
|
+
maxHops: options.columns.maxHops,
|
|
212
|
+
}
|
|
213
|
+
: undefined;
|
|
214
|
+
const findResult = collection.findColumns(findOptions);
|
|
215
|
+
const snapshots = findResult.map((v) => ("column" in v ? v.column : v));
|
|
216
|
+
const dataSnapshots = snapshots.filter((s) => !isColumnHidden(s.spec));
|
|
217
|
+
if (dataSnapshots.length === 0) return undefined;
|
|
218
|
+
|
|
219
|
+
// Convert snapshots to PColumn<PColumnDataUniversal>[]
|
|
220
|
+
const columns: PColumn<PColumnDataUniversal>[] = [];
|
|
221
|
+
for (const snap of dataSnapshots) {
|
|
222
|
+
if (!snap.data) return undefined;
|
|
223
|
+
const data = snap.data.get();
|
|
224
|
+
if (data === undefined) return undefined;
|
|
225
|
+
columns.push({ id: snap.id, spec: snap.spec, data });
|
|
226
|
+
}
|
|
203
227
|
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
maxHops: options.columns.maxHops,
|
|
211
|
-
}
|
|
212
|
-
: undefined;
|
|
213
|
-
const findResult = collection.findColumns(findOptions);
|
|
214
|
-
const snapshots = findResult.map((v) => ("column" in v ? v.column : v));
|
|
215
|
-
const dataSnapshots = snapshots.filter((s) => !isColumnHidden(s.spec));
|
|
216
|
-
if (dataSnapshots.length === 0) return undefined;
|
|
217
|
-
|
|
218
|
-
// Convert snapshots to PColumn<PColumnDataUniversal>[]
|
|
219
|
-
const columns: PColumn<PColumnDataUniversal>[] = [];
|
|
220
|
-
for (const snap of dataSnapshots) {
|
|
221
|
-
if (!snap.data) return undefined;
|
|
222
|
-
const data = snap.data.get();
|
|
223
|
-
if (data === undefined) return undefined;
|
|
224
|
-
columns.push({ id: snap.id, spec: snap.spec, data });
|
|
225
|
-
}
|
|
228
|
+
// Step 3: Normalize table state
|
|
229
|
+
const tableStateNormalized = upgradePlDataTableStateV2(options.state);
|
|
230
|
+
|
|
231
|
+
// Step 4: Get label columns from result pool and match to data columns
|
|
232
|
+
const allLabelColumns = getAllLabelColumns(ctx.resultPool);
|
|
233
|
+
if (!allLabelColumns) return undefined;
|
|
226
234
|
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
// Step 4: Get label columns from result pool and match to data columns
|
|
231
|
-
const allLabelColumns = getAllLabelColumns(ctx.resultPool);
|
|
232
|
-
if (!allLabelColumns) return undefined;
|
|
233
|
-
|
|
234
|
-
const fullLabelColumns = getMatchingLabelColumns(
|
|
235
|
-
columns.map(getColumnIdAndSpec),
|
|
236
|
-
allLabelColumns,
|
|
237
|
-
);
|
|
238
|
-
|
|
239
|
-
const fullColumns = [...columns, ...fullLabelColumns];
|
|
240
|
-
|
|
241
|
-
// Step 5: Build column ID set for filter/sorting validation
|
|
242
|
-
const fullColumnsAxes = uniqueBy(
|
|
243
|
-
fullColumns.flatMap((c) => c.spec.axesSpec.map((a) => getAxisId(a))),
|
|
244
|
-
(a) => canonicalizeJson<AxisId>(a),
|
|
245
|
-
);
|
|
246
|
-
const fullColumnsIds: PTableColumnId[] = [
|
|
247
|
-
...fullColumnsAxes.map((a) => ({ type: "axis", id: a }) satisfies PTableColumnIdAxis),
|
|
248
|
-
...fullColumns.map((c) => ({ type: "column", id: c.id }) satisfies PTableColumnIdColumn),
|
|
249
|
-
];
|
|
250
|
-
const fullColumnsIdsSet = new Set(fullColumnsIds.map((c) => canonicalizeJson<PTableColumnId>(c)));
|
|
251
|
-
const isValidColumnId = (id: string): boolean =>
|
|
252
|
-
fullColumnsIdsSet.has(id as CanonicalizedJson<PTableColumnId>);
|
|
253
|
-
|
|
254
|
-
// Step 6: Filtering validation
|
|
255
|
-
const stateFilters = tableStateNormalized.pTableParams.filters;
|
|
256
|
-
const opsFilters = options?.filters ?? null;
|
|
257
|
-
const filters: null | PlDataTableFilters =
|
|
258
|
-
stateFilters != null && opsFilters != null
|
|
259
|
-
? { type: "and", filters: [stateFilters, opsFilters] }
|
|
260
|
-
: (stateFilters ?? opsFilters);
|
|
261
|
-
const filterColumns = filters ? collectFilterSpecColumns(filters) : [];
|
|
262
|
-
const firstInvalidFilterColumn = filterColumns.find((col) => !isValidColumnId(col));
|
|
263
|
-
if (firstInvalidFilterColumn)
|
|
264
|
-
throw new Error(
|
|
265
|
-
`Invalid filter column ${firstInvalidFilterColumn}: column reference does not match the table columns`,
|
|
235
|
+
const fullLabelColumns = getMatchingLabelColumns(
|
|
236
|
+
columns.map(getColumnIdAndSpec),
|
|
237
|
+
allLabelColumns,
|
|
266
238
|
);
|
|
267
239
|
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
240
|
+
const fullColumns = [...columns, ...fullLabelColumns];
|
|
241
|
+
|
|
242
|
+
// Step 5: Build column ID set for filter/sorting validation
|
|
243
|
+
const fullColumnsAxes = uniqueBy(
|
|
244
|
+
fullColumns.flatMap((c) => c.spec.axesSpec.map((a) => getAxisId(a))),
|
|
245
|
+
(a) => canonicalizeJson<AxisId>(a),
|
|
246
|
+
);
|
|
247
|
+
const fullColumnsIds: PTableColumnId[] = [
|
|
248
|
+
...fullColumnsAxes.map((a) => ({ type: "axis", id: a }) satisfies PTableColumnIdAxis),
|
|
249
|
+
...fullColumns.map((c) => ({ type: "column", id: c.id }) satisfies PTableColumnIdColumn),
|
|
250
|
+
];
|
|
251
|
+
const fullColumnsIdsSet = new Set(
|
|
252
|
+
fullColumnsIds.map((c) => canonicalizeJson<PTableColumnId>(c)),
|
|
253
|
+
);
|
|
254
|
+
const isValidColumnId = (id: string): boolean =>
|
|
255
|
+
fullColumnsIdsSet.has(id as CanonicalizedJson<PTableColumnId>);
|
|
256
|
+
|
|
257
|
+
// Step 6: Filtering validation
|
|
258
|
+
const stateFilters = tableStateNormalized.pTableParams.filters;
|
|
259
|
+
const opsFilters = options?.filters ?? null;
|
|
260
|
+
const filters: null | PlDataTableFilters =
|
|
261
|
+
stateFilters != null && opsFilters != null
|
|
262
|
+
? { type: "and", filters: [stateFilters, opsFilters] }
|
|
263
|
+
: (stateFilters ?? opsFilters);
|
|
264
|
+
const filterColumns = filters ? collectFilterSpecColumns(filters) : [];
|
|
265
|
+
const firstInvalidFilterColumn = filterColumns.find((col) => !isValidColumnId(col));
|
|
266
|
+
if (firstInvalidFilterColumn)
|
|
267
|
+
throw new Error(
|
|
268
|
+
`Invalid filter column ${firstInvalidFilterColumn}: column reference does not match the table columns`,
|
|
269
|
+
);
|
|
270
|
+
|
|
271
|
+
// Step 7: Sorting validation
|
|
272
|
+
const userSorting = tableStateNormalized.pTableParams.sorting;
|
|
273
|
+
const sorting = (isEmpty(userSorting) ? options?.sorting : userSorting) ?? [];
|
|
274
|
+
const firstInvalidSortingColumn = sorting.find(
|
|
275
|
+
(s) => !isValidColumnId(canonicalizeJson<PTableColumnId>(s.column)),
|
|
276
|
+
);
|
|
277
|
+
if (firstInvalidSortingColumn)
|
|
278
|
+
throw new Error(
|
|
279
|
+
`Invalid sorting column ${JSON.stringify(firstInvalidSortingColumn.column)}: column reference does not match the table columns`,
|
|
280
|
+
);
|
|
281
|
+
|
|
282
|
+
// Step 8: Build full table definition and handles
|
|
283
|
+
const coreJoinType = options?.coreJoinType ?? "full";
|
|
284
|
+
const fullDef = createPTableDef({
|
|
285
|
+
columns,
|
|
286
|
+
labelColumns: fullLabelColumns,
|
|
287
|
+
coreJoinType,
|
|
288
|
+
filters,
|
|
289
|
+
sorting,
|
|
290
|
+
coreColumnPredicate: options?.coreColumnPredicate,
|
|
291
|
+
});
|
|
292
|
+
|
|
293
|
+
const fullHandle = ctx.createPTableV2(fullDef);
|
|
294
|
+
const pframeHandle = ctx.createPFrame(fullColumns);
|
|
295
|
+
if (!fullHandle || !pframeHandle) return undefined;
|
|
296
|
+
|
|
297
|
+
// Step 9: Determine hidden columns
|
|
298
|
+
const hiddenColumns = new Set<PObjectId>(
|
|
299
|
+
((): PObjectId[] => {
|
|
300
|
+
// Inner join works as a filter — all columns must be present
|
|
301
|
+
if (coreJoinType === "inner") return [];
|
|
302
|
+
|
|
303
|
+
const hiddenColIds = tableStateNormalized.pTableParams.hiddenColIds;
|
|
304
|
+
if (hiddenColIds) return hiddenColIds;
|
|
305
|
+
|
|
306
|
+
return columns.filter((c) => isColumnOptional(c.spec)).map((c) => c.id);
|
|
307
|
+
})(),
|
|
277
308
|
);
|
|
278
309
|
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
columns
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
if (coreColumnPredicate) {
|
|
313
|
-
const coreColumns = columns.flatMap((c) =>
|
|
314
|
-
coreColumnPredicate(getColumnIdAndSpec(c)) ? [c.id] : [],
|
|
310
|
+
// Preserve linker columns
|
|
311
|
+
columns.filter((c) => isLinkerColumn(c.spec)).forEach((c) => hiddenColumns.delete(c.id));
|
|
312
|
+
|
|
313
|
+
// Preserve core columns as they change the shape of join
|
|
314
|
+
const coreColumnPredicate = options?.coreColumnPredicate;
|
|
315
|
+
if (coreColumnPredicate) {
|
|
316
|
+
const coreColumns = columns.flatMap((c) =>
|
|
317
|
+
coreColumnPredicate(getColumnIdAndSpec(c)) ? [c.id] : [],
|
|
318
|
+
);
|
|
319
|
+
coreColumns.forEach((c) => hiddenColumns.delete(c));
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
// Preserve sorted columns from being hidden
|
|
323
|
+
sorting
|
|
324
|
+
.map((s) => s.column)
|
|
325
|
+
.filter((c): c is PTableColumnIdColumn => c.type === "column")
|
|
326
|
+
.forEach((c) => hiddenColumns.delete(c.id));
|
|
327
|
+
|
|
328
|
+
// Preserve filter columns from being hidden
|
|
329
|
+
if (filters) {
|
|
330
|
+
collectFilterSpecColumns(filters)
|
|
331
|
+
.flatMap((c) => {
|
|
332
|
+
const obj = parseJson(c);
|
|
333
|
+
return obj.type === "column" ? [obj.id] : [];
|
|
334
|
+
})
|
|
335
|
+
.forEach((c) => hiddenColumns.delete(c));
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
// Step 10: Build visible table definition
|
|
339
|
+
const visibleColumns = columns.filter((c) => !hiddenColumns.has(c.id));
|
|
340
|
+
const visibleLabelColumns = getMatchingLabelColumns(
|
|
341
|
+
visibleColumns.map(getColumnIdAndSpec),
|
|
342
|
+
allLabelColumns,
|
|
315
343
|
);
|
|
316
|
-
coreColumns.forEach((c) => hiddenColumns.delete(c));
|
|
317
|
-
}
|
|
318
344
|
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
345
|
+
if (!allPColumnsReady([...visibleColumns, ...visibleLabelColumns])) return undefined;
|
|
346
|
+
|
|
347
|
+
const visibleDef = createPTableDef({
|
|
348
|
+
columns: visibleColumns,
|
|
349
|
+
labelColumns: visibleLabelColumns,
|
|
350
|
+
coreJoinType,
|
|
351
|
+
filters,
|
|
352
|
+
sorting,
|
|
353
|
+
coreColumnPredicate,
|
|
354
|
+
});
|
|
355
|
+
const visibleHandle = ctx.createPTableV2(visibleDef);
|
|
356
|
+
|
|
357
|
+
if (!visibleHandle) return undefined;
|
|
358
|
+
|
|
359
|
+
return {
|
|
360
|
+
sourceId: tableStateNormalized.pTableParams.sourceId,
|
|
361
|
+
fullTableHandle: fullHandle,
|
|
362
|
+
fullPframeHandle: pframeHandle,
|
|
363
|
+
visibleTableHandle: visibleHandle,
|
|
364
|
+
} satisfies PlDataTableModel;
|
|
365
|
+
} finally {
|
|
366
|
+
collection.dispose();
|
|
333
367
|
}
|
|
334
|
-
|
|
335
|
-
// Step 10: Build visible table definition
|
|
336
|
-
const visibleColumns = columns.filter((c) => !hiddenColumns.has(c.id));
|
|
337
|
-
const visibleLabelColumns = getMatchingLabelColumns(
|
|
338
|
-
visibleColumns.map(getColumnIdAndSpec),
|
|
339
|
-
allLabelColumns,
|
|
340
|
-
);
|
|
341
|
-
|
|
342
|
-
if (!allPColumnsReady([...visibleColumns, ...visibleLabelColumns])) return undefined;
|
|
343
|
-
|
|
344
|
-
const visibleDef = createPTableDef({
|
|
345
|
-
columns: visibleColumns,
|
|
346
|
-
labelColumns: visibleLabelColumns,
|
|
347
|
-
coreJoinType,
|
|
348
|
-
filters,
|
|
349
|
-
sorting,
|
|
350
|
-
coreColumnPredicate,
|
|
351
|
-
});
|
|
352
|
-
const visibleHandle = ctx.createPTableV2(visibleDef);
|
|
353
|
-
|
|
354
|
-
if (!visibleHandle) return undefined;
|
|
355
|
-
|
|
356
|
-
return {
|
|
357
|
-
sourceId: tableStateNormalized.pTableParams.sourceId,
|
|
358
|
-
fullTableHandle: fullHandle,
|
|
359
|
-
fullPframeHandle: pframeHandle,
|
|
360
|
-
visibleTableHandle: visibleHandle,
|
|
361
|
-
} satisfies PlDataTableModel;
|
|
362
368
|
}
|
|
363
369
|
|
|
364
370
|
/** Normalize raw ColumnSource | ColumnSource[] into a flat list of sources. */
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { Services, type RequireServices } from "@milaboratories/pl-model-common";
|
|
1
2
|
import type { RenderCtxBase } from "../../../render";
|
|
2
3
|
import type { PlDataTableModel } from "../typesV5";
|
|
3
4
|
import { createPlDataTableOptionsV2, createPlDataTableV2 } from "./createPlDataTableV2";
|
|
@@ -8,12 +9,12 @@ export function createPlDataTable<A, U>(
|
|
|
8
9
|
ctx: RenderCtxBase<A, U>,
|
|
9
10
|
options: { version: "v2" } & createPlDataTableOptionsV2,
|
|
10
11
|
): ReturnType<typeof createPlDataTableV2>;
|
|
11
|
-
export function createPlDataTable<A, U
|
|
12
|
-
ctx: RenderCtxBase<A, U>,
|
|
12
|
+
export function createPlDataTable<A, U, S extends RequireServices<typeof Services.PFrameSpec>>(
|
|
13
|
+
ctx: RenderCtxBase<A, U, S>,
|
|
13
14
|
options: { version?: "v3" } & createPlDataTableOptionsV3,
|
|
14
15
|
): ReturnType<typeof createPlDataTableV3>;
|
|
15
|
-
export function createPlDataTable<A, U
|
|
16
|
-
ctx: RenderCtxBase<A, U>,
|
|
16
|
+
export function createPlDataTable<A, U, S extends RequireServices<typeof Services.PFrameSpec>>(
|
|
17
|
+
ctx: RenderCtxBase<A, U, S>,
|
|
17
18
|
options:
|
|
18
19
|
| ({ version: "v2" } & createPlDataTableOptionsV2)
|
|
19
20
|
| ({ version?: "v3" } & createPlDataTableOptionsV3),
|
package/src/index.ts
CHANGED
package/src/platforma.ts
CHANGED
|
@@ -9,6 +9,7 @@ import type {
|
|
|
9
9
|
OutputWithStatus,
|
|
10
10
|
} from "@milaboratories/pl-model-common";
|
|
11
11
|
import type { SdkInfo } from "./version";
|
|
12
|
+
import type { ServiceDispatch, UiServices as AllUiServices } from "@milaboratories/pl-model-common";
|
|
12
13
|
import type { BlockStatePatch } from "./block_state_patch";
|
|
13
14
|
import type { PluginRecord } from "./block_model";
|
|
14
15
|
import type { PluginHandle, PluginFactoryLike } from "./plugin_handle";
|
|
@@ -54,13 +55,18 @@ export interface PlatformaV3<
|
|
|
54
55
|
>,
|
|
55
56
|
Href extends `/${string}` = `/${string}`,
|
|
56
57
|
Plugins extends Record<string, unknown> = Record<string, unknown>,
|
|
58
|
+
UiServices extends Partial<AllUiServices> = Partial<AllUiServices>,
|
|
57
59
|
>
|
|
58
60
|
extends BlockApiV3<Data, Args, Outputs, Href>, DriverKit {
|
|
59
61
|
/** Information about SDK version current platforma environment was compiled with. */
|
|
60
62
|
readonly sdkInfo: SdkInfo;
|
|
61
63
|
readonly apiVersion: 3;
|
|
64
|
+
/** Service dispatch — lists available services, their methods, and invokes them. */
|
|
65
|
+
readonly serviceDispatch: ServiceDispatch;
|
|
62
66
|
/** @internal Type brand for plugin type inference. Not used at runtime. */
|
|
63
67
|
readonly __pluginsBrand?: Plugins;
|
|
68
|
+
/** @internal Type brand for UI service type inference. Not used at runtime. */
|
|
69
|
+
readonly __uiServicesBrand?: UiServices;
|
|
64
70
|
}
|
|
65
71
|
|
|
66
72
|
export type Platforma<
|
|
@@ -148,7 +154,13 @@ export type InferPluginData<Pl, PluginId extends string> =
|
|
|
148
154
|
* because PluginRecord doesn't carry Config (lost after factory.create()).
|
|
149
155
|
*/
|
|
150
156
|
export type InferPluginHandles<T extends Record<string, unknown>> = {
|
|
151
|
-
readonly [K in keyof T]: T[K] extends PluginRecord<
|
|
152
|
-
|
|
157
|
+
readonly [K in keyof T]: T[K] extends PluginRecord<
|
|
158
|
+
infer Data,
|
|
159
|
+
infer Params,
|
|
160
|
+
infer Outputs,
|
|
161
|
+
infer ModelServices,
|
|
162
|
+
infer UiServices
|
|
163
|
+
>
|
|
164
|
+
? { handle: PluginHandle<PluginFactoryLike<Data, Params, Outputs, ModelServices, UiServices>> }
|
|
153
165
|
: never;
|
|
154
166
|
};
|
package/src/plugin_handle.ts
CHANGED
|
@@ -23,37 +23,55 @@ export interface PluginFactoryLike<
|
|
|
23
23
|
Data extends Record<string, unknown> = Record<string, unknown>,
|
|
24
24
|
Params extends undefined | Record<string, unknown> = undefined | Record<string, unknown>,
|
|
25
25
|
Outputs extends Record<string, unknown> = Record<string, unknown>,
|
|
26
|
+
ModelServices = unknown,
|
|
27
|
+
UiServices = unknown,
|
|
26
28
|
> {
|
|
27
29
|
readonly __types?: {
|
|
28
30
|
data: Data;
|
|
29
31
|
params: Params;
|
|
30
32
|
outputs: Outputs;
|
|
33
|
+
modelServices: ModelServices;
|
|
34
|
+
uiServices: UiServices;
|
|
31
35
|
};
|
|
32
36
|
}
|
|
33
37
|
|
|
34
38
|
/** Extract the Data type from a PluginFactoryLike phantom. */
|
|
35
39
|
export type InferFactoryData<F extends PluginFactoryLike> = NonNullable<
|
|
36
|
-
F extends PluginFactoryLike<infer D, any, any> ? D : Record<string, unknown>
|
|
40
|
+
F extends PluginFactoryLike<infer D, any, any, any, any> ? D : Record<string, unknown>
|
|
37
41
|
>;
|
|
38
42
|
|
|
39
43
|
/** Extract the Params type from a PluginFactoryLike phantom. */
|
|
40
44
|
export type InferFactoryParams<F extends PluginFactoryLike> = NonNullable<
|
|
41
|
-
F extends PluginFactoryLike<any, infer P, any> ? P : undefined
|
|
45
|
+
F extends PluginFactoryLike<any, infer P, any, any, any> ? P : undefined
|
|
42
46
|
>;
|
|
43
47
|
|
|
44
48
|
/** Extract the Outputs type from a PluginFactoryLike phantom. */
|
|
45
49
|
export type InferFactoryOutputs<F extends PluginFactoryLike> = NonNullable<
|
|
46
|
-
F extends PluginFactoryLike<any, any, infer O> ? O : Record<string, unknown>
|
|
50
|
+
F extends PluginFactoryLike<any, any, infer O, any, any> ? O : Record<string, unknown>
|
|
47
51
|
>;
|
|
48
52
|
|
|
53
|
+
/** Extract the pre-resolved model services type from a PluginFactoryLike phantom. */
|
|
54
|
+
export type InferFactoryModelServices<F extends PluginFactoryLike> =
|
|
55
|
+
F extends PluginFactoryLike<any, any, any, infer ModelServices, any> ? ModelServices : {};
|
|
56
|
+
|
|
57
|
+
/** Extract the pre-resolved UI services type from a PluginFactoryLike phantom. */
|
|
58
|
+
export type InferFactoryUiServices<F extends PluginFactoryLike> =
|
|
59
|
+
F extends PluginFactoryLike<any, any, any, any, infer UiServices> ? UiServices : {};
|
|
60
|
+
|
|
49
61
|
/**
|
|
50
62
|
* Derive a typed PluginHandle from a PluginFactory type.
|
|
51
|
-
* Normalizes the brand to
|
|
63
|
+
* Normalizes the brand to data/params/outputs/services (strips config) so handles
|
|
52
64
|
* from InferPluginHandles match handles from InferPluginHandle.
|
|
53
65
|
*/
|
|
54
66
|
export type InferPluginHandle<F extends PluginFactoryLike> = NonNullable<
|
|
55
|
-
F extends PluginFactoryLike<
|
|
56
|
-
|
|
67
|
+
F extends PluginFactoryLike<
|
|
68
|
+
infer Data,
|
|
69
|
+
infer Params,
|
|
70
|
+
infer Outputs,
|
|
71
|
+
infer ModelServices,
|
|
72
|
+
infer UiServices
|
|
73
|
+
>
|
|
74
|
+
? PluginHandle<PluginFactoryLike<Data, Params, Outputs, ModelServices, UiServices>>
|
|
57
75
|
: PluginHandle
|
|
58
76
|
>;
|
|
59
77
|
|