@platforma-sdk/model 1.57.0 → 1.58.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 -13
- package/dist/block_model.cjs.map +1 -1
- package/dist/block_model.d.ts +4 -4
- package/dist/block_model.d.ts.map +1 -1
- package/dist/block_model.js +17 -13
- package/dist/block_model.js.map +1 -1
- package/dist/block_model_legacy.cjs +1 -0
- package/dist/block_model_legacy.cjs.map +1 -1
- package/dist/block_model_legacy.d.ts.map +1 -1
- package/dist/block_model_legacy.js +1 -0
- package/dist/block_model_legacy.js.map +1 -1
- package/dist/block_storage.cjs +18 -14
- package/dist/block_storage.cjs.map +1 -1
- package/dist/block_storage.d.ts +14 -10
- package/dist/block_storage.d.ts.map +1 -1
- package/dist/block_storage.js +18 -14
- package/dist/block_storage.js.map +1 -1
- package/dist/block_storage_callbacks.cjs +3 -3
- package/dist/block_storage_callbacks.cjs.map +1 -1
- package/dist/block_storage_callbacks.d.ts +4 -3
- package/dist/block_storage_callbacks.d.ts.map +1 -1
- package/dist/block_storage_callbacks.js +3 -3
- package/dist/block_storage_callbacks.js.map +1 -1
- package/dist/components/PFrameForGraphs.cjs +0 -117
- package/dist/components/PFrameForGraphs.cjs.map +1 -1
- package/dist/components/PFrameForGraphs.d.ts +3 -5
- package/dist/components/PFrameForGraphs.d.ts.map +1 -1
- package/dist/components/PFrameForGraphs.js +2 -117
- package/dist/components/PFrameForGraphs.js.map +1 -1
- package/dist/components/PlDataTable/index.d.ts +1 -1
- package/dist/components/PlDataTable/index.d.ts.map +1 -1
- package/dist/components/PlDataTable/table.cjs +16 -1
- package/dist/components/PlDataTable/table.cjs.map +1 -1
- package/dist/components/PlDataTable/table.d.ts.map +1 -1
- package/dist/components/PlDataTable/table.js +16 -2
- package/dist/components/PlDataTable/table.js.map +1 -1
- package/dist/index.cjs +8 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -2
- package/dist/index.js.map +1 -1
- package/dist/package.json.cjs +1 -1
- package/dist/package.json.js +1 -1
- package/dist/pframe_utils/axes.cjs +131 -0
- package/dist/pframe_utils/axes.cjs.map +1 -0
- package/dist/pframe_utils/axes.d.ts +15 -0
- package/dist/pframe_utils/axes.d.ts.map +1 -0
- package/dist/pframe_utils/axes.js +128 -0
- package/dist/pframe_utils/axes.js.map +1 -0
- package/dist/pframe_utils/columns.cjs +4 -7
- package/dist/pframe_utils/columns.cjs.map +1 -1
- package/dist/pframe_utils/columns.js +1 -4
- package/dist/pframe_utils/columns.js.map +1 -1
- package/dist/pframe_utils/index.cjs +0 -2
- package/dist/pframe_utils/index.cjs.map +1 -1
- package/dist/pframe_utils/index.js +0 -2
- package/dist/pframe_utils/index.js.map +1 -1
- package/dist/platforma.d.ts +12 -2
- package/dist/platforma.d.ts.map +1 -1
- package/dist/plugin_handle.cjs +29 -0
- package/dist/plugin_handle.cjs.map +1 -0
- package/dist/plugin_handle.d.ts +51 -0
- package/dist/plugin_handle.d.ts.map +1 -0
- package/dist/plugin_handle.js +25 -0
- package/dist/plugin_handle.js.map +1 -0
- package/dist/plugin_model.cjs +27 -27
- package/dist/plugin_model.cjs.map +1 -1
- package/dist/plugin_model.d.ts +41 -33
- package/dist/plugin_model.d.ts.map +1 -1
- package/dist/plugin_model.js +27 -27
- package/dist/plugin_model.js.map +1 -1
- package/dist/render/api.cjs +9 -5
- package/dist/render/api.cjs.map +1 -1
- package/dist/render/api.d.ts +11 -5
- package/dist/render/api.d.ts.map +1 -1
- package/dist/render/api.js +9 -5
- package/dist/render/api.js.map +1 -1
- package/package.json +5 -5
- package/src/block_model.ts +33 -19
- package/src/block_model_legacy.ts +1 -0
- package/src/block_storage.test.ts +3 -2
- package/src/block_storage.ts +30 -22
- package/src/block_storage_callbacks.ts +8 -7
- package/src/components/PFrameForGraphs.ts +4 -167
- package/src/components/PlDataTable/index.ts +6 -1
- package/src/components/PlDataTable/table.ts +3 -4
- package/src/index.ts +2 -1
- package/src/pframe_utils/axes.ts +175 -0
- package/src/pframe_utils/columns.ts +2 -2
- package/src/platforma.ts +17 -2
- package/src/plugin_handle.ts +85 -0
- package/src/plugin_model.ts +118 -56
- package/src/render/api.ts +21 -11
package/src/block_storage.ts
CHANGED
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
|
|
12
12
|
import type { Branded } from "@milaboratories/pl-model-common";
|
|
13
13
|
import type { DataMigrationResult, DataVersioned } from "./block_migrations";
|
|
14
|
+
import type { PluginHandle, PluginFactoryLike, InferFactoryData } from "./plugin_handle";
|
|
14
15
|
|
|
15
16
|
// =============================================================================
|
|
16
17
|
// Core Types
|
|
@@ -50,7 +51,7 @@ export type PluginName = Branded<string, "PluginName">;
|
|
|
50
51
|
* Plugin registry - maps pluginId (unique within a block) to pluginName (globally unique plugin type).
|
|
51
52
|
* Using a Record highlights that pluginIds must be unique within a block.
|
|
52
53
|
*/
|
|
53
|
-
export type PluginRegistry = Record<
|
|
54
|
+
export type PluginRegistry = Record<PluginHandle, PluginName>;
|
|
54
55
|
|
|
55
56
|
/**
|
|
56
57
|
* Versioned data - used for both block data and plugin data
|
|
@@ -75,8 +76,8 @@ export type BlockStorage<TState = unknown> = {
|
|
|
75
76
|
readonly [BLOCK_STORAGE_KEY]: BlockStorageSchemaVersion;
|
|
76
77
|
/** Registry of plugins: pluginId -> pluginName */
|
|
77
78
|
__pluginRegistry?: PluginRegistry;
|
|
78
|
-
/** Plugin-specific data, keyed by
|
|
79
|
-
__plugins?: Record<
|
|
79
|
+
/** Plugin-specific data, keyed by plugin handle */
|
|
80
|
+
__plugins?: Record<PluginHandle, VersionedData<unknown>>;
|
|
80
81
|
} & VersionedData<TState>;
|
|
81
82
|
|
|
82
83
|
/**
|
|
@@ -176,7 +177,7 @@ export function deriveDataFromStorage<TData = unknown>(rawStorage: unknown): TDa
|
|
|
176
177
|
/** Payload for storage mutation operations. SDK defines specific operations. */
|
|
177
178
|
export type MutateStoragePayload<T = unknown> =
|
|
178
179
|
| { operation: "update-block-data"; value: T }
|
|
179
|
-
| { operation: "update-plugin-data"; pluginId:
|
|
180
|
+
| { operation: "update-plugin-data"; pluginId: PluginHandle; value: unknown };
|
|
180
181
|
|
|
181
182
|
/**
|
|
182
183
|
* Updates the data in BlockStorage (immutable)
|
|
@@ -261,13 +262,13 @@ export interface MigrateBlockStorageConfig {
|
|
|
261
262
|
migrateBlockData: (versioned: DataVersioned<unknown>) => DataMigrationResult<unknown>;
|
|
262
263
|
/** Migrate each plugin's data. Return undefined to remove the plugin. */
|
|
263
264
|
migratePluginData: (
|
|
264
|
-
|
|
265
|
+
handle: PluginHandle,
|
|
265
266
|
versioned: DataVersioned<unknown>,
|
|
266
267
|
) => DataMigrationResult<unknown> | undefined;
|
|
267
268
|
/** The new plugin registry after migration (pluginId -> pluginName) */
|
|
268
269
|
newPluginRegistry: PluginRegistry;
|
|
269
270
|
/** Factory to create initial data for new plugins */
|
|
270
|
-
createPluginData: (
|
|
271
|
+
createPluginData: (handle: PluginHandle) => DataVersioned<unknown>;
|
|
271
272
|
}
|
|
272
273
|
|
|
273
274
|
/**
|
|
@@ -334,33 +335,34 @@ export function migrateBlockStorage(
|
|
|
334
335
|
// Step 2: Migrate plugins
|
|
335
336
|
const oldPlugins = storage.__plugins ?? {};
|
|
336
337
|
const oldRegistry = storage.__pluginRegistry ?? {};
|
|
337
|
-
const newPlugins: Record<
|
|
338
|
+
const newPlugins: Record<PluginHandle, VersionedData<unknown>> = {};
|
|
338
339
|
|
|
339
|
-
for (const [
|
|
340
|
-
const
|
|
341
|
-
const
|
|
340
|
+
for (const [key, pluginName] of Object.entries(newPluginRegistry)) {
|
|
341
|
+
const handle = key as PluginHandle;
|
|
342
|
+
const existingEntry = oldPlugins[handle];
|
|
343
|
+
const existingName = oldRegistry[handle];
|
|
342
344
|
|
|
343
345
|
try {
|
|
344
346
|
if (existingEntry && existingName === pluginName) {
|
|
345
347
|
// Plugin exists with same type - migrate its data
|
|
346
|
-
const migrated = migratePluginData(
|
|
348
|
+
const migrated = migratePluginData(handle, {
|
|
347
349
|
version: existingEntry.__dataVersion,
|
|
348
350
|
data: existingEntry.__data,
|
|
349
351
|
});
|
|
350
352
|
if (migrated) {
|
|
351
|
-
newPlugins[
|
|
353
|
+
newPlugins[handle] = { __dataVersion: migrated.version, __data: migrated.data };
|
|
352
354
|
}
|
|
353
355
|
// If undefined returned, plugin is intentionally removed
|
|
354
356
|
} else {
|
|
355
357
|
// New plugin or type changed - create with initial data
|
|
356
|
-
const initial = createPluginData(
|
|
357
|
-
newPlugins[
|
|
358
|
+
const initial = createPluginData(handle);
|
|
359
|
+
newPlugins[handle] = { __dataVersion: initial.version, __data: initial.data };
|
|
358
360
|
}
|
|
359
361
|
} catch (error) {
|
|
360
362
|
return {
|
|
361
363
|
success: false,
|
|
362
364
|
error: error instanceof Error ? error.message : String(error),
|
|
363
|
-
failedAt:
|
|
365
|
+
failedAt: handle,
|
|
364
366
|
};
|
|
365
367
|
}
|
|
366
368
|
}
|
|
@@ -384,14 +386,20 @@ export function migrateBlockStorage(
|
|
|
384
386
|
* Gets plugin-specific data from block storage.
|
|
385
387
|
* Accepts raw storage (any format) and normalizes internally.
|
|
386
388
|
*
|
|
389
|
+
* When called with a typed PluginHandle<F>, the return type is automatically
|
|
390
|
+
* inferred from the factory's phantom `__types.data` field.
|
|
391
|
+
*
|
|
387
392
|
* @param rawStorage - Raw block storage (may be legacy format or BlockStorage)
|
|
388
|
-
* @param
|
|
389
|
-
* @returns The plugin data
|
|
390
|
-
* @throws If
|
|
393
|
+
* @param handle - The plugin handle (branded plugin instance id)
|
|
394
|
+
* @returns The plugin data, typed via factory inference
|
|
395
|
+
* @throws If plugin is not found in storage
|
|
391
396
|
*/
|
|
392
|
-
export function getPluginData<
|
|
397
|
+
export function getPluginData<F extends PluginFactoryLike>(
|
|
398
|
+
rawStorage: unknown,
|
|
399
|
+
handle: PluginHandle<F>,
|
|
400
|
+
): InferFactoryData<F> {
|
|
393
401
|
const storage = normalizeBlockStorage(rawStorage);
|
|
394
|
-
const pluginEntry = storage.__plugins?.[
|
|
395
|
-
if (!pluginEntry) throw new Error(`Plugin '${
|
|
396
|
-
return pluginEntry.__data as
|
|
402
|
+
const pluginEntry = storage.__plugins?.[handle];
|
|
403
|
+
if (!pluginEntry) throw new Error(`Plugin '${handle}' not found in block storage`);
|
|
404
|
+
return pluginEntry.__data as InferFactoryData<F>;
|
|
397
405
|
}
|
|
@@ -24,6 +24,7 @@ import {
|
|
|
24
24
|
normalizeBlockStorage,
|
|
25
25
|
updateStorageData,
|
|
26
26
|
} from "./block_storage";
|
|
27
|
+
import type { PluginHandle } from "./plugin_handle";
|
|
27
28
|
|
|
28
29
|
import { stringifyJson, type StringifiedJson } from "@milaboratories/pl-model-common";
|
|
29
30
|
import type { DataMigrationResult, DataVersioned } from "./block_migrations";
|
|
@@ -37,17 +38,17 @@ export interface MigrationHooks {
|
|
|
37
38
|
migrateBlockData: (versioned: DataVersioned<unknown>) => DataMigrationResult<unknown>;
|
|
38
39
|
getPluginRegistry: () => PluginRegistry;
|
|
39
40
|
migratePluginData: (
|
|
40
|
-
|
|
41
|
+
handle: PluginHandle,
|
|
41
42
|
versioned: DataVersioned<unknown>,
|
|
42
43
|
) => DataMigrationResult<unknown> | undefined;
|
|
43
|
-
createPluginData: (
|
|
44
|
+
createPluginData: (handle: PluginHandle) => DataVersioned<unknown>;
|
|
44
45
|
}
|
|
45
46
|
|
|
46
47
|
/** Dependencies for initial storage creation */
|
|
47
48
|
export interface InitialStorageHooks {
|
|
48
49
|
getDefaultBlockData: () => DataVersioned<unknown>;
|
|
49
50
|
getPluginRegistry: () => PluginRegistry;
|
|
50
|
-
createPluginData: (
|
|
51
|
+
createPluginData: (handle: PluginHandle) => DataVersioned<unknown>;
|
|
51
52
|
}
|
|
52
53
|
|
|
53
54
|
/**
|
|
@@ -233,10 +234,10 @@ export function createInitialStorage(hooks: InitialStorageHooks): StringifiedJso
|
|
|
233
234
|
const blockDefault = hooks.getDefaultBlockData();
|
|
234
235
|
const pluginRegistry = hooks.getPluginRegistry();
|
|
235
236
|
|
|
236
|
-
const plugins: Record<
|
|
237
|
-
for (const
|
|
238
|
-
const initial = hooks.createPluginData(
|
|
239
|
-
plugins[
|
|
237
|
+
const plugins: Record<PluginHandle, VersionedData<unknown>> = {};
|
|
238
|
+
for (const handle of Object.keys(pluginRegistry) as PluginHandle[]) {
|
|
239
|
+
const initial = hooks.createPluginData(handle);
|
|
240
|
+
plugins[handle] = { __dataVersion: initial.version, __data: initial.data };
|
|
240
241
|
}
|
|
241
242
|
|
|
242
243
|
const storage: BlockStorage = {
|
|
@@ -1,55 +1,9 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
|
|
3
|
-
AxisSpecNormalized,
|
|
4
|
-
CanonicalizedJson,
|
|
5
|
-
PColumn,
|
|
6
|
-
PColumnSpec,
|
|
7
|
-
PFrameHandle,
|
|
8
|
-
PObjectId,
|
|
9
|
-
} from "@milaboratories/pl-model-common";
|
|
10
|
-
import {
|
|
11
|
-
Annotation,
|
|
12
|
-
canonicalizeJson,
|
|
13
|
-
getAxisId,
|
|
14
|
-
getColumnIdAndSpec,
|
|
15
|
-
LinkerMap,
|
|
16
|
-
matchAxisId,
|
|
17
|
-
readAnnotation,
|
|
18
|
-
readAnnotationJson,
|
|
19
|
-
stringifyJson,
|
|
20
|
-
} from "@milaboratories/pl-model-common";
|
|
1
|
+
import type { PColumn, PColumnSpec, PFrameHandle } from "@milaboratories/pl-model-common";
|
|
2
|
+
import { Annotation, readAnnotationJson } from "@milaboratories/pl-model-common";
|
|
21
3
|
import type { PColumnDataUniversal, RenderCtxBase } from "../render";
|
|
22
4
|
import { getAllRelatedColumns, getRelatedColumns } from "../pframe_utils/columns";
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
const colId = (id: PObjectId, domains: (Record<string, string> | undefined)[]) => {
|
|
26
|
-
let wid = id.toString();
|
|
27
|
-
domains?.forEach((domain) => {
|
|
28
|
-
if (domain) {
|
|
29
|
-
for (const [k, v] of Object.entries(domain)) {
|
|
30
|
-
wid += k;
|
|
31
|
-
wid += v;
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
});
|
|
35
|
-
return wid;
|
|
36
|
-
};
|
|
37
|
-
|
|
38
|
-
/** All combinations with 1 key from each list */
|
|
39
|
-
function getKeysCombinations(idsLists: AxisId[][]) {
|
|
40
|
-
if (!idsLists.length) {
|
|
41
|
-
return [];
|
|
42
|
-
}
|
|
43
|
-
let result: AxisId[][] = [[]];
|
|
44
|
-
idsLists.forEach((list) => {
|
|
45
|
-
const nextResult: AxisId[][] = [];
|
|
46
|
-
list.forEach((key) => {
|
|
47
|
-
nextResult.push(...result.map((resultItem) => [...resultItem, key]));
|
|
48
|
-
});
|
|
49
|
-
result = nextResult;
|
|
50
|
-
});
|
|
51
|
-
return result;
|
|
52
|
-
}
|
|
5
|
+
export type { AxesVault } from "../pframe_utils/axes";
|
|
6
|
+
export { enrichCompatible, getAvailableWithLinkersAxes } from "../pframe_utils/axes";
|
|
53
7
|
|
|
54
8
|
export function isHiddenFromGraphColumn(column: PColumnSpec): boolean {
|
|
55
9
|
return !!readAnnotationJson(column, Annotation.HideDataFromGraphs);
|
|
@@ -59,123 +13,6 @@ export function isHiddenFromUIColumn(column: PColumnSpec): boolean {
|
|
|
59
13
|
return !!readAnnotationJson(column, Annotation.HideDataFromUi);
|
|
60
14
|
}
|
|
61
15
|
|
|
62
|
-
export type AxesVault = Map<CanonicalizedJson<AxisId>, AxisSpecNormalized>;
|
|
63
|
-
|
|
64
|
-
export function getAvailableWithLinkersAxes(
|
|
65
|
-
linkerColumns: PColumn<PColumnDataUniversal>[],
|
|
66
|
-
blockAxes: AxesVault,
|
|
67
|
-
): AxesVault {
|
|
68
|
-
const linkerMap = LinkerMap.fromColumns(linkerColumns.map(getColumnIdAndSpec));
|
|
69
|
-
const availableAxes = linkerMap.getReachableByLinkersAxesFromAxesNormalized(
|
|
70
|
-
[...blockAxes.values()],
|
|
71
|
-
(linkerKeyId, sourceAxisId) => matchAxisId(sourceAxisId, linkerKeyId),
|
|
72
|
-
);
|
|
73
|
-
|
|
74
|
-
return new Map(
|
|
75
|
-
availableAxes.map((axisSpec) => {
|
|
76
|
-
const id = getAxisId(axisSpec);
|
|
77
|
-
return [canonicalizeJson(id), axisSpec];
|
|
78
|
-
}),
|
|
79
|
-
);
|
|
80
|
-
}
|
|
81
|
-
/** Add columns with fully compatible axes created from partial compatible ones */
|
|
82
|
-
export function enrichCompatible<T extends Omit<PColumn<PColumnDataUniversal>, "data">>(
|
|
83
|
-
blockAxes: AxesVault,
|
|
84
|
-
columns: T[],
|
|
85
|
-
): T[] {
|
|
86
|
-
return columns.flatMap((column) => getAdditionalColumnsForColumn(blockAxes, column));
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
function getAdditionalColumnsForColumn<T extends Omit<PColumn<PColumnDataUniversal>, "data">>(
|
|
90
|
-
blockAxes: AxesVault,
|
|
91
|
-
column: T,
|
|
92
|
-
): T[] {
|
|
93
|
-
const columnAxesIds = column.spec.axesSpec.map(getAxisId);
|
|
94
|
-
|
|
95
|
-
if (columnAxesIds.every((id) => blockAxes.has(canonicalizeJson(id)))) {
|
|
96
|
-
return [column]; // the column is compatible with its own domains without modifications
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
// options with different possible domains for every axis of secondary column
|
|
100
|
-
const secondaryIdsOptions = columnAxesIds.map((id) => {
|
|
101
|
-
const result = [];
|
|
102
|
-
for (const [_, mainId] of blockAxes) {
|
|
103
|
-
if (matchAxisId(mainId, id) && !matchAxisId(id, mainId)) {
|
|
104
|
-
result.push(mainId);
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
return result;
|
|
108
|
-
});
|
|
109
|
-
// all possible combinations of axes with added domains
|
|
110
|
-
const secondaryIdsVariants = getKeysCombinations(secondaryIdsOptions);
|
|
111
|
-
|
|
112
|
-
// sets of added to column domain fields
|
|
113
|
-
const allAddedDomainValues = new Set<string>();
|
|
114
|
-
const addedNotToAllVariantsDomainValues = new Set<string>();
|
|
115
|
-
const addedByVariantsDomainValues = secondaryIdsVariants.map((idsList) => {
|
|
116
|
-
const addedSet = new Set<string>();
|
|
117
|
-
idsList.map((axisId, idx) => {
|
|
118
|
-
const d1 = column.spec.axesSpec[idx].domain;
|
|
119
|
-
const d2 = axisId.domain;
|
|
120
|
-
Object.entries(d2 ?? {}).forEach(([key, value]) => {
|
|
121
|
-
if (d1?.[key] === undefined) {
|
|
122
|
-
const item = JSON.stringify([key, value]);
|
|
123
|
-
addedSet.add(item);
|
|
124
|
-
allAddedDomainValues.add(item);
|
|
125
|
-
}
|
|
126
|
-
});
|
|
127
|
-
return {
|
|
128
|
-
...axisId,
|
|
129
|
-
annotations: column.spec.axesSpec[idx].annotations,
|
|
130
|
-
};
|
|
131
|
-
});
|
|
132
|
-
return addedSet;
|
|
133
|
-
});
|
|
134
|
-
[...allAddedDomainValues].forEach((addedPart) => {
|
|
135
|
-
if (addedByVariantsDomainValues.some((s) => !s.has(addedPart))) {
|
|
136
|
-
addedNotToAllVariantsDomainValues.add(addedPart);
|
|
137
|
-
}
|
|
138
|
-
});
|
|
139
|
-
|
|
140
|
-
const additionalColumns = secondaryIdsVariants.map((idsList, idx) => {
|
|
141
|
-
const id = colId(
|
|
142
|
-
column.id,
|
|
143
|
-
idsList.map((id) => id.domain),
|
|
144
|
-
);
|
|
145
|
-
|
|
146
|
-
const label = readAnnotation(column.spec, Annotation.Label) ?? "";
|
|
147
|
-
const labelDomainPart = [...addedByVariantsDomainValues[idx]]
|
|
148
|
-
.filter((str) => addedNotToAllVariantsDomainValues.has(str))
|
|
149
|
-
.sort()
|
|
150
|
-
.map((v) => JSON.parse(v)?.[1]) // use in labels only domain values, but sort them by key to save the same order in all column variants
|
|
151
|
-
.join(" / ");
|
|
152
|
-
|
|
153
|
-
const annotations: Annotation = {
|
|
154
|
-
...column.spec.annotations,
|
|
155
|
-
[Annotation.Graph.IsVirtual]: stringifyJson(true),
|
|
156
|
-
};
|
|
157
|
-
if (label || labelDomainPart) {
|
|
158
|
-
annotations[Annotation.Label] =
|
|
159
|
-
label && labelDomainPart ? label + " / " + labelDomainPart : label + labelDomainPart;
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
return {
|
|
163
|
-
...column,
|
|
164
|
-
id: id as PObjectId,
|
|
165
|
-
spec: {
|
|
166
|
-
...column.spec,
|
|
167
|
-
axesSpec: idsList.map((axisId, idx) => ({
|
|
168
|
-
...axisId,
|
|
169
|
-
annotations: column.spec.axesSpec[idx].annotations,
|
|
170
|
-
})),
|
|
171
|
-
annotations,
|
|
172
|
-
},
|
|
173
|
-
};
|
|
174
|
-
});
|
|
175
|
-
|
|
176
|
-
return [column, ...additionalColumns];
|
|
177
|
-
}
|
|
178
|
-
|
|
179
16
|
/**
|
|
180
17
|
The aim of createPFrameForGraphs: to create pframe with block’s columns and all compatible columns from result pool
|
|
181
18
|
(including linker columns and all label columns).
|
|
@@ -21,4 +21,9 @@ export {
|
|
|
21
21
|
createPlDataTableStateV2,
|
|
22
22
|
} from "./state-migration";
|
|
23
23
|
|
|
24
|
-
export {
|
|
24
|
+
export {
|
|
25
|
+
isColumnHidden,
|
|
26
|
+
isColumnOptional,
|
|
27
|
+
createPlDataTableV2,
|
|
28
|
+
createPlDataTableSheet,
|
|
29
|
+
} from "./table";
|
|
@@ -39,6 +39,7 @@ import type { PlDataTableStateV2 } from "./state-migration";
|
|
|
39
39
|
import type { PlDataTableSheet } from "./v5";
|
|
40
40
|
import { getAllLabelColumns, getMatchingLabelColumns } from "./labels";
|
|
41
41
|
import { collectFilterSpecColumns } from "../../filters/traverse";
|
|
42
|
+
import { isEmpty } from "es-toolkit/compat";
|
|
42
43
|
|
|
43
44
|
/** Convert a PTableColumnId to a SpecQueryExpression reference. */
|
|
44
45
|
function columnIdToExpr(col: PTableColumnId): SpecQueryExpression {
|
|
@@ -198,10 +199,8 @@ export function createPlDataTableV2<A, U>(
|
|
|
198
199
|
);
|
|
199
200
|
|
|
200
201
|
// -- Sorting validation --
|
|
201
|
-
const
|
|
202
|
-
|
|
203
|
-
(s) => canonicalizeJson<PTableColumnId>(s.column),
|
|
204
|
-
);
|
|
202
|
+
const userSorting = tableStateNormalized.pTableParams.sorting;
|
|
203
|
+
const sorting = (isEmpty(userSorting) ? ops?.sorting : userSorting) ?? [];
|
|
205
204
|
const firstInvalidSortingColumn = sorting.find(
|
|
206
205
|
(s) => !isValidColumnId(canonicalizeJson<PTableColumnId>(s.column)),
|
|
207
206
|
);
|
package/src/index.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export * from "./block_state_patch";
|
|
2
2
|
export * from "./block_state_util";
|
|
3
|
+
export * from "./plugin_handle";
|
|
3
4
|
export * from "./block_storage";
|
|
4
5
|
export * from "./block_storage_facade";
|
|
5
6
|
export * from "./block_model_legacy";
|
|
@@ -14,7 +15,7 @@ export {
|
|
|
14
15
|
makeDataVersioned,
|
|
15
16
|
} from "./block_migrations";
|
|
16
17
|
export type { LegacyV1State } from "./block_migrations";
|
|
17
|
-
export
|
|
18
|
+
export * from "./plugin_model";
|
|
18
19
|
export * from "./bconfig";
|
|
19
20
|
export * from "./components";
|
|
20
21
|
export * from "./config";
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Axes utilities for PFrame graph operations.
|
|
3
|
+
*
|
|
4
|
+
* Extracted from PFrameForGraphs to break circular dependency
|
|
5
|
+
* between PFrameForGraphs and columns modules.
|
|
6
|
+
*
|
|
7
|
+
* @module pframe_utils/axes
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import type {
|
|
11
|
+
AxisId,
|
|
12
|
+
AxisSpecNormalized,
|
|
13
|
+
CanonicalizedJson,
|
|
14
|
+
PColumn,
|
|
15
|
+
PObjectId,
|
|
16
|
+
} from "@milaboratories/pl-model-common";
|
|
17
|
+
import {
|
|
18
|
+
Annotation,
|
|
19
|
+
canonicalizeJson,
|
|
20
|
+
getAxisId,
|
|
21
|
+
getColumnIdAndSpec,
|
|
22
|
+
LinkerMap,
|
|
23
|
+
matchAxisId,
|
|
24
|
+
readAnnotation,
|
|
25
|
+
stringifyJson,
|
|
26
|
+
} from "@milaboratories/pl-model-common";
|
|
27
|
+
import type { PColumnDataUniversal } from "../render";
|
|
28
|
+
|
|
29
|
+
export type AxesVault = Map<CanonicalizedJson<AxisId>, AxisSpecNormalized>;
|
|
30
|
+
|
|
31
|
+
/** Create id for column copy with added keys in axes domains */
|
|
32
|
+
const colId = (id: PObjectId, domains: (Record<string, string> | undefined)[]) => {
|
|
33
|
+
let wid = id.toString();
|
|
34
|
+
domains?.forEach((domain) => {
|
|
35
|
+
if (domain) {
|
|
36
|
+
for (const [k, v] of Object.entries(domain)) {
|
|
37
|
+
wid += k;
|
|
38
|
+
wid += v;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
return wid;
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
/** All combinations with 1 key from each list */
|
|
46
|
+
function getKeysCombinations(idsLists: AxisId[][]) {
|
|
47
|
+
if (!idsLists.length) {
|
|
48
|
+
return [];
|
|
49
|
+
}
|
|
50
|
+
let result: AxisId[][] = [[]];
|
|
51
|
+
idsLists.forEach((list) => {
|
|
52
|
+
const nextResult: AxisId[][] = [];
|
|
53
|
+
list.forEach((key) => {
|
|
54
|
+
nextResult.push(...result.map((resultItem) => [...resultItem, key]));
|
|
55
|
+
});
|
|
56
|
+
result = nextResult;
|
|
57
|
+
});
|
|
58
|
+
return result;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export function getAvailableWithLinkersAxes(
|
|
62
|
+
linkerColumns: PColumn<PColumnDataUniversal>[],
|
|
63
|
+
blockAxes: AxesVault,
|
|
64
|
+
): AxesVault {
|
|
65
|
+
const linkerMap = LinkerMap.fromColumns(linkerColumns.map(getColumnIdAndSpec));
|
|
66
|
+
const availableAxes = linkerMap.getReachableByLinkersAxesFromAxesNormalized(
|
|
67
|
+
[...blockAxes.values()],
|
|
68
|
+
(linkerKeyId, sourceAxisId) => matchAxisId(sourceAxisId, linkerKeyId),
|
|
69
|
+
);
|
|
70
|
+
|
|
71
|
+
return new Map(
|
|
72
|
+
availableAxes.map((axisSpec) => {
|
|
73
|
+
const id = getAxisId(axisSpec);
|
|
74
|
+
return [canonicalizeJson(id), axisSpec];
|
|
75
|
+
}),
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/** Add columns with fully compatible axes created from partial compatible ones */
|
|
80
|
+
export function enrichCompatible<T extends Omit<PColumn<PColumnDataUniversal>, "data">>(
|
|
81
|
+
blockAxes: AxesVault,
|
|
82
|
+
columns: T[],
|
|
83
|
+
): T[] {
|
|
84
|
+
return columns.flatMap((column) => getAdditionalColumnsForColumn(blockAxes, column));
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function getAdditionalColumnsForColumn<T extends Omit<PColumn<PColumnDataUniversal>, "data">>(
|
|
88
|
+
blockAxes: AxesVault,
|
|
89
|
+
column: T,
|
|
90
|
+
): T[] {
|
|
91
|
+
const columnAxesIds = column.spec.axesSpec.map(getAxisId);
|
|
92
|
+
|
|
93
|
+
if (columnAxesIds.every((id) => blockAxes.has(canonicalizeJson(id)))) {
|
|
94
|
+
return [column]; // the column is compatible with its own domains without modifications
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// options with different possible domains for every axis of secondary column
|
|
98
|
+
const secondaryIdsOptions = columnAxesIds.map((id) => {
|
|
99
|
+
const result = [];
|
|
100
|
+
for (const [_, mainId] of blockAxes) {
|
|
101
|
+
if (matchAxisId(mainId, id) && !matchAxisId(id, mainId)) {
|
|
102
|
+
result.push(mainId);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
return result;
|
|
106
|
+
});
|
|
107
|
+
// all possible combinations of axes with added domains
|
|
108
|
+
const secondaryIdsVariants = getKeysCombinations(secondaryIdsOptions);
|
|
109
|
+
|
|
110
|
+
// sets of added to column domain fields
|
|
111
|
+
const allAddedDomainValues = new Set<string>();
|
|
112
|
+
const addedNotToAllVariantsDomainValues = new Set<string>();
|
|
113
|
+
const addedByVariantsDomainValues = secondaryIdsVariants.map((idsList) => {
|
|
114
|
+
const addedSet = new Set<string>();
|
|
115
|
+
idsList.map((axisId, idx) => {
|
|
116
|
+
const d1 = column.spec.axesSpec[idx].domain;
|
|
117
|
+
const d2 = axisId.domain;
|
|
118
|
+
Object.entries(d2 ?? {}).forEach(([key, value]) => {
|
|
119
|
+
if (d1?.[key] === undefined) {
|
|
120
|
+
const item = JSON.stringify([key, value]);
|
|
121
|
+
addedSet.add(item);
|
|
122
|
+
allAddedDomainValues.add(item);
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
return {
|
|
126
|
+
...axisId,
|
|
127
|
+
annotations: column.spec.axesSpec[idx].annotations,
|
|
128
|
+
};
|
|
129
|
+
});
|
|
130
|
+
return addedSet;
|
|
131
|
+
});
|
|
132
|
+
[...allAddedDomainValues].forEach((addedPart) => {
|
|
133
|
+
if (addedByVariantsDomainValues.some((s) => !s.has(addedPart))) {
|
|
134
|
+
addedNotToAllVariantsDomainValues.add(addedPart);
|
|
135
|
+
}
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
const additionalColumns = secondaryIdsVariants.map((idsList, idx) => {
|
|
139
|
+
const id = colId(
|
|
140
|
+
column.id,
|
|
141
|
+
idsList.map((id) => id.domain),
|
|
142
|
+
);
|
|
143
|
+
|
|
144
|
+
const label = readAnnotation(column.spec, Annotation.Label) ?? "";
|
|
145
|
+
const labelDomainPart = [...addedByVariantsDomainValues[idx]]
|
|
146
|
+
.filter((str) => addedNotToAllVariantsDomainValues.has(str))
|
|
147
|
+
.sort()
|
|
148
|
+
.map((v) => JSON.parse(v)?.[1]) // use in labels only domain values, but sort them by key to save the same order in all column variants
|
|
149
|
+
.join(" / ");
|
|
150
|
+
|
|
151
|
+
const annotations: Annotation = {
|
|
152
|
+
...column.spec.annotations,
|
|
153
|
+
[Annotation.Graph.IsVirtual]: stringifyJson(true),
|
|
154
|
+
};
|
|
155
|
+
if (label || labelDomainPart) {
|
|
156
|
+
annotations[Annotation.Label] =
|
|
157
|
+
label && labelDomainPart ? label + " / " + labelDomainPart : label + labelDomainPart;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
return {
|
|
161
|
+
...column,
|
|
162
|
+
id: id as PObjectId,
|
|
163
|
+
spec: {
|
|
164
|
+
...column.spec,
|
|
165
|
+
axesSpec: idsList.map((axisId, idx) => ({
|
|
166
|
+
...axisId,
|
|
167
|
+
annotations: column.spec.axesSpec[idx].annotations,
|
|
168
|
+
})),
|
|
169
|
+
annotations,
|
|
170
|
+
},
|
|
171
|
+
};
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
return [column, ...additionalColumns];
|
|
175
|
+
}
|
|
@@ -7,8 +7,8 @@ import {
|
|
|
7
7
|
matchAxisId,
|
|
8
8
|
isLabelColumn,
|
|
9
9
|
} from "@milaboratories/pl-model-common";
|
|
10
|
-
import type { AxesVault } from "
|
|
11
|
-
import { enrichCompatible, getAvailableWithLinkersAxes } from "
|
|
10
|
+
import type { AxesVault } from "./axes";
|
|
11
|
+
import { enrichCompatible, getAvailableWithLinkersAxes } from "./axes";
|
|
12
12
|
import type { RenderCtxBase, PColumnDataUniversal } from "../render";
|
|
13
13
|
import { PColumnCollection } from "../render";
|
|
14
14
|
|
package/src/platforma.ts
CHANGED
|
@@ -10,6 +10,7 @@ import type {
|
|
|
10
10
|
import type { SdkInfo } from "./version";
|
|
11
11
|
import type { BlockStatePatch } from "./block_state_patch";
|
|
12
12
|
import type { PluginInstance } from "./block_model";
|
|
13
|
+
import type { PluginHandle, PluginFactoryLike } from "./plugin_handle";
|
|
13
14
|
|
|
14
15
|
/** Defines all methods to interact with the platform environment from within a block UI. @deprecated */
|
|
15
16
|
export interface PlatformaV1<
|
|
@@ -85,6 +86,7 @@ export type BlockModelInfo = {
|
|
|
85
86
|
withStatus: boolean;
|
|
86
87
|
}
|
|
87
88
|
>;
|
|
89
|
+
pluginIds: PluginHandle[];
|
|
88
90
|
};
|
|
89
91
|
|
|
90
92
|
export type PlatformaApiVersion = Platforma["apiVersion"];
|
|
@@ -124,14 +126,27 @@ export type InferBlockStatePatch<Pl extends Platforma> = BlockStatePatch<
|
|
|
124
126
|
|
|
125
127
|
/** Extract plugin IDs as a string literal union from a Platforma type. */
|
|
126
128
|
export type InferPluginNames<Pl> =
|
|
127
|
-
Pl extends PlatformaV3<
|
|
129
|
+
Pl extends PlatformaV3<unknown, unknown, BlockOutputsBase, `/${string}`, infer P>
|
|
130
|
+
? string & keyof P
|
|
131
|
+
: never;
|
|
128
132
|
|
|
129
133
|
/** Extract the Data type for a specific plugin by its ID. */
|
|
130
134
|
export type InferPluginData<Pl, PluginId extends string> =
|
|
131
|
-
Pl extends PlatformaV3<
|
|
135
|
+
Pl extends PlatformaV3<unknown, unknown, BlockOutputsBase, `/${string}`, infer P>
|
|
132
136
|
? PluginId extends keyof P
|
|
133
137
|
? P[PluginId] extends PluginInstance<infer D, any, any>
|
|
134
138
|
? D
|
|
135
139
|
: never
|
|
136
140
|
: never
|
|
137
141
|
: never;
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Map each plugin instance to a type-safe opaque handle branded with normalized phantom.
|
|
145
|
+
* Uses the same brand structure as InferPluginHandle — only data/params/outputs, no config —
|
|
146
|
+
* because PluginInstance doesn't carry Config (it's lost after factory.create()).
|
|
147
|
+
*/
|
|
148
|
+
export type InferPluginHandles<T extends Record<string, unknown>> = {
|
|
149
|
+
readonly [K in keyof T]: T[K] extends PluginInstance<infer Data, infer Params, infer Outputs>
|
|
150
|
+
? PluginHandle<PluginFactoryLike<Data, Params, Outputs>>
|
|
151
|
+
: never;
|
|
152
|
+
};
|