@platforma-sdk/model 1.54.13 → 1.55.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/bconfig/normalization.cjs +8 -1
- package/dist/bconfig/normalization.cjs.map +1 -1
- package/dist/bconfig/normalization.d.ts.map +1 -1
- package/dist/bconfig/normalization.js +8 -1
- package/dist/bconfig/normalization.js.map +1 -1
- package/dist/block_api_v3.d.ts +2 -2
- package/dist/block_api_v3.d.ts.map +1 -1
- package/dist/block_migrations.cjs +246 -214
- package/dist/block_migrations.cjs.map +1 -1
- package/dist/block_migrations.d.ts +180 -158
- package/dist/block_migrations.d.ts.map +1 -1
- package/dist/block_migrations.js +247 -214
- package/dist/block_migrations.js.map +1 -1
- package/dist/block_model.cjs +85 -35
- package/dist/block_model.cjs.map +1 -1
- package/dist/block_model.d.ts +66 -38
- package/dist/block_model.d.ts.map +1 -1
- package/dist/block_model.js +86 -36
- package/dist/block_model.js.map +1 -1
- package/dist/{builder.cjs → block_model_legacy.cjs} +2 -2
- package/dist/block_model_legacy.cjs.map +1 -0
- package/dist/{builder.d.ts → block_model_legacy.d.ts} +1 -1
- package/dist/block_model_legacy.d.ts.map +1 -0
- package/dist/{builder.js → block_model_legacy.js} +2 -2
- package/dist/block_model_legacy.js.map +1 -0
- package/dist/block_state_patch.d.ts +11 -1
- package/dist/block_state_patch.d.ts.map +1 -1
- package/dist/block_storage.cjs +126 -109
- package/dist/block_storage.cjs.map +1 -1
- package/dist/block_storage.d.ts +109 -112
- package/dist/block_storage.d.ts.map +1 -1
- package/dist/block_storage.js +126 -101
- package/dist/block_storage.js.map +1 -1
- package/dist/block_storage_callbacks.cjs +227 -0
- package/dist/block_storage_callbacks.cjs.map +1 -0
- package/dist/block_storage_callbacks.d.ts +113 -0
- package/dist/block_storage_callbacks.d.ts.map +1 -0
- package/dist/block_storage_callbacks.js +218 -0
- package/dist/block_storage_callbacks.js.map +1 -0
- package/dist/block_storage_facade.cjs +104 -0
- package/dist/block_storage_facade.cjs.map +1 -0
- package/dist/block_storage_facade.d.ts +168 -0
- package/dist/block_storage_facade.d.ts.map +1 -0
- package/dist/block_storage_facade.js +99 -0
- package/dist/block_storage_facade.js.map +1 -0
- package/dist/index.cjs +13 -14
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +8 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -4
- package/dist/index.js.map +1 -1
- package/dist/package.json.cjs +1 -1
- package/dist/package.json.js +1 -1
- package/dist/platforma.d.ts +11 -4
- package/dist/platforma.d.ts.map +1 -1
- package/dist/plugin_model.cjs +171 -0
- package/dist/plugin_model.cjs.map +1 -0
- package/dist/plugin_model.d.ts +162 -0
- package/dist/plugin_model.d.ts.map +1 -0
- package/dist/plugin_model.js +169 -0
- package/dist/plugin_model.js.map +1 -0
- package/dist/render/api.cjs +20 -21
- package/dist/render/api.cjs.map +1 -1
- package/dist/render/api.d.ts +8 -8
- package/dist/render/api.d.ts.map +1 -1
- package/dist/render/api.js +20 -21
- package/dist/render/api.js.map +1 -1
- package/dist/render/internal.cjs.map +1 -1
- package/dist/render/internal.d.ts +1 -1
- package/dist/render/internal.d.ts.map +1 -1
- package/dist/render/internal.js.map +1 -1
- package/dist/version.cjs +4 -0
- package/dist/version.cjs.map +1 -1
- package/dist/version.d.ts +4 -0
- package/dist/version.d.ts.map +1 -1
- package/dist/version.js +4 -1
- package/dist/version.js.map +1 -1
- package/package.json +5 -5
- package/src/bconfig/normalization.ts +8 -1
- package/src/block_api_v3.ts +2 -2
- package/src/block_migrations.test.ts +141 -171
- package/src/block_migrations.ts +300 -285
- package/src/block_model.ts +205 -95
- package/src/{builder.ts → block_model_legacy.ts} +1 -1
- package/src/block_state_patch.ts +13 -1
- package/src/block_storage.test.ts +283 -95
- package/src/block_storage.ts +199 -188
- package/src/block_storage_callbacks.ts +326 -0
- package/src/block_storage_facade.ts +199 -0
- package/src/index.ts +7 -3
- package/src/platforma.ts +26 -7
- package/src/plugin_model.test.ts +168 -0
- package/src/plugin_model.ts +242 -0
- package/src/render/api.ts +26 -24
- package/src/render/internal.ts +1 -1
- package/src/typing.test.ts +1 -1
- package/src/version.ts +8 -0
- package/dist/block_storage_vm.cjs +0 -262
- package/dist/block_storage_vm.cjs.map +0 -1
- package/dist/block_storage_vm.d.ts +0 -59
- package/dist/block_storage_vm.d.ts.map +0 -1
- package/dist/block_storage_vm.js +0 -258
- package/dist/block_storage_vm.js.map +0 -1
- package/dist/branding.d.ts +0 -7
- package/dist/branding.d.ts.map +0 -1
- package/dist/builder.cjs.map +0 -1
- package/dist/builder.d.ts.map +0 -1
- package/dist/builder.js.map +0 -1
- package/dist/sdk_info.cjs +0 -10
- package/dist/sdk_info.cjs.map +0 -1
- package/dist/sdk_info.d.ts +0 -5
- package/dist/sdk_info.d.ts.map +0 -1
- package/dist/sdk_info.js +0 -8
- package/dist/sdk_info.js.map +0 -1
- package/dist/unionize.d.ts +0 -12
- package/dist/unionize.d.ts.map +0 -1
- package/src/block_storage_vm.ts +0 -346
- package/src/branding.ts +0 -4
- package/src/sdk_info.ts +0 -9
- package/src/unionize.ts +0 -12
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { PluginModel } from "./plugin_model";
|
|
3
|
+
import type { PluginRenderCtx } from "./plugin_model";
|
|
4
|
+
import { DataModelBuilder } from "./block_migrations";
|
|
5
|
+
import { DATA_MODEL_DEFAULT_VERSION, type PluginName } from "./block_storage";
|
|
6
|
+
import type { ResultPool } from "./render";
|
|
7
|
+
|
|
8
|
+
// =============================================================================
|
|
9
|
+
// Test Fixtures
|
|
10
|
+
// =============================================================================
|
|
11
|
+
|
|
12
|
+
type Data = { count: number; label: string };
|
|
13
|
+
|
|
14
|
+
const dataModelChain = new DataModelBuilder().from<Data>(DATA_MODEL_DEFAULT_VERSION);
|
|
15
|
+
|
|
16
|
+
// Mock ResultPool for testing
|
|
17
|
+
const mockResultPool = {} as ResultPool;
|
|
18
|
+
|
|
19
|
+
// =============================================================================
|
|
20
|
+
// Tests
|
|
21
|
+
// =============================================================================
|
|
22
|
+
|
|
23
|
+
describe("PluginModel", () => {
|
|
24
|
+
it("creates PluginModel with required fields", () => {
|
|
25
|
+
const factory = PluginModel.define<Data>({
|
|
26
|
+
name: "testPlugin" as PluginName,
|
|
27
|
+
data: () => dataModelChain.init(() => ({ count: 0, label: "" })),
|
|
28
|
+
}).build();
|
|
29
|
+
|
|
30
|
+
const plugin = factory.create();
|
|
31
|
+
expect(plugin.name).toBe("testPlugin");
|
|
32
|
+
expect(plugin.outputs).toEqual({});
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it("creates PluginModel when calling create() with config", () => {
|
|
36
|
+
type Config = { initialCount: number };
|
|
37
|
+
|
|
38
|
+
const factory = PluginModel.define<Data, undefined, Config>({
|
|
39
|
+
name: "factoryPlugin" as PluginName,
|
|
40
|
+
data: (cfg) => dataModelChain.init(() => ({ count: cfg.initialCount, label: "initialized" })),
|
|
41
|
+
}).build();
|
|
42
|
+
|
|
43
|
+
const plugin = factory.create({ initialCount: 100 });
|
|
44
|
+
expect(plugin.name).toBe("factoryPlugin");
|
|
45
|
+
expect(plugin.dataModel.initialData()).toEqual({ count: 100, label: "initialized" });
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it("adds single output", () => {
|
|
49
|
+
const factory = PluginModel.define<Data, { multiplier: number }>({
|
|
50
|
+
name: "singleOutput" as PluginName,
|
|
51
|
+
data: () => dataModelChain.init(() => ({ count: 0, label: "" })),
|
|
52
|
+
})
|
|
53
|
+
.output("doubled", (ctx) => ctx.data.count * ctx.params.multiplier)
|
|
54
|
+
.build();
|
|
55
|
+
|
|
56
|
+
const plugin = factory.create();
|
|
57
|
+
expect(Object.keys(plugin.outputs)).toEqual(["doubled"]);
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
it("accumulates multiple outputs", () => {
|
|
61
|
+
const factory = PluginModel.define<Data, { prefix: string }>({
|
|
62
|
+
name: "multiOutput" as PluginName,
|
|
63
|
+
data: () => dataModelChain.init(() => ({ count: 0, label: "" })),
|
|
64
|
+
})
|
|
65
|
+
.output("formattedCount", (ctx) => `${ctx.params.prefix}${ctx.data.count}`)
|
|
66
|
+
.output("upperLabel", (ctx) => ctx.data.label.toUpperCase())
|
|
67
|
+
.output("isReady", (ctx) => ctx.data.count > 0)
|
|
68
|
+
.build();
|
|
69
|
+
|
|
70
|
+
const plugin = factory.create();
|
|
71
|
+
expect(Object.keys(plugin.outputs).sort()).toEqual(["formattedCount", "isReady", "upperLabel"]);
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
it("executes output functions with correct context", () => {
|
|
75
|
+
const factory = PluginModel.define<Data, { factor: number }>({
|
|
76
|
+
name: "contextTest" as PluginName,
|
|
77
|
+
data: () => dataModelChain.init(() => ({ count: 0, label: "" })),
|
|
78
|
+
})
|
|
79
|
+
.output("computed", (ctx) => ctx.data.count * ctx.params.factor)
|
|
80
|
+
.build();
|
|
81
|
+
|
|
82
|
+
const plugin = factory.create();
|
|
83
|
+
|
|
84
|
+
const ctx: PluginRenderCtx<Data, { factor: number }> = {
|
|
85
|
+
data: { count: 5, label: "" },
|
|
86
|
+
params: { factor: 3 },
|
|
87
|
+
resultPool: mockResultPool,
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
const result = plugin.outputs.computed(ctx);
|
|
91
|
+
expect(result).toBe(15);
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
it("allows outputs to access resultPool", () => {
|
|
95
|
+
const factory = PluginModel.define<Data>({
|
|
96
|
+
name: "resultPoolTest" as PluginName,
|
|
97
|
+
data: () => dataModelChain.init(() => ({ count: 0, label: "" })),
|
|
98
|
+
})
|
|
99
|
+
.output("hasResultPool", (ctx) => ctx.resultPool !== undefined)
|
|
100
|
+
.build();
|
|
101
|
+
|
|
102
|
+
const plugin = factory.create();
|
|
103
|
+
|
|
104
|
+
const ctx: PluginRenderCtx<Data> = {
|
|
105
|
+
data: { count: 0, label: "" },
|
|
106
|
+
params: undefined,
|
|
107
|
+
resultPool: mockResultPool,
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
expect(plugin.outputs.hasResultPool(ctx)).toBe(true);
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
it("returns valid PluginModel from factory.create()", () => {
|
|
114
|
+
const factory = PluginModel.define<Data, { items: string[] }, { option: boolean }>({
|
|
115
|
+
name: "completePlugin" as PluginName,
|
|
116
|
+
data: () => dataModelChain.init(() => ({ count: -1, label: "" })),
|
|
117
|
+
})
|
|
118
|
+
.output("currentItem", (ctx) => ctx.params.items[ctx.data.count])
|
|
119
|
+
.output("hasSelection", (ctx) => ctx.data.count >= 0)
|
|
120
|
+
.build();
|
|
121
|
+
|
|
122
|
+
const plugin = factory.create({ option: true });
|
|
123
|
+
expect(plugin.name).toBe("completePlugin");
|
|
124
|
+
expect(Object.keys(plugin.outputs).sort()).toEqual(["currentItem", "hasSelection"]);
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
it("allows creating plugin without outputs", () => {
|
|
128
|
+
const factory = PluginModel.define<Data>({
|
|
129
|
+
name: "noOutputs" as PluginName,
|
|
130
|
+
data: () => dataModelChain.init(() => ({ count: 0, label: "" })),
|
|
131
|
+
}).build();
|
|
132
|
+
|
|
133
|
+
const plugin = factory.create();
|
|
134
|
+
expect(plugin.outputs).toEqual({});
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
it("passes config to data model factory for initialization", () => {
|
|
138
|
+
type Config = { defaultCount: number; defaultLabel: string };
|
|
139
|
+
|
|
140
|
+
const factory = PluginModel.define<Data, undefined, Config>({
|
|
141
|
+
name: "configInitPlugin" as PluginName,
|
|
142
|
+
data: (cfg) =>
|
|
143
|
+
dataModelChain.init(() => ({ count: cfg.defaultCount, label: cfg.defaultLabel })),
|
|
144
|
+
}).build();
|
|
145
|
+
|
|
146
|
+
const plugin = factory.create({ defaultCount: 10, defaultLabel: "default" });
|
|
147
|
+
expect(plugin.dataModel.initialData()).toEqual({ count: 10, label: "default" });
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
it("does not modify original builder when chaining", () => {
|
|
151
|
+
const basePlugin = PluginModel.define<Data>({
|
|
152
|
+
name: "immutableTest" as PluginName,
|
|
153
|
+
data: () => dataModelChain.init(() => ({ count: 0, label: "" })),
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
const pluginWithOutput1 = basePlugin.output("first", (ctx) => ctx.data.count);
|
|
157
|
+
const pluginWithOutput2 = basePlugin.output("second", (ctx) => ctx.data.count * 2);
|
|
158
|
+
|
|
159
|
+
const factory1 = pluginWithOutput1.build();
|
|
160
|
+
const factory2 = pluginWithOutput2.build();
|
|
161
|
+
|
|
162
|
+
const plugin1 = factory1.create(undefined);
|
|
163
|
+
const plugin2 = factory2.create(undefined);
|
|
164
|
+
|
|
165
|
+
expect(Object.keys(plugin1.outputs)).toEqual(["first"]);
|
|
166
|
+
expect(Object.keys(plugin2.outputs)).toEqual(["second"]);
|
|
167
|
+
});
|
|
168
|
+
});
|
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PluginModel - Builder for creating plugin types with data model and outputs.
|
|
3
|
+
*
|
|
4
|
+
* Plugins are UI components with their own model logic and persistent state.
|
|
5
|
+
* Block developers register plugin instances via BlockModelV3.plugin() method.
|
|
6
|
+
*
|
|
7
|
+
* @module plugin_model
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import type { DataModel } from "./block_migrations";
|
|
11
|
+
import type { PluginName } from "./block_storage";
|
|
12
|
+
import type { ResultPool } from "./render";
|
|
13
|
+
|
|
14
|
+
/** Symbol for internal builder creation method */
|
|
15
|
+
const FROM_BUILDER = Symbol("fromBuilder");
|
|
16
|
+
|
|
17
|
+
// =============================================================================
|
|
18
|
+
// Plugin Render Context
|
|
19
|
+
// =============================================================================
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Context passed to plugin output functions.
|
|
23
|
+
* Provides access to plugin's persistent data, params derived from block context,
|
|
24
|
+
* and the result pool for accessing workflow outputs.
|
|
25
|
+
*/
|
|
26
|
+
export interface PluginRenderCtx<Data, Params = undefined> {
|
|
27
|
+
/** Plugin's persistent data */
|
|
28
|
+
readonly data: Data;
|
|
29
|
+
/** Params derived from block's RenderCtx */
|
|
30
|
+
readonly params: Params;
|
|
31
|
+
/** Result pool for accessing workflow outputs */
|
|
32
|
+
readonly resultPool: ResultPool;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// =============================================================================
|
|
36
|
+
// Plugin Type
|
|
37
|
+
// =============================================================================
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Configured plugin instance returned by PluginModelFactory.create().
|
|
41
|
+
* Contains the plugin's name, data model, and output definitions.
|
|
42
|
+
*/
|
|
43
|
+
export class PluginModel<Data = unknown, Params = undefined, Outputs = {}> {
|
|
44
|
+
/** Globally unique plugin name */
|
|
45
|
+
readonly name: PluginName;
|
|
46
|
+
/** Data model instance for this plugin */
|
|
47
|
+
readonly dataModel: DataModel<Data>;
|
|
48
|
+
/** Output definitions - functions that compute outputs from plugin context */
|
|
49
|
+
readonly outputs: { [K in keyof Outputs]: (ctx: PluginRenderCtx<Data, Params>) => Outputs[K] };
|
|
50
|
+
|
|
51
|
+
private constructor(input: {
|
|
52
|
+
name: PluginName;
|
|
53
|
+
dataModel: DataModel<Data>;
|
|
54
|
+
outputs: { [K in keyof Outputs]: (ctx: PluginRenderCtx<Data, Params>) => Outputs[K] };
|
|
55
|
+
}) {
|
|
56
|
+
this.name = input.name;
|
|
57
|
+
this.dataModel = input.dataModel;
|
|
58
|
+
this.outputs = input.outputs;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Internal method for creating PluginModel from factory.
|
|
63
|
+
* Uses Symbol key to prevent external access.
|
|
64
|
+
* @internal
|
|
65
|
+
*/
|
|
66
|
+
static [FROM_BUILDER]<D, P, O>(input: {
|
|
67
|
+
name: PluginName;
|
|
68
|
+
dataModel: DataModel<D>;
|
|
69
|
+
outputs: { [K in keyof O]: (ctx: PluginRenderCtx<D, P>) => O[K] };
|
|
70
|
+
}): PluginModel<D, P, O> {
|
|
71
|
+
return new this<D, P, O>(input);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Creates a new PluginModelBuilder for building plugin definitions.
|
|
76
|
+
*
|
|
77
|
+
* @param options.name - Globally unique plugin name
|
|
78
|
+
* @param options.data - Factory function that creates the data model from config
|
|
79
|
+
* @returns PluginModelBuilder for chaining output definitions
|
|
80
|
+
*
|
|
81
|
+
* @example
|
|
82
|
+
* const dataModelChain = new DataModelBuilder().from<MyData>(DATA_MODEL_DEFAULT_VERSION);
|
|
83
|
+
*
|
|
84
|
+
* const myPlugin = PluginModel.define<MyData, MyParams, MyConfig>({
|
|
85
|
+
* name: 'myPlugin' as PluginName,
|
|
86
|
+
* data: (cfg) => dataModelChain.init(() => ({ value: cfg.defaultValue })),
|
|
87
|
+
* })
|
|
88
|
+
* .output('computed', (ctx) => ctx.data.value * ctx.params.multiplier)
|
|
89
|
+
* .build();
|
|
90
|
+
*/
|
|
91
|
+
static define<Data, Params = undefined, Config = undefined>(options: {
|
|
92
|
+
name: PluginName;
|
|
93
|
+
data: (config?: Config) => DataModel<Data>;
|
|
94
|
+
}): PluginModelBuilder<Data, Params, Config> {
|
|
95
|
+
return PluginModelBuilder[FROM_BUILDER]<Data, Params, Config>(options);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Plugin factory returned by PluginModelBuilder.build().
|
|
101
|
+
* Call create() with config to get a configured PluginModel instance.
|
|
102
|
+
*/
|
|
103
|
+
class PluginModelFactory<Data, Params, Config, Outputs> {
|
|
104
|
+
private readonly name: PluginName;
|
|
105
|
+
private readonly data: (config?: Config) => DataModel<Data>;
|
|
106
|
+
private readonly outputs: {
|
|
107
|
+
[K in keyof Outputs]: (ctx: PluginRenderCtx<Data, Params>) => Outputs[K];
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
constructor(input: {
|
|
111
|
+
name: PluginName;
|
|
112
|
+
data: (config?: Config) => DataModel<Data>;
|
|
113
|
+
outputs: { [K in keyof Outputs]: (ctx: PluginRenderCtx<Data, Params>) => Outputs[K] };
|
|
114
|
+
}) {
|
|
115
|
+
this.name = input.name;
|
|
116
|
+
this.data = input.data;
|
|
117
|
+
this.outputs = input.outputs;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/** Create a configured PluginModel instance */
|
|
121
|
+
create(config?: Config): PluginModel<Data, Params, Outputs> {
|
|
122
|
+
return PluginModel[FROM_BUILDER]<Data, Params, Outputs>({
|
|
123
|
+
name: this.name,
|
|
124
|
+
dataModel: this.data(config),
|
|
125
|
+
outputs: this.outputs,
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// =============================================================================
|
|
131
|
+
// Plugin Model Builder
|
|
132
|
+
// =============================================================================
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Builder for creating PluginType with type-safe output definitions.
|
|
136
|
+
*
|
|
137
|
+
* Use `PluginModel.define()` to create a builder instance.
|
|
138
|
+
*
|
|
139
|
+
* @typeParam Data - Plugin's persistent data type
|
|
140
|
+
* @typeParam Params - Params derived from block's RenderCtx (optional)
|
|
141
|
+
* @typeParam Config - Static configuration passed to plugin factory (optional)
|
|
142
|
+
* @typeParam Outputs - Accumulated output types
|
|
143
|
+
*
|
|
144
|
+
* @example
|
|
145
|
+
* const dataModelChain = new DataModelBuilder().from<TableData>(DATA_MODEL_DEFAULT_VERSION);
|
|
146
|
+
*
|
|
147
|
+
* const dataTable = PluginModel.define<TableData, TableParams, TableConfig>({
|
|
148
|
+
* name: 'dataTable' as PluginName,
|
|
149
|
+
* data: (cfg) => {
|
|
150
|
+
* return dataModelChain.init(() => ({ state: createInitialState(cfg.ops) }));
|
|
151
|
+
* },
|
|
152
|
+
* })
|
|
153
|
+
* .output('model', (ctx) => createTableModel(ctx))
|
|
154
|
+
* .build();
|
|
155
|
+
*/
|
|
156
|
+
class PluginModelBuilder<
|
|
157
|
+
Data,
|
|
158
|
+
Params = undefined,
|
|
159
|
+
Config = undefined,
|
|
160
|
+
Outputs extends Record<string, unknown> = {},
|
|
161
|
+
> {
|
|
162
|
+
private readonly name: PluginName;
|
|
163
|
+
private readonly data: (config?: Config) => DataModel<Data>;
|
|
164
|
+
private readonly outputs: {
|
|
165
|
+
[K in keyof Outputs]: (ctx: PluginRenderCtx<Data, Params>) => Outputs[K];
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
private constructor(input: {
|
|
169
|
+
name: PluginName;
|
|
170
|
+
data: (config?: Config) => DataModel<Data>;
|
|
171
|
+
outputs?: { [K in keyof Outputs]: (ctx: PluginRenderCtx<Data, Params>) => Outputs[K] };
|
|
172
|
+
}) {
|
|
173
|
+
this.name = input.name;
|
|
174
|
+
this.data = input.data;
|
|
175
|
+
this.outputs =
|
|
176
|
+
input.outputs ??
|
|
177
|
+
({} as { [K in keyof Outputs]: (ctx: PluginRenderCtx<Data, Params>) => Outputs[K] });
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Internal method for creating PluginModelBuilder.
|
|
182
|
+
* Uses Symbol key to prevent external access.
|
|
183
|
+
* @internal
|
|
184
|
+
*/
|
|
185
|
+
static [FROM_BUILDER]<D, P, C, O extends Record<string, unknown> = {}>(input: {
|
|
186
|
+
name: PluginName;
|
|
187
|
+
data: (config?: C) => DataModel<D>;
|
|
188
|
+
outputs?: { [K in keyof O]: (ctx: PluginRenderCtx<D, P>) => O[K] };
|
|
189
|
+
}): PluginModelBuilder<D, P, C, O> {
|
|
190
|
+
return new this<D, P, C, O>(input);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Adds an output to the plugin.
|
|
195
|
+
*
|
|
196
|
+
* @param key - Output name
|
|
197
|
+
* @param fn - Function that computes the output value from plugin context
|
|
198
|
+
* @returns PluginModel with the new output added
|
|
199
|
+
*
|
|
200
|
+
* @example
|
|
201
|
+
* .output('model', (ctx) => createModel(ctx.params.columns, ctx.data.state))
|
|
202
|
+
* .output('isReady', (ctx) => ctx.params.columns !== undefined)
|
|
203
|
+
*/
|
|
204
|
+
output<const Key extends string, T>(
|
|
205
|
+
key: Key,
|
|
206
|
+
fn: (ctx: PluginRenderCtx<Data, Params>) => T,
|
|
207
|
+
): PluginModelBuilder<Data, Params, Config, Outputs & { [K in Key]: T }> {
|
|
208
|
+
return new PluginModelBuilder<Data, Params, Config, Outputs & { [K in Key]: T }>({
|
|
209
|
+
name: this.name,
|
|
210
|
+
data: this.data,
|
|
211
|
+
outputs: {
|
|
212
|
+
...this.outputs,
|
|
213
|
+
[key]: fn,
|
|
214
|
+
} as {
|
|
215
|
+
[K in keyof (Outputs & { [P in Key]: T })]: (
|
|
216
|
+
ctx: PluginRenderCtx<Data, Params>,
|
|
217
|
+
) => (Outputs & { [P in Key]: T })[K];
|
|
218
|
+
},
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* Finalizes the plugin definition and returns a ConfigurablePluginModel.
|
|
224
|
+
*
|
|
225
|
+
* @returns Callable plugin factory that accepts config and returns PluginModel
|
|
226
|
+
*
|
|
227
|
+
* @example
|
|
228
|
+
* const myPlugin = new PluginModelBuilder('myPlugin', () => dataModel)
|
|
229
|
+
* .output('value', (ctx) => ctx.data.value)
|
|
230
|
+
* .build();
|
|
231
|
+
*
|
|
232
|
+
* // Later, call create() with config to get a configured instance:
|
|
233
|
+
* const configured = myPlugin.create({ defaultValue: 'test' });
|
|
234
|
+
*/
|
|
235
|
+
build(): PluginModelFactory<Data, Params, Config, Outputs> {
|
|
236
|
+
return new PluginModelFactory<Data, Params, Config, Outputs>({
|
|
237
|
+
name: this.name,
|
|
238
|
+
data: this.data,
|
|
239
|
+
outputs: this.outputs,
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
}
|
package/src/render/api.ts
CHANGED
|
@@ -533,40 +533,40 @@ export class ResultPool implements ColumnProvider, AxisLabelProvider {
|
|
|
533
533
|
}
|
|
534
534
|
|
|
535
535
|
/** Main entry point to the API available within model lambdas (like outputs, sections, etc..) */
|
|
536
|
-
export abstract class RenderCtxBase<Args, Data> {
|
|
536
|
+
export abstract class RenderCtxBase<Args = unknown, Data = unknown> {
|
|
537
537
|
protected readonly ctx: GlobalCfgRenderCtx;
|
|
538
538
|
|
|
539
539
|
constructor() {
|
|
540
540
|
this.ctx = getCfgRenderCtx();
|
|
541
541
|
}
|
|
542
542
|
|
|
543
|
-
private
|
|
543
|
+
private dataCache?: { v: Data };
|
|
544
544
|
|
|
545
545
|
public get data(): Data {
|
|
546
|
-
if (this.
|
|
546
|
+
if (this.dataCache === undefined) {
|
|
547
547
|
const raw = this.ctx.data;
|
|
548
548
|
const value = typeof raw === "function" ? raw() : raw;
|
|
549
|
-
this.
|
|
549
|
+
this.dataCache = { v: value ? JSON.parse(value) : ({} as Data) };
|
|
550
550
|
}
|
|
551
|
-
return this.
|
|
551
|
+
return this.dataCache.v;
|
|
552
552
|
}
|
|
553
553
|
|
|
554
554
|
// lazy rendering because this feature is rarely used
|
|
555
|
-
private
|
|
555
|
+
private activeArgsCache?: { v?: Args };
|
|
556
556
|
|
|
557
557
|
/**
|
|
558
558
|
* Returns args snapshot the block was executed for (i.e. when "Run" button was pressed).
|
|
559
559
|
* Returns undefined, if block was never executed or stopped mid-way execution, so that the result was cleared.
|
|
560
560
|
* */
|
|
561
561
|
public get activeArgs(): Args | undefined {
|
|
562
|
-
if (this.
|
|
562
|
+
if (this.activeArgsCache === undefined) {
|
|
563
563
|
const raw = this.ctx.activeArgs;
|
|
564
564
|
const value = typeof raw === "function" ? raw() : raw;
|
|
565
|
-
this.
|
|
565
|
+
this.activeArgsCache = {
|
|
566
566
|
v: value ? JSON.parse(value) : undefined,
|
|
567
567
|
};
|
|
568
568
|
}
|
|
569
|
-
return this.
|
|
569
|
+
return this.activeArgsCache.v;
|
|
570
570
|
}
|
|
571
571
|
|
|
572
572
|
// /** Can be used to determine features provided by the desktop instance. */
|
|
@@ -711,41 +711,43 @@ export abstract class RenderCtxBase<Args, Data> {
|
|
|
711
711
|
}
|
|
712
712
|
|
|
713
713
|
/** Main entry point to the API available within model lambdas (like outputs, sections, etc..) for v3+ blocks */
|
|
714
|
-
export class RenderCtx<Args, Data> extends RenderCtxBase<Args, Data> {
|
|
715
|
-
private
|
|
714
|
+
export class RenderCtx<Args = unknown, Data = unknown> extends RenderCtxBase<Args, Data> {
|
|
715
|
+
private argsCache?: { v: Args | undefined };
|
|
716
716
|
public get args(): Args | undefined {
|
|
717
|
-
if (this.
|
|
717
|
+
if (this.argsCache === undefined) {
|
|
718
718
|
const raw = this.ctx.args;
|
|
719
719
|
const value = typeof raw === "function" ? raw() : raw;
|
|
720
|
-
|
|
721
|
-
this._argsCache = { v: value === undefined ? undefined : JSON.parse(value) };
|
|
720
|
+
this.argsCache = { v: value === undefined ? undefined : JSON.parse(value) };
|
|
722
721
|
}
|
|
723
|
-
return this.
|
|
722
|
+
return this.argsCache.v;
|
|
724
723
|
}
|
|
725
724
|
}
|
|
726
725
|
|
|
727
726
|
/** Render context for legacy v1/v2 blocks - provides backward compatibility */
|
|
728
|
-
export class RenderCtxLegacy<Args, UiState> extends RenderCtxBase<
|
|
729
|
-
|
|
727
|
+
export class RenderCtxLegacy<Args = unknown, UiState = unknown> extends RenderCtxBase<
|
|
728
|
+
Args,
|
|
729
|
+
UiState
|
|
730
|
+
> {
|
|
731
|
+
private argsCache?: { v: Args };
|
|
730
732
|
|
|
731
733
|
public get args(): Args {
|
|
732
|
-
if (this.
|
|
734
|
+
if (this.argsCache === undefined) {
|
|
733
735
|
const raw = this.ctx.args;
|
|
734
736
|
const value = typeof raw === "function" ? raw() : raw;
|
|
735
|
-
this.
|
|
737
|
+
this.argsCache = { v: JSON.parse(value) };
|
|
736
738
|
}
|
|
737
|
-
return this.
|
|
739
|
+
return this.argsCache.v;
|
|
738
740
|
}
|
|
739
741
|
|
|
740
|
-
private
|
|
742
|
+
private uiStateCache?: { v: UiState };
|
|
741
743
|
|
|
742
744
|
public get uiState(): UiState {
|
|
743
|
-
if (this.
|
|
745
|
+
if (this.uiStateCache === undefined) {
|
|
744
746
|
const raw = this.ctx.uiState!;
|
|
745
747
|
const value = typeof raw === "function" ? raw() : raw;
|
|
746
|
-
this.
|
|
748
|
+
this.uiStateCache = { v: value ? JSON.parse(value) : ({} as UiState) };
|
|
747
749
|
}
|
|
748
|
-
return this.
|
|
750
|
+
return this.uiStateCache.v;
|
|
749
751
|
}
|
|
750
752
|
}
|
|
751
753
|
|
package/src/render/internal.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Optional } from "utility-types";
|
|
2
|
-
import type { Branded } from "
|
|
2
|
+
import type { Branded } from "@milaboratories/pl-model-common";
|
|
3
3
|
import type { CommonFieldTraverseOps, FieldTraversalStep, ResourceType } from "./traversal_ops";
|
|
4
4
|
import type {
|
|
5
5
|
ArchiveFormat,
|
package/src/typing.test.ts
CHANGED
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
} from "@milaboratories/pl-model-common";
|
|
7
7
|
import { expect, test } from "vitest";
|
|
8
8
|
import { DeriveHref, StdCtx } from "./bconfig";
|
|
9
|
-
import { BlockModel } from "./
|
|
9
|
+
import { BlockModel } from "./block_model_legacy";
|
|
10
10
|
import {
|
|
11
11
|
Args,
|
|
12
12
|
ConfigResult,
|
package/src/version.ts
CHANGED