@platforma-sdk/model 1.57.2 → 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/index.cjs +7 -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 +3 -1
- 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 -8
- package/dist/pframe_utils/columns.cjs.map +1 -1
- package/dist/pframe_utils/columns.js +1 -5
- package/dist/pframe_utils/columns.js.map +1 -1
- package/dist/pframe_utils/index.cjs +0 -3
- package/dist/pframe_utils/index.cjs.map +1 -1
- package/dist/pframe_utils/index.js +0 -3
- 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/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
|
@@ -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
|
+
};
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PluginHandle — Opaque branded handle and output key utilities for plugin instances.
|
|
3
|
+
*
|
|
4
|
+
* Extracted into its own module to break circular dependencies:
|
|
5
|
+
* both block_storage.ts and plugin_model.ts can import from here
|
|
6
|
+
* without depending on each other.
|
|
7
|
+
*
|
|
8
|
+
* @module plugin_handle
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import type { Branded } from "@milaboratories/helpers";
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Phantom-only base type for constraining PluginHandle's type parameter.
|
|
15
|
+
*
|
|
16
|
+
* PluginFactory has create() → PluginModel with function properties, making it invariant
|
|
17
|
+
* under strictFunctionTypes. PluginFactoryLike exposes only the covariant `__types` phantom,
|
|
18
|
+
* avoiding the contravariance chain. Handles only need `__types` for type extraction.
|
|
19
|
+
*
|
|
20
|
+
* PluginFactory extends PluginFactoryLike, so every concrete factory satisfies this constraint.
|
|
21
|
+
*/
|
|
22
|
+
export interface PluginFactoryLike<
|
|
23
|
+
Data extends Record<string, unknown> = Record<string, unknown>,
|
|
24
|
+
Params extends undefined | Record<string, unknown> = undefined | Record<string, unknown>,
|
|
25
|
+
Outputs extends Record<string, unknown> = Record<string, unknown>,
|
|
26
|
+
> {
|
|
27
|
+
readonly __types?: {
|
|
28
|
+
data: Data;
|
|
29
|
+
params: Params;
|
|
30
|
+
outputs: Outputs;
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** Extract the Data type from a PluginFactoryLike phantom. */
|
|
35
|
+
export type InferFactoryData<F extends PluginFactoryLike> = NonNullable<
|
|
36
|
+
F extends PluginFactoryLike<infer D, any, any> ? D : Record<string, unknown>
|
|
37
|
+
>;
|
|
38
|
+
|
|
39
|
+
/** Extract the Params type from a PluginFactoryLike phantom. */
|
|
40
|
+
export type InferFactoryParams<F extends PluginFactoryLike> = NonNullable<
|
|
41
|
+
F extends PluginFactoryLike<any, infer P, any> ? P : undefined
|
|
42
|
+
>;
|
|
43
|
+
|
|
44
|
+
/** Extract the Outputs type from a PluginFactoryLike phantom. */
|
|
45
|
+
export type InferFactoryOutputs<F extends PluginFactoryLike> = NonNullable<
|
|
46
|
+
F extends PluginFactoryLike<any, any, infer O> ? O : Record<string, unknown>
|
|
47
|
+
>;
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Derive a typed PluginHandle from a PluginFactory type.
|
|
51
|
+
* Normalizes the brand to only data/params/outputs (strips config) so handles
|
|
52
|
+
* from InferPluginHandles match handles from InferPluginHandle.
|
|
53
|
+
*/
|
|
54
|
+
export type InferPluginHandle<F extends PluginFactoryLike> = NonNullable<
|
|
55
|
+
F extends PluginFactoryLike<infer Data, infer Params, infer Outputs>
|
|
56
|
+
? PluginHandle<PluginFactoryLike<Data, Params, Outputs>>
|
|
57
|
+
: PluginHandle
|
|
58
|
+
>;
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Opaque handle for a plugin instance. Runtime value is the plugin instance ID string.
|
|
62
|
+
* Branded with factory phantom `F` for type-safe data/outputs extraction.
|
|
63
|
+
* Constrained with PluginFactoryLike (not PluginFactory) to avoid variance issues.
|
|
64
|
+
*/
|
|
65
|
+
export type PluginHandle<F extends PluginFactoryLike = PluginFactoryLike> = Branded<string, F>;
|
|
66
|
+
|
|
67
|
+
const PLUGIN_OUTPUT_PREFIX = "plugin-output#";
|
|
68
|
+
|
|
69
|
+
/** Construct the output key for a plugin output in the block outputs map. */
|
|
70
|
+
export function pluginOutputKey<F extends PluginFactoryLike>(
|
|
71
|
+
handle: PluginHandle<F>,
|
|
72
|
+
outputKey: string,
|
|
73
|
+
): string {
|
|
74
|
+
return `${PLUGIN_OUTPUT_PREFIX}${handle}#${outputKey}`;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/** Check whether an output key belongs to a plugin (vs block-level output). */
|
|
78
|
+
export function isPluginOutputKey(key: string): boolean {
|
|
79
|
+
return key.startsWith(PLUGIN_OUTPUT_PREFIX);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/** Get the prefix used for all outputs of a specific plugin instance. */
|
|
83
|
+
export function pluginOutputPrefix<F extends PluginFactoryLike>(handle: PluginHandle<F>): string {
|
|
84
|
+
return pluginOutputKey(handle, "");
|
|
85
|
+
}
|
package/src/plugin_model.ts
CHANGED
|
@@ -7,37 +7,52 @@
|
|
|
7
7
|
* @module plugin_model
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
+
import type { BlockCodeKnownFeatureFlags } from "@milaboratories/pl-model-common";
|
|
10
11
|
import type { DataModel } from "./block_migrations";
|
|
11
12
|
import type { PluginName } from "./block_storage";
|
|
13
|
+
import type { PluginFactoryLike } from "./plugin_handle";
|
|
12
14
|
import type { PluginRenderCtx } from "./render";
|
|
13
15
|
|
|
14
16
|
/** Symbol for internal builder creation method */
|
|
15
17
|
const FROM_BUILDER = Symbol("fromBuilder");
|
|
16
18
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
19
|
+
export type PluginData = Record<string, unknown>;
|
|
20
|
+
export type PluginParams = undefined | Record<string, unknown>;
|
|
21
|
+
export type PluginOutputs = Record<string, unknown>;
|
|
22
|
+
export type PluginConfig = undefined | Record<string, unknown>;
|
|
20
23
|
|
|
21
24
|
/**
|
|
22
25
|
* Configured plugin instance returned by PluginModelFactory.create().
|
|
23
26
|
* Contains the plugin's name, data model, and output definitions.
|
|
24
27
|
*/
|
|
25
|
-
export class PluginModel<
|
|
28
|
+
export class PluginModel<
|
|
29
|
+
Data extends PluginData = PluginData,
|
|
30
|
+
Params extends PluginParams = undefined,
|
|
31
|
+
Outputs extends PluginOutputs = PluginOutputs,
|
|
32
|
+
> {
|
|
26
33
|
/** Globally unique plugin name */
|
|
27
34
|
readonly name: PluginName;
|
|
28
35
|
/** Data model instance for this plugin */
|
|
29
36
|
readonly dataModel: DataModel<Data>;
|
|
30
37
|
/** Output definitions - functions that compute outputs from plugin context */
|
|
31
|
-
readonly outputs: {
|
|
38
|
+
readonly outputs: {
|
|
39
|
+
[K in keyof Outputs]: (ctx: PluginRenderCtx<PluginFactoryLike<Data, Params>>) => Outputs[K];
|
|
40
|
+
};
|
|
41
|
+
/** Feature flags declared by this plugin */
|
|
42
|
+
readonly featureFlags?: BlockCodeKnownFeatureFlags;
|
|
32
43
|
|
|
33
|
-
private constructor(
|
|
44
|
+
private constructor(options: {
|
|
34
45
|
name: PluginName;
|
|
35
46
|
dataModel: DataModel<Data>;
|
|
36
|
-
outputs: {
|
|
47
|
+
outputs: {
|
|
48
|
+
[K in keyof Outputs]: (ctx: PluginRenderCtx<PluginFactoryLike<Data, Params>>) => Outputs[K];
|
|
49
|
+
};
|
|
50
|
+
featureFlags?: BlockCodeKnownFeatureFlags;
|
|
37
51
|
}) {
|
|
38
|
-
this.name =
|
|
39
|
-
this.dataModel =
|
|
40
|
-
this.outputs =
|
|
52
|
+
this.name = options.name;
|
|
53
|
+
this.dataModel = options.dataModel;
|
|
54
|
+
this.outputs = options.outputs;
|
|
55
|
+
this.featureFlags = options.featureFlags;
|
|
41
56
|
}
|
|
42
57
|
|
|
43
58
|
/**
|
|
@@ -45,12 +60,19 @@ export class PluginModel<Data = unknown, Params = undefined, Outputs = {}> {
|
|
|
45
60
|
* Uses Symbol key to prevent external access.
|
|
46
61
|
* @internal
|
|
47
62
|
*/
|
|
48
|
-
static [FROM_BUILDER]<
|
|
63
|
+
static [FROM_BUILDER]<
|
|
64
|
+
Data extends PluginData,
|
|
65
|
+
Params extends PluginParams,
|
|
66
|
+
Outputs extends PluginOutputs,
|
|
67
|
+
>(options: {
|
|
49
68
|
name: PluginName;
|
|
50
|
-
dataModel: DataModel<
|
|
51
|
-
outputs: {
|
|
52
|
-
|
|
53
|
-
|
|
69
|
+
dataModel: DataModel<Data>;
|
|
70
|
+
outputs: {
|
|
71
|
+
[K in keyof Outputs]: (ctx: PluginRenderCtx<PluginFactoryLike<Data, Params>>) => Outputs[K];
|
|
72
|
+
};
|
|
73
|
+
featureFlags?: BlockCodeKnownFeatureFlags;
|
|
74
|
+
}): PluginModel<Data, Params, Outputs> {
|
|
75
|
+
return new PluginModel<Data, Params, Outputs>(options);
|
|
54
76
|
}
|
|
55
77
|
|
|
56
78
|
/**
|
|
@@ -63,40 +85,66 @@ export class PluginModel<Data = unknown, Params = undefined, Outputs = {}> {
|
|
|
63
85
|
* @example
|
|
64
86
|
* const dataModelChain = new DataModelBuilder().from<MyData>(DATA_MODEL_DEFAULT_VERSION);
|
|
65
87
|
*
|
|
66
|
-
* const myPlugin = PluginModel.define
|
|
88
|
+
* const myPlugin = PluginModel.define({
|
|
67
89
|
* name: 'myPlugin' as PluginName,
|
|
68
90
|
* data: (cfg) => dataModelChain.init(() => ({ value: cfg.defaultValue })),
|
|
69
91
|
* })
|
|
70
92
|
* .output('computed', (ctx) => ctx.data.value * ctx.params.multiplier)
|
|
71
93
|
* .build();
|
|
72
94
|
*/
|
|
73
|
-
static define<
|
|
95
|
+
static define<
|
|
96
|
+
Data extends PluginData,
|
|
97
|
+
Params extends PluginParams,
|
|
98
|
+
Config extends PluginConfig,
|
|
99
|
+
>(options: {
|
|
74
100
|
name: PluginName;
|
|
75
101
|
data: (config?: Config) => DataModel<Data>;
|
|
76
|
-
|
|
77
|
-
|
|
102
|
+
featureFlags?: BlockCodeKnownFeatureFlags;
|
|
103
|
+
}): PluginModelBuilder<Data, Params, {}, Config> {
|
|
104
|
+
return PluginModelBuilder[FROM_BUILDER](options);
|
|
78
105
|
}
|
|
79
106
|
}
|
|
80
107
|
|
|
81
|
-
/**
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
108
|
+
/** Plugin factory returned by PluginModelBuilder.build(). */
|
|
109
|
+
export interface PluginFactory<
|
|
110
|
+
Data extends PluginData = PluginData,
|
|
111
|
+
Params extends PluginParams = undefined,
|
|
112
|
+
Outputs extends PluginOutputs = PluginOutputs,
|
|
113
|
+
Config extends PluginConfig = undefined,
|
|
114
|
+
> extends PluginFactoryLike {
|
|
115
|
+
create(config?: Config): PluginModel<Data, Params, Outputs>;
|
|
116
|
+
/**
|
|
117
|
+
* @internal Phantom field for structural type extraction.
|
|
118
|
+
* Enables InferFactoryData/InferFactoryOutputs to work via PluginFactoryLike.
|
|
119
|
+
*/
|
|
120
|
+
readonly __types?: { data: Data; params: Params; outputs: Outputs; config: Config };
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
class PluginModelFactory<
|
|
124
|
+
Data extends PluginData = PluginData,
|
|
125
|
+
Params extends PluginParams = undefined,
|
|
126
|
+
Outputs extends PluginOutputs = PluginOutputs,
|
|
127
|
+
Config extends PluginConfig = undefined,
|
|
128
|
+
> implements PluginFactory<Data, Params, Outputs, Config> {
|
|
86
129
|
private readonly name: PluginName;
|
|
87
130
|
private readonly data: (config?: Config) => DataModel<Data>;
|
|
88
131
|
private readonly outputs: {
|
|
89
|
-
[K in keyof Outputs]: (ctx: PluginRenderCtx<Data, Params
|
|
132
|
+
[K in keyof Outputs]: (ctx: PluginRenderCtx<PluginFactoryLike<Data, Params>>) => Outputs[K];
|
|
90
133
|
};
|
|
134
|
+
private readonly featureFlags?: BlockCodeKnownFeatureFlags;
|
|
91
135
|
|
|
92
|
-
constructor(
|
|
136
|
+
constructor(options: {
|
|
93
137
|
name: PluginName;
|
|
94
138
|
data: (config?: Config) => DataModel<Data>;
|
|
95
|
-
outputs: {
|
|
139
|
+
outputs: {
|
|
140
|
+
[K in keyof Outputs]: (ctx: PluginRenderCtx<PluginFactoryLike<Data, Params>>) => Outputs[K];
|
|
141
|
+
};
|
|
142
|
+
featureFlags?: BlockCodeKnownFeatureFlags;
|
|
96
143
|
}) {
|
|
97
|
-
this.name =
|
|
98
|
-
this.data =
|
|
99
|
-
this.outputs =
|
|
144
|
+
this.name = options.name;
|
|
145
|
+
this.data = options.data;
|
|
146
|
+
this.outputs = options.outputs;
|
|
147
|
+
this.featureFlags = options.featureFlags;
|
|
100
148
|
}
|
|
101
149
|
|
|
102
150
|
/** Create a configured PluginModel instance */
|
|
@@ -105,14 +153,11 @@ class PluginModelFactory<Data, Params, Config, Outputs> {
|
|
|
105
153
|
name: this.name,
|
|
106
154
|
dataModel: this.data(config),
|
|
107
155
|
outputs: this.outputs,
|
|
156
|
+
featureFlags: this.featureFlags,
|
|
108
157
|
});
|
|
109
158
|
}
|
|
110
159
|
}
|
|
111
160
|
|
|
112
|
-
// =============================================================================
|
|
113
|
-
// Plugin Model Builder
|
|
114
|
-
// =============================================================================
|
|
115
|
-
|
|
116
161
|
/**
|
|
117
162
|
* Builder for creating PluginType with type-safe output definitions.
|
|
118
163
|
*
|
|
@@ -136,27 +181,34 @@ class PluginModelFactory<Data, Params, Config, Outputs> {
|
|
|
136
181
|
* .build();
|
|
137
182
|
*/
|
|
138
183
|
class PluginModelBuilder<
|
|
139
|
-
Data,
|
|
140
|
-
Params = undefined,
|
|
141
|
-
|
|
142
|
-
|
|
184
|
+
Data extends PluginData = PluginData,
|
|
185
|
+
Params extends PluginParams = undefined,
|
|
186
|
+
Outputs extends PluginOutputs = PluginOutputs,
|
|
187
|
+
Config extends PluginConfig = undefined,
|
|
143
188
|
> {
|
|
144
189
|
private readonly name: PluginName;
|
|
145
190
|
private readonly data: (config?: Config) => DataModel<Data>;
|
|
146
191
|
private readonly outputs: {
|
|
147
|
-
[K in keyof Outputs]: (ctx: PluginRenderCtx<Data, Params
|
|
192
|
+
[K in keyof Outputs]: (ctx: PluginRenderCtx<PluginFactoryLike<Data, Params>>) => Outputs[K];
|
|
148
193
|
};
|
|
194
|
+
private readonly featureFlags?: BlockCodeKnownFeatureFlags;
|
|
149
195
|
|
|
150
|
-
private constructor(
|
|
196
|
+
private constructor(options: {
|
|
151
197
|
name: PluginName;
|
|
152
198
|
data: (config?: Config) => DataModel<Data>;
|
|
153
|
-
outputs?: {
|
|
199
|
+
outputs?: {
|
|
200
|
+
[K in keyof Outputs]: (ctx: PluginRenderCtx<PluginFactoryLike<Data, Params>>) => Outputs[K];
|
|
201
|
+
};
|
|
202
|
+
featureFlags?: BlockCodeKnownFeatureFlags;
|
|
154
203
|
}) {
|
|
155
|
-
this.name =
|
|
156
|
-
this.data =
|
|
204
|
+
this.name = options.name;
|
|
205
|
+
this.data = options.data;
|
|
157
206
|
this.outputs =
|
|
158
|
-
|
|
159
|
-
({} as {
|
|
207
|
+
options.outputs ??
|
|
208
|
+
({} as {
|
|
209
|
+
[K in keyof Outputs]: (ctx: PluginRenderCtx<PluginFactoryLike<Data, Params>>) => Outputs[K];
|
|
210
|
+
});
|
|
211
|
+
this.featureFlags = options.featureFlags;
|
|
160
212
|
}
|
|
161
213
|
|
|
162
214
|
/**
|
|
@@ -164,12 +216,20 @@ class PluginModelBuilder<
|
|
|
164
216
|
* Uses Symbol key to prevent external access.
|
|
165
217
|
* @internal
|
|
166
218
|
*/
|
|
167
|
-
static [FROM_BUILDER]<
|
|
219
|
+
static [FROM_BUILDER]<
|
|
220
|
+
Data extends PluginData,
|
|
221
|
+
Params extends PluginParams,
|
|
222
|
+
Outputs extends PluginOutputs,
|
|
223
|
+
Config extends PluginConfig,
|
|
224
|
+
>(options: {
|
|
168
225
|
name: PluginName;
|
|
169
|
-
data: (config?:
|
|
170
|
-
outputs?: {
|
|
171
|
-
|
|
172
|
-
|
|
226
|
+
data: (config?: Config) => DataModel<Data>;
|
|
227
|
+
outputs?: {
|
|
228
|
+
[K in keyof Outputs]: (ctx: PluginRenderCtx<PluginFactoryLike<Data, Params>>) => Outputs[K];
|
|
229
|
+
};
|
|
230
|
+
featureFlags?: BlockCodeKnownFeatureFlags;
|
|
231
|
+
}): PluginModelBuilder<Data, Params, Outputs, Config> {
|
|
232
|
+
return new PluginModelBuilder(options);
|
|
173
233
|
}
|
|
174
234
|
|
|
175
235
|
/**
|
|
@@ -185,17 +245,18 @@ class PluginModelBuilder<
|
|
|
185
245
|
*/
|
|
186
246
|
output<const Key extends string, T>(
|
|
187
247
|
key: Key,
|
|
188
|
-
fn: (ctx: PluginRenderCtx<Data, Params
|
|
189
|
-
): PluginModelBuilder<Data, Params,
|
|
190
|
-
return new PluginModelBuilder<Data, Params,
|
|
248
|
+
fn: (ctx: PluginRenderCtx<PluginFactoryLike<Data, Params>>) => T,
|
|
249
|
+
): PluginModelBuilder<Data, Params, Outputs & { [K in Key]: T }, Config> {
|
|
250
|
+
return new PluginModelBuilder<Data, Params, Outputs & { [K in Key]: T }, Config>({
|
|
191
251
|
name: this.name,
|
|
192
252
|
data: this.data,
|
|
253
|
+
featureFlags: this.featureFlags,
|
|
193
254
|
outputs: {
|
|
194
255
|
...this.outputs,
|
|
195
256
|
[key]: fn,
|
|
196
257
|
} as {
|
|
197
258
|
[K in keyof (Outputs & { [P in Key]: T })]: (
|
|
198
|
-
ctx: PluginRenderCtx<Data, Params
|
|
259
|
+
ctx: PluginRenderCtx<PluginFactoryLike<Data, Params>>,
|
|
199
260
|
) => (Outputs & { [P in Key]: T })[K];
|
|
200
261
|
},
|
|
201
262
|
});
|
|
@@ -214,11 +275,12 @@ class PluginModelBuilder<
|
|
|
214
275
|
* // Later, call create() with config to get a configured instance:
|
|
215
276
|
* const configured = myPlugin.create({ defaultValue: 'test' });
|
|
216
277
|
*/
|
|
217
|
-
build():
|
|
218
|
-
return new PluginModelFactory<Data, Params,
|
|
278
|
+
build(): PluginFactory<Data, Params, Outputs, Config> {
|
|
279
|
+
return new PluginModelFactory<Data, Params, Outputs, Config>({
|
|
219
280
|
name: this.name,
|
|
220
281
|
data: this.data,
|
|
221
282
|
outputs: this.outputs,
|
|
283
|
+
featureFlags: this.featureFlags,
|
|
222
284
|
});
|
|
223
285
|
}
|
|
224
286
|
}
|
package/src/render/api.ts
CHANGED
|
@@ -50,6 +50,12 @@ import canonicalize from "canonicalize";
|
|
|
50
50
|
import type { Optional } from "utility-types";
|
|
51
51
|
import { getCfgRenderCtx } from "../internal";
|
|
52
52
|
import { getPluginData } from "../block_storage";
|
|
53
|
+
import type {
|
|
54
|
+
PluginHandle,
|
|
55
|
+
PluginFactoryLike,
|
|
56
|
+
InferFactoryData,
|
|
57
|
+
InferFactoryParams,
|
|
58
|
+
} from "../plugin_handle";
|
|
53
59
|
import { TreeNodeAccessor, ifDef } from "./accessor";
|
|
54
60
|
import type { FutureRef } from "./future";
|
|
55
61
|
import type { AccessorHandle, GlobalCfgRenderCtx } from "./internal";
|
|
@@ -756,40 +762,44 @@ export class RenderCtxLegacy<Args = unknown, UiState = unknown> extends RenderCt
|
|
|
756
762
|
/**
|
|
757
763
|
* Render context for plugin output functions.
|
|
758
764
|
* Reads plugin data from blockStorage and derives params from pre-wrapped input callbacks.
|
|
765
|
+
*
|
|
766
|
+
* Parameterized on the factory-like phantom F so that getPluginData returns
|
|
767
|
+
* InferFactoryData<F> directly — no casts needed for the data getter.
|
|
768
|
+
*
|
|
769
|
+
* @typeParam F - PluginFactoryLike phantom carrying data/params/outputs types
|
|
759
770
|
*/
|
|
760
|
-
export class PluginRenderCtx<
|
|
771
|
+
export class PluginRenderCtx<F extends PluginFactoryLike = PluginFactoryLike> {
|
|
761
772
|
private readonly ctx: GlobalCfgRenderCtx;
|
|
762
|
-
private readonly
|
|
773
|
+
private readonly handle: PluginHandle<F>;
|
|
763
774
|
private readonly wrappedInputs: Record<string, () => unknown>;
|
|
764
775
|
|
|
765
|
-
constructor(
|
|
776
|
+
constructor(handle: PluginHandle<F>, wrappedInputs: Record<string, () => unknown>) {
|
|
766
777
|
this.ctx = getCfgRenderCtx();
|
|
767
|
-
this.
|
|
778
|
+
this.handle = handle;
|
|
768
779
|
this.wrappedInputs = wrappedInputs;
|
|
769
780
|
}
|
|
770
781
|
|
|
771
|
-
private dataCache?: { v:
|
|
782
|
+
private dataCache?: { v: InferFactoryData<F> };
|
|
772
783
|
|
|
773
784
|
/** Plugin's persistent data from blockStorage.__plugins.{pluginId}.__data */
|
|
774
|
-
public get data():
|
|
785
|
+
public get data(): InferFactoryData<F> {
|
|
775
786
|
if (this.dataCache === undefined) {
|
|
776
787
|
const raw = this.ctx.blockStorage();
|
|
777
|
-
|
|
778
|
-
this.dataCache = { v: pluginData };
|
|
788
|
+
this.dataCache = { v: getPluginData(parseJson(raw), this.handle) };
|
|
779
789
|
}
|
|
780
790
|
return this.dataCache.v;
|
|
781
791
|
}
|
|
782
792
|
|
|
783
|
-
private paramsCache?: { v:
|
|
793
|
+
private paramsCache?: { v: InferFactoryParams<F> };
|
|
784
794
|
|
|
785
795
|
/** Params derived from block context via pre-wrapped input callbacks */
|
|
786
|
-
public get params():
|
|
796
|
+
public get params(): InferFactoryParams<F> {
|
|
787
797
|
if (this.paramsCache === undefined) {
|
|
788
798
|
const result: Record<string, unknown> = {};
|
|
789
799
|
for (const [key, fn] of Object.entries(this.wrappedInputs)) {
|
|
790
800
|
result[key] = fn();
|
|
791
801
|
}
|
|
792
|
-
this.paramsCache = { v: result as
|
|
802
|
+
this.paramsCache = { v: result as InferFactoryParams<F> };
|
|
793
803
|
}
|
|
794
804
|
return this.paramsCache.v;
|
|
795
805
|
}
|