@platforma-sdk/model 1.61.0 → 1.61.1

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.
@@ -1 +1 @@
1
- {"version":3,"file":"plugin_model.cjs","names":["DATA_MODEL_LEGACY_VERSION","DataModelBuilder"],"sources":["../src/plugin_model.ts"],"sourcesContent":["/**\n * PluginModel - Builder for creating plugin types with data model and outputs.\n *\n * Plugins are UI components with their own model logic and persistent state.\n * Block developers register plugin instances via BlockModelV3.plugin() method.\n *\n * @module plugin_model\n */\n\nimport type { BlockCodeKnownFeatureFlags } from \"@milaboratories/pl-model-common\";\nimport {\n type DataModel,\n DataModelBuilder,\n type DataRecoverFn,\n type DataVersioned,\n type TransferTarget,\n} from \"./block_migrations\";\nimport { type PluginName, DATA_MODEL_LEGACY_VERSION } from \"./block_storage\";\nimport type { PluginFactoryLike } from \"./plugin_handle\";\nimport type { PluginRenderCtx } from \"./render\";\n\n/** Symbol for internal builder creation method */\nconst FROM_BUILDER = Symbol(\"fromBuilder\");\n\n/** Symbol for internal plugin model creation — not accessible to external consumers */\nexport const CREATE_PLUGIN_MODEL = Symbol(\"createPluginModel\");\n\n/** Sentinel for PluginInstance without transferAt — no transfer possible. */\nconst NO_TRANSFER_VERSION = \"\";\n\nexport type PluginData = Record<string, unknown>;\nexport type PluginParams = undefined | Record<string, unknown>;\nexport type PluginOutputs = Record<string, unknown>;\nexport type PluginConfig = undefined | Record<string, unknown>;\n\n/**\n * Plugin data model with typed migration chain and config-aware initialization.\n *\n * @typeParam Data - Current (latest) plugin data type\n * @typeParam Versions - Map of version keys to their data types (accumulated by the chain)\n * @typeParam Config - Config type passed to init function (undefined if none)\n */\nexport class PluginDataModel<\n Data extends PluginData,\n Versions extends Record<string, unknown> = {},\n Config = undefined,\n> {\n readonly dataModel: DataModel<Data>;\n private readonly configInitFn: (config?: Config) => Data;\n\n /** @internal Phantom field to anchor the Versions type parameter. */\n declare readonly __versions?: Versions;\n\n private constructor(dataModel: DataModel<Data>, configInitFn: (config?: Config) => Data) {\n this.dataModel = dataModel;\n this.configInitFn = configInitFn;\n }\n\n /** @internal */\n static [FROM_BUILDER]<Data extends PluginData, Versions extends Record<string, unknown>, Config>(\n dataModel: DataModel<Data>,\n configInitFn: (config?: Config) => Data,\n ): PluginDataModel<Data, Versions, Config> {\n return new PluginDataModel<Data, Versions, Config>(dataModel, configInitFn);\n }\n\n /** Create fresh data with optional config. */\n getDefaultData(config?: Config): DataVersioned<Data> {\n const data = this.configInitFn(config);\n return { version: this.dataModel.version, data };\n }\n}\n\n/** Internal state for plugin data model chain. */\ntype PluginChainState = {\n initialVersion: string;\n migrations: Array<{ toVersion: string; fn: (data: unknown) => unknown }>;\n recoverFn?: (version: string, data: unknown) => unknown;\n recoverAtIndex?: number;\n};\n\n/**\n * Builder for creating PluginDataModel with type-safe migrations.\n * Mirrors DataModelBuilder — same .from(), .migrate(), .recover(), .init() chain.\n *\n * @example\n * const pluginData = new PluginDataModelBuilder()\n * .from<TableData>(\"v1\")\n * .migrate<FilteredTableData>(\"v2\", (v1) => ({ ...v1, filters: [] }))\n * .init<TableConfig>((config?) => ({\n * state: createDefaultState(config?.ops),\n * filters: [],\n * }));\n */\nexport class PluginDataModelBuilder {\n from<Data extends PluginData, const V extends string>(\n version: V,\n ): PluginDataModelInitialChain<Data, Record<V, Data>> {\n return PluginDataModelInitialChain[FROM_BUILDER]<Data, Record<V, Data>>({\n initialVersion: version,\n migrations: [],\n });\n }\n}\n\n/**\n * Chain returned by .migrate(). Supports .migrate(), .recover(), .init().\n * No .upgradeLegacy() — that is only available on the initial chain.\n */\nexport class PluginDataModelChain<\n Data extends PluginData,\n Versions extends Record<string, unknown>,\n> {\n protected constructor(protected readonly state: PluginChainState) {}\n\n /** @internal */\n static [FROM_BUILDER]<Data extends PluginData, Versions extends Record<string, unknown>>(\n state: PluginChainState,\n ): PluginDataModelChain<Data, Versions> {\n return new PluginDataModelChain(state);\n }\n\n /**\n * Add a migration step transforming data from the current version to the next.\n */\n migrate<Next extends PluginData, const NextV extends string>(\n version: NextV,\n fn: (current: Data) => Next,\n ): PluginDataModelChain<Next, Versions & Record<NextV, Next>> {\n return PluginDataModelChain[FROM_BUILDER]<Next, Versions & Record<NextV, Next>>({\n ...this.state,\n migrations: [\n ...this.state.migrations,\n { toVersion: version, fn: fn as (data: unknown) => unknown },\n ],\n });\n }\n\n /**\n * Set a recovery handler for unknown or legacy versions.\n * Can only be called once — the returned chain has no recover() method.\n */\n recover(fn: DataRecoverFn<Data>): PluginDataModelWithRecover<Data, Versions> {\n return PluginDataModelWithRecover[FROM_BUILDER]<Data, Versions>({\n ...this.state,\n recoverFn: fn as (version: string, data: unknown) => unknown,\n recoverAtIndex: this.state.migrations.length,\n });\n }\n\n /** Finalize the PluginDataModel. */\n init<Config = undefined>(fn: (config?: Config) => Data): PluginDataModel<Data, Versions, Config> {\n return buildPluginDataModel(this.state, fn);\n }\n}\n\n/**\n * Initial chain returned by new PluginDataModelBuilder().from().\n * Extends PluginDataModelChain with .upgradeLegacy() — available only before\n * any .migrate() calls, matching the block's DataModelInitialChain pattern.\n */\nexport class PluginDataModelInitialChain<\n Data extends PluginData,\n Versions extends Record<string, unknown>,\n> extends PluginDataModelChain<Data, Versions> {\n /** @internal */\n static override [FROM_BUILDER]<Data extends PluginData, Versions extends Record<string, unknown>>(\n state: PluginChainState,\n ): PluginDataModelInitialChain<Data, Versions> {\n return new PluginDataModelInitialChain(state);\n }\n\n /**\n * Handle data from a previous plugin type occupying this slot.\n * Prepends a migration from DATA_MODEL_LEGACY_VERSION to the initial version.\n * When a plugin type changes, the old data arrives with DATA_MODEL_LEGACY_VERSION —\n * this function transforms it into the initial version, then the normal chain runs.\n *\n * Must be called right after .from() — not available after .migrate().\n * Mutually exclusive with recover().\n *\n * @param fn - Transform from old plugin's raw data to this plugin's initial data type\n */\n upgradeLegacy(fn: (data: unknown) => Data): PluginDataModelWithRecover<Data, Versions> {\n return PluginDataModelWithRecover[FROM_BUILDER]<Data, Versions>({\n ...this.state,\n migrations: [\n { toVersion: this.state.initialVersion, fn: fn as (data: unknown) => unknown },\n ...this.state.migrations,\n ],\n initialVersion: DATA_MODEL_LEGACY_VERSION,\n });\n }\n}\n\n/**\n * Chain after .recover() — supports .migrate(), .init(). No second recover().\n */\nexport class PluginDataModelWithRecover<\n Data extends PluginData,\n Versions extends Record<string, unknown>,\n> {\n private constructor(private readonly state: PluginChainState) {}\n\n /** @internal */\n static [FROM_BUILDER]<Data extends PluginData, Versions extends Record<string, unknown>>(\n state: PluginChainState,\n ): PluginDataModelWithRecover<Data, Versions> {\n return new PluginDataModelWithRecover(state);\n }\n\n migrate<Next extends PluginData, const NextV extends string>(\n version: NextV,\n fn: (current: Data) => Next,\n ): PluginDataModelWithRecover<Next, Versions & Record<NextV, Next>> {\n return PluginDataModelWithRecover[FROM_BUILDER]<Next, Versions & Record<NextV, Next>>({\n ...this.state,\n migrations: [\n ...this.state.migrations,\n { toVersion: version, fn: fn as (data: unknown) => unknown },\n ],\n });\n }\n\n init<Config = undefined>(fn: (config?: Config) => Data): PluginDataModel<Data, Versions, Config> {\n return buildPluginDataModel(this.state, fn);\n }\n}\n\n/**\n * Builds a PluginDataModel by replaying the stored chain state through DataModelBuilder.\n * @internal\n */\nfunction buildPluginDataModel<\n Data extends PluginData,\n Versions extends Record<string, unknown>,\n Config,\n>(\n state: PluginChainState,\n configInitFn: (config?: Config) => Data,\n): PluginDataModel<Data, Versions, Config> {\n // Build inner DataModel by replaying migrations through DataModelBuilder chain.\n // Uses `any` internally — type safety is maintained by the public chain types.\n let chain: any = new DataModelBuilder().from(state.initialVersion);\n\n for (let i = 0; i < state.migrations.length; i++) {\n if (state.recoverFn !== undefined && state.recoverAtIndex === i) {\n chain = chain.recover(state.recoverFn);\n }\n chain = chain.migrate(state.migrations[i].toVersion, state.migrations[i].fn);\n }\n\n // If recover was placed at or after the last migration\n if (state.recoverFn !== undefined && state.recoverAtIndex === state.migrations.length) {\n chain = chain.recover(state.recoverFn);\n }\n\n const dataModel: DataModel<Data> = chain.init(() => configInitFn());\n\n return PluginDataModel[FROM_BUILDER]<Data, Versions, Config>(dataModel, configInitFn);\n}\n\n/**\n * A named plugin instance created by `factory.create({ pluginId, ... })`.\n * Passed to both `.transfer()` (on migration chain) and `.plugin()` (on BlockModelV3).\n * Implements TransferTarget so the migration chain can accept it.\n *\n * @typeParam Id - Plugin instance ID literal type\n * @typeParam Data - Plugin data type\n * @typeParam Params - Plugin params type\n * @typeParam Outputs - Plugin outputs type\n * @typeParam TransferData - Type of data entering the plugin via transfer (never if no transfer)\n */\nexport class PluginInstance<\n Id extends string = string,\n Data extends PluginData = PluginData,\n Params extends PluginParams = undefined,\n Outputs extends PluginOutputs = PluginOutputs,\n TransferData = never,\n> implements TransferTarget<Id, TransferData> {\n readonly id: Id;\n readonly transferVersion: string;\n /** @internal */\n readonly __transferBrand?: TransferData;\n private readonly factory: PluginModelFactory<Data, Params, Outputs, any, any>;\n private readonly config: any;\n\n private constructor(\n id: Id,\n factory: PluginModelFactory<Data, Params, Outputs, any, any>,\n transferVersion: string,\n config?: any,\n ) {\n this.id = id;\n this.factory = factory;\n this.transferVersion = transferVersion;\n this.config = config;\n }\n\n /** @internal */\n static [FROM_BUILDER]<\n Id extends string,\n Data extends PluginData,\n Params extends PluginParams,\n Outputs extends PluginOutputs,\n TransferData,\n >(\n id: Id,\n factory: PluginModelFactory<Data, Params, Outputs, any, any>,\n transferVersion: string,\n config?: any,\n ): PluginInstance<Id, Data, Params, Outputs, TransferData> {\n return new PluginInstance(id, factory, transferVersion, config);\n }\n\n /** @internal Create a PluginModel from this instance. Used by BlockModelV3.plugin(). */\n [CREATE_PLUGIN_MODEL](): PluginModel<Data, Params, Outputs> {\n return this.factory[CREATE_PLUGIN_MODEL](this.config);\n }\n}\n\n/**\n * Configured plugin instance returned by PluginModelFactory[CREATE_PLUGIN_MODEL]().\n * Contains the plugin's name, data model, and output definitions.\n */\nexport class PluginModel<\n Data extends PluginData = PluginData,\n Params extends PluginParams = undefined,\n Outputs extends PluginOutputs = PluginOutputs,\n> {\n /** Globally unique plugin name */\n readonly name: PluginName;\n /** Data model instance for this plugin */\n readonly dataModel: DataModel<Data>;\n /** Output definitions - functions that compute outputs from plugin context */\n readonly outputs: {\n [K in keyof Outputs]: (ctx: PluginRenderCtx<PluginFactoryLike<Data, Params>>) => Outputs[K];\n };\n /** Feature flags declared by this plugin */\n readonly featureFlags?: BlockCodeKnownFeatureFlags;\n /** Create fresh default data. Config (if any) is captured at creation time. */\n readonly getDefaultData: () => DataVersioned<Data>;\n\n private constructor(options: {\n name: PluginName;\n dataModel: DataModel<Data>;\n outputs: {\n [K in keyof Outputs]: (ctx: PluginRenderCtx<PluginFactoryLike<Data, Params>>) => Outputs[K];\n };\n featureFlags?: BlockCodeKnownFeatureFlags;\n getDefaultData: () => DataVersioned<Data>;\n }) {\n this.name = options.name;\n this.dataModel = options.dataModel;\n this.outputs = options.outputs;\n this.featureFlags = options.featureFlags;\n this.getDefaultData = options.getDefaultData;\n }\n\n /**\n * Internal method for creating PluginModel from factory.\n * Uses Symbol key to prevent external access.\n * @internal\n */\n static [FROM_BUILDER]<\n Data extends PluginData,\n Params extends PluginParams,\n Outputs extends PluginOutputs,\n >(options: {\n name: PluginName;\n dataModel: DataModel<Data>;\n outputs: {\n [K in keyof Outputs]: (ctx: PluginRenderCtx<PluginFactoryLike<Data, Params>>) => Outputs[K];\n };\n featureFlags?: BlockCodeKnownFeatureFlags;\n getDefaultData: () => DataVersioned<Data>;\n }): PluginModel<Data, Params, Outputs> {\n return new PluginModel<Data, Params, Outputs>(options);\n }\n\n /**\n * Creates a new PluginModelBuilder with a PluginDataModel (supports transfer / config).\n *\n * @example\n * const pluginData = new PluginDataModelBuilder().from<TableData>(\"v1\")\n * .migrate<FilteredTableData>(\"v2\", (v1) => ({ ...v1, filters: [] }))\n * .init<TableConfig>((config?) => ({\n * state: createDefaultState(config?.ops),\n * filters: [],\n * }));\n *\n * const myPlugin = PluginModel.define({\n * name: 'myPlugin' as PluginName,\n * data: pluginData,\n * }).build();\n */\n static define<\n Data extends PluginData,\n Params extends PluginParams = undefined,\n Versions extends Record<string, unknown> = {},\n Config extends PluginConfig = undefined,\n >(options: {\n name: PluginName;\n data: PluginDataModel<Data, Versions, Config>;\n featureFlags?: BlockCodeKnownFeatureFlags;\n }): PluginModelBuilder<Data, Params, {}, Config, Versions>;\n /**\n * Creates a new PluginModelBuilder with a data model factory function (backward compatible).\n *\n * @example\n * const myPlugin = PluginModel.define({\n * name: 'myPlugin' as PluginName,\n * data: (cfg) => dataModelChain.init(() => ({ value: cfg.defaultValue })),\n * }).build();\n */\n static define<\n Data extends PluginData,\n Params extends PluginParams = undefined,\n Config extends PluginConfig = undefined,\n >(options: {\n name: PluginName;\n data: (config?: Config) => DataModel<Data>;\n featureFlags?: BlockCodeKnownFeatureFlags;\n }): PluginModelBuilder<Data, Params, {}, Config, {}>;\n static define(options: {\n name: PluginName;\n data: PluginDataModel<any, any, any> | ((config?: any) => DataModel<any>);\n featureFlags?: BlockCodeKnownFeatureFlags;\n }): PluginModelBuilder {\n if (options.data instanceof PluginDataModel) {\n const pdm = options.data;\n return PluginModelBuilder[FROM_BUILDER]({\n name: options.name,\n dataFn: () => pdm.dataModel,\n getDefaultDataFn: (config: any) => pdm.getDefaultData(config),\n featureFlags: options.featureFlags,\n });\n }\n return PluginModelBuilder[FROM_BUILDER]({\n name: options.name,\n dataFn: options.data,\n featureFlags: options.featureFlags,\n });\n }\n}\n\n/** Plugin factory returned by PluginModelBuilder.build(). */\nexport interface PluginFactory<\n Data extends PluginData = PluginData,\n Params extends PluginParams = undefined,\n Outputs extends PluginOutputs = PluginOutputs,\n Config extends PluginConfig = undefined,\n Versions extends Record<string, unknown> = {},\n> extends PluginFactoryLike<Data, Params, Outputs> {\n /** Create a named plugin instance, optionally with transfer at a specific version. */\n create<const Id extends string, const V extends string & keyof Versions = never>(options: {\n pluginId: Id;\n transferAt?: V;\n config?: Config;\n }): PluginInstance<Id, Data, Params, Outputs, Versions[V]>;\n\n /**\n * @internal Phantom field for structural type extraction.\n * Enables InferFactoryData/InferFactoryOutputs to work via PluginFactoryLike.\n */\n readonly __types?: {\n data: Data;\n params: Params;\n outputs: Outputs;\n config: Config;\n versions: Versions;\n };\n}\n\nclass PluginModelFactory<\n Data extends PluginData = PluginData,\n Params extends PluginParams = undefined,\n Outputs extends PluginOutputs = PluginOutputs,\n Config extends PluginConfig = undefined,\n Versions extends Record<string, unknown> = {},\n> implements PluginFactory<Data, Params, Outputs, Config, Versions> {\n private readonly name: PluginName;\n private readonly dataFn: (config?: Config) => DataModel<Data>;\n private readonly getDefaultDataFn?: (config?: Config) => DataVersioned<Data>;\n readonly outputs: {\n [K in keyof Outputs]: (ctx: PluginRenderCtx<PluginFactoryLike<Data, Params>>) => Outputs[K];\n };\n private readonly featureFlags?: BlockCodeKnownFeatureFlags;\n\n private constructor(options: {\n name: PluginName;\n dataFn: (config?: Config) => DataModel<Data>;\n getDefaultDataFn?: (config?: Config) => DataVersioned<Data>;\n outputs: {\n [K in keyof Outputs]: (ctx: PluginRenderCtx<PluginFactoryLike<Data, Params>>) => Outputs[K];\n };\n featureFlags?: BlockCodeKnownFeatureFlags;\n }) {\n this.name = options.name;\n this.dataFn = options.dataFn;\n this.getDefaultDataFn = options.getDefaultDataFn;\n this.outputs = options.outputs;\n this.featureFlags = options.featureFlags;\n }\n\n /** @internal */\n static [FROM_BUILDER]<\n Data extends PluginData,\n Params extends PluginParams,\n Outputs extends PluginOutputs,\n Config extends PluginConfig,\n Versions extends Record<string, unknown>,\n >(options: {\n name: PluginName;\n dataFn: (config?: Config) => DataModel<Data>;\n getDefaultDataFn?: (config?: Config) => DataVersioned<Data>;\n outputs: {\n [K in keyof Outputs]: (ctx: PluginRenderCtx<PluginFactoryLike<Data, Params>>) => Outputs[K];\n };\n featureFlags?: BlockCodeKnownFeatureFlags;\n }): PluginModelFactory<Data, Params, Outputs, Config, Versions> {\n return new PluginModelFactory(options);\n }\n\n create<const Id extends string, const V extends string & keyof Versions = never>(options: {\n pluginId: Id;\n transferAt?: V;\n config?: Config;\n }): PluginInstance<Id, Data, Params, Outputs, Versions[V]> {\n const transferVersion = options.transferAt ?? NO_TRANSFER_VERSION;\n return PluginInstance[FROM_BUILDER]<Id, Data, Params, Outputs, Versions[V]>(\n options.pluginId as Id,\n this as any,\n transferVersion,\n options.config,\n );\n }\n\n /** @internal Create a PluginModel from config. Config is captured in getDefaultData closure. */\n [CREATE_PLUGIN_MODEL](config?: Config): PluginModel<Data, Params, Outputs> {\n const dataModel = this.dataFn(config);\n const getDefaultDataFn = this.getDefaultDataFn;\n return PluginModel[FROM_BUILDER]<Data, Params, Outputs>({\n name: this.name,\n dataModel,\n outputs: this.outputs,\n featureFlags: this.featureFlags,\n getDefaultData: getDefaultDataFn\n ? () => getDefaultDataFn(config)\n : () => dataModel.getDefaultData(),\n });\n }\n}\n\n/**\n * Builder for creating PluginType with type-safe output definitions.\n *\n * Use `PluginModel.define()` to create a builder instance.\n *\n * @typeParam Data - Plugin's persistent data type\n * @typeParam Params - Params derived from block's RenderCtx (optional)\n * @typeParam Config - Static configuration passed to plugin factory (optional)\n * @typeParam Outputs - Accumulated output types\n * @typeParam Versions - Version map from PluginDataModel (empty for function-based data)\n *\n * @example\n * const dataTable = PluginModel.define({\n * name: 'dataTable' as PluginName,\n * data: pluginDataModel,\n * })\n * .output('model', (ctx) => createTableModel(ctx))\n * .build();\n */\nclass PluginModelBuilder<\n Data extends PluginData = PluginData,\n Params extends PluginParams = undefined,\n Outputs extends PluginOutputs = PluginOutputs,\n Config extends PluginConfig = undefined,\n Versions extends Record<string, unknown> = {},\n> {\n private readonly name: PluginName;\n private readonly dataFn: (config?: Config) => DataModel<Data>;\n private readonly getDefaultDataFn?: (config?: Config) => DataVersioned<Data>;\n private readonly outputs: {\n [K in keyof Outputs]: (ctx: PluginRenderCtx<PluginFactoryLike<Data, Params>>) => Outputs[K];\n };\n private readonly featureFlags?: BlockCodeKnownFeatureFlags;\n\n private constructor(options: {\n name: PluginName;\n dataFn: (config?: Config) => DataModel<Data>;\n getDefaultDataFn?: (config?: Config) => DataVersioned<Data>;\n outputs?: {\n [K in keyof Outputs]: (ctx: PluginRenderCtx<PluginFactoryLike<Data, Params>>) => Outputs[K];\n };\n featureFlags?: BlockCodeKnownFeatureFlags;\n }) {\n this.name = options.name;\n this.dataFn = options.dataFn;\n this.getDefaultDataFn = options.getDefaultDataFn;\n this.outputs =\n options.outputs ??\n ({} as {\n [K in keyof Outputs]: (ctx: PluginRenderCtx<PluginFactoryLike<Data, Params>>) => Outputs[K];\n });\n this.featureFlags = options.featureFlags;\n }\n\n /** @internal */\n static [FROM_BUILDER]<\n Data extends PluginData,\n Params extends PluginParams,\n Outputs extends PluginOutputs,\n Config extends PluginConfig,\n Versions extends Record<string, unknown> = {},\n >(options: {\n name: PluginName;\n dataFn: (config?: Config) => DataModel<Data>;\n getDefaultDataFn?: (config?: Config) => DataVersioned<Data>;\n outputs?: {\n [K in keyof Outputs]: (ctx: PluginRenderCtx<PluginFactoryLike<Data, Params>>) => Outputs[K];\n };\n featureFlags?: BlockCodeKnownFeatureFlags;\n }): PluginModelBuilder<Data, Params, Outputs, Config, Versions> {\n return new PluginModelBuilder(options);\n }\n\n /**\n * Adds an output to the plugin.\n *\n * @param key - Output name\n * @param fn - Function that computes the output value from plugin context\n * @returns PluginModel with the new output added\n *\n * @example\n * .output('model', (ctx) => createModel(ctx.params.columns, ctx.data.state))\n * .output('isReady', (ctx) => ctx.params.columns !== undefined)\n */\n output<const Key extends string, T>(\n key: Key,\n fn: (ctx: PluginRenderCtx<PluginFactoryLike<Data, Params>>) => T,\n ): PluginModelBuilder<Data, Params, Outputs & { [K in Key]: T }, Config, Versions> {\n return new PluginModelBuilder<Data, Params, Outputs & { [K in Key]: T }, Config, Versions>({\n name: this.name,\n dataFn: this.dataFn,\n getDefaultDataFn: this.getDefaultDataFn,\n featureFlags: this.featureFlags,\n outputs: {\n ...this.outputs,\n [key]: fn,\n } as {\n [K in keyof (Outputs & { [P in Key]: T })]: (\n ctx: PluginRenderCtx<PluginFactoryLike<Data, Params>>,\n ) => (Outputs & { [P in Key]: T })[K];\n },\n });\n }\n\n /**\n * Finalizes the plugin definition and returns a PluginFactory.\n *\n * @returns Plugin factory that creates named plugin instances via .create()\n *\n * @example\n * const myPlugin = PluginModel.define({ ... })\n * .output('value', (ctx) => ctx.data.value)\n * .build();\n *\n * // Create a named instance:\n * const table = myPlugin.create({ pluginId: 'mainTable', config: { ... } });\n */\n build(): PluginFactory<Data, Params, Outputs, Config, Versions> {\n return PluginModelFactory[FROM_BUILDER]<Data, Params, Outputs, Config, Versions>({\n name: this.name,\n dataFn: this.dataFn,\n getDefaultDataFn: this.getDefaultDataFn,\n outputs: this.outputs,\n featureFlags: this.featureFlags,\n });\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"plugin_model.cjs","names":["DATA_MODEL_LEGACY_VERSION","DataModelBuilder"],"sources":["../src/plugin_model.ts"],"sourcesContent":["/**\n * PluginModel - Builder for creating plugin types with data model and outputs.\n *\n * Plugins are UI components with their own model logic and persistent state.\n * Block developers register plugin instances via BlockModelV3.plugin() method.\n *\n * @module plugin_model\n */\n\nimport type { BlockCodeKnownFeatureFlags, OutputWithStatus } from \"@milaboratories/pl-model-common\";\nimport {\n type DataModel,\n DataModelBuilder,\n type DataRecoverFn,\n type DataVersioned,\n type TransferTarget,\n} from \"./block_migrations\";\nimport { type PluginName, DATA_MODEL_LEGACY_VERSION } from \"./block_storage\";\nimport type { PluginFactoryLike } from \"./plugin_handle\";\nimport type { PluginRenderCtx } from \"./render\";\n\n/** Symbol for internal builder creation method */\nconst FROM_BUILDER = Symbol(\"fromBuilder\");\n\n/** Symbol for internal plugin model creation — not accessible to external consumers */\nexport const CREATE_PLUGIN_MODEL = Symbol(\"createPluginModel\");\n\n/** Sentinel for PluginInstance without transferAt — no transfer possible. */\nconst NO_TRANSFER_VERSION = \"\";\n\nexport type PluginData = Record<string, unknown>;\nexport type PluginParams = undefined | Record<string, unknown>;\nexport type PluginOutputs = Record<string, unknown>;\nexport type PluginConfig = undefined | Record<string, unknown>;\n\n/**\n * Plugin data model with typed migration chain and config-aware initialization.\n *\n * @typeParam Data - Current (latest) plugin data type\n * @typeParam Versions - Map of version keys to their data types (accumulated by the chain)\n * @typeParam Config - Config type passed to init function (undefined if none)\n */\nexport class PluginDataModel<\n Data extends PluginData,\n Versions extends Record<string, unknown> = {},\n Config = undefined,\n> {\n readonly dataModel: DataModel<Data>;\n private readonly configInitFn: (config?: Config) => Data;\n\n /** @internal Phantom field to anchor the Versions type parameter. */\n declare readonly __versions?: Versions;\n\n private constructor(dataModel: DataModel<Data>, configInitFn: (config?: Config) => Data) {\n this.dataModel = dataModel;\n this.configInitFn = configInitFn;\n }\n\n /** @internal */\n static [FROM_BUILDER]<Data extends PluginData, Versions extends Record<string, unknown>, Config>(\n dataModel: DataModel<Data>,\n configInitFn: (config?: Config) => Data,\n ): PluginDataModel<Data, Versions, Config> {\n return new PluginDataModel<Data, Versions, Config>(dataModel, configInitFn);\n }\n\n /** Create fresh data with optional config. */\n getDefaultData(config?: Config): DataVersioned<Data> {\n const data = this.configInitFn(config);\n return { version: this.dataModel.version, data };\n }\n}\n\n/** Internal state for plugin data model chain. */\ntype PluginChainState = {\n initialVersion: string;\n migrations: Array<{ toVersion: string; fn: (data: unknown) => unknown }>;\n recoverFn?: (version: string, data: unknown) => unknown;\n recoverAtIndex?: number;\n};\n\n/** Version → persisted data shape for each migration step (third generic of `PluginModel.define` when `data` is a `PluginDataModel`). */\nexport type PluginDataModelVersions<\n M extends PluginDataModel<PluginData, Record<string, unknown>, unknown>,\n> = M extends PluginDataModel<PluginData, infer Versions, unknown> ? Versions : never;\n\n/**\n * Builder for creating PluginDataModel with type-safe migrations.\n * Mirrors DataModelBuilder — same .from(), .migrate(), .recover(), .init() chain.\n *\n * @example\n * const pluginData = new PluginDataModelBuilder()\n * .from<TableData>(\"v1\")\n * .migrate<FilteredTableData>(\"v2\", (v1) => ({ ...v1, filters: [] }))\n * .init<TableConfig>((config?) => ({\n * state: createDefaultState(config?.ops),\n * filters: [],\n * }));\n */\nexport class PluginDataModelBuilder {\n from<Data extends PluginData, const V extends string>(\n version: V,\n ): PluginDataModelInitialChain<Data, Record<V, Data>> {\n return PluginDataModelInitialChain[FROM_BUILDER]<Data, Record<V, Data>>({\n initialVersion: version,\n migrations: [],\n });\n }\n}\n\n/**\n * Chain returned by .migrate(). Supports .migrate(), .recover(), .init().\n * No .upgradeLegacy() — that is only available on the initial chain.\n */\nexport class PluginDataModelChain<\n Data extends PluginData,\n Versions extends Record<string, unknown>,\n> {\n protected constructor(protected readonly state: PluginChainState) {}\n\n /** @internal */\n static [FROM_BUILDER]<Data extends PluginData, Versions extends Record<string, unknown>>(\n state: PluginChainState,\n ): PluginDataModelChain<Data, Versions> {\n return new PluginDataModelChain(state);\n }\n\n /**\n * Add a migration step transforming data from the current version to the next.\n */\n migrate<Next extends PluginData, const NextV extends string>(\n version: NextV,\n fn: (current: Data) => Next,\n ): PluginDataModelChain<Next, Versions & Record<NextV, Next>> {\n return PluginDataModelChain[FROM_BUILDER]<Next, Versions & Record<NextV, Next>>({\n ...this.state,\n migrations: [\n ...this.state.migrations,\n { toVersion: version, fn: fn as (data: unknown) => unknown },\n ],\n });\n }\n\n /**\n * Set a recovery handler for unknown or legacy versions.\n * Can only be called once — the returned chain has no recover() method.\n */\n recover(fn: DataRecoverFn<Data>): PluginDataModelWithRecover<Data, Versions> {\n return PluginDataModelWithRecover[FROM_BUILDER]<Data, Versions>({\n ...this.state,\n recoverFn: fn as (version: string, data: unknown) => unknown,\n recoverAtIndex: this.state.migrations.length,\n });\n }\n\n /** Finalize the PluginDataModel. */\n init<Config = undefined>(fn: (config?: Config) => Data): PluginDataModel<Data, Versions, Config> {\n return buildPluginDataModel(this.state, fn);\n }\n}\n\n/**\n * Initial chain returned by new PluginDataModelBuilder().from().\n * Extends PluginDataModelChain with .upgradeLegacy() — available only before\n * any .migrate() calls, matching the block's DataModelInitialChain pattern.\n */\nexport class PluginDataModelInitialChain<\n Data extends PluginData,\n Versions extends Record<string, unknown>,\n> extends PluginDataModelChain<Data, Versions> {\n /** @internal */\n static override [FROM_BUILDER]<Data extends PluginData, Versions extends Record<string, unknown>>(\n state: PluginChainState,\n ): PluginDataModelInitialChain<Data, Versions> {\n return new PluginDataModelInitialChain(state);\n }\n\n /**\n * Handle data from a previous plugin type occupying this slot.\n * Prepends a migration from DATA_MODEL_LEGACY_VERSION to the initial version.\n * When a plugin type changes, the old data arrives with DATA_MODEL_LEGACY_VERSION —\n * this function transforms it into the initial version, then the normal chain runs.\n *\n * Must be called right after .from() — not available after .migrate().\n * Mutually exclusive with recover().\n *\n * @param fn - Transform from old plugin's raw data to this plugin's initial data type\n */\n upgradeLegacy(fn: (data: unknown) => Data): PluginDataModelWithRecover<Data, Versions> {\n return PluginDataModelWithRecover[FROM_BUILDER]<Data, Versions>({\n ...this.state,\n migrations: [\n { toVersion: this.state.initialVersion, fn: fn as (data: unknown) => unknown },\n ...this.state.migrations,\n ],\n initialVersion: DATA_MODEL_LEGACY_VERSION,\n });\n }\n}\n\n/**\n * Chain after .recover() — supports .migrate(), .init(). No second recover().\n */\nexport class PluginDataModelWithRecover<\n Data extends PluginData,\n Versions extends Record<string, unknown>,\n> {\n private constructor(private readonly state: PluginChainState) {}\n\n /** @internal */\n static [FROM_BUILDER]<Data extends PluginData, Versions extends Record<string, unknown>>(\n state: PluginChainState,\n ): PluginDataModelWithRecover<Data, Versions> {\n return new PluginDataModelWithRecover(state);\n }\n\n migrate<Next extends PluginData, const NextV extends string>(\n version: NextV,\n fn: (current: Data) => Next,\n ): PluginDataModelWithRecover<Next, Versions & Record<NextV, Next>> {\n return PluginDataModelWithRecover[FROM_BUILDER]<Next, Versions & Record<NextV, Next>>({\n ...this.state,\n migrations: [\n ...this.state.migrations,\n { toVersion: version, fn: fn as (data: unknown) => unknown },\n ],\n });\n }\n\n init<Config = undefined>(fn: (config?: Config) => Data): PluginDataModel<Data, Versions, Config> {\n return buildPluginDataModel(this.state, fn);\n }\n}\n\n/**\n * Builds a PluginDataModel by replaying the stored chain state through DataModelBuilder.\n * @internal\n */\nfunction buildPluginDataModel<\n Data extends PluginData,\n Versions extends Record<string, unknown>,\n Config,\n>(\n state: PluginChainState,\n configInitFn: (config?: Config) => Data,\n): PluginDataModel<Data, Versions, Config> {\n // Build inner DataModel by replaying migrations through DataModelBuilder chain.\n // Uses `any` internally — type safety is maintained by the public chain types.\n let chain: any = new DataModelBuilder().from(state.initialVersion);\n\n for (let i = 0; i < state.migrations.length; i++) {\n if (state.recoverFn !== undefined && state.recoverAtIndex === i) {\n chain = chain.recover(state.recoverFn);\n }\n chain = chain.migrate(state.migrations[i].toVersion, state.migrations[i].fn);\n }\n\n // If recover was placed at or after the last migration\n if (state.recoverFn !== undefined && state.recoverAtIndex === state.migrations.length) {\n chain = chain.recover(state.recoverFn);\n }\n\n const dataModel: DataModel<Data> = chain.init(() => configInitFn());\n\n return PluginDataModel[FROM_BUILDER]<Data, Versions, Config>(dataModel, configInitFn);\n}\n\n/**\n * A named plugin instance created by `factory.create({ pluginId, ... })`.\n * Passed to both `.transfer()` (on migration chain) and `.plugin()` (on BlockModelV3).\n * Implements TransferTarget so the migration chain can accept it.\n *\n * @typeParam Id - Plugin instance ID literal type\n * @typeParam Data - Plugin data type\n * @typeParam Params - Plugin params type\n * @typeParam Outputs - Plugin outputs type\n * @typeParam TransferData - Type of data entering the plugin via transfer (never if no transfer)\n */\nexport class PluginInstance<\n Id extends string = string,\n Data extends PluginData = PluginData,\n Params extends PluginParams = undefined,\n Outputs extends PluginOutputs = PluginOutputs,\n TransferData = never,\n> implements TransferTarget<Id, TransferData> {\n readonly id: Id;\n readonly transferVersion: string;\n /** @internal Phantom for type inference of Data/Params/Outputs; never set at runtime. */\n readonly __instanceTypes?: { data: Data; params: Params; outputs: Outputs };\n private readonly factory: PluginModelFactory<Data, Params, Outputs, any, any>;\n private readonly config: any;\n\n private constructor(\n id: Id,\n factory: PluginModelFactory<Data, Params, Outputs, any, any>,\n transferVersion: string,\n config?: any,\n ) {\n this.id = id;\n this.factory = factory;\n this.transferVersion = transferVersion;\n this.config = config;\n }\n\n /** @internal */\n static [FROM_BUILDER]<\n Id extends string,\n Data extends PluginData,\n Params extends PluginParams,\n Outputs extends PluginOutputs,\n TransferData,\n >(\n id: Id,\n factory: PluginModelFactory<Data, Params, Outputs, any, any>,\n transferVersion: string,\n config?: any,\n ): PluginInstance<Id, Data, Params, Outputs, TransferData> {\n return new PluginInstance(id, factory, transferVersion, config);\n }\n\n /** @internal Create a PluginModel from this instance. Used by BlockModelV3.plugin(). */\n [CREATE_PLUGIN_MODEL](): PluginModel<Data, Params, Outputs> {\n return this.factory[CREATE_PLUGIN_MODEL](this.config);\n }\n}\n\n/**\n * Configured plugin instance returned by PluginModelFactory[CREATE_PLUGIN_MODEL]().\n * Contains the plugin's name, data model, and output definitions.\n */\nexport class PluginModel<\n Data extends PluginData = PluginData,\n Params extends PluginParams = undefined,\n Outputs extends PluginOutputs = PluginOutputs,\n> {\n /** Globally unique plugin name */\n readonly name: PluginName;\n /** Data model instance for this plugin */\n readonly dataModel: DataModel<Data>;\n /** Output definitions - functions that compute outputs from plugin context */\n readonly outputs: {\n [K in keyof Outputs]: (ctx: PluginRenderCtx<PluginFactoryLike<Data, Params>>) => Outputs[K];\n };\n /** Per-output flags (e.g. withStatus) */\n readonly outputFlags: Record<string, { withStatus: boolean }>;\n /** Feature flags declared by this plugin */\n readonly featureFlags?: BlockCodeKnownFeatureFlags;\n /** Create fresh default data. Config (if any) is captured at creation time. */\n readonly getDefaultData: () => DataVersioned<Data>;\n\n private constructor(options: {\n name: PluginName;\n dataModel: DataModel<Data>;\n outputs: {\n [K in keyof Outputs]: (ctx: PluginRenderCtx<PluginFactoryLike<Data, Params>>) => Outputs[K];\n };\n outputFlags: Record<string, { withStatus: boolean }>;\n featureFlags?: BlockCodeKnownFeatureFlags;\n getDefaultData: () => DataVersioned<Data>;\n }) {\n this.name = options.name;\n this.dataModel = options.dataModel;\n this.outputs = options.outputs;\n this.outputFlags = options.outputFlags;\n this.featureFlags = options.featureFlags;\n this.getDefaultData = options.getDefaultData;\n }\n\n /**\n * Internal method for creating PluginModel from factory.\n * Uses Symbol key to prevent external access.\n * @internal\n */\n static [FROM_BUILDER]<\n Data extends PluginData,\n Params extends PluginParams,\n Outputs extends PluginOutputs,\n >(options: {\n name: PluginName;\n dataModel: DataModel<Data>;\n outputs: {\n [K in keyof Outputs]: (ctx: PluginRenderCtx<PluginFactoryLike<Data, Params>>) => Outputs[K];\n };\n outputFlags: Record<string, { withStatus: boolean }>;\n featureFlags?: BlockCodeKnownFeatureFlags;\n getDefaultData: () => DataVersioned<Data>;\n }): PluginModel<Data, Params, Outputs> {\n return new PluginModel<Data, Params, Outputs>(options);\n }\n\n /**\n * Creates a new PluginModelBuilder with a PluginDataModel (supports transfer / config).\n *\n * @example\n * const pluginData = new PluginDataModelBuilder().from<TableData>(\"v1\")\n * .migrate<FilteredTableData>(\"v2\", (v1) => ({ ...v1, filters: [] }))\n * .init<TableConfig>((config?) => ({\n * state: createDefaultState(config?.ops),\n * filters: [],\n * }));\n *\n * const myPlugin = PluginModel.define({\n * name: 'myPlugin' as PluginName,\n * data: pluginData,\n * }).build();\n */\n static define<\n Data extends PluginData,\n Params extends PluginParams = undefined,\n Versions extends Record<string, unknown> = {},\n Config extends PluginConfig = undefined,\n >(options: {\n name: PluginName;\n data: PluginDataModel<Data, Versions, Config>;\n featureFlags?: BlockCodeKnownFeatureFlags;\n }): PluginModelBuilder<Data, Params, {}, Config, Versions>;\n /**\n * Creates a new PluginModelBuilder with a data model factory function (backward compatible).\n *\n * @example\n * const myPlugin = PluginModel.define({\n * name: 'myPlugin' as PluginName,\n * data: (cfg) => dataModelChain.init(() => ({ value: cfg.defaultValue })),\n * }).build();\n */\n static define<\n Data extends PluginData,\n Params extends PluginParams = undefined,\n Config extends PluginConfig = undefined,\n >(options: {\n name: PluginName;\n data: (config?: Config) => DataModel<Data>;\n featureFlags?: BlockCodeKnownFeatureFlags;\n }): PluginModelBuilder<Data, Params, {}, Config, {}>;\n static define(options: {\n name: PluginName;\n data: PluginDataModel<any, any, any> | ((config?: any) => DataModel<any>);\n featureFlags?: BlockCodeKnownFeatureFlags;\n }): PluginModelBuilder {\n if (options.data instanceof PluginDataModel) {\n const pdm = options.data;\n return PluginModelBuilder[FROM_BUILDER]({\n name: options.name,\n dataFn: () => pdm.dataModel,\n getDefaultDataFn: (config: any) => pdm.getDefaultData(config),\n featureFlags: options.featureFlags,\n });\n }\n return PluginModelBuilder[FROM_BUILDER]({\n name: options.name,\n dataFn: options.data,\n featureFlags: options.featureFlags,\n });\n }\n}\n\n/** Plugin factory returned by PluginModelBuilder.build(). */\nexport interface PluginFactory<\n Data extends PluginData = PluginData,\n Params extends PluginParams = undefined,\n Outputs extends PluginOutputs = PluginOutputs,\n Config extends PluginConfig = undefined,\n Versions extends Record<string, unknown> = {},\n> extends PluginFactoryLike<Data, Params, Outputs> {\n /** Create a named plugin instance, optionally with transfer at a specific version. */\n create<const Id extends string, const V extends string & keyof Versions = never>(options: {\n pluginId: Id;\n transferAt?: V;\n config?: Config;\n }): PluginInstance<Id, Data, Params, Outputs, Versions[V]>;\n\n /**\n * @internal Phantom field for structural type extraction.\n * Enables InferFactoryData/InferFactoryOutputs to work via PluginFactoryLike.\n */\n readonly __types?: {\n data: Data;\n params: Params;\n outputs: Outputs;\n config: Config;\n versions: Versions;\n };\n}\n\nclass PluginModelFactory<\n Data extends PluginData = PluginData,\n Params extends PluginParams = undefined,\n Outputs extends PluginOutputs = PluginOutputs,\n Config extends PluginConfig = undefined,\n Versions extends Record<string, unknown> = {},\n> implements PluginFactory<Data, Params, Outputs, Config, Versions> {\n private readonly name: PluginName;\n private readonly dataFn: (config?: Config) => DataModel<Data>;\n private readonly getDefaultDataFn?: (config?: Config) => DataVersioned<Data>;\n readonly outputs: {\n [K in keyof Outputs]: (ctx: PluginRenderCtx<PluginFactoryLike<Data, Params>>) => Outputs[K];\n };\n private readonly outputFlags: Record<string, { withStatus: boolean }>;\n private readonly featureFlags?: BlockCodeKnownFeatureFlags;\n\n private constructor(options: {\n name: PluginName;\n dataFn: (config?: Config) => DataModel<Data>;\n getDefaultDataFn?: (config?: Config) => DataVersioned<Data>;\n outputs: {\n [K in keyof Outputs]: (ctx: PluginRenderCtx<PluginFactoryLike<Data, Params>>) => Outputs[K];\n };\n outputFlags: Record<string, { withStatus: boolean }>;\n featureFlags?: BlockCodeKnownFeatureFlags;\n }) {\n this.name = options.name;\n this.dataFn = options.dataFn;\n this.getDefaultDataFn = options.getDefaultDataFn;\n this.outputs = options.outputs;\n this.outputFlags = options.outputFlags;\n this.featureFlags = options.featureFlags;\n }\n\n /** @internal */\n static [FROM_BUILDER]<\n Data extends PluginData,\n Params extends PluginParams,\n Outputs extends PluginOutputs,\n Config extends PluginConfig,\n Versions extends Record<string, unknown>,\n >(options: {\n name: PluginName;\n dataFn: (config?: Config) => DataModel<Data>;\n getDefaultDataFn?: (config?: Config) => DataVersioned<Data>;\n outputs: {\n [K in keyof Outputs]: (ctx: PluginRenderCtx<PluginFactoryLike<Data, Params>>) => Outputs[K];\n };\n outputFlags: Record<string, { withStatus: boolean }>;\n featureFlags?: BlockCodeKnownFeatureFlags;\n }): PluginModelFactory<Data, Params, Outputs, Config, Versions> {\n return new PluginModelFactory(options);\n }\n\n create<const Id extends string, const V extends string & keyof Versions = never>(options: {\n pluginId: Id;\n transferAt?: V;\n config?: Config;\n }): PluginInstance<Id, Data, Params, Outputs, Versions[V]> {\n const transferVersion = options.transferAt ?? NO_TRANSFER_VERSION;\n return PluginInstance[FROM_BUILDER]<Id, Data, Params, Outputs, Versions[V]>(\n options.pluginId as Id,\n this as any,\n transferVersion,\n options.config,\n );\n }\n\n /** @internal Create a PluginModel from config. Config is captured in getDefaultData closure. */\n [CREATE_PLUGIN_MODEL](config?: Config): PluginModel<Data, Params, Outputs> {\n const dataModel = this.dataFn(config);\n const getDefaultDataFn = this.getDefaultDataFn;\n return PluginModel[FROM_BUILDER]<Data, Params, Outputs>({\n name: this.name,\n dataModel,\n outputs: this.outputs,\n outputFlags: this.outputFlags,\n featureFlags: this.featureFlags,\n getDefaultData: getDefaultDataFn\n ? () => getDefaultDataFn(config)\n : () => dataModel.getDefaultData(),\n });\n }\n}\n\n/**\n * Builder for creating PluginType with type-safe output definitions.\n *\n * Use `PluginModel.define()` to create a builder instance.\n *\n * @typeParam Data - Plugin's persistent data type\n * @typeParam Params - Params derived from block's RenderCtx (optional)\n * @typeParam Config - Static configuration passed to plugin factory (optional)\n * @typeParam Outputs - Accumulated output types\n * @typeParam Versions - Version map from PluginDataModel (empty for function-based data)\n *\n * @example\n * const dataTable = PluginModel.define({\n * name: 'dataTable' as PluginName,\n * data: pluginDataModel,\n * })\n * .output('model', (ctx) => createTableModel(ctx))\n * .build();\n */\nclass PluginModelBuilder<\n Data extends PluginData = PluginData,\n Params extends PluginParams = undefined,\n Outputs extends PluginOutputs = PluginOutputs,\n Config extends PluginConfig = undefined,\n Versions extends Record<string, unknown> = {},\n> {\n private readonly name: PluginName;\n private readonly dataFn: (config?: Config) => DataModel<Data>;\n private readonly getDefaultDataFn?: (config?: Config) => DataVersioned<Data>;\n private readonly outputs: {\n [K in keyof Outputs]: (ctx: PluginRenderCtx<PluginFactoryLike<Data, Params>>) => Outputs[K];\n };\n private readonly outputFlags: Record<string, { withStatus: boolean }>;\n private readonly featureFlags?: BlockCodeKnownFeatureFlags;\n\n private constructor(options: {\n name: PluginName;\n dataFn: (config?: Config) => DataModel<Data>;\n getDefaultDataFn?: (config?: Config) => DataVersioned<Data>;\n outputs?: {\n [K in keyof Outputs]: (ctx: PluginRenderCtx<PluginFactoryLike<Data, Params>>) => Outputs[K];\n };\n outputFlags?: Record<string, { withStatus: boolean }>;\n featureFlags?: BlockCodeKnownFeatureFlags;\n }) {\n this.name = options.name;\n this.dataFn = options.dataFn;\n this.getDefaultDataFn = options.getDefaultDataFn;\n this.outputs =\n options.outputs ??\n ({} as {\n [K in keyof Outputs]: (ctx: PluginRenderCtx<PluginFactoryLike<Data, Params>>) => Outputs[K];\n });\n this.outputFlags = options.outputFlags ?? {};\n this.featureFlags = options.featureFlags;\n }\n\n /** @internal */\n static [FROM_BUILDER]<\n Data extends PluginData,\n Params extends PluginParams,\n Outputs extends PluginOutputs,\n Config extends PluginConfig,\n Versions extends Record<string, unknown> = {},\n >(options: {\n name: PluginName;\n dataFn: (config?: Config) => DataModel<Data>;\n getDefaultDataFn?: (config?: Config) => DataVersioned<Data>;\n outputs?: {\n [K in keyof Outputs]: (ctx: PluginRenderCtx<PluginFactoryLike<Data, Params>>) => Outputs[K];\n };\n outputFlags?: Record<string, { withStatus: boolean }>;\n featureFlags?: BlockCodeKnownFeatureFlags;\n }): PluginModelBuilder<Data, Params, Outputs, Config, Versions> {\n return new PluginModelBuilder(options);\n }\n\n /**\n * Adds an output to the plugin.\n *\n * @param key - Output name\n * @param fn - Function that computes the output value from plugin context\n * @returns PluginModel with the new output added\n *\n * @example\n * .output('model', (ctx) => createModel(ctx.params.columns, ctx.data.state))\n * .output('isReady', (ctx) => ctx.params.columns !== undefined)\n */\n output<const Key extends string, T>(\n key: Key,\n fn: (ctx: PluginRenderCtx<PluginFactoryLike<Data, Params>>) => T,\n ): PluginModelBuilder<Data, Params, Outputs & { [K in Key]: T }, Config, Versions> {\n return new PluginModelBuilder<Data, Params, Outputs & { [K in Key]: T }, Config, Versions>({\n name: this.name,\n dataFn: this.dataFn,\n getDefaultDataFn: this.getDefaultDataFn,\n featureFlags: this.featureFlags,\n outputs: {\n ...this.outputs,\n [key]: fn,\n } as {\n [K in keyof (Outputs & { [P in Key]: T })]: (\n ctx: PluginRenderCtx<PluginFactoryLike<Data, Params>>,\n ) => (Outputs & { [P in Key]: T })[K];\n },\n outputFlags: { ...this.outputFlags, [key]: { withStatus: false } },\n });\n }\n\n /**\n * Adds an output wrapped with status information to the plugin.\n *\n * The UI receives the full {@link OutputWithStatus} object instead of an unwrapped value,\n * allowing it to distinguish between pending, success, and error states.\n *\n * @param key - Output name\n * @param fn - Function that computes the output value from plugin context\n * @returns PluginModel with the new status-wrapped output added\n *\n * @example\n * .outputWithStatus('table', (ctx) => {\n * const pCols = ctx.params.pFrame?.getPColumns();\n * if (pCols === undefined) return undefined;\n * return createPlDataTableV2(ctx, pCols, ctx.data.tableState);\n * })\n */\n outputWithStatus<const Key extends string, T>(\n key: Key,\n fn: (ctx: PluginRenderCtx<PluginFactoryLike<Data, Params>>) => T,\n ): PluginModelBuilder<\n Data,\n Params,\n Outputs & { [K in Key]: OutputWithStatus<T> },\n Config,\n Versions\n > {\n return new PluginModelBuilder<\n Data,\n Params,\n Outputs & { [K in Key]: OutputWithStatus<T> },\n Config,\n Versions\n >({\n name: this.name,\n dataFn: this.dataFn,\n getDefaultDataFn: this.getDefaultDataFn,\n featureFlags: this.featureFlags,\n outputs: {\n ...this.outputs,\n [key]: fn,\n } as {\n [K in keyof (Outputs & { [P in Key]: OutputWithStatus<T> })]: (\n ctx: PluginRenderCtx<PluginFactoryLike<Data, Params>>,\n ) => (Outputs & { [P in Key]: OutputWithStatus<T> })[K];\n },\n outputFlags: { ...this.outputFlags, [key]: { withStatus: true } },\n });\n }\n\n /**\n * Finalizes the plugin definition and returns a PluginFactory.\n *\n * @returns Plugin factory that creates named plugin instances via .create()\n *\n * @example\n * const myPlugin = PluginModel.define({ ... })\n * .output('value', (ctx) => ctx.data.value)\n * .build();\n *\n * // Create a named instance:\n * const table = myPlugin.create({ pluginId: 'mainTable', config: { ... } });\n */\n build(): PluginFactory<Data, Params, Outputs, Config, Versions> {\n return PluginModelFactory[FROM_BUILDER]<Data, Params, Outputs, Config, Versions>({\n name: this.name,\n dataFn: this.dataFn,\n getDefaultDataFn: this.getDefaultDataFn,\n outputs: this.outputs,\n outputFlags: this.outputFlags,\n featureFlags: this.featureFlags,\n });\n }\n}\n"],"mappings":"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"}
@@ -3,7 +3,7 @@ import { DataModel, DataRecoverFn, DataVersioned, TransferTarget } from "./block
3
3
  import { PluginName } from "./block_storage.js";
4
4
  import { PluginRenderCtx } from "./render/api.js";
5
5
  import "./render/index.js";
6
- import { BlockCodeKnownFeatureFlags } from "@milaboratories/pl-model-common";
6
+ import { BlockCodeKnownFeatureFlags, OutputWithStatus } from "@milaboratories/pl-model-common";
7
7
 
8
8
  //#region src/plugin_model.d.ts
9
9
  /** Symbol for internal builder creation method */
@@ -42,6 +42,8 @@ type PluginChainState = {
42
42
  recoverFn?: (version: string, data: unknown) => unknown;
43
43
  recoverAtIndex?: number;
44
44
  };
45
+ /** Version → persisted data shape for each migration step (third generic of `PluginModel.define` when `data` is a `PluginDataModel`). */
46
+ type PluginDataModelVersions<M extends PluginDataModel<PluginData, Record<string, unknown>, unknown>> = M extends PluginDataModel<PluginData, infer Versions, unknown> ? Versions : never;
45
47
  /**
46
48
  * Builder for creating PluginDataModel with type-safe migrations.
47
49
  * Mirrors DataModelBuilder — same .from(), .migrate(), .recover(), .init() chain.
@@ -125,8 +127,12 @@ declare class PluginDataModelWithRecover<Data extends PluginData, Versions exten
125
127
  declare class PluginInstance<Id extends string = string, Data extends PluginData = PluginData, Params extends PluginParams = undefined, Outputs extends PluginOutputs = PluginOutputs, TransferData = never> implements TransferTarget<Id, TransferData> {
126
128
  readonly id: Id;
127
129
  readonly transferVersion: string;
128
- /** @internal */
129
- readonly __transferBrand?: TransferData;
130
+ /** @internal Phantom for type inference of Data/Params/Outputs; never set at runtime. */
131
+ readonly __instanceTypes?: {
132
+ data: Data;
133
+ params: Params;
134
+ outputs: Outputs;
135
+ };
130
136
  private readonly factory;
131
137
  private readonly config;
132
138
  private constructor();
@@ -146,6 +152,10 @@ declare class PluginModel<Data extends PluginData = PluginData, Params extends P
146
152
  readonly dataModel: DataModel<Data>;
147
153
  /** Output definitions - functions that compute outputs from plugin context */
148
154
  readonly outputs: { [K in keyof Outputs]: (ctx: PluginRenderCtx<PluginFactoryLike<Data, Params>>) => Outputs[K] };
155
+ /** Per-output flags (e.g. withStatus) */
156
+ readonly outputFlags: Record<string, {
157
+ withStatus: boolean;
158
+ }>;
149
159
  /** Feature flags declared by this plugin */
150
160
  readonly featureFlags?: BlockCodeKnownFeatureFlags;
151
161
  /** Create fresh default data. Config (if any) is captured at creation time. */
@@ -160,6 +170,9 @@ declare class PluginModel<Data extends PluginData = PluginData, Params extends P
160
170
  name: PluginName;
161
171
  dataModel: DataModel<Data>;
162
172
  outputs: { [K in keyof Outputs]: (ctx: PluginRenderCtx<PluginFactoryLike<Data, Params>>) => Outputs[K] };
173
+ outputFlags: Record<string, {
174
+ withStatus: boolean;
175
+ }>;
163
176
  featureFlags?: BlockCodeKnownFeatureFlags;
164
177
  getDefaultData: () => DataVersioned<Data>;
165
178
  }): PluginModel<Data, Params, Outputs>;
@@ -224,6 +237,7 @@ declare class PluginModelFactory<Data extends PluginData = PluginData, Params ex
224
237
  private readonly dataFn;
225
238
  private readonly getDefaultDataFn?;
226
239
  readonly outputs: { [K in keyof Outputs]: (ctx: PluginRenderCtx<PluginFactoryLike<Data, Params>>) => Outputs[K] };
240
+ private readonly outputFlags;
227
241
  private readonly featureFlags?;
228
242
  private constructor();
229
243
  /** @internal */
@@ -232,6 +246,9 @@ declare class PluginModelFactory<Data extends PluginData = PluginData, Params ex
232
246
  dataFn: (config?: Config) => DataModel<Data>;
233
247
  getDefaultDataFn?: (config?: Config) => DataVersioned<Data>;
234
248
  outputs: { [K in keyof Outputs]: (ctx: PluginRenderCtx<PluginFactoryLike<Data, Params>>) => Outputs[K] };
249
+ outputFlags: Record<string, {
250
+ withStatus: boolean;
251
+ }>;
235
252
  featureFlags?: BlockCodeKnownFeatureFlags;
236
253
  }): PluginModelFactory<Data, Params, Outputs, Config, Versions>;
237
254
  create<const Id extends string, const V extends string & keyof Versions = never>(options: {
@@ -266,6 +283,7 @@ declare class PluginModelBuilder<Data extends PluginData = PluginData, Params ex
266
283
  private readonly dataFn;
267
284
  private readonly getDefaultDataFn?;
268
285
  private readonly outputs;
286
+ private readonly outputFlags;
269
287
  private readonly featureFlags?;
270
288
  private constructor();
271
289
  /** @internal */
@@ -274,6 +292,9 @@ declare class PluginModelBuilder<Data extends PluginData = PluginData, Params ex
274
292
  dataFn: (config?: Config) => DataModel<Data>;
275
293
  getDefaultDataFn?: (config?: Config) => DataVersioned<Data>;
276
294
  outputs?: { [K in keyof Outputs]: (ctx: PluginRenderCtx<PluginFactoryLike<Data, Params>>) => Outputs[K] };
295
+ outputFlags?: Record<string, {
296
+ withStatus: boolean;
297
+ }>;
277
298
  featureFlags?: BlockCodeKnownFeatureFlags;
278
299
  }): PluginModelBuilder<Data, Params, Outputs, Config, Versions>;
279
300
  /**
@@ -288,6 +309,24 @@ declare class PluginModelBuilder<Data extends PluginData = PluginData, Params ex
288
309
  * .output('isReady', (ctx) => ctx.params.columns !== undefined)
289
310
  */
290
311
  output<const Key extends string, T>(key: Key, fn: (ctx: PluginRenderCtx<PluginFactoryLike<Data, Params>>) => T): PluginModelBuilder<Data, Params, Outputs & { [K in Key]: T }, Config, Versions>;
312
+ /**
313
+ * Adds an output wrapped with status information to the plugin.
314
+ *
315
+ * The UI receives the full {@link OutputWithStatus} object instead of an unwrapped value,
316
+ * allowing it to distinguish between pending, success, and error states.
317
+ *
318
+ * @param key - Output name
319
+ * @param fn - Function that computes the output value from plugin context
320
+ * @returns PluginModel with the new status-wrapped output added
321
+ *
322
+ * @example
323
+ * .outputWithStatus('table', (ctx) => {
324
+ * const pCols = ctx.params.pFrame?.getPColumns();
325
+ * if (pCols === undefined) return undefined;
326
+ * return createPlDataTableV2(ctx, pCols, ctx.data.tableState);
327
+ * })
328
+ */
329
+ outputWithStatus<const Key extends string, T>(key: Key, fn: (ctx: PluginRenderCtx<PluginFactoryLike<Data, Params>>) => T): PluginModelBuilder<Data, Params, Outputs & { [K in Key]: OutputWithStatus<T> }, Config, Versions>;
291
330
  /**
292
331
  * Finalizes the plugin definition and returns a PluginFactory.
293
332
  *
@@ -304,5 +343,5 @@ declare class PluginModelBuilder<Data extends PluginData = PluginData, Params ex
304
343
  build(): PluginFactory<Data, Params, Outputs, Config, Versions>;
305
344
  }
306
345
  //#endregion
307
- export { PluginConfig, PluginData, PluginDataModel, PluginDataModelBuilder, PluginFactory, PluginInstance, PluginModel, PluginOutputs, PluginParams };
346
+ export { PluginConfig, PluginData, PluginDataModel, PluginDataModelBuilder, PluginDataModelVersions, PluginFactory, PluginInstance, PluginModel, PluginOutputs, PluginParams };
308
347
  //# sourceMappingURL=plugin_model.d.ts.map
@@ -180,8 +180,8 @@ function buildPluginDataModel(state, configInitFn) {
180
180
  var PluginInstance = class PluginInstance {
181
181
  id;
182
182
  transferVersion;
183
- /** @internal */
184
- __transferBrand;
183
+ /** @internal Phantom for type inference of Data/Params/Outputs; never set at runtime. */
184
+ __instanceTypes;
185
185
  factory;
186
186
  config;
187
187
  constructor(id, factory, transferVersion, config) {
@@ -210,6 +210,8 @@ var PluginModel = class PluginModel {
210
210
  dataModel;
211
211
  /** Output definitions - functions that compute outputs from plugin context */
212
212
  outputs;
213
+ /** Per-output flags (e.g. withStatus) */
214
+ outputFlags;
213
215
  /** Feature flags declared by this plugin */
214
216
  featureFlags;
215
217
  /** Create fresh default data. Config (if any) is captured at creation time. */
@@ -218,6 +220,7 @@ var PluginModel = class PluginModel {
218
220
  this.name = options.name;
219
221
  this.dataModel = options.dataModel;
220
222
  this.outputs = options.outputs;
223
+ this.outputFlags = options.outputFlags;
221
224
  this.featureFlags = options.featureFlags;
222
225
  this.getDefaultData = options.getDefaultData;
223
226
  }
@@ -251,12 +254,14 @@ var PluginModelFactory = class PluginModelFactory {
251
254
  dataFn;
252
255
  getDefaultDataFn;
253
256
  outputs;
257
+ outputFlags;
254
258
  featureFlags;
255
259
  constructor(options) {
256
260
  this.name = options.name;
257
261
  this.dataFn = options.dataFn;
258
262
  this.getDefaultDataFn = options.getDefaultDataFn;
259
263
  this.outputs = options.outputs;
264
+ this.outputFlags = options.outputFlags;
260
265
  this.featureFlags = options.featureFlags;
261
266
  }
262
267
  /** @internal */
@@ -275,6 +280,7 @@ var PluginModelFactory = class PluginModelFactory {
275
280
  name: this.name,
276
281
  dataModel,
277
282
  outputs: this.outputs,
283
+ outputFlags: this.outputFlags,
278
284
  featureFlags: this.featureFlags,
279
285
  getDefaultData: getDefaultDataFn ? () => getDefaultDataFn(config) : () => dataModel.getDefaultData()
280
286
  });
@@ -304,12 +310,14 @@ var PluginModelBuilder = class PluginModelBuilder {
304
310
  dataFn;
305
311
  getDefaultDataFn;
306
312
  outputs;
313
+ outputFlags;
307
314
  featureFlags;
308
315
  constructor(options) {
309
316
  this.name = options.name;
310
317
  this.dataFn = options.dataFn;
311
318
  this.getDefaultDataFn = options.getDefaultDataFn;
312
319
  this.outputs = options.outputs ?? {};
320
+ this.outputFlags = options.outputFlags ?? {};
313
321
  this.featureFlags = options.featureFlags;
314
322
  }
315
323
  /** @internal */
@@ -336,6 +344,43 @@ var PluginModelBuilder = class PluginModelBuilder {
336
344
  outputs: {
337
345
  ...this.outputs,
338
346
  [key]: fn
347
+ },
348
+ outputFlags: {
349
+ ...this.outputFlags,
350
+ [key]: { withStatus: false }
351
+ }
352
+ });
353
+ }
354
+ /**
355
+ * Adds an output wrapped with status information to the plugin.
356
+ *
357
+ * The UI receives the full {@link OutputWithStatus} object instead of an unwrapped value,
358
+ * allowing it to distinguish between pending, success, and error states.
359
+ *
360
+ * @param key - Output name
361
+ * @param fn - Function that computes the output value from plugin context
362
+ * @returns PluginModel with the new status-wrapped output added
363
+ *
364
+ * @example
365
+ * .outputWithStatus('table', (ctx) => {
366
+ * const pCols = ctx.params.pFrame?.getPColumns();
367
+ * if (pCols === undefined) return undefined;
368
+ * return createPlDataTableV2(ctx, pCols, ctx.data.tableState);
369
+ * })
370
+ */
371
+ outputWithStatus(key, fn) {
372
+ return new PluginModelBuilder({
373
+ name: this.name,
374
+ dataFn: this.dataFn,
375
+ getDefaultDataFn: this.getDefaultDataFn,
376
+ featureFlags: this.featureFlags,
377
+ outputs: {
378
+ ...this.outputs,
379
+ [key]: fn
380
+ },
381
+ outputFlags: {
382
+ ...this.outputFlags,
383
+ [key]: { withStatus: true }
339
384
  }
340
385
  });
341
386
  }
@@ -358,6 +403,7 @@ var PluginModelBuilder = class PluginModelBuilder {
358
403
  dataFn: this.dataFn,
359
404
  getDefaultDataFn: this.getDefaultDataFn,
360
405
  outputs: this.outputs,
406
+ outputFlags: this.outputFlags,
361
407
  featureFlags: this.featureFlags
362
408
  });
363
409
  }
@@ -1 +1 @@
1
- {"version":3,"file":"plugin_model.js","names":[],"sources":["../src/plugin_model.ts"],"sourcesContent":["/**\n * PluginModel - Builder for creating plugin types with data model and outputs.\n *\n * Plugins are UI components with their own model logic and persistent state.\n * Block developers register plugin instances via BlockModelV3.plugin() method.\n *\n * @module plugin_model\n */\n\nimport type { BlockCodeKnownFeatureFlags } from \"@milaboratories/pl-model-common\";\nimport {\n type DataModel,\n DataModelBuilder,\n type DataRecoverFn,\n type DataVersioned,\n type TransferTarget,\n} from \"./block_migrations\";\nimport { type PluginName, DATA_MODEL_LEGACY_VERSION } from \"./block_storage\";\nimport type { PluginFactoryLike } from \"./plugin_handle\";\nimport type { PluginRenderCtx } from \"./render\";\n\n/** Symbol for internal builder creation method */\nconst FROM_BUILDER = Symbol(\"fromBuilder\");\n\n/** Symbol for internal plugin model creation — not accessible to external consumers */\nexport const CREATE_PLUGIN_MODEL = Symbol(\"createPluginModel\");\n\n/** Sentinel for PluginInstance without transferAt — no transfer possible. */\nconst NO_TRANSFER_VERSION = \"\";\n\nexport type PluginData = Record<string, unknown>;\nexport type PluginParams = undefined | Record<string, unknown>;\nexport type PluginOutputs = Record<string, unknown>;\nexport type PluginConfig = undefined | Record<string, unknown>;\n\n/**\n * Plugin data model with typed migration chain and config-aware initialization.\n *\n * @typeParam Data - Current (latest) plugin data type\n * @typeParam Versions - Map of version keys to their data types (accumulated by the chain)\n * @typeParam Config - Config type passed to init function (undefined if none)\n */\nexport class PluginDataModel<\n Data extends PluginData,\n Versions extends Record<string, unknown> = {},\n Config = undefined,\n> {\n readonly dataModel: DataModel<Data>;\n private readonly configInitFn: (config?: Config) => Data;\n\n /** @internal Phantom field to anchor the Versions type parameter. */\n declare readonly __versions?: Versions;\n\n private constructor(dataModel: DataModel<Data>, configInitFn: (config?: Config) => Data) {\n this.dataModel = dataModel;\n this.configInitFn = configInitFn;\n }\n\n /** @internal */\n static [FROM_BUILDER]<Data extends PluginData, Versions extends Record<string, unknown>, Config>(\n dataModel: DataModel<Data>,\n configInitFn: (config?: Config) => Data,\n ): PluginDataModel<Data, Versions, Config> {\n return new PluginDataModel<Data, Versions, Config>(dataModel, configInitFn);\n }\n\n /** Create fresh data with optional config. */\n getDefaultData(config?: Config): DataVersioned<Data> {\n const data = this.configInitFn(config);\n return { version: this.dataModel.version, data };\n }\n}\n\n/** Internal state for plugin data model chain. */\ntype PluginChainState = {\n initialVersion: string;\n migrations: Array<{ toVersion: string; fn: (data: unknown) => unknown }>;\n recoverFn?: (version: string, data: unknown) => unknown;\n recoverAtIndex?: number;\n};\n\n/**\n * Builder for creating PluginDataModel with type-safe migrations.\n * Mirrors DataModelBuilder — same .from(), .migrate(), .recover(), .init() chain.\n *\n * @example\n * const pluginData = new PluginDataModelBuilder()\n * .from<TableData>(\"v1\")\n * .migrate<FilteredTableData>(\"v2\", (v1) => ({ ...v1, filters: [] }))\n * .init<TableConfig>((config?) => ({\n * state: createDefaultState(config?.ops),\n * filters: [],\n * }));\n */\nexport class PluginDataModelBuilder {\n from<Data extends PluginData, const V extends string>(\n version: V,\n ): PluginDataModelInitialChain<Data, Record<V, Data>> {\n return PluginDataModelInitialChain[FROM_BUILDER]<Data, Record<V, Data>>({\n initialVersion: version,\n migrations: [],\n });\n }\n}\n\n/**\n * Chain returned by .migrate(). Supports .migrate(), .recover(), .init().\n * No .upgradeLegacy() — that is only available on the initial chain.\n */\nexport class PluginDataModelChain<\n Data extends PluginData,\n Versions extends Record<string, unknown>,\n> {\n protected constructor(protected readonly state: PluginChainState) {}\n\n /** @internal */\n static [FROM_BUILDER]<Data extends PluginData, Versions extends Record<string, unknown>>(\n state: PluginChainState,\n ): PluginDataModelChain<Data, Versions> {\n return new PluginDataModelChain(state);\n }\n\n /**\n * Add a migration step transforming data from the current version to the next.\n */\n migrate<Next extends PluginData, const NextV extends string>(\n version: NextV,\n fn: (current: Data) => Next,\n ): PluginDataModelChain<Next, Versions & Record<NextV, Next>> {\n return PluginDataModelChain[FROM_BUILDER]<Next, Versions & Record<NextV, Next>>({\n ...this.state,\n migrations: [\n ...this.state.migrations,\n { toVersion: version, fn: fn as (data: unknown) => unknown },\n ],\n });\n }\n\n /**\n * Set a recovery handler for unknown or legacy versions.\n * Can only be called once — the returned chain has no recover() method.\n */\n recover(fn: DataRecoverFn<Data>): PluginDataModelWithRecover<Data, Versions> {\n return PluginDataModelWithRecover[FROM_BUILDER]<Data, Versions>({\n ...this.state,\n recoverFn: fn as (version: string, data: unknown) => unknown,\n recoverAtIndex: this.state.migrations.length,\n });\n }\n\n /** Finalize the PluginDataModel. */\n init<Config = undefined>(fn: (config?: Config) => Data): PluginDataModel<Data, Versions, Config> {\n return buildPluginDataModel(this.state, fn);\n }\n}\n\n/**\n * Initial chain returned by new PluginDataModelBuilder().from().\n * Extends PluginDataModelChain with .upgradeLegacy() — available only before\n * any .migrate() calls, matching the block's DataModelInitialChain pattern.\n */\nexport class PluginDataModelInitialChain<\n Data extends PluginData,\n Versions extends Record<string, unknown>,\n> extends PluginDataModelChain<Data, Versions> {\n /** @internal */\n static override [FROM_BUILDER]<Data extends PluginData, Versions extends Record<string, unknown>>(\n state: PluginChainState,\n ): PluginDataModelInitialChain<Data, Versions> {\n return new PluginDataModelInitialChain(state);\n }\n\n /**\n * Handle data from a previous plugin type occupying this slot.\n * Prepends a migration from DATA_MODEL_LEGACY_VERSION to the initial version.\n * When a plugin type changes, the old data arrives with DATA_MODEL_LEGACY_VERSION —\n * this function transforms it into the initial version, then the normal chain runs.\n *\n * Must be called right after .from() — not available after .migrate().\n * Mutually exclusive with recover().\n *\n * @param fn - Transform from old plugin's raw data to this plugin's initial data type\n */\n upgradeLegacy(fn: (data: unknown) => Data): PluginDataModelWithRecover<Data, Versions> {\n return PluginDataModelWithRecover[FROM_BUILDER]<Data, Versions>({\n ...this.state,\n migrations: [\n { toVersion: this.state.initialVersion, fn: fn as (data: unknown) => unknown },\n ...this.state.migrations,\n ],\n initialVersion: DATA_MODEL_LEGACY_VERSION,\n });\n }\n}\n\n/**\n * Chain after .recover() — supports .migrate(), .init(). No second recover().\n */\nexport class PluginDataModelWithRecover<\n Data extends PluginData,\n Versions extends Record<string, unknown>,\n> {\n private constructor(private readonly state: PluginChainState) {}\n\n /** @internal */\n static [FROM_BUILDER]<Data extends PluginData, Versions extends Record<string, unknown>>(\n state: PluginChainState,\n ): PluginDataModelWithRecover<Data, Versions> {\n return new PluginDataModelWithRecover(state);\n }\n\n migrate<Next extends PluginData, const NextV extends string>(\n version: NextV,\n fn: (current: Data) => Next,\n ): PluginDataModelWithRecover<Next, Versions & Record<NextV, Next>> {\n return PluginDataModelWithRecover[FROM_BUILDER]<Next, Versions & Record<NextV, Next>>({\n ...this.state,\n migrations: [\n ...this.state.migrations,\n { toVersion: version, fn: fn as (data: unknown) => unknown },\n ],\n });\n }\n\n init<Config = undefined>(fn: (config?: Config) => Data): PluginDataModel<Data, Versions, Config> {\n return buildPluginDataModel(this.state, fn);\n }\n}\n\n/**\n * Builds a PluginDataModel by replaying the stored chain state through DataModelBuilder.\n * @internal\n */\nfunction buildPluginDataModel<\n Data extends PluginData,\n Versions extends Record<string, unknown>,\n Config,\n>(\n state: PluginChainState,\n configInitFn: (config?: Config) => Data,\n): PluginDataModel<Data, Versions, Config> {\n // Build inner DataModel by replaying migrations through DataModelBuilder chain.\n // Uses `any` internally — type safety is maintained by the public chain types.\n let chain: any = new DataModelBuilder().from(state.initialVersion);\n\n for (let i = 0; i < state.migrations.length; i++) {\n if (state.recoverFn !== undefined && state.recoverAtIndex === i) {\n chain = chain.recover(state.recoverFn);\n }\n chain = chain.migrate(state.migrations[i].toVersion, state.migrations[i].fn);\n }\n\n // If recover was placed at or after the last migration\n if (state.recoverFn !== undefined && state.recoverAtIndex === state.migrations.length) {\n chain = chain.recover(state.recoverFn);\n }\n\n const dataModel: DataModel<Data> = chain.init(() => configInitFn());\n\n return PluginDataModel[FROM_BUILDER]<Data, Versions, Config>(dataModel, configInitFn);\n}\n\n/**\n * A named plugin instance created by `factory.create({ pluginId, ... })`.\n * Passed to both `.transfer()` (on migration chain) and `.plugin()` (on BlockModelV3).\n * Implements TransferTarget so the migration chain can accept it.\n *\n * @typeParam Id - Plugin instance ID literal type\n * @typeParam Data - Plugin data type\n * @typeParam Params - Plugin params type\n * @typeParam Outputs - Plugin outputs type\n * @typeParam TransferData - Type of data entering the plugin via transfer (never if no transfer)\n */\nexport class PluginInstance<\n Id extends string = string,\n Data extends PluginData = PluginData,\n Params extends PluginParams = undefined,\n Outputs extends PluginOutputs = PluginOutputs,\n TransferData = never,\n> implements TransferTarget<Id, TransferData> {\n readonly id: Id;\n readonly transferVersion: string;\n /** @internal */\n readonly __transferBrand?: TransferData;\n private readonly factory: PluginModelFactory<Data, Params, Outputs, any, any>;\n private readonly config: any;\n\n private constructor(\n id: Id,\n factory: PluginModelFactory<Data, Params, Outputs, any, any>,\n transferVersion: string,\n config?: any,\n ) {\n this.id = id;\n this.factory = factory;\n this.transferVersion = transferVersion;\n this.config = config;\n }\n\n /** @internal */\n static [FROM_BUILDER]<\n Id extends string,\n Data extends PluginData,\n Params extends PluginParams,\n Outputs extends PluginOutputs,\n TransferData,\n >(\n id: Id,\n factory: PluginModelFactory<Data, Params, Outputs, any, any>,\n transferVersion: string,\n config?: any,\n ): PluginInstance<Id, Data, Params, Outputs, TransferData> {\n return new PluginInstance(id, factory, transferVersion, config);\n }\n\n /** @internal Create a PluginModel from this instance. Used by BlockModelV3.plugin(). */\n [CREATE_PLUGIN_MODEL](): PluginModel<Data, Params, Outputs> {\n return this.factory[CREATE_PLUGIN_MODEL](this.config);\n }\n}\n\n/**\n * Configured plugin instance returned by PluginModelFactory[CREATE_PLUGIN_MODEL]().\n * Contains the plugin's name, data model, and output definitions.\n */\nexport class PluginModel<\n Data extends PluginData = PluginData,\n Params extends PluginParams = undefined,\n Outputs extends PluginOutputs = PluginOutputs,\n> {\n /** Globally unique plugin name */\n readonly name: PluginName;\n /** Data model instance for this plugin */\n readonly dataModel: DataModel<Data>;\n /** Output definitions - functions that compute outputs from plugin context */\n readonly outputs: {\n [K in keyof Outputs]: (ctx: PluginRenderCtx<PluginFactoryLike<Data, Params>>) => Outputs[K];\n };\n /** Feature flags declared by this plugin */\n readonly featureFlags?: BlockCodeKnownFeatureFlags;\n /** Create fresh default data. Config (if any) is captured at creation time. */\n readonly getDefaultData: () => DataVersioned<Data>;\n\n private constructor(options: {\n name: PluginName;\n dataModel: DataModel<Data>;\n outputs: {\n [K in keyof Outputs]: (ctx: PluginRenderCtx<PluginFactoryLike<Data, Params>>) => Outputs[K];\n };\n featureFlags?: BlockCodeKnownFeatureFlags;\n getDefaultData: () => DataVersioned<Data>;\n }) {\n this.name = options.name;\n this.dataModel = options.dataModel;\n this.outputs = options.outputs;\n this.featureFlags = options.featureFlags;\n this.getDefaultData = options.getDefaultData;\n }\n\n /**\n * Internal method for creating PluginModel from factory.\n * Uses Symbol key to prevent external access.\n * @internal\n */\n static [FROM_BUILDER]<\n Data extends PluginData,\n Params extends PluginParams,\n Outputs extends PluginOutputs,\n >(options: {\n name: PluginName;\n dataModel: DataModel<Data>;\n outputs: {\n [K in keyof Outputs]: (ctx: PluginRenderCtx<PluginFactoryLike<Data, Params>>) => Outputs[K];\n };\n featureFlags?: BlockCodeKnownFeatureFlags;\n getDefaultData: () => DataVersioned<Data>;\n }): PluginModel<Data, Params, Outputs> {\n return new PluginModel<Data, Params, Outputs>(options);\n }\n\n /**\n * Creates a new PluginModelBuilder with a PluginDataModel (supports transfer / config).\n *\n * @example\n * const pluginData = new PluginDataModelBuilder().from<TableData>(\"v1\")\n * .migrate<FilteredTableData>(\"v2\", (v1) => ({ ...v1, filters: [] }))\n * .init<TableConfig>((config?) => ({\n * state: createDefaultState(config?.ops),\n * filters: [],\n * }));\n *\n * const myPlugin = PluginModel.define({\n * name: 'myPlugin' as PluginName,\n * data: pluginData,\n * }).build();\n */\n static define<\n Data extends PluginData,\n Params extends PluginParams = undefined,\n Versions extends Record<string, unknown> = {},\n Config extends PluginConfig = undefined,\n >(options: {\n name: PluginName;\n data: PluginDataModel<Data, Versions, Config>;\n featureFlags?: BlockCodeKnownFeatureFlags;\n }): PluginModelBuilder<Data, Params, {}, Config, Versions>;\n /**\n * Creates a new PluginModelBuilder with a data model factory function (backward compatible).\n *\n * @example\n * const myPlugin = PluginModel.define({\n * name: 'myPlugin' as PluginName,\n * data: (cfg) => dataModelChain.init(() => ({ value: cfg.defaultValue })),\n * }).build();\n */\n static define<\n Data extends PluginData,\n Params extends PluginParams = undefined,\n Config extends PluginConfig = undefined,\n >(options: {\n name: PluginName;\n data: (config?: Config) => DataModel<Data>;\n featureFlags?: BlockCodeKnownFeatureFlags;\n }): PluginModelBuilder<Data, Params, {}, Config, {}>;\n static define(options: {\n name: PluginName;\n data: PluginDataModel<any, any, any> | ((config?: any) => DataModel<any>);\n featureFlags?: BlockCodeKnownFeatureFlags;\n }): PluginModelBuilder {\n if (options.data instanceof PluginDataModel) {\n const pdm = options.data;\n return PluginModelBuilder[FROM_BUILDER]({\n name: options.name,\n dataFn: () => pdm.dataModel,\n getDefaultDataFn: (config: any) => pdm.getDefaultData(config),\n featureFlags: options.featureFlags,\n });\n }\n return PluginModelBuilder[FROM_BUILDER]({\n name: options.name,\n dataFn: options.data,\n featureFlags: options.featureFlags,\n });\n }\n}\n\n/** Plugin factory returned by PluginModelBuilder.build(). */\nexport interface PluginFactory<\n Data extends PluginData = PluginData,\n Params extends PluginParams = undefined,\n Outputs extends PluginOutputs = PluginOutputs,\n Config extends PluginConfig = undefined,\n Versions extends Record<string, unknown> = {},\n> extends PluginFactoryLike<Data, Params, Outputs> {\n /** Create a named plugin instance, optionally with transfer at a specific version. */\n create<const Id extends string, const V extends string & keyof Versions = never>(options: {\n pluginId: Id;\n transferAt?: V;\n config?: Config;\n }): PluginInstance<Id, Data, Params, Outputs, Versions[V]>;\n\n /**\n * @internal Phantom field for structural type extraction.\n * Enables InferFactoryData/InferFactoryOutputs to work via PluginFactoryLike.\n */\n readonly __types?: {\n data: Data;\n params: Params;\n outputs: Outputs;\n config: Config;\n versions: Versions;\n };\n}\n\nclass PluginModelFactory<\n Data extends PluginData = PluginData,\n Params extends PluginParams = undefined,\n Outputs extends PluginOutputs = PluginOutputs,\n Config extends PluginConfig = undefined,\n Versions extends Record<string, unknown> = {},\n> implements PluginFactory<Data, Params, Outputs, Config, Versions> {\n private readonly name: PluginName;\n private readonly dataFn: (config?: Config) => DataModel<Data>;\n private readonly getDefaultDataFn?: (config?: Config) => DataVersioned<Data>;\n readonly outputs: {\n [K in keyof Outputs]: (ctx: PluginRenderCtx<PluginFactoryLike<Data, Params>>) => Outputs[K];\n };\n private readonly featureFlags?: BlockCodeKnownFeatureFlags;\n\n private constructor(options: {\n name: PluginName;\n dataFn: (config?: Config) => DataModel<Data>;\n getDefaultDataFn?: (config?: Config) => DataVersioned<Data>;\n outputs: {\n [K in keyof Outputs]: (ctx: PluginRenderCtx<PluginFactoryLike<Data, Params>>) => Outputs[K];\n };\n featureFlags?: BlockCodeKnownFeatureFlags;\n }) {\n this.name = options.name;\n this.dataFn = options.dataFn;\n this.getDefaultDataFn = options.getDefaultDataFn;\n this.outputs = options.outputs;\n this.featureFlags = options.featureFlags;\n }\n\n /** @internal */\n static [FROM_BUILDER]<\n Data extends PluginData,\n Params extends PluginParams,\n Outputs extends PluginOutputs,\n Config extends PluginConfig,\n Versions extends Record<string, unknown>,\n >(options: {\n name: PluginName;\n dataFn: (config?: Config) => DataModel<Data>;\n getDefaultDataFn?: (config?: Config) => DataVersioned<Data>;\n outputs: {\n [K in keyof Outputs]: (ctx: PluginRenderCtx<PluginFactoryLike<Data, Params>>) => Outputs[K];\n };\n featureFlags?: BlockCodeKnownFeatureFlags;\n }): PluginModelFactory<Data, Params, Outputs, Config, Versions> {\n return new PluginModelFactory(options);\n }\n\n create<const Id extends string, const V extends string & keyof Versions = never>(options: {\n pluginId: Id;\n transferAt?: V;\n config?: Config;\n }): PluginInstance<Id, Data, Params, Outputs, Versions[V]> {\n const transferVersion = options.transferAt ?? NO_TRANSFER_VERSION;\n return PluginInstance[FROM_BUILDER]<Id, Data, Params, Outputs, Versions[V]>(\n options.pluginId as Id,\n this as any,\n transferVersion,\n options.config,\n );\n }\n\n /** @internal Create a PluginModel from config. Config is captured in getDefaultData closure. */\n [CREATE_PLUGIN_MODEL](config?: Config): PluginModel<Data, Params, Outputs> {\n const dataModel = this.dataFn(config);\n const getDefaultDataFn = this.getDefaultDataFn;\n return PluginModel[FROM_BUILDER]<Data, Params, Outputs>({\n name: this.name,\n dataModel,\n outputs: this.outputs,\n featureFlags: this.featureFlags,\n getDefaultData: getDefaultDataFn\n ? () => getDefaultDataFn(config)\n : () => dataModel.getDefaultData(),\n });\n }\n}\n\n/**\n * Builder for creating PluginType with type-safe output definitions.\n *\n * Use `PluginModel.define()` to create a builder instance.\n *\n * @typeParam Data - Plugin's persistent data type\n * @typeParam Params - Params derived from block's RenderCtx (optional)\n * @typeParam Config - Static configuration passed to plugin factory (optional)\n * @typeParam Outputs - Accumulated output types\n * @typeParam Versions - Version map from PluginDataModel (empty for function-based data)\n *\n * @example\n * const dataTable = PluginModel.define({\n * name: 'dataTable' as PluginName,\n * data: pluginDataModel,\n * })\n * .output('model', (ctx) => createTableModel(ctx))\n * .build();\n */\nclass PluginModelBuilder<\n Data extends PluginData = PluginData,\n Params extends PluginParams = undefined,\n Outputs extends PluginOutputs = PluginOutputs,\n Config extends PluginConfig = undefined,\n Versions extends Record<string, unknown> = {},\n> {\n private readonly name: PluginName;\n private readonly dataFn: (config?: Config) => DataModel<Data>;\n private readonly getDefaultDataFn?: (config?: Config) => DataVersioned<Data>;\n private readonly outputs: {\n [K in keyof Outputs]: (ctx: PluginRenderCtx<PluginFactoryLike<Data, Params>>) => Outputs[K];\n };\n private readonly featureFlags?: BlockCodeKnownFeatureFlags;\n\n private constructor(options: {\n name: PluginName;\n dataFn: (config?: Config) => DataModel<Data>;\n getDefaultDataFn?: (config?: Config) => DataVersioned<Data>;\n outputs?: {\n [K in keyof Outputs]: (ctx: PluginRenderCtx<PluginFactoryLike<Data, Params>>) => Outputs[K];\n };\n featureFlags?: BlockCodeKnownFeatureFlags;\n }) {\n this.name = options.name;\n this.dataFn = options.dataFn;\n this.getDefaultDataFn = options.getDefaultDataFn;\n this.outputs =\n options.outputs ??\n ({} as {\n [K in keyof Outputs]: (ctx: PluginRenderCtx<PluginFactoryLike<Data, Params>>) => Outputs[K];\n });\n this.featureFlags = options.featureFlags;\n }\n\n /** @internal */\n static [FROM_BUILDER]<\n Data extends PluginData,\n Params extends PluginParams,\n Outputs extends PluginOutputs,\n Config extends PluginConfig,\n Versions extends Record<string, unknown> = {},\n >(options: {\n name: PluginName;\n dataFn: (config?: Config) => DataModel<Data>;\n getDefaultDataFn?: (config?: Config) => DataVersioned<Data>;\n outputs?: {\n [K in keyof Outputs]: (ctx: PluginRenderCtx<PluginFactoryLike<Data, Params>>) => Outputs[K];\n };\n featureFlags?: BlockCodeKnownFeatureFlags;\n }): PluginModelBuilder<Data, Params, Outputs, Config, Versions> {\n return new PluginModelBuilder(options);\n }\n\n /**\n * Adds an output to the plugin.\n *\n * @param key - Output name\n * @param fn - Function that computes the output value from plugin context\n * @returns PluginModel with the new output added\n *\n * @example\n * .output('model', (ctx) => createModel(ctx.params.columns, ctx.data.state))\n * .output('isReady', (ctx) => ctx.params.columns !== undefined)\n */\n output<const Key extends string, T>(\n key: Key,\n fn: (ctx: PluginRenderCtx<PluginFactoryLike<Data, Params>>) => T,\n ): PluginModelBuilder<Data, Params, Outputs & { [K in Key]: T }, Config, Versions> {\n return new PluginModelBuilder<Data, Params, Outputs & { [K in Key]: T }, Config, Versions>({\n name: this.name,\n dataFn: this.dataFn,\n getDefaultDataFn: this.getDefaultDataFn,\n featureFlags: this.featureFlags,\n outputs: {\n ...this.outputs,\n [key]: fn,\n } as {\n [K in keyof (Outputs & { [P in Key]: T })]: (\n ctx: PluginRenderCtx<PluginFactoryLike<Data, Params>>,\n ) => (Outputs & { [P in Key]: T })[K];\n },\n });\n }\n\n /**\n * Finalizes the plugin definition and returns a PluginFactory.\n *\n * @returns Plugin factory that creates named plugin instances via .create()\n *\n * @example\n * const myPlugin = PluginModel.define({ ... })\n * .output('value', (ctx) => ctx.data.value)\n * .build();\n *\n * // Create a named instance:\n * const table = myPlugin.create({ pluginId: 'mainTable', config: { ... } });\n */\n build(): PluginFactory<Data, Params, Outputs, Config, Versions> {\n return PluginModelFactory[FROM_BUILDER]<Data, Params, Outputs, Config, Versions>({\n name: this.name,\n dataFn: this.dataFn,\n getDefaultDataFn: this.getDefaultDataFn,\n outputs: this.outputs,\n featureFlags: this.featureFlags,\n });\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"plugin_model.js","names":[],"sources":["../src/plugin_model.ts"],"sourcesContent":["/**\n * PluginModel - Builder for creating plugin types with data model and outputs.\n *\n * Plugins are UI components with their own model logic and persistent state.\n * Block developers register plugin instances via BlockModelV3.plugin() method.\n *\n * @module plugin_model\n */\n\nimport type { BlockCodeKnownFeatureFlags, OutputWithStatus } from \"@milaboratories/pl-model-common\";\nimport {\n type DataModel,\n DataModelBuilder,\n type DataRecoverFn,\n type DataVersioned,\n type TransferTarget,\n} from \"./block_migrations\";\nimport { type PluginName, DATA_MODEL_LEGACY_VERSION } from \"./block_storage\";\nimport type { PluginFactoryLike } from \"./plugin_handle\";\nimport type { PluginRenderCtx } from \"./render\";\n\n/** Symbol for internal builder creation method */\nconst FROM_BUILDER = Symbol(\"fromBuilder\");\n\n/** Symbol for internal plugin model creation — not accessible to external consumers */\nexport const CREATE_PLUGIN_MODEL = Symbol(\"createPluginModel\");\n\n/** Sentinel for PluginInstance without transferAt — no transfer possible. */\nconst NO_TRANSFER_VERSION = \"\";\n\nexport type PluginData = Record<string, unknown>;\nexport type PluginParams = undefined | Record<string, unknown>;\nexport type PluginOutputs = Record<string, unknown>;\nexport type PluginConfig = undefined | Record<string, unknown>;\n\n/**\n * Plugin data model with typed migration chain and config-aware initialization.\n *\n * @typeParam Data - Current (latest) plugin data type\n * @typeParam Versions - Map of version keys to their data types (accumulated by the chain)\n * @typeParam Config - Config type passed to init function (undefined if none)\n */\nexport class PluginDataModel<\n Data extends PluginData,\n Versions extends Record<string, unknown> = {},\n Config = undefined,\n> {\n readonly dataModel: DataModel<Data>;\n private readonly configInitFn: (config?: Config) => Data;\n\n /** @internal Phantom field to anchor the Versions type parameter. */\n declare readonly __versions?: Versions;\n\n private constructor(dataModel: DataModel<Data>, configInitFn: (config?: Config) => Data) {\n this.dataModel = dataModel;\n this.configInitFn = configInitFn;\n }\n\n /** @internal */\n static [FROM_BUILDER]<Data extends PluginData, Versions extends Record<string, unknown>, Config>(\n dataModel: DataModel<Data>,\n configInitFn: (config?: Config) => Data,\n ): PluginDataModel<Data, Versions, Config> {\n return new PluginDataModel<Data, Versions, Config>(dataModel, configInitFn);\n }\n\n /** Create fresh data with optional config. */\n getDefaultData(config?: Config): DataVersioned<Data> {\n const data = this.configInitFn(config);\n return { version: this.dataModel.version, data };\n }\n}\n\n/** Internal state for plugin data model chain. */\ntype PluginChainState = {\n initialVersion: string;\n migrations: Array<{ toVersion: string; fn: (data: unknown) => unknown }>;\n recoverFn?: (version: string, data: unknown) => unknown;\n recoverAtIndex?: number;\n};\n\n/** Version → persisted data shape for each migration step (third generic of `PluginModel.define` when `data` is a `PluginDataModel`). */\nexport type PluginDataModelVersions<\n M extends PluginDataModel<PluginData, Record<string, unknown>, unknown>,\n> = M extends PluginDataModel<PluginData, infer Versions, unknown> ? Versions : never;\n\n/**\n * Builder for creating PluginDataModel with type-safe migrations.\n * Mirrors DataModelBuilder — same .from(), .migrate(), .recover(), .init() chain.\n *\n * @example\n * const pluginData = new PluginDataModelBuilder()\n * .from<TableData>(\"v1\")\n * .migrate<FilteredTableData>(\"v2\", (v1) => ({ ...v1, filters: [] }))\n * .init<TableConfig>((config?) => ({\n * state: createDefaultState(config?.ops),\n * filters: [],\n * }));\n */\nexport class PluginDataModelBuilder {\n from<Data extends PluginData, const V extends string>(\n version: V,\n ): PluginDataModelInitialChain<Data, Record<V, Data>> {\n return PluginDataModelInitialChain[FROM_BUILDER]<Data, Record<V, Data>>({\n initialVersion: version,\n migrations: [],\n });\n }\n}\n\n/**\n * Chain returned by .migrate(). Supports .migrate(), .recover(), .init().\n * No .upgradeLegacy() — that is only available on the initial chain.\n */\nexport class PluginDataModelChain<\n Data extends PluginData,\n Versions extends Record<string, unknown>,\n> {\n protected constructor(protected readonly state: PluginChainState) {}\n\n /** @internal */\n static [FROM_BUILDER]<Data extends PluginData, Versions extends Record<string, unknown>>(\n state: PluginChainState,\n ): PluginDataModelChain<Data, Versions> {\n return new PluginDataModelChain(state);\n }\n\n /**\n * Add a migration step transforming data from the current version to the next.\n */\n migrate<Next extends PluginData, const NextV extends string>(\n version: NextV,\n fn: (current: Data) => Next,\n ): PluginDataModelChain<Next, Versions & Record<NextV, Next>> {\n return PluginDataModelChain[FROM_BUILDER]<Next, Versions & Record<NextV, Next>>({\n ...this.state,\n migrations: [\n ...this.state.migrations,\n { toVersion: version, fn: fn as (data: unknown) => unknown },\n ],\n });\n }\n\n /**\n * Set a recovery handler for unknown or legacy versions.\n * Can only be called once — the returned chain has no recover() method.\n */\n recover(fn: DataRecoverFn<Data>): PluginDataModelWithRecover<Data, Versions> {\n return PluginDataModelWithRecover[FROM_BUILDER]<Data, Versions>({\n ...this.state,\n recoverFn: fn as (version: string, data: unknown) => unknown,\n recoverAtIndex: this.state.migrations.length,\n });\n }\n\n /** Finalize the PluginDataModel. */\n init<Config = undefined>(fn: (config?: Config) => Data): PluginDataModel<Data, Versions, Config> {\n return buildPluginDataModel(this.state, fn);\n }\n}\n\n/**\n * Initial chain returned by new PluginDataModelBuilder().from().\n * Extends PluginDataModelChain with .upgradeLegacy() — available only before\n * any .migrate() calls, matching the block's DataModelInitialChain pattern.\n */\nexport class PluginDataModelInitialChain<\n Data extends PluginData,\n Versions extends Record<string, unknown>,\n> extends PluginDataModelChain<Data, Versions> {\n /** @internal */\n static override [FROM_BUILDER]<Data extends PluginData, Versions extends Record<string, unknown>>(\n state: PluginChainState,\n ): PluginDataModelInitialChain<Data, Versions> {\n return new PluginDataModelInitialChain(state);\n }\n\n /**\n * Handle data from a previous plugin type occupying this slot.\n * Prepends a migration from DATA_MODEL_LEGACY_VERSION to the initial version.\n * When a plugin type changes, the old data arrives with DATA_MODEL_LEGACY_VERSION —\n * this function transforms it into the initial version, then the normal chain runs.\n *\n * Must be called right after .from() — not available after .migrate().\n * Mutually exclusive with recover().\n *\n * @param fn - Transform from old plugin's raw data to this plugin's initial data type\n */\n upgradeLegacy(fn: (data: unknown) => Data): PluginDataModelWithRecover<Data, Versions> {\n return PluginDataModelWithRecover[FROM_BUILDER]<Data, Versions>({\n ...this.state,\n migrations: [\n { toVersion: this.state.initialVersion, fn: fn as (data: unknown) => unknown },\n ...this.state.migrations,\n ],\n initialVersion: DATA_MODEL_LEGACY_VERSION,\n });\n }\n}\n\n/**\n * Chain after .recover() — supports .migrate(), .init(). No second recover().\n */\nexport class PluginDataModelWithRecover<\n Data extends PluginData,\n Versions extends Record<string, unknown>,\n> {\n private constructor(private readonly state: PluginChainState) {}\n\n /** @internal */\n static [FROM_BUILDER]<Data extends PluginData, Versions extends Record<string, unknown>>(\n state: PluginChainState,\n ): PluginDataModelWithRecover<Data, Versions> {\n return new PluginDataModelWithRecover(state);\n }\n\n migrate<Next extends PluginData, const NextV extends string>(\n version: NextV,\n fn: (current: Data) => Next,\n ): PluginDataModelWithRecover<Next, Versions & Record<NextV, Next>> {\n return PluginDataModelWithRecover[FROM_BUILDER]<Next, Versions & Record<NextV, Next>>({\n ...this.state,\n migrations: [\n ...this.state.migrations,\n { toVersion: version, fn: fn as (data: unknown) => unknown },\n ],\n });\n }\n\n init<Config = undefined>(fn: (config?: Config) => Data): PluginDataModel<Data, Versions, Config> {\n return buildPluginDataModel(this.state, fn);\n }\n}\n\n/**\n * Builds a PluginDataModel by replaying the stored chain state through DataModelBuilder.\n * @internal\n */\nfunction buildPluginDataModel<\n Data extends PluginData,\n Versions extends Record<string, unknown>,\n Config,\n>(\n state: PluginChainState,\n configInitFn: (config?: Config) => Data,\n): PluginDataModel<Data, Versions, Config> {\n // Build inner DataModel by replaying migrations through DataModelBuilder chain.\n // Uses `any` internally — type safety is maintained by the public chain types.\n let chain: any = new DataModelBuilder().from(state.initialVersion);\n\n for (let i = 0; i < state.migrations.length; i++) {\n if (state.recoverFn !== undefined && state.recoverAtIndex === i) {\n chain = chain.recover(state.recoverFn);\n }\n chain = chain.migrate(state.migrations[i].toVersion, state.migrations[i].fn);\n }\n\n // If recover was placed at or after the last migration\n if (state.recoverFn !== undefined && state.recoverAtIndex === state.migrations.length) {\n chain = chain.recover(state.recoverFn);\n }\n\n const dataModel: DataModel<Data> = chain.init(() => configInitFn());\n\n return PluginDataModel[FROM_BUILDER]<Data, Versions, Config>(dataModel, configInitFn);\n}\n\n/**\n * A named plugin instance created by `factory.create({ pluginId, ... })`.\n * Passed to both `.transfer()` (on migration chain) and `.plugin()` (on BlockModelV3).\n * Implements TransferTarget so the migration chain can accept it.\n *\n * @typeParam Id - Plugin instance ID literal type\n * @typeParam Data - Plugin data type\n * @typeParam Params - Plugin params type\n * @typeParam Outputs - Plugin outputs type\n * @typeParam TransferData - Type of data entering the plugin via transfer (never if no transfer)\n */\nexport class PluginInstance<\n Id extends string = string,\n Data extends PluginData = PluginData,\n Params extends PluginParams = undefined,\n Outputs extends PluginOutputs = PluginOutputs,\n TransferData = never,\n> implements TransferTarget<Id, TransferData> {\n readonly id: Id;\n readonly transferVersion: string;\n /** @internal Phantom for type inference of Data/Params/Outputs; never set at runtime. */\n readonly __instanceTypes?: { data: Data; params: Params; outputs: Outputs };\n private readonly factory: PluginModelFactory<Data, Params, Outputs, any, any>;\n private readonly config: any;\n\n private constructor(\n id: Id,\n factory: PluginModelFactory<Data, Params, Outputs, any, any>,\n transferVersion: string,\n config?: any,\n ) {\n this.id = id;\n this.factory = factory;\n this.transferVersion = transferVersion;\n this.config = config;\n }\n\n /** @internal */\n static [FROM_BUILDER]<\n Id extends string,\n Data extends PluginData,\n Params extends PluginParams,\n Outputs extends PluginOutputs,\n TransferData,\n >(\n id: Id,\n factory: PluginModelFactory<Data, Params, Outputs, any, any>,\n transferVersion: string,\n config?: any,\n ): PluginInstance<Id, Data, Params, Outputs, TransferData> {\n return new PluginInstance(id, factory, transferVersion, config);\n }\n\n /** @internal Create a PluginModel from this instance. Used by BlockModelV3.plugin(). */\n [CREATE_PLUGIN_MODEL](): PluginModel<Data, Params, Outputs> {\n return this.factory[CREATE_PLUGIN_MODEL](this.config);\n }\n}\n\n/**\n * Configured plugin instance returned by PluginModelFactory[CREATE_PLUGIN_MODEL]().\n * Contains the plugin's name, data model, and output definitions.\n */\nexport class PluginModel<\n Data extends PluginData = PluginData,\n Params extends PluginParams = undefined,\n Outputs extends PluginOutputs = PluginOutputs,\n> {\n /** Globally unique plugin name */\n readonly name: PluginName;\n /** Data model instance for this plugin */\n readonly dataModel: DataModel<Data>;\n /** Output definitions - functions that compute outputs from plugin context */\n readonly outputs: {\n [K in keyof Outputs]: (ctx: PluginRenderCtx<PluginFactoryLike<Data, Params>>) => Outputs[K];\n };\n /** Per-output flags (e.g. withStatus) */\n readonly outputFlags: Record<string, { withStatus: boolean }>;\n /** Feature flags declared by this plugin */\n readonly featureFlags?: BlockCodeKnownFeatureFlags;\n /** Create fresh default data. Config (if any) is captured at creation time. */\n readonly getDefaultData: () => DataVersioned<Data>;\n\n private constructor(options: {\n name: PluginName;\n dataModel: DataModel<Data>;\n outputs: {\n [K in keyof Outputs]: (ctx: PluginRenderCtx<PluginFactoryLike<Data, Params>>) => Outputs[K];\n };\n outputFlags: Record<string, { withStatus: boolean }>;\n featureFlags?: BlockCodeKnownFeatureFlags;\n getDefaultData: () => DataVersioned<Data>;\n }) {\n this.name = options.name;\n this.dataModel = options.dataModel;\n this.outputs = options.outputs;\n this.outputFlags = options.outputFlags;\n this.featureFlags = options.featureFlags;\n this.getDefaultData = options.getDefaultData;\n }\n\n /**\n * Internal method for creating PluginModel from factory.\n * Uses Symbol key to prevent external access.\n * @internal\n */\n static [FROM_BUILDER]<\n Data extends PluginData,\n Params extends PluginParams,\n Outputs extends PluginOutputs,\n >(options: {\n name: PluginName;\n dataModel: DataModel<Data>;\n outputs: {\n [K in keyof Outputs]: (ctx: PluginRenderCtx<PluginFactoryLike<Data, Params>>) => Outputs[K];\n };\n outputFlags: Record<string, { withStatus: boolean }>;\n featureFlags?: BlockCodeKnownFeatureFlags;\n getDefaultData: () => DataVersioned<Data>;\n }): PluginModel<Data, Params, Outputs> {\n return new PluginModel<Data, Params, Outputs>(options);\n }\n\n /**\n * Creates a new PluginModelBuilder with a PluginDataModel (supports transfer / config).\n *\n * @example\n * const pluginData = new PluginDataModelBuilder().from<TableData>(\"v1\")\n * .migrate<FilteredTableData>(\"v2\", (v1) => ({ ...v1, filters: [] }))\n * .init<TableConfig>((config?) => ({\n * state: createDefaultState(config?.ops),\n * filters: [],\n * }));\n *\n * const myPlugin = PluginModel.define({\n * name: 'myPlugin' as PluginName,\n * data: pluginData,\n * }).build();\n */\n static define<\n Data extends PluginData,\n Params extends PluginParams = undefined,\n Versions extends Record<string, unknown> = {},\n Config extends PluginConfig = undefined,\n >(options: {\n name: PluginName;\n data: PluginDataModel<Data, Versions, Config>;\n featureFlags?: BlockCodeKnownFeatureFlags;\n }): PluginModelBuilder<Data, Params, {}, Config, Versions>;\n /**\n * Creates a new PluginModelBuilder with a data model factory function (backward compatible).\n *\n * @example\n * const myPlugin = PluginModel.define({\n * name: 'myPlugin' as PluginName,\n * data: (cfg) => dataModelChain.init(() => ({ value: cfg.defaultValue })),\n * }).build();\n */\n static define<\n Data extends PluginData,\n Params extends PluginParams = undefined,\n Config extends PluginConfig = undefined,\n >(options: {\n name: PluginName;\n data: (config?: Config) => DataModel<Data>;\n featureFlags?: BlockCodeKnownFeatureFlags;\n }): PluginModelBuilder<Data, Params, {}, Config, {}>;\n static define(options: {\n name: PluginName;\n data: PluginDataModel<any, any, any> | ((config?: any) => DataModel<any>);\n featureFlags?: BlockCodeKnownFeatureFlags;\n }): PluginModelBuilder {\n if (options.data instanceof PluginDataModel) {\n const pdm = options.data;\n return PluginModelBuilder[FROM_BUILDER]({\n name: options.name,\n dataFn: () => pdm.dataModel,\n getDefaultDataFn: (config: any) => pdm.getDefaultData(config),\n featureFlags: options.featureFlags,\n });\n }\n return PluginModelBuilder[FROM_BUILDER]({\n name: options.name,\n dataFn: options.data,\n featureFlags: options.featureFlags,\n });\n }\n}\n\n/** Plugin factory returned by PluginModelBuilder.build(). */\nexport interface PluginFactory<\n Data extends PluginData = PluginData,\n Params extends PluginParams = undefined,\n Outputs extends PluginOutputs = PluginOutputs,\n Config extends PluginConfig = undefined,\n Versions extends Record<string, unknown> = {},\n> extends PluginFactoryLike<Data, Params, Outputs> {\n /** Create a named plugin instance, optionally with transfer at a specific version. */\n create<const Id extends string, const V extends string & keyof Versions = never>(options: {\n pluginId: Id;\n transferAt?: V;\n config?: Config;\n }): PluginInstance<Id, Data, Params, Outputs, Versions[V]>;\n\n /**\n * @internal Phantom field for structural type extraction.\n * Enables InferFactoryData/InferFactoryOutputs to work via PluginFactoryLike.\n */\n readonly __types?: {\n data: Data;\n params: Params;\n outputs: Outputs;\n config: Config;\n versions: Versions;\n };\n}\n\nclass PluginModelFactory<\n Data extends PluginData = PluginData,\n Params extends PluginParams = undefined,\n Outputs extends PluginOutputs = PluginOutputs,\n Config extends PluginConfig = undefined,\n Versions extends Record<string, unknown> = {},\n> implements PluginFactory<Data, Params, Outputs, Config, Versions> {\n private readonly name: PluginName;\n private readonly dataFn: (config?: Config) => DataModel<Data>;\n private readonly getDefaultDataFn?: (config?: Config) => DataVersioned<Data>;\n readonly outputs: {\n [K in keyof Outputs]: (ctx: PluginRenderCtx<PluginFactoryLike<Data, Params>>) => Outputs[K];\n };\n private readonly outputFlags: Record<string, { withStatus: boolean }>;\n private readonly featureFlags?: BlockCodeKnownFeatureFlags;\n\n private constructor(options: {\n name: PluginName;\n dataFn: (config?: Config) => DataModel<Data>;\n getDefaultDataFn?: (config?: Config) => DataVersioned<Data>;\n outputs: {\n [K in keyof Outputs]: (ctx: PluginRenderCtx<PluginFactoryLike<Data, Params>>) => Outputs[K];\n };\n outputFlags: Record<string, { withStatus: boolean }>;\n featureFlags?: BlockCodeKnownFeatureFlags;\n }) {\n this.name = options.name;\n this.dataFn = options.dataFn;\n this.getDefaultDataFn = options.getDefaultDataFn;\n this.outputs = options.outputs;\n this.outputFlags = options.outputFlags;\n this.featureFlags = options.featureFlags;\n }\n\n /** @internal */\n static [FROM_BUILDER]<\n Data extends PluginData,\n Params extends PluginParams,\n Outputs extends PluginOutputs,\n Config extends PluginConfig,\n Versions extends Record<string, unknown>,\n >(options: {\n name: PluginName;\n dataFn: (config?: Config) => DataModel<Data>;\n getDefaultDataFn?: (config?: Config) => DataVersioned<Data>;\n outputs: {\n [K in keyof Outputs]: (ctx: PluginRenderCtx<PluginFactoryLike<Data, Params>>) => Outputs[K];\n };\n outputFlags: Record<string, { withStatus: boolean }>;\n featureFlags?: BlockCodeKnownFeatureFlags;\n }): PluginModelFactory<Data, Params, Outputs, Config, Versions> {\n return new PluginModelFactory(options);\n }\n\n create<const Id extends string, const V extends string & keyof Versions = never>(options: {\n pluginId: Id;\n transferAt?: V;\n config?: Config;\n }): PluginInstance<Id, Data, Params, Outputs, Versions[V]> {\n const transferVersion = options.transferAt ?? NO_TRANSFER_VERSION;\n return PluginInstance[FROM_BUILDER]<Id, Data, Params, Outputs, Versions[V]>(\n options.pluginId as Id,\n this as any,\n transferVersion,\n options.config,\n );\n }\n\n /** @internal Create a PluginModel from config. Config is captured in getDefaultData closure. */\n [CREATE_PLUGIN_MODEL](config?: Config): PluginModel<Data, Params, Outputs> {\n const dataModel = this.dataFn(config);\n const getDefaultDataFn = this.getDefaultDataFn;\n return PluginModel[FROM_BUILDER]<Data, Params, Outputs>({\n name: this.name,\n dataModel,\n outputs: this.outputs,\n outputFlags: this.outputFlags,\n featureFlags: this.featureFlags,\n getDefaultData: getDefaultDataFn\n ? () => getDefaultDataFn(config)\n : () => dataModel.getDefaultData(),\n });\n }\n}\n\n/**\n * Builder for creating PluginType with type-safe output definitions.\n *\n * Use `PluginModel.define()` to create a builder instance.\n *\n * @typeParam Data - Plugin's persistent data type\n * @typeParam Params - Params derived from block's RenderCtx (optional)\n * @typeParam Config - Static configuration passed to plugin factory (optional)\n * @typeParam Outputs - Accumulated output types\n * @typeParam Versions - Version map from PluginDataModel (empty for function-based data)\n *\n * @example\n * const dataTable = PluginModel.define({\n * name: 'dataTable' as PluginName,\n * data: pluginDataModel,\n * })\n * .output('model', (ctx) => createTableModel(ctx))\n * .build();\n */\nclass PluginModelBuilder<\n Data extends PluginData = PluginData,\n Params extends PluginParams = undefined,\n Outputs extends PluginOutputs = PluginOutputs,\n Config extends PluginConfig = undefined,\n Versions extends Record<string, unknown> = {},\n> {\n private readonly name: PluginName;\n private readonly dataFn: (config?: Config) => DataModel<Data>;\n private readonly getDefaultDataFn?: (config?: Config) => DataVersioned<Data>;\n private readonly outputs: {\n [K in keyof Outputs]: (ctx: PluginRenderCtx<PluginFactoryLike<Data, Params>>) => Outputs[K];\n };\n private readonly outputFlags: Record<string, { withStatus: boolean }>;\n private readonly featureFlags?: BlockCodeKnownFeatureFlags;\n\n private constructor(options: {\n name: PluginName;\n dataFn: (config?: Config) => DataModel<Data>;\n getDefaultDataFn?: (config?: Config) => DataVersioned<Data>;\n outputs?: {\n [K in keyof Outputs]: (ctx: PluginRenderCtx<PluginFactoryLike<Data, Params>>) => Outputs[K];\n };\n outputFlags?: Record<string, { withStatus: boolean }>;\n featureFlags?: BlockCodeKnownFeatureFlags;\n }) {\n this.name = options.name;\n this.dataFn = options.dataFn;\n this.getDefaultDataFn = options.getDefaultDataFn;\n this.outputs =\n options.outputs ??\n ({} as {\n [K in keyof Outputs]: (ctx: PluginRenderCtx<PluginFactoryLike<Data, Params>>) => Outputs[K];\n });\n this.outputFlags = options.outputFlags ?? {};\n this.featureFlags = options.featureFlags;\n }\n\n /** @internal */\n static [FROM_BUILDER]<\n Data extends PluginData,\n Params extends PluginParams,\n Outputs extends PluginOutputs,\n Config extends PluginConfig,\n Versions extends Record<string, unknown> = {},\n >(options: {\n name: PluginName;\n dataFn: (config?: Config) => DataModel<Data>;\n getDefaultDataFn?: (config?: Config) => DataVersioned<Data>;\n outputs?: {\n [K in keyof Outputs]: (ctx: PluginRenderCtx<PluginFactoryLike<Data, Params>>) => Outputs[K];\n };\n outputFlags?: Record<string, { withStatus: boolean }>;\n featureFlags?: BlockCodeKnownFeatureFlags;\n }): PluginModelBuilder<Data, Params, Outputs, Config, Versions> {\n return new PluginModelBuilder(options);\n }\n\n /**\n * Adds an output to the plugin.\n *\n * @param key - Output name\n * @param fn - Function that computes the output value from plugin context\n * @returns PluginModel with the new output added\n *\n * @example\n * .output('model', (ctx) => createModel(ctx.params.columns, ctx.data.state))\n * .output('isReady', (ctx) => ctx.params.columns !== undefined)\n */\n output<const Key extends string, T>(\n key: Key,\n fn: (ctx: PluginRenderCtx<PluginFactoryLike<Data, Params>>) => T,\n ): PluginModelBuilder<Data, Params, Outputs & { [K in Key]: T }, Config, Versions> {\n return new PluginModelBuilder<Data, Params, Outputs & { [K in Key]: T }, Config, Versions>({\n name: this.name,\n dataFn: this.dataFn,\n getDefaultDataFn: this.getDefaultDataFn,\n featureFlags: this.featureFlags,\n outputs: {\n ...this.outputs,\n [key]: fn,\n } as {\n [K in keyof (Outputs & { [P in Key]: T })]: (\n ctx: PluginRenderCtx<PluginFactoryLike<Data, Params>>,\n ) => (Outputs & { [P in Key]: T })[K];\n },\n outputFlags: { ...this.outputFlags, [key]: { withStatus: false } },\n });\n }\n\n /**\n * Adds an output wrapped with status information to the plugin.\n *\n * The UI receives the full {@link OutputWithStatus} object instead of an unwrapped value,\n * allowing it to distinguish between pending, success, and error states.\n *\n * @param key - Output name\n * @param fn - Function that computes the output value from plugin context\n * @returns PluginModel with the new status-wrapped output added\n *\n * @example\n * .outputWithStatus('table', (ctx) => {\n * const pCols = ctx.params.pFrame?.getPColumns();\n * if (pCols === undefined) return undefined;\n * return createPlDataTableV2(ctx, pCols, ctx.data.tableState);\n * })\n */\n outputWithStatus<const Key extends string, T>(\n key: Key,\n fn: (ctx: PluginRenderCtx<PluginFactoryLike<Data, Params>>) => T,\n ): PluginModelBuilder<\n Data,\n Params,\n Outputs & { [K in Key]: OutputWithStatus<T> },\n Config,\n Versions\n > {\n return new PluginModelBuilder<\n Data,\n Params,\n Outputs & { [K in Key]: OutputWithStatus<T> },\n Config,\n Versions\n >({\n name: this.name,\n dataFn: this.dataFn,\n getDefaultDataFn: this.getDefaultDataFn,\n featureFlags: this.featureFlags,\n outputs: {\n ...this.outputs,\n [key]: fn,\n } as {\n [K in keyof (Outputs & { [P in Key]: OutputWithStatus<T> })]: (\n ctx: PluginRenderCtx<PluginFactoryLike<Data, Params>>,\n ) => (Outputs & { [P in Key]: OutputWithStatus<T> })[K];\n },\n outputFlags: { ...this.outputFlags, [key]: { withStatus: true } },\n });\n }\n\n /**\n * Finalizes the plugin definition and returns a PluginFactory.\n *\n * @returns Plugin factory that creates named plugin instances via .create()\n *\n * @example\n * const myPlugin = PluginModel.define({ ... })\n * .output('value', (ctx) => ctx.data.value)\n * .build();\n *\n * // Create a named instance:\n * const table = myPlugin.create({ pluginId: 'mainTable', config: { ... } });\n */\n build(): PluginFactory<Data, Params, Outputs, Config, Versions> {\n return PluginModelFactory[FROM_BUILDER]<Data, Params, Outputs, Config, Versions>({\n name: this.name,\n dataFn: this.dataFn,\n getDefaultDataFn: this.getDefaultDataFn,\n outputs: this.outputs,\n outputFlags: this.outputFlags,\n featureFlags: this.featureFlags,\n });\n }\n}\n"],"mappings":"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"}
@@ -480,20 +480,19 @@ var RenderCtxLegacy = class extends RenderCtxBase {
480
480
  *
481
481
  * @typeParam F - PluginFactoryLike phantom carrying data/params/outputs types
482
482
  */
483
- var PluginRenderCtx = class {
484
- ctx;
483
+ var PluginRenderCtx = class extends RenderCtxBase {
485
484
  handle;
486
485
  wrappedInputs;
487
486
  constructor(handle, wrappedInputs) {
488
- this.ctx = require_internal.getCfgRenderCtx();
487
+ super();
489
488
  this.handle = handle;
490
489
  this.wrappedInputs = wrappedInputs;
491
490
  }
492
- dataCache;
491
+ pluginDataCache;
493
492
  /** Plugin's persistent data from blockStorage.__plugins.{pluginId}.__data */
494
493
  get data() {
495
- if (this.dataCache === void 0) this.dataCache = { v: require_block_storage.getPluginData((0, _milaboratories_pl_model_common.parseJson)(this.ctx.blockStorage()), this.handle) };
496
- return this.dataCache.v;
494
+ if (this.pluginDataCache === void 0) this.pluginDataCache = { v: require_block_storage.getPluginData((0, _milaboratories_pl_model_common.parseJson)(this.ctx.blockStorage()), this.handle) };
495
+ return this.pluginDataCache.v;
497
496
  }
498
497
  paramsCache;
499
498
  /** Params derived from block context via pre-wrapped input callbacks */
@@ -505,8 +504,6 @@ var PluginRenderCtx = class {
505
504
  }
506
505
  return this.paramsCache.v;
507
506
  }
508
- /** Result pool — same as block, from cfgRenderCtx methods */
509
- resultPool = new ResultPool();
510
507
  };
511
508
 
512
509
  //#endregion