@platforma-sdk/model 1.58.0 → 1.58.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.
Files changed (45) hide show
  1. package/dist/block_migrations.cjs +60 -77
  2. package/dist/block_migrations.cjs.map +1 -1
  3. package/dist/block_migrations.d.ts +35 -32
  4. package/dist/block_migrations.d.ts.map +1 -1
  5. package/dist/block_migrations.js +60 -78
  6. package/dist/block_migrations.js.map +1 -1
  7. package/dist/block_model.cjs +1 -1
  8. package/dist/block_model.cjs.map +1 -1
  9. package/dist/block_model.d.ts +1 -1
  10. package/dist/block_model.js +1 -1
  11. package/dist/block_model.js.map +1 -1
  12. package/dist/block_storage.cjs +6 -6
  13. package/dist/block_storage.cjs.map +1 -1
  14. package/dist/block_storage.d.ts +7 -7
  15. package/dist/block_storage.d.ts.map +1 -1
  16. package/dist/block_storage.js +6 -6
  17. package/dist/block_storage.js.map +1 -1
  18. package/dist/block_storage_callbacks.cjs +1 -0
  19. package/dist/block_storage_callbacks.cjs.map +1 -1
  20. package/dist/block_storage_callbacks.d.ts +4 -3
  21. package/dist/block_storage_callbacks.d.ts.map +1 -1
  22. package/dist/block_storage_callbacks.js +1 -0
  23. package/dist/block_storage_callbacks.js.map +1 -1
  24. package/dist/index.cjs +1 -3
  25. package/dist/index.cjs.map +1 -1
  26. package/dist/index.d.ts +2 -2
  27. package/dist/index.d.ts.map +1 -1
  28. package/dist/index.js +2 -2
  29. package/dist/package.json.cjs +1 -1
  30. package/dist/package.json.js +1 -1
  31. package/dist/plugin_model.cjs +2 -2
  32. package/dist/plugin_model.cjs.map +1 -1
  33. package/dist/plugin_model.d.ts +2 -2
  34. package/dist/plugin_model.js +2 -2
  35. package/dist/plugin_model.js.map +1 -1
  36. package/package.json +5 -5
  37. package/src/block_migrations.test.ts +109 -12
  38. package/src/block_migrations.ts +63 -87
  39. package/src/block_model.ts +1 -1
  40. package/src/block_storage.test.ts +8 -8
  41. package/src/block_storage.ts +10 -10
  42. package/src/block_storage_callbacks.ts +4 -3
  43. package/src/index.ts +22 -1
  44. package/src/plugin_model.test.ts +2 -2
  45. package/src/plugin_model.ts +2 -2
@@ -1 +1 @@
1
- {"version":3,"file":"block_model.js","sources":["../src/block_model.ts"],"sourcesContent":["import type {\n BlockRenderingMode,\n BlockSection,\n OutputWithStatus,\n PlRef,\n BlockCodeKnownFeatureFlags,\n BlockConfigContainer,\n} from \"@milaboratories/pl-model-common\";\nimport { mergeFeatureFlags } from \"@milaboratories/pl-model-common\";\nimport { getPlatformaInstance, isInUI, createAndRegisterRenderLambda } from \"./internal\";\nimport type { DataModel } from \"./block_migrations\";\nimport type { PlatformaV3 } from \"./platforma\";\nimport type { InferRenderFunctionReturn, RenderFunction } from \"./render\";\nimport { BlockRenderCtx, PluginRenderCtx } from \"./render\";\nimport type { PluginData, PluginModel, PluginOutputs, PluginParams } from \"./plugin_model\";\nimport { type PluginHandle, pluginOutputKey } from \"./plugin_handle\";\nimport type { RenderCtxBase } from \"./render\";\nimport { PlatformaSDKVersion } from \"./version\";\nimport {\n applyStorageUpdate,\n getStorageDebugView,\n migrateStorage,\n createInitialStorage,\n deriveArgsFromStorage,\n derivePrerunArgsFromStorage,\n} from \"./block_storage_callbacks\";\nimport { type PluginName } from \"./block_storage\";\nimport type {\n ConfigRenderLambda,\n DeriveHref,\n ConfigRenderLambdaFlags,\n InferOutputsFromLambdas,\n} from \"./bconfig\";\nimport { downgradeCfgOrLambda, isConfigLambda } from \"./bconfig\";\nimport type { PlatformaExtended } from \"./platforma\";\nimport {\n BLOCK_STORAGE_FACADE_VERSION,\n BlockStorageFacadeCallbacks,\n BlockStorageFacadeHandles,\n registerFacadeCallbacks,\n} from \"./block_storage_facade\";\n\ntype SectionsExpectedType = readonly BlockSection[];\n\ntype NoOb = Record<string, never>;\n\n/**\n * Per-property lambdas for deriving plugin params from block render context.\n * Each property is a function that receives the block's RenderCtxBase and returns the param value.\n */\nexport type ParamsInput<Params, BArgs = unknown, BData = unknown> = {\n [K in keyof Params]: (ctx: RenderCtxBase<BArgs, BData>) => Params[K];\n};\n\n/**\n * Type-erased version of ParamsInput for internal storage.\n */\ntype ParamsInputErased = Record<string, (ctx: RenderCtxBase) => unknown>;\n\n/**\n * Registered plugin: model + param derivation lambdas.\n * Type parameters are carried by PluginModel generic.\n */\nexport type PluginInstance<\n Data extends PluginData = PluginData,\n Params extends PluginParams = undefined,\n Outputs extends PluginOutputs = PluginOutputs,\n> = {\n readonly model: PluginModel<Data, Params, Outputs>;\n readonly inputs: ParamsInputErased;\n};\n\ninterface BlockModelV3Config<\n OutputsCfg extends Record<string, ConfigRenderLambda>,\n Data,\n Plugins extends Record<string, PluginInstance> = {},\n> {\n renderingMode: BlockRenderingMode;\n dataModel: DataModel<Data>;\n outputs: OutputsCfg;\n sections: ConfigRenderLambda;\n title: ConfigRenderLambda | undefined;\n subtitle: ConfigRenderLambda | undefined;\n tags: ConfigRenderLambda | undefined;\n enrichmentTargets: ConfigRenderLambda | undefined;\n featureFlags: BlockCodeKnownFeatureFlags;\n argsFunction: ((data: unknown) => unknown) | undefined;\n prerunArgsFunction: ((data: unknown) => unknown) | undefined;\n plugins: Plugins;\n}\n\n/** Main entry point that each block should use in it's \"config\" module. Don't forget\n * to call {@link done()} at the end of configuration. Value returned by this builder must be\n * exported as constant with name \"platforma\" from the \"config\" module.\n * API version is 3 (for UI) and 2 (for model) */\nexport class BlockModelV3<\n Args,\n OutputsCfg extends Record<string, ConfigRenderLambda>,\n Data extends Record<string, unknown> = Record<string, unknown>,\n Href extends `/${string}` = \"/\",\n Plugins extends Record<string, PluginInstance> = {},\n> {\n private constructor(private readonly config: BlockModelV3Config<OutputsCfg, Data, Plugins>) {}\n\n public static readonly INITIAL_BLOCK_FEATURE_FLAGS: BlockCodeKnownFeatureFlags = {\n supportsLazyState: true,\n requiresUIAPIVersion: 3,\n requiresModelAPIVersion: BLOCK_STORAGE_FACADE_VERSION,\n requiresCreatePTable: 2,\n };\n\n /**\n * Creates a new BlockModelV3 builder with the specified data model.\n *\n * @example\n * const dataModel = new DataModelBuilder()\n * .from<BlockData>(DATA_MODEL_DEFAULT_VERSION)\n * .init(() => ({ numbers: [], labels: [] }));\n *\n * BlockModelV3.create(dataModel)\n * .args((data) => ({ numbers: data.numbers }))\n * .sections(() => [{ type: 'link', href: '/', label: 'Main' }])\n * .done();\n *\n * @param dataModel The data model that defines initial data and migrations\n */\n public static create<Data extends Record<string, unknown>>(\n dataModel: DataModel<Data>,\n ): BlockModelV3<NoOb, {}, Data> {\n return new BlockModelV3<NoOb, {}, Data>({\n renderingMode: \"Heavy\",\n dataModel,\n outputs: {},\n sections: createAndRegisterRenderLambda({ handle: \"sections\", lambda: () => [] }, true),\n title: undefined,\n subtitle: undefined,\n tags: undefined,\n enrichmentTargets: undefined,\n featureFlags: { ...BlockModelV3.INITIAL_BLOCK_FEATURE_FLAGS },\n argsFunction: undefined,\n prerunArgsFunction: undefined,\n plugins: {},\n });\n }\n\n /**\n * Add output cell wrapped with additional status information to the configuration\n *\n * @param key output cell name, that can be later used to retrieve the rendered value\n * @param rf callback calculating output value using context, that allows to access\n * workflows outputs and interact with platforma drivers\n * @param flags additional flags that may alter lambda rendering procedure\n * */\n public output<const Key extends string, const RF extends RenderFunction<Args, Data, unknown>>(\n key: Key,\n rf: RF,\n flags: ConfigRenderLambdaFlags & { withStatus: true },\n ): BlockModelV3<\n Args,\n OutputsCfg & {\n [K in Key]: ConfigRenderLambda<InferRenderFunctionReturn<RF>> & {\n withStatus: true;\n };\n },\n Data,\n Href,\n Plugins\n >;\n /**\n * Add output cell to the configuration\n *\n * @param key output cell name, that can be later used to retrieve the rendered value\n * @param rf callback calculating output value using context, that allows to access\n * workflows outputs and interact with platforma drivers\n * @param flags additional flags that may alter lambda rendering procedure\n * */\n public output<const Key extends string, const RF extends RenderFunction<Args, Data, unknown>>(\n key: Key,\n rf: RF,\n flags?: ConfigRenderLambdaFlags,\n ): BlockModelV3<\n Args,\n OutputsCfg & {\n [K in Key]: ConfigRenderLambda<InferRenderFunctionReturn<RF>>;\n },\n Data,\n Href,\n Plugins\n >;\n public output(\n key: string,\n cfgOrRf: RenderFunction<Args, Data, unknown>,\n flags: ConfigRenderLambdaFlags = {},\n ): BlockModelV3<Args, OutputsCfg, Data, Href, Plugins> {\n return new BlockModelV3({\n ...this.config,\n outputs: {\n ...this.config.outputs,\n [key]: createAndRegisterRenderLambda({\n handle: `block-output#${key}`,\n lambda: () => cfgOrRf(new BlockRenderCtx<Args, Data>()),\n ...flags,\n }),\n },\n });\n }\n\n /** Shortcut for {@link output} with retentive flag set to true. */\n public retentiveOutput<\n const Key extends string,\n const RF extends RenderFunction<Args, Data, unknown>,\n >(\n key: Key,\n rf: RF,\n ): BlockModelV3<\n Args,\n OutputsCfg & {\n [K in Key]: ConfigRenderLambda<InferRenderFunctionReturn<RF>>;\n },\n Data,\n Href,\n Plugins\n > {\n return this.output(key, rf, { retentive: true });\n }\n\n /** Shortcut for {@link output} with withStatus flag set to true. */\n public outputWithStatus<\n const Key extends string,\n const RF extends RenderFunction<Args, Data, unknown>,\n >(key: Key, rf: RF) {\n return this.output(key, rf, { withStatus: true });\n }\n\n /**\n * Sets a function to derive block args from data.\n * This is called during setData to compute the args that will be used for block execution.\n *\n * @example\n * .args<BlockArgs>((data) => ({ numbers: data.numbers }))\n *\n * @example\n * .args<BlockArgs>((data) => {\n * if (data.numbers.length === 0) throw new Error('Numbers required'); // block not ready\n * return { numbers: data.numbers };\n * })\n */\n public args<Args>(\n lambda: (data: Data) => Args,\n ): BlockModelV3<Args, OutputsCfg, Data, Href, Plugins> {\n return new BlockModelV3<Args, OutputsCfg, Data, Href, Plugins>({\n ...this.config,\n argsFunction: lambda as (data: unknown) => unknown,\n });\n }\n\n /**\n * Sets a function to derive pre-run args from data (optional).\n * This is called during setData to compute the args that will be used for staging/pre-run phase.\n *\n * If not defined, defaults to using the args() function result.\n * If defined, uses its return value for the staging / prerun phase.\n *\n * The staging / prerun phase runs only if currentPrerunArgs differs from the executed\n * version of prerunArgs (same comparison logic as currentArgs vs prodArgs).\n *\n * @example\n * .prerunArgs((data) => ({ numbers: data.numbers }))\n *\n * @example\n * .prerunArgs((data) => {\n * // Return undefined to skip staging for this block\n * if (!data.isReady) return undefined;\n * return { numbers: data.numbers };\n * })\n */\n public prerunArgs(\n fn: (data: Data) => unknown,\n ): BlockModelV3<Args, OutputsCfg, Data, Href, Plugins> {\n return new BlockModelV3<Args, OutputsCfg, Data, Href, Plugins>({\n ...this.config,\n prerunArgsFunction: fn as (data: unknown) => unknown,\n });\n }\n\n /** Sets the lambda to generate list of sections in the left block overviews panel. */\n public sections<\n const Ret extends SectionsExpectedType,\n const RF extends RenderFunction<Args, Data, Ret>,\n >(rf: RF): BlockModelV3<Args, OutputsCfg, Data, DeriveHref<ReturnType<RF>>, Plugins> {\n return new BlockModelV3<Args, OutputsCfg, Data, DeriveHref<ReturnType<RF>>, Plugins>({\n ...this.config,\n // Replace the default sections callback with the user-provided one\n sections: createAndRegisterRenderLambda(\n { handle: \"sections\", lambda: () => rf(new BlockRenderCtx<Args, Data>()) },\n true,\n ),\n });\n }\n\n /** Sets a rendering function to derive block title, shown for the block in the left blocks-overview panel. */\n public title(\n rf: RenderFunction<Args, Data, string>,\n ): BlockModelV3<Args, OutputsCfg, Data, Href, Plugins> {\n return new BlockModelV3<Args, OutputsCfg, Data, Href, Plugins>({\n ...this.config,\n title: createAndRegisterRenderLambda({\n handle: \"title\",\n lambda: () => rf(new BlockRenderCtx<Args, Data>()),\n }),\n });\n }\n\n public subtitle(\n rf: RenderFunction<Args, Data, string>,\n ): BlockModelV3<Args, OutputsCfg, Data, Href, Plugins> {\n return new BlockModelV3<Args, OutputsCfg, Data, Href, Plugins>({\n ...this.config,\n subtitle: createAndRegisterRenderLambda({\n handle: \"subtitle\",\n lambda: () => rf(new BlockRenderCtx<Args, Data>()),\n }),\n });\n }\n\n public tags(\n rf: RenderFunction<Args, Data, string[]>,\n ): BlockModelV3<Args, OutputsCfg, Data, Href, Plugins> {\n return new BlockModelV3<Args, OutputsCfg, Data, Href, Plugins>({\n ...this.config,\n tags: createAndRegisterRenderLambda({\n handle: \"tags\",\n lambda: () => rf(new BlockRenderCtx<Args, Data>()),\n }),\n });\n }\n\n /** Sets or overrides feature flags for the block. */\n public withFeatureFlags(\n flags: Partial<BlockCodeKnownFeatureFlags>,\n ): BlockModelV3<Args, OutputsCfg, Data, Href, Plugins> {\n return new BlockModelV3<Args, OutputsCfg, Data, Href, Plugins>({\n ...this.config,\n featureFlags: { ...this.config.featureFlags, ...flags },\n });\n }\n\n /**\n * Defines how to derive list of upstream references this block is meant to enrich with its exports from block args.\n * Influences dependency graph construction.\n */\n public enriches(\n lambda: (args: Args) => PlRef[],\n ): BlockModelV3<Args, OutputsCfg, Data, Href, Plugins> {\n return new BlockModelV3<Args, OutputsCfg, Data, Href, Plugins>({\n ...this.config,\n enrichmentTargets: createAndRegisterRenderLambda({\n handle: \"enrichmentTargets\",\n lambda: lambda,\n }),\n });\n }\n\n /**\n * Registers a plugin instance with the block.\n *\n * Plugins are UI components with their own model logic and persistent state.\n * Each plugin must have a unique pluginId within the block.\n *\n * @param pluginId - Unique identifier for this plugin instance within the block\n * @param plugin - Configured PluginModel instance (created via factory.create(config))\n * @param params - Per-property lambdas deriving plugin params from block RenderCtx\n *\n * @example\n * .plugin('mainTable', dataTablePlugin.create({ defaultOps: {...} }), {\n * columns: (ctx) => ctx.outputs?.resolve(\"data\")?.getPColumns(),\n * sourceId: (ctx) => ctx.data.selectedSource,\n * })\n */\n public plugin<\n const PluginId extends string,\n PData extends PluginData,\n PParams extends PluginParams,\n POutputs extends PluginOutputs,\n >(\n pluginId: PluginId,\n plugin: PluginModel<PData, PParams, POutputs>,\n params?: ParamsInput<PParams, Args, Data>,\n ): BlockModelV3<\n Args,\n OutputsCfg,\n Data,\n Href,\n Plugins & { [K in PluginId]: PluginInstance<PData, PParams, POutputs> }\n > {\n // Validate pluginId uniqueness\n if (pluginId in this.config.plugins) {\n throw new Error(`Plugin '${pluginId}' already registered`);\n }\n\n const instance: PluginInstance<PData, PParams, POutputs> = {\n model: plugin,\n inputs: (params ?? {}) as ParamsInputErased,\n };\n\n return new BlockModelV3({\n ...this.config,\n plugins: {\n ...this.config.plugins,\n [pluginId]: instance,\n },\n featureFlags: mergeFeatureFlags(this.config.featureFlags, plugin.featureFlags ?? {}),\n });\n }\n\n /** Renders all provided block settings into a pre-configured platforma API\n * instance, that can be used in frontend to interact with block data, and\n * other features provided by the platforma to the block. */\n public done(): PlatformaExtended<\n PlatformaV3<Data, Args, InferOutputsFromLambdas<OutputsCfg>, Href, Plugins>\n > {\n return this.withFeatureFlags({\n ...this.config.featureFlags,\n })._done();\n }\n\n public _done(): PlatformaExtended<\n PlatformaV3<Data, Args, InferOutputsFromLambdas<OutputsCfg>, Href, Plugins>\n > {\n if (this.config.argsFunction === undefined) throw new Error(\"Args rendering function not set.\");\n\n const apiVersion = 3;\n\n // Build plugin registry\n const { plugins } = this.config;\n const pluginRegistry: Record<string, PluginName> = {};\n const pluginHandles = Object.keys(plugins) as PluginHandle[];\n for (const handle of pluginHandles) {\n pluginRegistry[handle] = plugins[handle].model.name;\n }\n\n const { dataModel, argsFunction, prerunArgsFunction } = this.config;\n\n function getPlugin(handle: PluginHandle): PluginInstance {\n const plugin = plugins[handle];\n if (!plugin) throw new Error(`Plugin model not found for '${handle}'`);\n return plugin;\n }\n\n // Register ALL facade callbacks here, with dependencies captured via closures\n registerFacadeCallbacks({\n [BlockStorageFacadeCallbacks.StorageApplyUpdate]: applyStorageUpdate,\n [BlockStorageFacadeCallbacks.StorageDebugView]: getStorageDebugView,\n [BlockStorageFacadeCallbacks.StorageMigrate]: (currentStorageJson) =>\n migrateStorage(currentStorageJson, {\n migrateBlockData: (v) => dataModel.migrate(v),\n getPluginRegistry: () => pluginRegistry,\n migratePluginData: (handle, v) => getPlugin(handle).model.dataModel.migrate(v),\n createPluginData: (handle) => getPlugin(handle).model.dataModel.getDefaultData(),\n }),\n [BlockStorageFacadeCallbacks.StorageInitial]: () =>\n createInitialStorage({\n getDefaultBlockData: () => dataModel.getDefaultData(),\n getPluginRegistry: () => pluginRegistry,\n createPluginData: (handle) => getPlugin(handle).model.dataModel.getDefaultData(),\n }),\n [BlockStorageFacadeCallbacks.ArgsDerive]: (storageJson) =>\n deriveArgsFromStorage(storageJson, argsFunction),\n [BlockStorageFacadeCallbacks.PrerunArgsDerive]: (storageJson) =>\n derivePrerunArgsFromStorage(storageJson, argsFunction, prerunArgsFunction),\n });\n\n // Register plugin input and output lambdas\n const pluginOutputs: Record<string, ConfigRenderLambda> = {};\n for (const handle of pluginHandles) {\n const { model, inputs } = plugins[handle];\n // Wrap plugin param lambdas: close over BlockRenderCtx creation\n const wrappedInputs: Record<string, () => unknown> = {};\n for (const [paramKey, paramFn] of Object.entries(inputs)) {\n wrappedInputs[paramKey] = () => paramFn(new BlockRenderCtx());\n }\n\n // Register plugin outputs (in config pack, evaluated by middle layer)\n const outputs = model.outputs as Record<string, (ctx: PluginRenderCtx) => unknown>;\n for (const [outputKey, outputFn] of Object.entries(outputs)) {\n const key = pluginOutputKey(handle, outputKey);\n pluginOutputs[key] = createAndRegisterRenderLambda({\n handle: key,\n lambda: () => outputFn(new PluginRenderCtx(handle, wrappedInputs)),\n });\n }\n }\n const allOutputs = { ...this.config.outputs, ...pluginOutputs };\n\n const blockConfig: BlockConfigContainer = {\n v4: {\n configVersion: 4,\n modelAPIVersion: BLOCK_STORAGE_FACADE_VERSION,\n sdkVersion: PlatformaSDKVersion,\n renderingMode: this.config.renderingMode,\n sections: this.config.sections,\n title: this.config.title,\n subtitle: this.config.subtitle,\n tags: this.config.tags,\n outputs: allOutputs,\n enrichmentTargets: this.config.enrichmentTargets,\n featureFlags: this.config.featureFlags,\n blockLifecycleCallbacks: { ...BlockStorageFacadeHandles },\n },\n\n // fields below are added to allow previous desktop versions read generated configs\n sdkVersion: PlatformaSDKVersion,\n renderingMode: this.config.renderingMode,\n sections: this.config.sections,\n outputs: Object.fromEntries(\n Object.entries(this.config.outputs).map(([key, value]) => [\n key,\n downgradeCfgOrLambda(value),\n ]),\n ),\n };\n\n globalThis.platformaApiVersion = apiVersion;\n\n if (!isInUI())\n // we are in the configuration rendering routine, not in actual UI\n return { config: blockConfig } as any;\n // normal operation inside the UI\n else\n return {\n ...getPlatformaInstance({\n sdkVersion: PlatformaSDKVersion,\n apiVersion,\n }),\n blockModelInfo: {\n outputs: Object.fromEntries(\n Object.entries(allOutputs).map(([key, value]) => [\n key,\n {\n withStatus: Boolean(isConfigLambda(value) && value.withStatus),\n },\n ]),\n ),\n pluginIds: pluginHandles,\n },\n } as any;\n }\n}\n\n// Type tests for BlockModelV3\n\nexport type Expect<T extends true> = T;\n\nexport type Equal<X, Y> =\n (<T>() => T extends X ? 1 : 2) extends <T>() => T extends Y ? 1 : 2 ? true : false;\n\nexport type Merge<A, B> = {\n [K in keyof A | keyof B]: K extends keyof B ? B[K] : K extends keyof A ? A[K] : never;\n};\n\n// Helper types for testing\ntype _TestArgs = { inputFile: string; threshold: number };\ntype _TestData = { selectedTab: string };\ntype _TestOutputs = {\n result: ConfigRenderLambda<string>;\n count: ConfigRenderLambda<number>;\n};\n\n// Test: Merge type works correctly\ntype _MergeTest1 = Expect<Equal<Merge<{ a: 1 }, { b: 2 }>, { a: 1; b: 2 }>>;\ntype _MergeTest2 = Expect<Equal<Merge<{ a: 1 }, { a: 2 }>, { a: 2 }>>;\ntype _MergeTest3 = Expect<Equal<Merge<{ a: 1; b: 1 }, { b: 2; c: 3 }>, { a: 1; b: 2; c: 3 }>>;\n\n// Test: create() returns a BlockModelV3 instance\n// Note: Due to function overloads, ReturnType uses the last overload signature.\n// We verify the structure is correct using a simpler assignability test.\ntype _CreateResult = ReturnType<typeof BlockModelV3.create>;\ntype _CreateIsBlockModelV3 =\n _CreateResult extends BlockModelV3<infer _A, infer _O, infer _S> ? true : false;\ntype _CreateTest = Expect<_CreateIsBlockModelV3>;\n\n// Test: BlockModelV3Config interface structure\ntype _ConfigTest = Expect<\n Equal<\n BlockModelV3Config<_TestOutputs, _TestData>,\n {\n renderingMode: BlockRenderingMode;\n argsFunction: ((data: unknown) => unknown) | undefined;\n prerunArgsFunction: ((data: unknown) => unknown) | undefined;\n dataModel: DataModel<_TestData>;\n outputs: _TestOutputs;\n sections: ConfigRenderLambda;\n title: ConfigRenderLambda | undefined;\n subtitle: ConfigRenderLambda | undefined;\n tags: ConfigRenderLambda | undefined;\n enrichmentTargets: ConfigRenderLambda | undefined;\n featureFlags: BlockCodeKnownFeatureFlags;\n plugins: {};\n }\n >\n>;\n\n// Test: Default Href is '/'\ntype _HrefDefaultTest =\n BlockModelV3<_TestArgs, {}, _TestData> extends BlockModelV3<_TestArgs, {}, _TestData, \"/\">\n ? true\n : false;\ntype _VerifyHrefDefault = Expect<_HrefDefaultTest>;\n\n// Test: Custom Href can be specified\ntype _CustomHref = \"/settings\" | \"/main\";\ntype _HrefCustomBuilder = BlockModelV3<_TestArgs, {}, _TestData, _CustomHref>;\ntype _HrefCustomTest =\n _HrefCustomBuilder extends BlockModelV3<_TestArgs, {}, _TestData, _CustomHref> ? true : false;\ntype _VerifyHrefCustom = Expect<_HrefCustomTest>;\n\n// Test: Output type accumulation with & intersection\ntype _OutputsAccumulation = { a: ConfigRenderLambda<string> } & {\n b: ConfigRenderLambda<number>;\n};\ntype _VerifyOutputsHaveKeys = Expect<Equal<keyof _OutputsAccumulation, \"a\" | \"b\">>;\n\n// Test: Builder with all type parameters specified compiles\ntype _FullBuilder = BlockModelV3<_TestArgs, _TestOutputs, _TestData, \"/main\">;\ntype _FullBuilderTest =\n _FullBuilder extends BlockModelV3<_TestArgs, _TestOutputs, _TestData, \"/main\"> ? true : false;\ntype _VerifyFullBuilder = Expect<_FullBuilderTest>;\n\n// Test: InferOutputsFromLambdas maps outputs correctly\ntype _InferOutputsTest = InferOutputsFromLambdas<{\n myOutput: ConfigRenderLambda<number>;\n}>;\ntype _VerifyInferOutputs = Expect<\n Equal<_InferOutputsTest, { myOutput: OutputWithStatus<number> & { __unwrap: true } }>\n>;\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"block_model.js","sources":["../src/block_model.ts"],"sourcesContent":["import type {\n BlockRenderingMode,\n BlockSection,\n OutputWithStatus,\n PlRef,\n BlockCodeKnownFeatureFlags,\n BlockConfigContainer,\n} from \"@milaboratories/pl-model-common\";\nimport { mergeFeatureFlags } from \"@milaboratories/pl-model-common\";\nimport { getPlatformaInstance, isInUI, createAndRegisterRenderLambda } from \"./internal\";\nimport type { DataModel } from \"./block_migrations\";\nimport type { PlatformaV3 } from \"./platforma\";\nimport type { InferRenderFunctionReturn, RenderFunction } from \"./render\";\nimport { BlockRenderCtx, PluginRenderCtx } from \"./render\";\nimport type { PluginData, PluginModel, PluginOutputs, PluginParams } from \"./plugin_model\";\nimport { type PluginHandle, pluginOutputKey } from \"./plugin_handle\";\nimport type { RenderCtxBase } from \"./render\";\nimport { PlatformaSDKVersion } from \"./version\";\nimport {\n applyStorageUpdate,\n getStorageDebugView,\n migrateStorage,\n createInitialStorage,\n deriveArgsFromStorage,\n derivePrerunArgsFromStorage,\n} from \"./block_storage_callbacks\";\nimport { type PluginName } from \"./block_storage\";\nimport type {\n ConfigRenderLambda,\n DeriveHref,\n ConfigRenderLambdaFlags,\n InferOutputsFromLambdas,\n} from \"./bconfig\";\nimport { downgradeCfgOrLambda, isConfigLambda } from \"./bconfig\";\nimport type { PlatformaExtended } from \"./platforma\";\nimport {\n BLOCK_STORAGE_FACADE_VERSION,\n BlockStorageFacadeCallbacks,\n BlockStorageFacadeHandles,\n registerFacadeCallbacks,\n} from \"./block_storage_facade\";\n\ntype SectionsExpectedType = readonly BlockSection[];\n\ntype NoOb = Record<string, never>;\n\n/**\n * Per-property lambdas for deriving plugin params from block render context.\n * Each property is a function that receives the block's RenderCtxBase and returns the param value.\n */\nexport type ParamsInput<Params, BArgs = unknown, BData = unknown> = {\n [K in keyof Params]: (ctx: RenderCtxBase<BArgs, BData>) => Params[K];\n};\n\n/**\n * Type-erased version of ParamsInput for internal storage.\n */\ntype ParamsInputErased = Record<string, (ctx: RenderCtxBase) => unknown>;\n\n/**\n * Registered plugin: model + param derivation lambdas.\n * Type parameters are carried by PluginModel generic.\n */\nexport type PluginInstance<\n Data extends PluginData = PluginData,\n Params extends PluginParams = undefined,\n Outputs extends PluginOutputs = PluginOutputs,\n> = {\n readonly model: PluginModel<Data, Params, Outputs>;\n readonly inputs: ParamsInputErased;\n};\n\ninterface BlockModelV3Config<\n OutputsCfg extends Record<string, ConfigRenderLambda>,\n Data,\n Plugins extends Record<string, PluginInstance> = {},\n> {\n renderingMode: BlockRenderingMode;\n dataModel: DataModel<Data>;\n outputs: OutputsCfg;\n sections: ConfigRenderLambda;\n title: ConfigRenderLambda | undefined;\n subtitle: ConfigRenderLambda | undefined;\n tags: ConfigRenderLambda | undefined;\n enrichmentTargets: ConfigRenderLambda | undefined;\n featureFlags: BlockCodeKnownFeatureFlags;\n argsFunction: ((data: unknown) => unknown) | undefined;\n prerunArgsFunction: ((data: unknown) => unknown) | undefined;\n plugins: Plugins;\n}\n\n/** Main entry point that each block should use in it's \"config\" module. Don't forget\n * to call {@link done()} at the end of configuration. Value returned by this builder must be\n * exported as constant with name \"platforma\" from the \"config\" module.\n * API version is 3 (for UI) and 2 (for model) */\nexport class BlockModelV3<\n Args,\n OutputsCfg extends Record<string, ConfigRenderLambda>,\n Data extends Record<string, unknown> = Record<string, unknown>,\n Href extends `/${string}` = \"/\",\n Plugins extends Record<string, PluginInstance> = {},\n> {\n private constructor(private readonly config: BlockModelV3Config<OutputsCfg, Data, Plugins>) {}\n\n public static readonly INITIAL_BLOCK_FEATURE_FLAGS: BlockCodeKnownFeatureFlags = {\n supportsLazyState: true,\n requiresUIAPIVersion: 3,\n requiresModelAPIVersion: BLOCK_STORAGE_FACADE_VERSION,\n requiresCreatePTable: 2,\n };\n\n /**\n * Creates a new BlockModelV3 builder with the specified data model.\n *\n * @example\n * const dataModel = new DataModelBuilder()\n * .from<BlockData>(\"v1\")\n * .init(() => ({ numbers: [], labels: [] }));\n *\n * BlockModelV3.create(dataModel)\n * .args((data) => ({ numbers: data.numbers }))\n * .sections(() => [{ type: 'link', href: '/', label: 'Main' }])\n * .done();\n *\n * @param dataModel The data model that defines initial data and migrations\n */\n public static create<Data extends Record<string, unknown>>(\n dataModel: DataModel<Data>,\n ): BlockModelV3<NoOb, {}, Data> {\n return new BlockModelV3<NoOb, {}, Data>({\n renderingMode: \"Heavy\",\n dataModel,\n outputs: {},\n sections: createAndRegisterRenderLambda({ handle: \"sections\", lambda: () => [] }, true),\n title: undefined,\n subtitle: undefined,\n tags: undefined,\n enrichmentTargets: undefined,\n featureFlags: { ...BlockModelV3.INITIAL_BLOCK_FEATURE_FLAGS },\n argsFunction: undefined,\n prerunArgsFunction: undefined,\n plugins: {},\n });\n }\n\n /**\n * Add output cell wrapped with additional status information to the configuration\n *\n * @param key output cell name, that can be later used to retrieve the rendered value\n * @param rf callback calculating output value using context, that allows to access\n * workflows outputs and interact with platforma drivers\n * @param flags additional flags that may alter lambda rendering procedure\n * */\n public output<const Key extends string, const RF extends RenderFunction<Args, Data, unknown>>(\n key: Key,\n rf: RF,\n flags: ConfigRenderLambdaFlags & { withStatus: true },\n ): BlockModelV3<\n Args,\n OutputsCfg & {\n [K in Key]: ConfigRenderLambda<InferRenderFunctionReturn<RF>> & {\n withStatus: true;\n };\n },\n Data,\n Href,\n Plugins\n >;\n /**\n * Add output cell to the configuration\n *\n * @param key output cell name, that can be later used to retrieve the rendered value\n * @param rf callback calculating output value using context, that allows to access\n * workflows outputs and interact with platforma drivers\n * @param flags additional flags that may alter lambda rendering procedure\n * */\n public output<const Key extends string, const RF extends RenderFunction<Args, Data, unknown>>(\n key: Key,\n rf: RF,\n flags?: ConfigRenderLambdaFlags,\n ): BlockModelV3<\n Args,\n OutputsCfg & {\n [K in Key]: ConfigRenderLambda<InferRenderFunctionReturn<RF>>;\n },\n Data,\n Href,\n Plugins\n >;\n public output(\n key: string,\n cfgOrRf: RenderFunction<Args, Data, unknown>,\n flags: ConfigRenderLambdaFlags = {},\n ): BlockModelV3<Args, OutputsCfg, Data, Href, Plugins> {\n return new BlockModelV3({\n ...this.config,\n outputs: {\n ...this.config.outputs,\n [key]: createAndRegisterRenderLambda({\n handle: `block-output#${key}`,\n lambda: () => cfgOrRf(new BlockRenderCtx<Args, Data>()),\n ...flags,\n }),\n },\n });\n }\n\n /** Shortcut for {@link output} with retentive flag set to true. */\n public retentiveOutput<\n const Key extends string,\n const RF extends RenderFunction<Args, Data, unknown>,\n >(\n key: Key,\n rf: RF,\n ): BlockModelV3<\n Args,\n OutputsCfg & {\n [K in Key]: ConfigRenderLambda<InferRenderFunctionReturn<RF>>;\n },\n Data,\n Href,\n Plugins\n > {\n return this.output(key, rf, { retentive: true });\n }\n\n /** Shortcut for {@link output} with withStatus flag set to true. */\n public outputWithStatus<\n const Key extends string,\n const RF extends RenderFunction<Args, Data, unknown>,\n >(key: Key, rf: RF) {\n return this.output(key, rf, { withStatus: true });\n }\n\n /**\n * Sets a function to derive block args from data.\n * This is called during setData to compute the args that will be used for block execution.\n *\n * @example\n * .args<BlockArgs>((data) => ({ numbers: data.numbers }))\n *\n * @example\n * .args<BlockArgs>((data) => {\n * if (data.numbers.length === 0) throw new Error('Numbers required'); // block not ready\n * return { numbers: data.numbers };\n * })\n */\n public args<Args>(\n lambda: (data: Data) => Args,\n ): BlockModelV3<Args, OutputsCfg, Data, Href, Plugins> {\n return new BlockModelV3<Args, OutputsCfg, Data, Href, Plugins>({\n ...this.config,\n argsFunction: lambda as (data: unknown) => unknown,\n });\n }\n\n /**\n * Sets a function to derive pre-run args from data (optional).\n * This is called during setData to compute the args that will be used for staging/pre-run phase.\n *\n * If not defined, defaults to using the args() function result.\n * If defined, uses its return value for the staging / prerun phase.\n *\n * The staging / prerun phase runs only if currentPrerunArgs differs from the executed\n * version of prerunArgs (same comparison logic as currentArgs vs prodArgs).\n *\n * @example\n * .prerunArgs((data) => ({ numbers: data.numbers }))\n *\n * @example\n * .prerunArgs((data) => {\n * // Return undefined to skip staging for this block\n * if (!data.isReady) return undefined;\n * return { numbers: data.numbers };\n * })\n */\n public prerunArgs(\n fn: (data: Data) => unknown,\n ): BlockModelV3<Args, OutputsCfg, Data, Href, Plugins> {\n return new BlockModelV3<Args, OutputsCfg, Data, Href, Plugins>({\n ...this.config,\n prerunArgsFunction: fn as (data: unknown) => unknown,\n });\n }\n\n /** Sets the lambda to generate list of sections in the left block overviews panel. */\n public sections<\n const Ret extends SectionsExpectedType,\n const RF extends RenderFunction<Args, Data, Ret>,\n >(rf: RF): BlockModelV3<Args, OutputsCfg, Data, DeriveHref<ReturnType<RF>>, Plugins> {\n return new BlockModelV3<Args, OutputsCfg, Data, DeriveHref<ReturnType<RF>>, Plugins>({\n ...this.config,\n // Replace the default sections callback with the user-provided one\n sections: createAndRegisterRenderLambda(\n { handle: \"sections\", lambda: () => rf(new BlockRenderCtx<Args, Data>()) },\n true,\n ),\n });\n }\n\n /** Sets a rendering function to derive block title, shown for the block in the left blocks-overview panel. */\n public title(\n rf: RenderFunction<Args, Data, string>,\n ): BlockModelV3<Args, OutputsCfg, Data, Href, Plugins> {\n return new BlockModelV3<Args, OutputsCfg, Data, Href, Plugins>({\n ...this.config,\n title: createAndRegisterRenderLambda({\n handle: \"title\",\n lambda: () => rf(new BlockRenderCtx<Args, Data>()),\n }),\n });\n }\n\n public subtitle(\n rf: RenderFunction<Args, Data, string>,\n ): BlockModelV3<Args, OutputsCfg, Data, Href, Plugins> {\n return new BlockModelV3<Args, OutputsCfg, Data, Href, Plugins>({\n ...this.config,\n subtitle: createAndRegisterRenderLambda({\n handle: \"subtitle\",\n lambda: () => rf(new BlockRenderCtx<Args, Data>()),\n }),\n });\n }\n\n public tags(\n rf: RenderFunction<Args, Data, string[]>,\n ): BlockModelV3<Args, OutputsCfg, Data, Href, Plugins> {\n return new BlockModelV3<Args, OutputsCfg, Data, Href, Plugins>({\n ...this.config,\n tags: createAndRegisterRenderLambda({\n handle: \"tags\",\n lambda: () => rf(new BlockRenderCtx<Args, Data>()),\n }),\n });\n }\n\n /** Sets or overrides feature flags for the block. */\n public withFeatureFlags(\n flags: Partial<BlockCodeKnownFeatureFlags>,\n ): BlockModelV3<Args, OutputsCfg, Data, Href, Plugins> {\n return new BlockModelV3<Args, OutputsCfg, Data, Href, Plugins>({\n ...this.config,\n featureFlags: { ...this.config.featureFlags, ...flags },\n });\n }\n\n /**\n * Defines how to derive list of upstream references this block is meant to enrich with its exports from block args.\n * Influences dependency graph construction.\n */\n public enriches(\n lambda: (args: Args) => PlRef[],\n ): BlockModelV3<Args, OutputsCfg, Data, Href, Plugins> {\n return new BlockModelV3<Args, OutputsCfg, Data, Href, Plugins>({\n ...this.config,\n enrichmentTargets: createAndRegisterRenderLambda({\n handle: \"enrichmentTargets\",\n lambda: lambda,\n }),\n });\n }\n\n /**\n * Registers a plugin instance with the block.\n *\n * Plugins are UI components with their own model logic and persistent state.\n * Each plugin must have a unique pluginId within the block.\n *\n * @param pluginId - Unique identifier for this plugin instance within the block\n * @param plugin - Configured PluginModel instance (created via factory.create(config))\n * @param params - Per-property lambdas deriving plugin params from block RenderCtx\n *\n * @example\n * .plugin('mainTable', dataTablePlugin.create({ defaultOps: {...} }), {\n * columns: (ctx) => ctx.outputs?.resolve(\"data\")?.getPColumns(),\n * sourceId: (ctx) => ctx.data.selectedSource,\n * })\n */\n public plugin<\n const PluginId extends string,\n PData extends PluginData,\n PParams extends PluginParams,\n POutputs extends PluginOutputs,\n >(\n pluginId: PluginId,\n plugin: PluginModel<PData, PParams, POutputs>,\n params?: ParamsInput<PParams, Args, Data>,\n ): BlockModelV3<\n Args,\n OutputsCfg,\n Data,\n Href,\n Plugins & { [K in PluginId]: PluginInstance<PData, PParams, POutputs> }\n > {\n // Validate pluginId uniqueness\n if (pluginId in this.config.plugins) {\n throw new Error(`Plugin '${pluginId}' already registered`);\n }\n\n const instance: PluginInstance<PData, PParams, POutputs> = {\n model: plugin,\n inputs: (params ?? {}) as ParamsInputErased,\n };\n\n return new BlockModelV3({\n ...this.config,\n plugins: {\n ...this.config.plugins,\n [pluginId]: instance,\n },\n featureFlags: mergeFeatureFlags(this.config.featureFlags, plugin.featureFlags ?? {}),\n });\n }\n\n /** Renders all provided block settings into a pre-configured platforma API\n * instance, that can be used in frontend to interact with block data, and\n * other features provided by the platforma to the block. */\n public done(): PlatformaExtended<\n PlatformaV3<Data, Args, InferOutputsFromLambdas<OutputsCfg>, Href, Plugins>\n > {\n return this.withFeatureFlags({\n ...this.config.featureFlags,\n })._done();\n }\n\n public _done(): PlatformaExtended<\n PlatformaV3<Data, Args, InferOutputsFromLambdas<OutputsCfg>, Href, Plugins>\n > {\n if (this.config.argsFunction === undefined) throw new Error(\"Args rendering function not set.\");\n\n const apiVersion = 3;\n\n // Build plugin registry\n const { plugins } = this.config;\n const pluginRegistry: Record<string, PluginName> = {};\n const pluginHandles = Object.keys(plugins) as PluginHandle[];\n for (const handle of pluginHandles) {\n pluginRegistry[handle] = plugins[handle].model.name;\n }\n\n const { dataModel, argsFunction, prerunArgsFunction } = this.config;\n\n function getPlugin(handle: PluginHandle): PluginInstance {\n const plugin = plugins[handle];\n if (!plugin) throw new Error(`Plugin model not found for '${handle}'`);\n return plugin;\n }\n\n // Register ALL facade callbacks here, with dependencies captured via closures\n registerFacadeCallbacks({\n [BlockStorageFacadeCallbacks.StorageApplyUpdate]: applyStorageUpdate,\n [BlockStorageFacadeCallbacks.StorageDebugView]: getStorageDebugView,\n [BlockStorageFacadeCallbacks.StorageMigrate]: (currentStorageJson) =>\n migrateStorage(currentStorageJson, {\n migrateBlockData: (v) => dataModel.migrate(v),\n getPluginRegistry: () => pluginRegistry,\n migratePluginData: (handle, v) => getPlugin(handle).model.dataModel.migrate(v),\n createPluginData: (handle) => getPlugin(handle).model.dataModel.getDefaultData(),\n }),\n [BlockStorageFacadeCallbacks.StorageInitial]: () =>\n createInitialStorage({\n getDefaultBlockData: () => dataModel.getDefaultData(),\n getPluginRegistry: () => pluginRegistry,\n createPluginData: (handle) => getPlugin(handle).model.dataModel.getDefaultData(),\n }),\n [BlockStorageFacadeCallbacks.ArgsDerive]: (storageJson) =>\n deriveArgsFromStorage(storageJson, argsFunction),\n [BlockStorageFacadeCallbacks.PrerunArgsDerive]: (storageJson) =>\n derivePrerunArgsFromStorage(storageJson, argsFunction, prerunArgsFunction),\n });\n\n // Register plugin input and output lambdas\n const pluginOutputs: Record<string, ConfigRenderLambda> = {};\n for (const handle of pluginHandles) {\n const { model, inputs } = plugins[handle];\n // Wrap plugin param lambdas: close over BlockRenderCtx creation\n const wrappedInputs: Record<string, () => unknown> = {};\n for (const [paramKey, paramFn] of Object.entries(inputs)) {\n wrappedInputs[paramKey] = () => paramFn(new BlockRenderCtx());\n }\n\n // Register plugin outputs (in config pack, evaluated by middle layer)\n const outputs = model.outputs as Record<string, (ctx: PluginRenderCtx) => unknown>;\n for (const [outputKey, outputFn] of Object.entries(outputs)) {\n const key = pluginOutputKey(handle, outputKey);\n pluginOutputs[key] = createAndRegisterRenderLambda({\n handle: key,\n lambda: () => outputFn(new PluginRenderCtx(handle, wrappedInputs)),\n });\n }\n }\n const allOutputs = { ...this.config.outputs, ...pluginOutputs };\n\n const blockConfig: BlockConfigContainer = {\n v4: {\n configVersion: 4,\n modelAPIVersion: BLOCK_STORAGE_FACADE_VERSION,\n sdkVersion: PlatformaSDKVersion,\n renderingMode: this.config.renderingMode,\n sections: this.config.sections,\n title: this.config.title,\n subtitle: this.config.subtitle,\n tags: this.config.tags,\n outputs: allOutputs,\n enrichmentTargets: this.config.enrichmentTargets,\n featureFlags: this.config.featureFlags,\n blockLifecycleCallbacks: { ...BlockStorageFacadeHandles },\n },\n\n // fields below are added to allow previous desktop versions read generated configs\n sdkVersion: PlatformaSDKVersion,\n renderingMode: this.config.renderingMode,\n sections: this.config.sections,\n outputs: Object.fromEntries(\n Object.entries(this.config.outputs).map(([key, value]) => [\n key,\n downgradeCfgOrLambda(value),\n ]),\n ),\n };\n\n globalThis.platformaApiVersion = apiVersion;\n\n if (!isInUI())\n // we are in the configuration rendering routine, not in actual UI\n return { config: blockConfig } as any;\n // normal operation inside the UI\n else\n return {\n ...getPlatformaInstance({\n sdkVersion: PlatformaSDKVersion,\n apiVersion,\n }),\n blockModelInfo: {\n outputs: Object.fromEntries(\n Object.entries(allOutputs).map(([key, value]) => [\n key,\n {\n withStatus: Boolean(isConfigLambda(value) && value.withStatus),\n },\n ]),\n ),\n pluginIds: pluginHandles,\n },\n } as any;\n }\n}\n\n// Type tests for BlockModelV3\n\nexport type Expect<T extends true> = T;\n\nexport type Equal<X, Y> =\n (<T>() => T extends X ? 1 : 2) extends <T>() => T extends Y ? 1 : 2 ? true : false;\n\nexport type Merge<A, B> = {\n [K in keyof A | keyof B]: K extends keyof B ? B[K] : K extends keyof A ? A[K] : never;\n};\n\n// Helper types for testing\ntype _TestArgs = { inputFile: string; threshold: number };\ntype _TestData = { selectedTab: string };\ntype _TestOutputs = {\n result: ConfigRenderLambda<string>;\n count: ConfigRenderLambda<number>;\n};\n\n// Test: Merge type works correctly\ntype _MergeTest1 = Expect<Equal<Merge<{ a: 1 }, { b: 2 }>, { a: 1; b: 2 }>>;\ntype _MergeTest2 = Expect<Equal<Merge<{ a: 1 }, { a: 2 }>, { a: 2 }>>;\ntype _MergeTest3 = Expect<Equal<Merge<{ a: 1; b: 1 }, { b: 2; c: 3 }>, { a: 1; b: 2; c: 3 }>>;\n\n// Test: create() returns a BlockModelV3 instance\n// Note: Due to function overloads, ReturnType uses the last overload signature.\n// We verify the structure is correct using a simpler assignability test.\ntype _CreateResult = ReturnType<typeof BlockModelV3.create>;\ntype _CreateIsBlockModelV3 =\n _CreateResult extends BlockModelV3<infer _A, infer _O, infer _S> ? true : false;\ntype _CreateTest = Expect<_CreateIsBlockModelV3>;\n\n// Test: BlockModelV3Config interface structure\ntype _ConfigTest = Expect<\n Equal<\n BlockModelV3Config<_TestOutputs, _TestData>,\n {\n renderingMode: BlockRenderingMode;\n argsFunction: ((data: unknown) => unknown) | undefined;\n prerunArgsFunction: ((data: unknown) => unknown) | undefined;\n dataModel: DataModel<_TestData>;\n outputs: _TestOutputs;\n sections: ConfigRenderLambda;\n title: ConfigRenderLambda | undefined;\n subtitle: ConfigRenderLambda | undefined;\n tags: ConfigRenderLambda | undefined;\n enrichmentTargets: ConfigRenderLambda | undefined;\n featureFlags: BlockCodeKnownFeatureFlags;\n plugins: {};\n }\n >\n>;\n\n// Test: Default Href is '/'\ntype _HrefDefaultTest =\n BlockModelV3<_TestArgs, {}, _TestData> extends BlockModelV3<_TestArgs, {}, _TestData, \"/\">\n ? true\n : false;\ntype _VerifyHrefDefault = Expect<_HrefDefaultTest>;\n\n// Test: Custom Href can be specified\ntype _CustomHref = \"/settings\" | \"/main\";\ntype _HrefCustomBuilder = BlockModelV3<_TestArgs, {}, _TestData, _CustomHref>;\ntype _HrefCustomTest =\n _HrefCustomBuilder extends BlockModelV3<_TestArgs, {}, _TestData, _CustomHref> ? true : false;\ntype _VerifyHrefCustom = Expect<_HrefCustomTest>;\n\n// Test: Output type accumulation with & intersection\ntype _OutputsAccumulation = { a: ConfigRenderLambda<string> } & {\n b: ConfigRenderLambda<number>;\n};\ntype _VerifyOutputsHaveKeys = Expect<Equal<keyof _OutputsAccumulation, \"a\" | \"b\">>;\n\n// Test: Builder with all type parameters specified compiles\ntype _FullBuilder = BlockModelV3<_TestArgs, _TestOutputs, _TestData, \"/main\">;\ntype _FullBuilderTest =\n _FullBuilder extends BlockModelV3<_TestArgs, _TestOutputs, _TestData, \"/main\"> ? true : false;\ntype _VerifyFullBuilder = Expect<_FullBuilderTest>;\n\n// Test: InferOutputsFromLambdas maps outputs correctly\ntype _InferOutputsTest = InferOutputsFromLambdas<{\n myOutput: ConfigRenderLambda<number>;\n}>;\ntype _VerifyInferOutputs = Expect<\n Equal<_InferOutputsTest, { myOutput: OutputWithStatus<number> & { __unwrap: true } }>\n>;\n"],"names":[],"mappings":"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"}
@@ -27,7 +27,7 @@ const BLOCK_STORAGE_SCHEMA_VERSION = "v1";
27
27
  * Default data version for new blocks without migrations.
28
28
  * Unique identifier ensures blocks are created via DataModel API.
29
29
  */
30
- const DATA_MODEL_DEFAULT_VERSION = "__pl_v1_d4e8f2a1__";
30
+ const DATA_MODEL_LEGACY_VERSION = "__pl_v1_d4e8f2a1__";
31
31
  /**
32
32
  * Type guard to check if a value is a valid BlockStorage object.
33
33
  * Checks for the discriminator key and valid schema version.
@@ -47,10 +47,10 @@ function isBlockStorage(value) {
47
47
  * Creates a BlockStorage with the given initial data
48
48
  *
49
49
  * @param initialData - The initial data value (defaults to empty object)
50
- * @param version - The initial data version key (defaults to DATA_MODEL_DEFAULT_VERSION)
50
+ * @param version - The initial data version key (defaults to DATA_MODEL_LEGACY_VERSION)
51
51
  * @returns A new BlockStorage instance with discriminator key
52
52
  */
53
- function createBlockStorage(initialData = {}, version = DATA_MODEL_DEFAULT_VERSION) {
53
+ function createBlockStorage(initialData = {}, version = DATA_MODEL_LEGACY_VERSION) {
54
54
  return {
55
55
  [BLOCK_STORAGE_KEY]: BLOCK_STORAGE_SCHEMA_VERSION,
56
56
  __dataVersion: version,
@@ -74,7 +74,7 @@ function normalizeBlockStorage(raw) {
74
74
  ...storage,
75
75
  // Fix for early released version where __dataVersion was a number
76
76
  __dataVersion: typeof storage.__dataVersion === "number"
77
- ? DATA_MODEL_DEFAULT_VERSION
77
+ ? DATA_MODEL_LEGACY_VERSION
78
78
  : storage.__dataVersion,
79
79
  // Ensure plugin fields have defaults
80
80
  __pluginRegistry: storage.__pluginRegistry ?? {},
@@ -127,7 +127,7 @@ function updateStorageData(storage, payload) {
127
127
  const { pluginId, value } = payload;
128
128
  const currentPlugins = storage.__plugins ?? {};
129
129
  const existingEntry = currentPlugins[pluginId];
130
- const version = existingEntry?.__dataVersion ?? DATA_MODEL_DEFAULT_VERSION;
130
+ const version = existingEntry?.__dataVersion ?? DATA_MODEL_LEGACY_VERSION;
131
131
  return {
132
132
  ...storage,
133
133
  __plugins: {
@@ -269,7 +269,7 @@ function getPluginData(rawStorage, handle) {
269
269
 
270
270
  exports.BLOCK_STORAGE_KEY = BLOCK_STORAGE_KEY;
271
271
  exports.BLOCK_STORAGE_SCHEMA_VERSION = BLOCK_STORAGE_SCHEMA_VERSION;
272
- exports.DATA_MODEL_DEFAULT_VERSION = DATA_MODEL_DEFAULT_VERSION;
272
+ exports.DATA_MODEL_LEGACY_VERSION = DATA_MODEL_LEGACY_VERSION;
273
273
  exports.createBlockStorage = createBlockStorage;
274
274
  exports.deriveDataFromStorage = deriveDataFromStorage;
275
275
  exports.getPluginData = getPluginData;
@@ -1 +1 @@
1
- {"version":3,"file":"block_storage.cjs","sources":["../src/block_storage.ts"],"sourcesContent":["/**\n * BlockStorage - Typed storage abstraction for block persistent data.\n *\n * This module provides:\n * - A typed structure for block storage with versioning and plugin support\n * - Utility functions for manipulating storage\n * - Handler interfaces for model-level customization\n *\n * @module block_storage\n */\n\nimport type { Branded } from \"@milaboratories/pl-model-common\";\nimport type { DataMigrationResult, DataVersioned } from \"./block_migrations\";\nimport type { PluginHandle, PluginFactoryLike, InferFactoryData } from \"./plugin_handle\";\n\n// =============================================================================\n// Core Types\n// =============================================================================\n\n/**\n * Discriminator key for BlockStorage format detection.\n * This unique hash-based key identifies data as BlockStorage vs legacy formats.\n */\nexport const BLOCK_STORAGE_KEY = \"__pl_a7f3e2b9__\";\n\n/**\n * Current BlockStorage schema version.\n * Increment this when the storage structure itself changes (not block state migrations).\n */\nexport const BLOCK_STORAGE_SCHEMA_VERSION = \"v1\";\n\n/**\n * Default data version for new blocks without migrations.\n * Unique identifier ensures blocks are created via DataModel API.\n */\nexport const DATA_MODEL_DEFAULT_VERSION = \"__pl_v1_d4e8f2a1__\";\n\n/**\n * Type for valid schema versions\n */\nexport type BlockStorageSchemaVersion = \"v1\"; // Add 'v2', 'v3', etc. as schema evolves\n\n/**\n * Branded type for plugin names - globally unique plugin type identifiers.\n * Using a branded type enforces explicit casting (`as PluginName`) which makes\n * it easy to find all plugin name definitions in the codebase and verify uniqueness.\n */\nexport type PluginName = Branded<string, \"PluginName\">;\n\n/**\n * Plugin registry - maps pluginId (unique within a block) to pluginName (globally unique plugin type).\n * Using a Record highlights that pluginIds must be unique within a block.\n */\nexport type PluginRegistry = Record<PluginHandle, PluginName>;\n\n/**\n * Versioned data - used for both block data and plugin data\n */\nexport interface VersionedData<TData = unknown> {\n /** Version of the data, used for migrations */\n __dataVersion: string;\n /** The persistent data */\n __data: TData;\n}\n\n/**\n * Core BlockStorage type that holds:\n * - __pl_a7f3e2b9__: Schema version (discriminator key identifies BlockStorage format)\n * - __dataVersion: Version key for block data migrations\n * - __data: The block's user-facing data (state)\n * - __pluginRegistry: Map from pluginId to pluginName (optional)\n * - __plugins: Plugin-specific data keyed by pluginId (optional)\n */\nexport type BlockStorage<TState = unknown> = {\n /** Schema version - the key itself is the discriminator */\n readonly [BLOCK_STORAGE_KEY]: BlockStorageSchemaVersion;\n /** Registry of plugins: pluginId -> pluginName */\n __pluginRegistry?: PluginRegistry;\n /** Plugin-specific data, keyed by plugin handle */\n __plugins?: Record<PluginHandle, VersionedData<unknown>>;\n} & VersionedData<TState>;\n\n/**\n * Type guard to check if a value is a valid BlockStorage object.\n * Checks for the discriminator key and valid schema version.\n */\nexport function isBlockStorage(value: unknown): value is BlockStorage {\n if (value === null || typeof value !== \"object\") return false;\n const obj = value as Record<string, unknown>;\n const schemaVersion = obj[BLOCK_STORAGE_KEY];\n // Currently only 'v1' is valid, but this allows future versions\n return schemaVersion === \"v1\"; // Add more versions as schema evolves\n}\n\n// =============================================================================\n// Factory Functions\n// =============================================================================\n\n/**\n * Creates a BlockStorage with the given initial data\n *\n * @param initialData - The initial data value (defaults to empty object)\n * @param version - The initial data version key (defaults to DATA_MODEL_DEFAULT_VERSION)\n * @returns A new BlockStorage instance with discriminator key\n */\nexport function createBlockStorage<TState = unknown>(\n initialData: TState = {} as TState,\n version: string = DATA_MODEL_DEFAULT_VERSION,\n): BlockStorage<TState> {\n return {\n [BLOCK_STORAGE_KEY]: BLOCK_STORAGE_SCHEMA_VERSION,\n __dataVersion: version,\n __data: initialData,\n __pluginRegistry: {},\n __plugins: {},\n };\n}\n\n/**\n * Normalizes raw storage data to BlockStorage format.\n * If the input is already a BlockStorage, returns it as-is (with defaults for missing fields).\n * If the input is legacy format (raw state), wraps it in BlockStorage structure.\n *\n * @param raw - Raw storage data (may be legacy format or BlockStorage)\n * @returns Normalized BlockStorage\n */\nexport function normalizeBlockStorage<TState = unknown>(raw: unknown): BlockStorage<TState> {\n if (isBlockStorage(raw)) {\n const storage = raw as BlockStorage<TState>;\n return {\n ...storage,\n // Fix for early released version where __dataVersion was a number\n __dataVersion:\n typeof storage.__dataVersion === \"number\"\n ? DATA_MODEL_DEFAULT_VERSION\n : storage.__dataVersion,\n // Ensure plugin fields have defaults\n __pluginRegistry: storage.__pluginRegistry ?? {},\n __plugins: storage.__plugins ?? {},\n };\n }\n // Legacy format: raw is the state directly\n return createBlockStorage(raw as TState);\n}\n\n// =============================================================================\n// Data Access & Update Functions\n// =============================================================================\n\n/**\n * Gets the data from BlockStorage\n *\n * @param storage - The BlockStorage instance\n * @returns The data value\n */\nexport function getStorageData<TState>(storage: BlockStorage<TState>): TState {\n return storage.__data;\n}\n\n/**\n * Derives data from raw block storage.\n * This function is meant to be called from sdk/ui-vue to extract\n * user-facing data from the raw storage returned by the middle layer.\n *\n * The middle layer returns raw storage (opaque to it), and the UI\n * uses this function to derive the actual data value.\n *\n * @param rawStorage - Raw storage data from middle layer (may be any format)\n * @returns The extracted data value, or undefined if storage is undefined/null\n */\nexport function deriveDataFromStorage<TData = unknown>(rawStorage: unknown): TData {\n // Normalize to BlockStorage format (handles legacy formats too)\n const storage = normalizeBlockStorage<TData>(rawStorage);\n return getStorageData(storage);\n}\n\n/** Payload for storage mutation operations. SDK defines specific operations. */\nexport type MutateStoragePayload<T = unknown> =\n | { operation: \"update-block-data\"; value: T }\n | { operation: \"update-plugin-data\"; pluginId: PluginHandle; value: unknown };\n\n/**\n * Updates the data in BlockStorage (immutable)\n *\n * @param storage - The current BlockStorage\n * @param payload - The update payload with operation and value\n * @returns A new BlockStorage with updated data\n */\nexport function updateStorageData<TValue = unknown>(\n storage: BlockStorage<TValue>,\n payload: MutateStoragePayload<TValue>,\n): BlockStorage<TValue> {\n switch (payload.operation) {\n case \"update-block-data\":\n return { ...storage, __data: payload.value };\n case \"update-plugin-data\": {\n const { pluginId, value } = payload;\n const currentPlugins = storage.__plugins ?? {};\n const existingEntry = currentPlugins[pluginId];\n const version = existingEntry?.__dataVersion ?? DATA_MODEL_DEFAULT_VERSION;\n return {\n ...storage,\n __plugins: {\n ...currentPlugins,\n [pluginId]: {\n __dataVersion: version,\n __data: value,\n },\n },\n };\n }\n default:\n throw new Error(`Unknown storage operation: ${(payload as { operation: string }).operation}`);\n }\n}\n\n/**\n * Storage debug view returned by __pl_storage_debugView callback.\n * Used by developer tools to display block storage info.\n */\nexport interface StorageDebugView {\n /** Current data version key */\n dataVersion: string;\n /** Raw data payload stored in BlockStorage */\n data: unknown;\n}\n\n// =============================================================================\n// Atomic Migration\n// =============================================================================\n\n/**\n * Result of a successful atomic migration.\n */\nexport interface MigrationSuccess<TState> {\n success: true;\n /** The fully migrated storage - commit this to persist */\n storage: BlockStorage<TState>;\n}\n\n/**\n * Result of a failed atomic migration.\n * The original storage is untouched - user must choose to abort or reset.\n */\nexport interface MigrationFailure {\n success: false;\n /** Description of what failed */\n error: string;\n /** Which step failed: 'block' or pluginId */\n failedAt: string;\n}\n\nexport type MigrationResult<TState> = MigrationSuccess<TState> | MigrationFailure;\n\n/**\n * Configuration for atomic block storage migration.\n * Callbacks use DataVersioned format (the DataModel API format).\n * Conversion to internal VersionedData format is handled by migrateBlockStorage().\n */\nexport interface MigrateBlockStorageConfig {\n /** Migrate block data from any version to latest */\n migrateBlockData: (versioned: DataVersioned<unknown>) => DataMigrationResult<unknown>;\n /** Migrate each plugin's data. Return undefined to remove the plugin. */\n migratePluginData: (\n handle: PluginHandle,\n versioned: DataVersioned<unknown>,\n ) => DataMigrationResult<unknown> | undefined;\n /** The new plugin registry after migration (pluginId -> pluginName) */\n newPluginRegistry: PluginRegistry;\n /** Factory to create initial data for new plugins */\n createPluginData: (handle: PluginHandle) => DataVersioned<unknown>;\n}\n\n/**\n * Performs atomic migration of block storage including block data and all plugins.\n *\n * Migration is atomic: either everything succeeds and a new storage is returned,\n * or an error is returned and the original storage is completely untouched.\n *\n * Migration steps:\n * 1. Migrate block data\n * 2. For each plugin in newPluginRegistry:\n * - If plugin exists with same name: migrate its data\n * - Otherwise (new or type changed): create with initial data\n * Plugins not in newPluginRegistry are dropped.\n *\n * If any step throws, migration fails and original storage is preserved.\n * User can then choose to:\n * - Abort: keep original storage, don't update block\n * - Reset: call createBlockStorage() to start fresh\n *\n * @param storage - The original storage (will not be modified)\n * @param config - Migration configuration\n * @returns Migration result - either success with new storage, or failure with error info\n *\n * @example\n * const result = migrateBlockStorage(storage, {\n * migrateBlockData: (versioned) => blockDataModel.migrate(versioned),\n * migratePluginData: (pluginId, versioned) => getPluginModel(pluginId).migrate(versioned),\n * newPluginRegistry: { table1: 'dataTable' as PluginName },\n * createPluginData: (pluginId) => getPluginModel(pluginId).getDefaultData(),\n * });\n *\n * if (result.success) {\n * commitStorage(result.storage);\n * } else {\n * const userChoice = await askUser(`Migration failed: ${result.error}. Reset data?`);\n * if (userChoice === 'reset') {\n * commitStorage(createBlockStorage(initialData, currentVersion));\n * }\n * // else: abort, keep original\n * }\n */\nexport function migrateBlockStorage(\n storage: BlockStorage<unknown>,\n config: MigrateBlockStorageConfig,\n): MigrationResult<unknown> {\n const { migrateBlockData, migratePluginData, newPluginRegistry, createPluginData } = config;\n\n // Step 1: Migrate block data\n let migratedData: unknown;\n let newVersion: string;\n try {\n const result = migrateBlockData({ version: storage.__dataVersion, data: storage.__data });\n migratedData = result.data;\n newVersion = result.version;\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : String(error),\n failedAt: \"block\",\n };\n }\n\n // Step 2: Migrate plugins\n const oldPlugins = storage.__plugins ?? {};\n const oldRegistry = storage.__pluginRegistry ?? {};\n const newPlugins: Record<PluginHandle, VersionedData<unknown>> = {};\n\n for (const [key, pluginName] of Object.entries(newPluginRegistry)) {\n const handle = key as PluginHandle;\n const existingEntry = oldPlugins[handle];\n const existingName = oldRegistry[handle];\n\n try {\n if (existingEntry && existingName === pluginName) {\n // Plugin exists with same type - migrate its data\n const migrated = migratePluginData(handle, {\n version: existingEntry.__dataVersion,\n data: existingEntry.__data,\n });\n if (migrated) {\n newPlugins[handle] = { __dataVersion: migrated.version, __data: migrated.data };\n }\n // If undefined returned, plugin is intentionally removed\n } else {\n // New plugin or type changed - create with initial data\n const initial = createPluginData(handle);\n newPlugins[handle] = { __dataVersion: initial.version, __data: initial.data };\n }\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : String(error),\n failedAt: handle,\n };\n }\n }\n\n // Step 3: Build final storage atomically\n const migratedStorage: BlockStorage = {\n [BLOCK_STORAGE_KEY]: BLOCK_STORAGE_SCHEMA_VERSION,\n __dataVersion: newVersion,\n __data: migratedData,\n __pluginRegistry: newPluginRegistry,\n __plugins: newPlugins,\n };\n\n return {\n success: true,\n storage: migratedStorage,\n };\n}\n\n/**\n * Gets plugin-specific data from block storage.\n * Accepts raw storage (any format) and normalizes internally.\n *\n * When called with a typed PluginHandle<F>, the return type is automatically\n * inferred from the factory's phantom `__types.data` field.\n *\n * @param rawStorage - Raw block storage (may be legacy format or BlockStorage)\n * @param handle - The plugin handle (branded plugin instance id)\n * @returns The plugin data, typed via factory inference\n * @throws If plugin is not found in storage\n */\nexport function getPluginData<F extends PluginFactoryLike>(\n rawStorage: unknown,\n handle: PluginHandle<F>,\n): InferFactoryData<F> {\n const storage = normalizeBlockStorage(rawStorage);\n const pluginEntry = storage.__plugins?.[handle];\n if (!pluginEntry) throw new Error(`Plugin '${handle}' not found in block storage`);\n return pluginEntry.__data as InferFactoryData<F>;\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"block_storage.cjs","sources":["../src/block_storage.ts"],"sourcesContent":["/**\n * BlockStorage - Typed storage abstraction for block persistent data.\n *\n * This module provides:\n * - A typed structure for block storage with versioning and plugin support\n * - Utility functions for manipulating storage\n * - Handler interfaces for model-level customization\n *\n * @module block_storage\n */\n\nimport type { Branded } from \"@milaboratories/pl-model-common\";\nimport type { DataVersioned } from \"./block_migrations\";\nimport type { PluginHandle, PluginFactoryLike, InferFactoryData } from \"./plugin_handle\";\n\n// =============================================================================\n// Core Types\n// =============================================================================\n\n/**\n * Discriminator key for BlockStorage format detection.\n * This unique hash-based key identifies data as BlockStorage vs legacy formats.\n */\nexport const BLOCK_STORAGE_KEY = \"__pl_a7f3e2b9__\";\n\n/**\n * Current BlockStorage schema version.\n * Increment this when the storage structure itself changes (not block state migrations).\n */\nexport const BLOCK_STORAGE_SCHEMA_VERSION = \"v1\";\n\n/**\n * Default data version for new blocks without migrations.\n * Unique identifier ensures blocks are created via DataModel API.\n */\nexport const DATA_MODEL_LEGACY_VERSION = \"__pl_v1_d4e8f2a1__\";\n\n/**\n * Type for valid schema versions\n */\nexport type BlockStorageSchemaVersion = \"v1\"; // Add 'v2', 'v3', etc. as schema evolves\n\n/**\n * Branded type for plugin names - globally unique plugin type identifiers.\n * Using a branded type enforces explicit casting (`as PluginName`) which makes\n * it easy to find all plugin name definitions in the codebase and verify uniqueness.\n */\nexport type PluginName = Branded<string, \"PluginName\">;\n\n/**\n * Plugin registry - maps pluginId (unique within a block) to pluginName (globally unique plugin type).\n * Using a Record highlights that pluginIds must be unique within a block.\n */\nexport type PluginRegistry = Record<PluginHandle, PluginName>;\n\n/**\n * Versioned data - used for both block data and plugin data\n */\nexport interface VersionedData<TData = unknown> {\n /** Version of the data, used for migrations */\n __dataVersion: string;\n /** The persistent data */\n __data: TData;\n}\n\n/**\n * Core BlockStorage type that holds:\n * - __pl_a7f3e2b9__: Schema version (discriminator key identifies BlockStorage format)\n * - __dataVersion: Version key for block data migrations\n * - __data: The block's user-facing data (state)\n * - __pluginRegistry: Map from pluginId to pluginName (optional)\n * - __plugins: Plugin-specific data keyed by pluginId (optional)\n */\nexport type BlockStorage<TState = unknown> = {\n /** Schema version - the key itself is the discriminator */\n readonly [BLOCK_STORAGE_KEY]: BlockStorageSchemaVersion;\n /** Registry of plugins: pluginId -> pluginName */\n __pluginRegistry?: PluginRegistry;\n /** Plugin-specific data, keyed by plugin handle */\n __plugins?: Record<PluginHandle, VersionedData<unknown>>;\n} & VersionedData<TState>;\n\n/**\n * Type guard to check if a value is a valid BlockStorage object.\n * Checks for the discriminator key and valid schema version.\n */\nexport function isBlockStorage(value: unknown): value is BlockStorage {\n if (value === null || typeof value !== \"object\") return false;\n const obj = value as Record<string, unknown>;\n const schemaVersion = obj[BLOCK_STORAGE_KEY];\n // Currently only 'v1' is valid, but this allows future versions\n return schemaVersion === \"v1\"; // Add more versions as schema evolves\n}\n\n// =============================================================================\n// Factory Functions\n// =============================================================================\n\n/**\n * Creates a BlockStorage with the given initial data\n *\n * @param initialData - The initial data value (defaults to empty object)\n * @param version - The initial data version key (defaults to DATA_MODEL_LEGACY_VERSION)\n * @returns A new BlockStorage instance with discriminator key\n */\nexport function createBlockStorage<TState = unknown>(\n initialData: TState = {} as TState,\n version: string = DATA_MODEL_LEGACY_VERSION,\n): BlockStorage<TState> {\n return {\n [BLOCK_STORAGE_KEY]: BLOCK_STORAGE_SCHEMA_VERSION,\n __dataVersion: version,\n __data: initialData,\n __pluginRegistry: {},\n __plugins: {},\n };\n}\n\n/**\n * Normalizes raw storage data to BlockStorage format.\n * If the input is already a BlockStorage, returns it as-is (with defaults for missing fields).\n * If the input is legacy format (raw state), wraps it in BlockStorage structure.\n *\n * @param raw - Raw storage data (may be legacy format or BlockStorage)\n * @returns Normalized BlockStorage\n */\nexport function normalizeBlockStorage<TState = unknown>(raw: unknown): BlockStorage<TState> {\n if (isBlockStorage(raw)) {\n const storage = raw as BlockStorage<TState>;\n return {\n ...storage,\n // Fix for early released version where __dataVersion was a number\n __dataVersion:\n typeof storage.__dataVersion === \"number\"\n ? DATA_MODEL_LEGACY_VERSION\n : storage.__dataVersion,\n // Ensure plugin fields have defaults\n __pluginRegistry: storage.__pluginRegistry ?? {},\n __plugins: storage.__plugins ?? {},\n };\n }\n // Legacy format: raw is the state directly\n return createBlockStorage(raw as TState);\n}\n\n// =============================================================================\n// Data Access & Update Functions\n// =============================================================================\n\n/**\n * Gets the data from BlockStorage\n *\n * @param storage - The BlockStorage instance\n * @returns The data value\n */\nexport function getStorageData<TState>(storage: BlockStorage<TState>): TState {\n return storage.__data;\n}\n\n/**\n * Derives data from raw block storage.\n * This function is meant to be called from sdk/ui-vue to extract\n * user-facing data from the raw storage returned by the middle layer.\n *\n * The middle layer returns raw storage (opaque to it), and the UI\n * uses this function to derive the actual data value.\n *\n * @param rawStorage - Raw storage data from middle layer (may be any format)\n * @returns The extracted data value, or undefined if storage is undefined/null\n */\nexport function deriveDataFromStorage<TData = unknown>(rawStorage: unknown): TData {\n // Normalize to BlockStorage format (handles legacy formats too)\n const storage = normalizeBlockStorage<TData>(rawStorage);\n return getStorageData(storage);\n}\n\n/** Payload for storage mutation operations. SDK defines specific operations. */\nexport type MutateStoragePayload<T = unknown> =\n | { operation: \"update-block-data\"; value: T }\n | { operation: \"update-plugin-data\"; pluginId: PluginHandle; value: unknown };\n\n/**\n * Updates the data in BlockStorage (immutable)\n *\n * @param storage - The current BlockStorage\n * @param payload - The update payload with operation and value\n * @returns A new BlockStorage with updated data\n */\nexport function updateStorageData<TValue = unknown>(\n storage: BlockStorage<TValue>,\n payload: MutateStoragePayload<TValue>,\n): BlockStorage<TValue> {\n switch (payload.operation) {\n case \"update-block-data\":\n return { ...storage, __data: payload.value };\n case \"update-plugin-data\": {\n const { pluginId, value } = payload;\n const currentPlugins = storage.__plugins ?? {};\n const existingEntry = currentPlugins[pluginId];\n const version = existingEntry?.__dataVersion ?? DATA_MODEL_LEGACY_VERSION;\n return {\n ...storage,\n __plugins: {\n ...currentPlugins,\n [pluginId]: {\n __dataVersion: version,\n __data: value,\n },\n },\n };\n }\n default:\n throw new Error(`Unknown storage operation: ${(payload as { operation: string }).operation}`);\n }\n}\n\n/**\n * Storage debug view returned by __pl_storage_debugView callback.\n * Used by developer tools to display block storage info.\n */\nexport interface StorageDebugView {\n /** Current data version key */\n dataVersion: string;\n /** Raw data payload stored in BlockStorage */\n data: unknown;\n}\n\n// =============================================================================\n// Atomic Migration\n// =============================================================================\n\n/**\n * Result of a successful atomic migration.\n */\nexport interface MigrationSuccess<TState> {\n success: true;\n /** The fully migrated storage - commit this to persist */\n storage: BlockStorage<TState>;\n}\n\n/**\n * Result of a failed atomic migration.\n * The original storage is untouched - user must choose to abort or reset.\n */\nexport interface MigrationFailure {\n success: false;\n /** Description of what failed */\n error: string;\n /** Which step failed: 'block' or pluginId */\n failedAt: string;\n}\n\nexport type MigrationResult<TState> = MigrationSuccess<TState> | MigrationFailure;\n\n/**\n * Configuration for atomic block storage migration.\n * Callbacks use DataVersioned format (the DataModel API format).\n * Conversion to internal VersionedData format is handled by migrateBlockStorage().\n */\nexport interface MigrateBlockStorageConfig {\n /** Migrate block data from any version to latest. Throws on failure. */\n migrateBlockData: (versioned: DataVersioned<unknown>) => DataVersioned<unknown>;\n /** Migrate each plugin's data. Return undefined to remove the plugin. Throws on failure. */\n migratePluginData: (\n handle: PluginHandle,\n versioned: DataVersioned<unknown>,\n ) => DataVersioned<unknown> | undefined;\n /** The new plugin registry after migration (pluginId -> pluginName) */\n newPluginRegistry: PluginRegistry;\n /** Factory to create initial data for new plugins */\n createPluginData: (handle: PluginHandle) => DataVersioned<unknown>;\n}\n\n/**\n * Performs atomic migration of block storage including block data and all plugins.\n *\n * Migration is atomic: either everything succeeds and a new storage is returned,\n * or an error is returned and the original storage is completely untouched.\n *\n * Migration steps:\n * 1. Migrate block data\n * 2. For each plugin in newPluginRegistry:\n * - If plugin exists with same name: migrate its data\n * - Otherwise (new or type changed): create with initial data\n * Plugins not in newPluginRegistry are dropped.\n *\n * If any step throws, migration fails and original storage is preserved.\n * User can then choose to:\n * - Abort: keep original storage, don't update block\n * - Reset: call createBlockStorage() to start fresh\n *\n * @param storage - The original storage (will not be modified)\n * @param config - Migration configuration\n * @returns Migration result - either success with new storage, or failure with error info\n *\n * @example\n * const result = migrateBlockStorage(storage, {\n * migrateBlockData: (versioned) => blockDataModel.migrate(versioned),\n * migratePluginData: (pluginId, versioned) => getPluginModel(pluginId).migrate(versioned),\n * newPluginRegistry: { table1: 'dataTable' as PluginName },\n * createPluginData: (pluginId) => getPluginModel(pluginId).getDefaultData(),\n * });\n *\n * if (result.success) {\n * commitStorage(result.storage);\n * } else {\n * const userChoice = await askUser(`Migration failed: ${result.error}. Reset data?`);\n * if (userChoice === 'reset') {\n * commitStorage(createBlockStorage(initialData, currentVersion));\n * }\n * // else: abort, keep original\n * }\n */\nexport function migrateBlockStorage(\n storage: BlockStorage<unknown>,\n config: MigrateBlockStorageConfig,\n): MigrationResult<unknown> {\n const { migrateBlockData, migratePluginData, newPluginRegistry, createPluginData } = config;\n\n // Step 1: Migrate block data\n let migratedData: unknown;\n let newVersion: string;\n try {\n const result = migrateBlockData({ version: storage.__dataVersion, data: storage.__data });\n migratedData = result.data;\n newVersion = result.version;\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : String(error),\n failedAt: \"block\",\n };\n }\n\n // Step 2: Migrate plugins\n const oldPlugins = storage.__plugins ?? {};\n const oldRegistry = storage.__pluginRegistry ?? {};\n const newPlugins: Record<PluginHandle, VersionedData<unknown>> = {};\n\n for (const [key, pluginName] of Object.entries(newPluginRegistry)) {\n const handle = key as PluginHandle;\n const existingEntry = oldPlugins[handle];\n const existingName = oldRegistry[handle];\n\n try {\n if (existingEntry && existingName === pluginName) {\n // Plugin exists with same type - migrate its data\n const migrated = migratePluginData(handle, {\n version: existingEntry.__dataVersion,\n data: existingEntry.__data,\n });\n if (migrated) {\n newPlugins[handle] = { __dataVersion: migrated.version, __data: migrated.data };\n }\n // If undefined returned, plugin is intentionally removed\n } else {\n // New plugin or type changed - create with initial data\n const initial = createPluginData(handle);\n newPlugins[handle] = { __dataVersion: initial.version, __data: initial.data };\n }\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : String(error),\n failedAt: handle,\n };\n }\n }\n\n // Step 3: Build final storage atomically\n const migratedStorage: BlockStorage = {\n [BLOCK_STORAGE_KEY]: BLOCK_STORAGE_SCHEMA_VERSION,\n __dataVersion: newVersion,\n __data: migratedData,\n __pluginRegistry: newPluginRegistry,\n __plugins: newPlugins,\n };\n\n return {\n success: true,\n storage: migratedStorage,\n };\n}\n\n/**\n * Gets plugin-specific data from block storage.\n * Accepts raw storage (any format) and normalizes internally.\n *\n * When called with a typed PluginHandle<F>, the return type is automatically\n * inferred from the factory's phantom `__types.data` field.\n *\n * @param rawStorage - Raw block storage (may be legacy format or BlockStorage)\n * @param handle - The plugin handle (branded plugin instance id)\n * @returns The plugin data, typed via factory inference\n * @throws If plugin is not found in storage\n */\nexport function getPluginData<F extends PluginFactoryLike>(\n rawStorage: unknown,\n handle: PluginHandle<F>,\n): InferFactoryData<F> {\n const storage = normalizeBlockStorage(rawStorage);\n const pluginEntry = storage.__plugins?.[handle];\n if (!pluginEntry) throw new Error(`Plugin '${handle}' not found in block storage`);\n return pluginEntry.__data as InferFactoryData<F>;\n}\n"],"names":[],"mappings":"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"}
@@ -9,7 +9,7 @@
9
9
  * @module block_storage
10
10
  */
11
11
  import type { Branded } from "@milaboratories/pl-model-common";
12
- import type { DataMigrationResult, DataVersioned } from "./block_migrations";
12
+ import type { DataVersioned } from "./block_migrations";
13
13
  import type { PluginHandle, PluginFactoryLike, InferFactoryData } from "./plugin_handle";
14
14
  /**
15
15
  * Discriminator key for BlockStorage format detection.
@@ -25,7 +25,7 @@ export declare const BLOCK_STORAGE_SCHEMA_VERSION = "v1";
25
25
  * Default data version for new blocks without migrations.
26
26
  * Unique identifier ensures blocks are created via DataModel API.
27
27
  */
28
- export declare const DATA_MODEL_DEFAULT_VERSION = "__pl_v1_d4e8f2a1__";
28
+ export declare const DATA_MODEL_LEGACY_VERSION = "__pl_v1_d4e8f2a1__";
29
29
  /**
30
30
  * Type for valid schema versions
31
31
  */
@@ -75,7 +75,7 @@ export declare function isBlockStorage(value: unknown): value is BlockStorage;
75
75
  * Creates a BlockStorage with the given initial data
76
76
  *
77
77
  * @param initialData - The initial data value (defaults to empty object)
78
- * @param version - The initial data version key (defaults to DATA_MODEL_DEFAULT_VERSION)
78
+ * @param version - The initial data version key (defaults to DATA_MODEL_LEGACY_VERSION)
79
79
  * @returns A new BlockStorage instance with discriminator key
80
80
  */
81
81
  export declare function createBlockStorage<TState = unknown>(initialData?: TState, version?: string): BlockStorage<TState>;
@@ -160,10 +160,10 @@ export type MigrationResult<TState> = MigrationSuccess<TState> | MigrationFailur
160
160
  * Conversion to internal VersionedData format is handled by migrateBlockStorage().
161
161
  */
162
162
  export interface MigrateBlockStorageConfig {
163
- /** Migrate block data from any version to latest */
164
- migrateBlockData: (versioned: DataVersioned<unknown>) => DataMigrationResult<unknown>;
165
- /** Migrate each plugin's data. Return undefined to remove the plugin. */
166
- migratePluginData: (handle: PluginHandle, versioned: DataVersioned<unknown>) => DataMigrationResult<unknown> | undefined;
163
+ /** Migrate block data from any version to latest. Throws on failure. */
164
+ migrateBlockData: (versioned: DataVersioned<unknown>) => DataVersioned<unknown>;
165
+ /** Migrate each plugin's data. Return undefined to remove the plugin. Throws on failure. */
166
+ migratePluginData: (handle: PluginHandle, versioned: DataVersioned<unknown>) => DataVersioned<unknown> | undefined;
167
167
  /** The new plugin registry after migration (pluginId -> pluginName) */
168
168
  newPluginRegistry: PluginRegistry;
169
169
  /** Factory to create initial data for new plugins */
@@ -1 +1 @@
1
- {"version":3,"file":"block_storage.d.ts","sourceRoot":"","sources":["../src/block_storage.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"block_storage.d.ts","sourceRoot":"","sources":["../src/block_storage.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,iCAAiC,CAAC;AAC/D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACxD,OAAO,KAAK,EAAE,YAAY,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAMzF;;;GAGG;AACH,eAAO,MAAM,iBAAiB,oBAAoB,CAAC;AAEnD;;;GAGG;AACH,eAAO,MAAM,4BAA4B,OAAO,CAAC;AAEjD;;;GAGG;AACH,eAAO,MAAM,yBAAyB,uBAAuB,CAAC;AAE9D;;GAEG;AACH,MAAM,MAAM,yBAAyB,GAAG,IAAI,CAAC;AAE7C;;;;GAIG;AACH,MAAM,MAAM,UAAU,GAAG,OAAO,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;AAEvD;;;GAGG;AACH,MAAM,MAAM,cAAc,GAAG,MAAM,CAAC,YAAY,EAAE,UAAU,CAAC,CAAC;AAE9D;;GAEG;AACH,MAAM,WAAW,aAAa,CAAC,KAAK,GAAG,OAAO;IAC5C,+CAA+C;IAC/C,aAAa,EAAE,MAAM,CAAC;IACtB,0BAA0B;IAC1B,MAAM,EAAE,KAAK,CAAC;CACf;AAED;;;;;;;GAOG;AACH,MAAM,MAAM,YAAY,CAAC,MAAM,GAAG,OAAO,IAAI;IAC3C,2DAA2D;IAC3D,QAAQ,CAAC,CAAC,iBAAiB,CAAC,EAAE,yBAAyB,CAAC;IACxD,kDAAkD;IAClD,gBAAgB,CAAC,EAAE,cAAc,CAAC;IAClC,mDAAmD;IACnD,SAAS,CAAC,EAAE,MAAM,CAAC,YAAY,EAAE,aAAa,CAAC,OAAO,CAAC,CAAC,CAAC;CAC1D,GAAG,aAAa,CAAC,MAAM,CAAC,CAAC;AAE1B;;;GAGG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,YAAY,CAMpE;AAMD;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,GAAG,OAAO,EACjD,WAAW,GAAE,MAAqB,EAClC,OAAO,GAAE,MAAkC,GAC1C,YAAY,CAAC,MAAM,CAAC,CAQtB;AAED;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,GAAG,OAAO,EAAE,GAAG,EAAE,OAAO,GAAG,YAAY,CAAC,MAAM,CAAC,CAiB1F;AAMD;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,OAAO,EAAE,YAAY,CAAC,MAAM,CAAC,GAAG,MAAM,CAE5E;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,GAAG,OAAO,EAAE,UAAU,EAAE,OAAO,GAAG,KAAK,CAIjF;AAED,gFAAgF;AAChF,MAAM,MAAM,oBAAoB,CAAC,CAAC,GAAG,OAAO,IACxC;IAAE,SAAS,EAAE,mBAAmB,CAAC;IAAC,KAAK,EAAE,CAAC,CAAA;CAAE,GAC5C;IAAE,SAAS,EAAE,oBAAoB,CAAC;IAAC,QAAQ,EAAE,YAAY,CAAC;IAAC,KAAK,EAAE,OAAO,CAAA;CAAE,CAAC;AAEhF;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,GAAG,OAAO,EAChD,OAAO,EAAE,YAAY,CAAC,MAAM,CAAC,EAC7B,OAAO,EAAE,oBAAoB,CAAC,MAAM,CAAC,GACpC,YAAY,CAAC,MAAM,CAAC,CAuBtB;AAED;;;GAGG;AACH,MAAM,WAAW,gBAAgB;IAC/B,+BAA+B;IAC/B,WAAW,EAAE,MAAM,CAAC;IACpB,8CAA8C;IAC9C,IAAI,EAAE,OAAO,CAAC;CACf;AAMD;;GAEG;AACH,MAAM,WAAW,gBAAgB,CAAC,MAAM;IACtC,OAAO,EAAE,IAAI,CAAC;IACd,0DAA0D;IAC1D,OAAO,EAAE,YAAY,CAAC,MAAM,CAAC,CAAC;CAC/B;AAED;;;GAGG;AACH,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,KAAK,CAAC;IACf,iCAAiC;IACjC,KAAK,EAAE,MAAM,CAAC;IACd,6CAA6C;IAC7C,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,MAAM,eAAe,CAAC,MAAM,IAAI,gBAAgB,CAAC,MAAM,CAAC,GAAG,gBAAgB,CAAC;AAElF;;;;GAIG;AACH,MAAM,WAAW,yBAAyB;IACxC,wEAAwE;IACxE,gBAAgB,EAAE,CAAC,SAAS,EAAE,aAAa,CAAC,OAAO,CAAC,KAAK,aAAa,CAAC,OAAO,CAAC,CAAC;IAChF,4FAA4F;IAC5F,iBAAiB,EAAE,CACjB,MAAM,EAAE,YAAY,EACpB,SAAS,EAAE,aAAa,CAAC,OAAO,CAAC,KAC9B,aAAa,CAAC,OAAO,CAAC,GAAG,SAAS,CAAC;IACxC,uEAAuE;IACvE,iBAAiB,EAAE,cAAc,CAAC;IAClC,qDAAqD;IACrD,gBAAgB,EAAE,CAAC,MAAM,EAAE,YAAY,KAAK,aAAa,CAAC,OAAO,CAAC,CAAC;CACpE;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuCG;AACH,wBAAgB,mBAAmB,CACjC,OAAO,EAAE,YAAY,CAAC,OAAO,CAAC,EAC9B,MAAM,EAAE,yBAAyB,GAChC,eAAe,CAAC,OAAO,CAAC,CAkE1B;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,aAAa,CAAC,CAAC,SAAS,iBAAiB,EACvD,UAAU,EAAE,OAAO,EACnB,MAAM,EAAE,YAAY,CAAC,CAAC,CAAC,GACtB,gBAAgB,CAAC,CAAC,CAAC,CAKrB"}
@@ -25,7 +25,7 @@ const BLOCK_STORAGE_SCHEMA_VERSION = "v1";
25
25
  * Default data version for new blocks without migrations.
26
26
  * Unique identifier ensures blocks are created via DataModel API.
27
27
  */
28
- const DATA_MODEL_DEFAULT_VERSION = "__pl_v1_d4e8f2a1__";
28
+ const DATA_MODEL_LEGACY_VERSION = "__pl_v1_d4e8f2a1__";
29
29
  /**
30
30
  * Type guard to check if a value is a valid BlockStorage object.
31
31
  * Checks for the discriminator key and valid schema version.
@@ -45,10 +45,10 @@ function isBlockStorage(value) {
45
45
  * Creates a BlockStorage with the given initial data
46
46
  *
47
47
  * @param initialData - The initial data value (defaults to empty object)
48
- * @param version - The initial data version key (defaults to DATA_MODEL_DEFAULT_VERSION)
48
+ * @param version - The initial data version key (defaults to DATA_MODEL_LEGACY_VERSION)
49
49
  * @returns A new BlockStorage instance with discriminator key
50
50
  */
51
- function createBlockStorage(initialData = {}, version = DATA_MODEL_DEFAULT_VERSION) {
51
+ function createBlockStorage(initialData = {}, version = DATA_MODEL_LEGACY_VERSION) {
52
52
  return {
53
53
  [BLOCK_STORAGE_KEY]: BLOCK_STORAGE_SCHEMA_VERSION,
54
54
  __dataVersion: version,
@@ -72,7 +72,7 @@ function normalizeBlockStorage(raw) {
72
72
  ...storage,
73
73
  // Fix for early released version where __dataVersion was a number
74
74
  __dataVersion: typeof storage.__dataVersion === "number"
75
- ? DATA_MODEL_DEFAULT_VERSION
75
+ ? DATA_MODEL_LEGACY_VERSION
76
76
  : storage.__dataVersion,
77
77
  // Ensure plugin fields have defaults
78
78
  __pluginRegistry: storage.__pluginRegistry ?? {},
@@ -125,7 +125,7 @@ function updateStorageData(storage, payload) {
125
125
  const { pluginId, value } = payload;
126
126
  const currentPlugins = storage.__plugins ?? {};
127
127
  const existingEntry = currentPlugins[pluginId];
128
- const version = existingEntry?.__dataVersion ?? DATA_MODEL_DEFAULT_VERSION;
128
+ const version = existingEntry?.__dataVersion ?? DATA_MODEL_LEGACY_VERSION;
129
129
  return {
130
130
  ...storage,
131
131
  __plugins: {
@@ -265,5 +265,5 @@ function getPluginData(rawStorage, handle) {
265
265
  return pluginEntry.__data;
266
266
  }
267
267
 
268
- export { BLOCK_STORAGE_KEY, BLOCK_STORAGE_SCHEMA_VERSION, DATA_MODEL_DEFAULT_VERSION, createBlockStorage, deriveDataFromStorage, getPluginData, getStorageData, isBlockStorage, migrateBlockStorage, normalizeBlockStorage, updateStorageData };
268
+ export { BLOCK_STORAGE_KEY, BLOCK_STORAGE_SCHEMA_VERSION, DATA_MODEL_LEGACY_VERSION, createBlockStorage, deriveDataFromStorage, getPluginData, getStorageData, isBlockStorage, migrateBlockStorage, normalizeBlockStorage, updateStorageData };
269
269
  //# sourceMappingURL=block_storage.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"block_storage.js","sources":["../src/block_storage.ts"],"sourcesContent":["/**\n * BlockStorage - Typed storage abstraction for block persistent data.\n *\n * This module provides:\n * - A typed structure for block storage with versioning and plugin support\n * - Utility functions for manipulating storage\n * - Handler interfaces for model-level customization\n *\n * @module block_storage\n */\n\nimport type { Branded } from \"@milaboratories/pl-model-common\";\nimport type { DataMigrationResult, DataVersioned } from \"./block_migrations\";\nimport type { PluginHandle, PluginFactoryLike, InferFactoryData } from \"./plugin_handle\";\n\n// =============================================================================\n// Core Types\n// =============================================================================\n\n/**\n * Discriminator key for BlockStorage format detection.\n * This unique hash-based key identifies data as BlockStorage vs legacy formats.\n */\nexport const BLOCK_STORAGE_KEY = \"__pl_a7f3e2b9__\";\n\n/**\n * Current BlockStorage schema version.\n * Increment this when the storage structure itself changes (not block state migrations).\n */\nexport const BLOCK_STORAGE_SCHEMA_VERSION = \"v1\";\n\n/**\n * Default data version for new blocks without migrations.\n * Unique identifier ensures blocks are created via DataModel API.\n */\nexport const DATA_MODEL_DEFAULT_VERSION = \"__pl_v1_d4e8f2a1__\";\n\n/**\n * Type for valid schema versions\n */\nexport type BlockStorageSchemaVersion = \"v1\"; // Add 'v2', 'v3', etc. as schema evolves\n\n/**\n * Branded type for plugin names - globally unique plugin type identifiers.\n * Using a branded type enforces explicit casting (`as PluginName`) which makes\n * it easy to find all plugin name definitions in the codebase and verify uniqueness.\n */\nexport type PluginName = Branded<string, \"PluginName\">;\n\n/**\n * Plugin registry - maps pluginId (unique within a block) to pluginName (globally unique plugin type).\n * Using a Record highlights that pluginIds must be unique within a block.\n */\nexport type PluginRegistry = Record<PluginHandle, PluginName>;\n\n/**\n * Versioned data - used for both block data and plugin data\n */\nexport interface VersionedData<TData = unknown> {\n /** Version of the data, used for migrations */\n __dataVersion: string;\n /** The persistent data */\n __data: TData;\n}\n\n/**\n * Core BlockStorage type that holds:\n * - __pl_a7f3e2b9__: Schema version (discriminator key identifies BlockStorage format)\n * - __dataVersion: Version key for block data migrations\n * - __data: The block's user-facing data (state)\n * - __pluginRegistry: Map from pluginId to pluginName (optional)\n * - __plugins: Plugin-specific data keyed by pluginId (optional)\n */\nexport type BlockStorage<TState = unknown> = {\n /** Schema version - the key itself is the discriminator */\n readonly [BLOCK_STORAGE_KEY]: BlockStorageSchemaVersion;\n /** Registry of plugins: pluginId -> pluginName */\n __pluginRegistry?: PluginRegistry;\n /** Plugin-specific data, keyed by plugin handle */\n __plugins?: Record<PluginHandle, VersionedData<unknown>>;\n} & VersionedData<TState>;\n\n/**\n * Type guard to check if a value is a valid BlockStorage object.\n * Checks for the discriminator key and valid schema version.\n */\nexport function isBlockStorage(value: unknown): value is BlockStorage {\n if (value === null || typeof value !== \"object\") return false;\n const obj = value as Record<string, unknown>;\n const schemaVersion = obj[BLOCK_STORAGE_KEY];\n // Currently only 'v1' is valid, but this allows future versions\n return schemaVersion === \"v1\"; // Add more versions as schema evolves\n}\n\n// =============================================================================\n// Factory Functions\n// =============================================================================\n\n/**\n * Creates a BlockStorage with the given initial data\n *\n * @param initialData - The initial data value (defaults to empty object)\n * @param version - The initial data version key (defaults to DATA_MODEL_DEFAULT_VERSION)\n * @returns A new BlockStorage instance with discriminator key\n */\nexport function createBlockStorage<TState = unknown>(\n initialData: TState = {} as TState,\n version: string = DATA_MODEL_DEFAULT_VERSION,\n): BlockStorage<TState> {\n return {\n [BLOCK_STORAGE_KEY]: BLOCK_STORAGE_SCHEMA_VERSION,\n __dataVersion: version,\n __data: initialData,\n __pluginRegistry: {},\n __plugins: {},\n };\n}\n\n/**\n * Normalizes raw storage data to BlockStorage format.\n * If the input is already a BlockStorage, returns it as-is (with defaults for missing fields).\n * If the input is legacy format (raw state), wraps it in BlockStorage structure.\n *\n * @param raw - Raw storage data (may be legacy format or BlockStorage)\n * @returns Normalized BlockStorage\n */\nexport function normalizeBlockStorage<TState = unknown>(raw: unknown): BlockStorage<TState> {\n if (isBlockStorage(raw)) {\n const storage = raw as BlockStorage<TState>;\n return {\n ...storage,\n // Fix for early released version where __dataVersion was a number\n __dataVersion:\n typeof storage.__dataVersion === \"number\"\n ? DATA_MODEL_DEFAULT_VERSION\n : storage.__dataVersion,\n // Ensure plugin fields have defaults\n __pluginRegistry: storage.__pluginRegistry ?? {},\n __plugins: storage.__plugins ?? {},\n };\n }\n // Legacy format: raw is the state directly\n return createBlockStorage(raw as TState);\n}\n\n// =============================================================================\n// Data Access & Update Functions\n// =============================================================================\n\n/**\n * Gets the data from BlockStorage\n *\n * @param storage - The BlockStorage instance\n * @returns The data value\n */\nexport function getStorageData<TState>(storage: BlockStorage<TState>): TState {\n return storage.__data;\n}\n\n/**\n * Derives data from raw block storage.\n * This function is meant to be called from sdk/ui-vue to extract\n * user-facing data from the raw storage returned by the middle layer.\n *\n * The middle layer returns raw storage (opaque to it), and the UI\n * uses this function to derive the actual data value.\n *\n * @param rawStorage - Raw storage data from middle layer (may be any format)\n * @returns The extracted data value, or undefined if storage is undefined/null\n */\nexport function deriveDataFromStorage<TData = unknown>(rawStorage: unknown): TData {\n // Normalize to BlockStorage format (handles legacy formats too)\n const storage = normalizeBlockStorage<TData>(rawStorage);\n return getStorageData(storage);\n}\n\n/** Payload for storage mutation operations. SDK defines specific operations. */\nexport type MutateStoragePayload<T = unknown> =\n | { operation: \"update-block-data\"; value: T }\n | { operation: \"update-plugin-data\"; pluginId: PluginHandle; value: unknown };\n\n/**\n * Updates the data in BlockStorage (immutable)\n *\n * @param storage - The current BlockStorage\n * @param payload - The update payload with operation and value\n * @returns A new BlockStorage with updated data\n */\nexport function updateStorageData<TValue = unknown>(\n storage: BlockStorage<TValue>,\n payload: MutateStoragePayload<TValue>,\n): BlockStorage<TValue> {\n switch (payload.operation) {\n case \"update-block-data\":\n return { ...storage, __data: payload.value };\n case \"update-plugin-data\": {\n const { pluginId, value } = payload;\n const currentPlugins = storage.__plugins ?? {};\n const existingEntry = currentPlugins[pluginId];\n const version = existingEntry?.__dataVersion ?? DATA_MODEL_DEFAULT_VERSION;\n return {\n ...storage,\n __plugins: {\n ...currentPlugins,\n [pluginId]: {\n __dataVersion: version,\n __data: value,\n },\n },\n };\n }\n default:\n throw new Error(`Unknown storage operation: ${(payload as { operation: string }).operation}`);\n }\n}\n\n/**\n * Storage debug view returned by __pl_storage_debugView callback.\n * Used by developer tools to display block storage info.\n */\nexport interface StorageDebugView {\n /** Current data version key */\n dataVersion: string;\n /** Raw data payload stored in BlockStorage */\n data: unknown;\n}\n\n// =============================================================================\n// Atomic Migration\n// =============================================================================\n\n/**\n * Result of a successful atomic migration.\n */\nexport interface MigrationSuccess<TState> {\n success: true;\n /** The fully migrated storage - commit this to persist */\n storage: BlockStorage<TState>;\n}\n\n/**\n * Result of a failed atomic migration.\n * The original storage is untouched - user must choose to abort or reset.\n */\nexport interface MigrationFailure {\n success: false;\n /** Description of what failed */\n error: string;\n /** Which step failed: 'block' or pluginId */\n failedAt: string;\n}\n\nexport type MigrationResult<TState> = MigrationSuccess<TState> | MigrationFailure;\n\n/**\n * Configuration for atomic block storage migration.\n * Callbacks use DataVersioned format (the DataModel API format).\n * Conversion to internal VersionedData format is handled by migrateBlockStorage().\n */\nexport interface MigrateBlockStorageConfig {\n /** Migrate block data from any version to latest */\n migrateBlockData: (versioned: DataVersioned<unknown>) => DataMigrationResult<unknown>;\n /** Migrate each plugin's data. Return undefined to remove the plugin. */\n migratePluginData: (\n handle: PluginHandle,\n versioned: DataVersioned<unknown>,\n ) => DataMigrationResult<unknown> | undefined;\n /** The new plugin registry after migration (pluginId -> pluginName) */\n newPluginRegistry: PluginRegistry;\n /** Factory to create initial data for new plugins */\n createPluginData: (handle: PluginHandle) => DataVersioned<unknown>;\n}\n\n/**\n * Performs atomic migration of block storage including block data and all plugins.\n *\n * Migration is atomic: either everything succeeds and a new storage is returned,\n * or an error is returned and the original storage is completely untouched.\n *\n * Migration steps:\n * 1. Migrate block data\n * 2. For each plugin in newPluginRegistry:\n * - If plugin exists with same name: migrate its data\n * - Otherwise (new or type changed): create with initial data\n * Plugins not in newPluginRegistry are dropped.\n *\n * If any step throws, migration fails and original storage is preserved.\n * User can then choose to:\n * - Abort: keep original storage, don't update block\n * - Reset: call createBlockStorage() to start fresh\n *\n * @param storage - The original storage (will not be modified)\n * @param config - Migration configuration\n * @returns Migration result - either success with new storage, or failure with error info\n *\n * @example\n * const result = migrateBlockStorage(storage, {\n * migrateBlockData: (versioned) => blockDataModel.migrate(versioned),\n * migratePluginData: (pluginId, versioned) => getPluginModel(pluginId).migrate(versioned),\n * newPluginRegistry: { table1: 'dataTable' as PluginName },\n * createPluginData: (pluginId) => getPluginModel(pluginId).getDefaultData(),\n * });\n *\n * if (result.success) {\n * commitStorage(result.storage);\n * } else {\n * const userChoice = await askUser(`Migration failed: ${result.error}. Reset data?`);\n * if (userChoice === 'reset') {\n * commitStorage(createBlockStorage(initialData, currentVersion));\n * }\n * // else: abort, keep original\n * }\n */\nexport function migrateBlockStorage(\n storage: BlockStorage<unknown>,\n config: MigrateBlockStorageConfig,\n): MigrationResult<unknown> {\n const { migrateBlockData, migratePluginData, newPluginRegistry, createPluginData } = config;\n\n // Step 1: Migrate block data\n let migratedData: unknown;\n let newVersion: string;\n try {\n const result = migrateBlockData({ version: storage.__dataVersion, data: storage.__data });\n migratedData = result.data;\n newVersion = result.version;\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : String(error),\n failedAt: \"block\",\n };\n }\n\n // Step 2: Migrate plugins\n const oldPlugins = storage.__plugins ?? {};\n const oldRegistry = storage.__pluginRegistry ?? {};\n const newPlugins: Record<PluginHandle, VersionedData<unknown>> = {};\n\n for (const [key, pluginName] of Object.entries(newPluginRegistry)) {\n const handle = key as PluginHandle;\n const existingEntry = oldPlugins[handle];\n const existingName = oldRegistry[handle];\n\n try {\n if (existingEntry && existingName === pluginName) {\n // Plugin exists with same type - migrate its data\n const migrated = migratePluginData(handle, {\n version: existingEntry.__dataVersion,\n data: existingEntry.__data,\n });\n if (migrated) {\n newPlugins[handle] = { __dataVersion: migrated.version, __data: migrated.data };\n }\n // If undefined returned, plugin is intentionally removed\n } else {\n // New plugin or type changed - create with initial data\n const initial = createPluginData(handle);\n newPlugins[handle] = { __dataVersion: initial.version, __data: initial.data };\n }\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : String(error),\n failedAt: handle,\n };\n }\n }\n\n // Step 3: Build final storage atomically\n const migratedStorage: BlockStorage = {\n [BLOCK_STORAGE_KEY]: BLOCK_STORAGE_SCHEMA_VERSION,\n __dataVersion: newVersion,\n __data: migratedData,\n __pluginRegistry: newPluginRegistry,\n __plugins: newPlugins,\n };\n\n return {\n success: true,\n storage: migratedStorage,\n };\n}\n\n/**\n * Gets plugin-specific data from block storage.\n * Accepts raw storage (any format) and normalizes internally.\n *\n * When called with a typed PluginHandle<F>, the return type is automatically\n * inferred from the factory's phantom `__types.data` field.\n *\n * @param rawStorage - Raw block storage (may be legacy format or BlockStorage)\n * @param handle - The plugin handle (branded plugin instance id)\n * @returns The plugin data, typed via factory inference\n * @throws If plugin is not found in storage\n */\nexport function getPluginData<F extends PluginFactoryLike>(\n rawStorage: unknown,\n handle: PluginHandle<F>,\n): InferFactoryData<F> {\n const storage = normalizeBlockStorage(rawStorage);\n const pluginEntry = storage.__plugins?.[handle];\n if (!pluginEntry) throw new Error(`Plugin '${handle}' not found in block storage`);\n return pluginEntry.__data as InferFactoryData<F>;\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"block_storage.js","sources":["../src/block_storage.ts"],"sourcesContent":["/**\n * BlockStorage - Typed storage abstraction for block persistent data.\n *\n * This module provides:\n * - A typed structure for block storage with versioning and plugin support\n * - Utility functions for manipulating storage\n * - Handler interfaces for model-level customization\n *\n * @module block_storage\n */\n\nimport type { Branded } from \"@milaboratories/pl-model-common\";\nimport type { DataVersioned } from \"./block_migrations\";\nimport type { PluginHandle, PluginFactoryLike, InferFactoryData } from \"./plugin_handle\";\n\n// =============================================================================\n// Core Types\n// =============================================================================\n\n/**\n * Discriminator key for BlockStorage format detection.\n * This unique hash-based key identifies data as BlockStorage vs legacy formats.\n */\nexport const BLOCK_STORAGE_KEY = \"__pl_a7f3e2b9__\";\n\n/**\n * Current BlockStorage schema version.\n * Increment this when the storage structure itself changes (not block state migrations).\n */\nexport const BLOCK_STORAGE_SCHEMA_VERSION = \"v1\";\n\n/**\n * Default data version for new blocks without migrations.\n * Unique identifier ensures blocks are created via DataModel API.\n */\nexport const DATA_MODEL_LEGACY_VERSION = \"__pl_v1_d4e8f2a1__\";\n\n/**\n * Type for valid schema versions\n */\nexport type BlockStorageSchemaVersion = \"v1\"; // Add 'v2', 'v3', etc. as schema evolves\n\n/**\n * Branded type for plugin names - globally unique plugin type identifiers.\n * Using a branded type enforces explicit casting (`as PluginName`) which makes\n * it easy to find all plugin name definitions in the codebase and verify uniqueness.\n */\nexport type PluginName = Branded<string, \"PluginName\">;\n\n/**\n * Plugin registry - maps pluginId (unique within a block) to pluginName (globally unique plugin type).\n * Using a Record highlights that pluginIds must be unique within a block.\n */\nexport type PluginRegistry = Record<PluginHandle, PluginName>;\n\n/**\n * Versioned data - used for both block data and plugin data\n */\nexport interface VersionedData<TData = unknown> {\n /** Version of the data, used for migrations */\n __dataVersion: string;\n /** The persistent data */\n __data: TData;\n}\n\n/**\n * Core BlockStorage type that holds:\n * - __pl_a7f3e2b9__: Schema version (discriminator key identifies BlockStorage format)\n * - __dataVersion: Version key for block data migrations\n * - __data: The block's user-facing data (state)\n * - __pluginRegistry: Map from pluginId to pluginName (optional)\n * - __plugins: Plugin-specific data keyed by pluginId (optional)\n */\nexport type BlockStorage<TState = unknown> = {\n /** Schema version - the key itself is the discriminator */\n readonly [BLOCK_STORAGE_KEY]: BlockStorageSchemaVersion;\n /** Registry of plugins: pluginId -> pluginName */\n __pluginRegistry?: PluginRegistry;\n /** Plugin-specific data, keyed by plugin handle */\n __plugins?: Record<PluginHandle, VersionedData<unknown>>;\n} & VersionedData<TState>;\n\n/**\n * Type guard to check if a value is a valid BlockStorage object.\n * Checks for the discriminator key and valid schema version.\n */\nexport function isBlockStorage(value: unknown): value is BlockStorage {\n if (value === null || typeof value !== \"object\") return false;\n const obj = value as Record<string, unknown>;\n const schemaVersion = obj[BLOCK_STORAGE_KEY];\n // Currently only 'v1' is valid, but this allows future versions\n return schemaVersion === \"v1\"; // Add more versions as schema evolves\n}\n\n// =============================================================================\n// Factory Functions\n// =============================================================================\n\n/**\n * Creates a BlockStorage with the given initial data\n *\n * @param initialData - The initial data value (defaults to empty object)\n * @param version - The initial data version key (defaults to DATA_MODEL_LEGACY_VERSION)\n * @returns A new BlockStorage instance with discriminator key\n */\nexport function createBlockStorage<TState = unknown>(\n initialData: TState = {} as TState,\n version: string = DATA_MODEL_LEGACY_VERSION,\n): BlockStorage<TState> {\n return {\n [BLOCK_STORAGE_KEY]: BLOCK_STORAGE_SCHEMA_VERSION,\n __dataVersion: version,\n __data: initialData,\n __pluginRegistry: {},\n __plugins: {},\n };\n}\n\n/**\n * Normalizes raw storage data to BlockStorage format.\n * If the input is already a BlockStorage, returns it as-is (with defaults for missing fields).\n * If the input is legacy format (raw state), wraps it in BlockStorage structure.\n *\n * @param raw - Raw storage data (may be legacy format or BlockStorage)\n * @returns Normalized BlockStorage\n */\nexport function normalizeBlockStorage<TState = unknown>(raw: unknown): BlockStorage<TState> {\n if (isBlockStorage(raw)) {\n const storage = raw as BlockStorage<TState>;\n return {\n ...storage,\n // Fix for early released version where __dataVersion was a number\n __dataVersion:\n typeof storage.__dataVersion === \"number\"\n ? DATA_MODEL_LEGACY_VERSION\n : storage.__dataVersion,\n // Ensure plugin fields have defaults\n __pluginRegistry: storage.__pluginRegistry ?? {},\n __plugins: storage.__plugins ?? {},\n };\n }\n // Legacy format: raw is the state directly\n return createBlockStorage(raw as TState);\n}\n\n// =============================================================================\n// Data Access & Update Functions\n// =============================================================================\n\n/**\n * Gets the data from BlockStorage\n *\n * @param storage - The BlockStorage instance\n * @returns The data value\n */\nexport function getStorageData<TState>(storage: BlockStorage<TState>): TState {\n return storage.__data;\n}\n\n/**\n * Derives data from raw block storage.\n * This function is meant to be called from sdk/ui-vue to extract\n * user-facing data from the raw storage returned by the middle layer.\n *\n * The middle layer returns raw storage (opaque to it), and the UI\n * uses this function to derive the actual data value.\n *\n * @param rawStorage - Raw storage data from middle layer (may be any format)\n * @returns The extracted data value, or undefined if storage is undefined/null\n */\nexport function deriveDataFromStorage<TData = unknown>(rawStorage: unknown): TData {\n // Normalize to BlockStorage format (handles legacy formats too)\n const storage = normalizeBlockStorage<TData>(rawStorage);\n return getStorageData(storage);\n}\n\n/** Payload for storage mutation operations. SDK defines specific operations. */\nexport type MutateStoragePayload<T = unknown> =\n | { operation: \"update-block-data\"; value: T }\n | { operation: \"update-plugin-data\"; pluginId: PluginHandle; value: unknown };\n\n/**\n * Updates the data in BlockStorage (immutable)\n *\n * @param storage - The current BlockStorage\n * @param payload - The update payload with operation and value\n * @returns A new BlockStorage with updated data\n */\nexport function updateStorageData<TValue = unknown>(\n storage: BlockStorage<TValue>,\n payload: MutateStoragePayload<TValue>,\n): BlockStorage<TValue> {\n switch (payload.operation) {\n case \"update-block-data\":\n return { ...storage, __data: payload.value };\n case \"update-plugin-data\": {\n const { pluginId, value } = payload;\n const currentPlugins = storage.__plugins ?? {};\n const existingEntry = currentPlugins[pluginId];\n const version = existingEntry?.__dataVersion ?? DATA_MODEL_LEGACY_VERSION;\n return {\n ...storage,\n __plugins: {\n ...currentPlugins,\n [pluginId]: {\n __dataVersion: version,\n __data: value,\n },\n },\n };\n }\n default:\n throw new Error(`Unknown storage operation: ${(payload as { operation: string }).operation}`);\n }\n}\n\n/**\n * Storage debug view returned by __pl_storage_debugView callback.\n * Used by developer tools to display block storage info.\n */\nexport interface StorageDebugView {\n /** Current data version key */\n dataVersion: string;\n /** Raw data payload stored in BlockStorage */\n data: unknown;\n}\n\n// =============================================================================\n// Atomic Migration\n// =============================================================================\n\n/**\n * Result of a successful atomic migration.\n */\nexport interface MigrationSuccess<TState> {\n success: true;\n /** The fully migrated storage - commit this to persist */\n storage: BlockStorage<TState>;\n}\n\n/**\n * Result of a failed atomic migration.\n * The original storage is untouched - user must choose to abort or reset.\n */\nexport interface MigrationFailure {\n success: false;\n /** Description of what failed */\n error: string;\n /** Which step failed: 'block' or pluginId */\n failedAt: string;\n}\n\nexport type MigrationResult<TState> = MigrationSuccess<TState> | MigrationFailure;\n\n/**\n * Configuration for atomic block storage migration.\n * Callbacks use DataVersioned format (the DataModel API format).\n * Conversion to internal VersionedData format is handled by migrateBlockStorage().\n */\nexport interface MigrateBlockStorageConfig {\n /** Migrate block data from any version to latest. Throws on failure. */\n migrateBlockData: (versioned: DataVersioned<unknown>) => DataVersioned<unknown>;\n /** Migrate each plugin's data. Return undefined to remove the plugin. Throws on failure. */\n migratePluginData: (\n handle: PluginHandle,\n versioned: DataVersioned<unknown>,\n ) => DataVersioned<unknown> | undefined;\n /** The new plugin registry after migration (pluginId -> pluginName) */\n newPluginRegistry: PluginRegistry;\n /** Factory to create initial data for new plugins */\n createPluginData: (handle: PluginHandle) => DataVersioned<unknown>;\n}\n\n/**\n * Performs atomic migration of block storage including block data and all plugins.\n *\n * Migration is atomic: either everything succeeds and a new storage is returned,\n * or an error is returned and the original storage is completely untouched.\n *\n * Migration steps:\n * 1. Migrate block data\n * 2. For each plugin in newPluginRegistry:\n * - If plugin exists with same name: migrate its data\n * - Otherwise (new or type changed): create with initial data\n * Plugins not in newPluginRegistry are dropped.\n *\n * If any step throws, migration fails and original storage is preserved.\n * User can then choose to:\n * - Abort: keep original storage, don't update block\n * - Reset: call createBlockStorage() to start fresh\n *\n * @param storage - The original storage (will not be modified)\n * @param config - Migration configuration\n * @returns Migration result - either success with new storage, or failure with error info\n *\n * @example\n * const result = migrateBlockStorage(storage, {\n * migrateBlockData: (versioned) => blockDataModel.migrate(versioned),\n * migratePluginData: (pluginId, versioned) => getPluginModel(pluginId).migrate(versioned),\n * newPluginRegistry: { table1: 'dataTable' as PluginName },\n * createPluginData: (pluginId) => getPluginModel(pluginId).getDefaultData(),\n * });\n *\n * if (result.success) {\n * commitStorage(result.storage);\n * } else {\n * const userChoice = await askUser(`Migration failed: ${result.error}. Reset data?`);\n * if (userChoice === 'reset') {\n * commitStorage(createBlockStorage(initialData, currentVersion));\n * }\n * // else: abort, keep original\n * }\n */\nexport function migrateBlockStorage(\n storage: BlockStorage<unknown>,\n config: MigrateBlockStorageConfig,\n): MigrationResult<unknown> {\n const { migrateBlockData, migratePluginData, newPluginRegistry, createPluginData } = config;\n\n // Step 1: Migrate block data\n let migratedData: unknown;\n let newVersion: string;\n try {\n const result = migrateBlockData({ version: storage.__dataVersion, data: storage.__data });\n migratedData = result.data;\n newVersion = result.version;\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : String(error),\n failedAt: \"block\",\n };\n }\n\n // Step 2: Migrate plugins\n const oldPlugins = storage.__plugins ?? {};\n const oldRegistry = storage.__pluginRegistry ?? {};\n const newPlugins: Record<PluginHandle, VersionedData<unknown>> = {};\n\n for (const [key, pluginName] of Object.entries(newPluginRegistry)) {\n const handle = key as PluginHandle;\n const existingEntry = oldPlugins[handle];\n const existingName = oldRegistry[handle];\n\n try {\n if (existingEntry && existingName === pluginName) {\n // Plugin exists with same type - migrate its data\n const migrated = migratePluginData(handle, {\n version: existingEntry.__dataVersion,\n data: existingEntry.__data,\n });\n if (migrated) {\n newPlugins[handle] = { __dataVersion: migrated.version, __data: migrated.data };\n }\n // If undefined returned, plugin is intentionally removed\n } else {\n // New plugin or type changed - create with initial data\n const initial = createPluginData(handle);\n newPlugins[handle] = { __dataVersion: initial.version, __data: initial.data };\n }\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : String(error),\n failedAt: handle,\n };\n }\n }\n\n // Step 3: Build final storage atomically\n const migratedStorage: BlockStorage = {\n [BLOCK_STORAGE_KEY]: BLOCK_STORAGE_SCHEMA_VERSION,\n __dataVersion: newVersion,\n __data: migratedData,\n __pluginRegistry: newPluginRegistry,\n __plugins: newPlugins,\n };\n\n return {\n success: true,\n storage: migratedStorage,\n };\n}\n\n/**\n * Gets plugin-specific data from block storage.\n * Accepts raw storage (any format) and normalizes internally.\n *\n * When called with a typed PluginHandle<F>, the return type is automatically\n * inferred from the factory's phantom `__types.data` field.\n *\n * @param rawStorage - Raw block storage (may be legacy format or BlockStorage)\n * @param handle - The plugin handle (branded plugin instance id)\n * @returns The plugin data, typed via factory inference\n * @throws If plugin is not found in storage\n */\nexport function getPluginData<F extends PluginFactoryLike>(\n rawStorage: unknown,\n handle: PluginHandle<F>,\n): InferFactoryData<F> {\n const storage = normalizeBlockStorage(rawStorage);\n const pluginEntry = storage.__plugins?.[handle];\n if (!pluginEntry) throw new Error(`Plugin '${handle}' not found in block storage`);\n return pluginEntry.__data as InferFactoryData<F>;\n}\n"],"names":[],"mappings":"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"}
@@ -143,6 +143,7 @@ function migrateStorage(currentStorageJson, hooks) {
143
143
  *
144
144
  * @param hooks - Dependencies for creating initial block and plugin data
145
145
  * @returns Initial storage as branded JSON string
146
+ * @throws If initialDataFn or createPluginData throws
146
147
  */
147
148
  function createInitialStorage(hooks) {
148
149
  const blockDefault = hooks.getDefaultBlockData();
@@ -1 +1 @@
1
- {"version":3,"file":"block_storage_callbacks.cjs","sources":["../src/block_storage_callbacks.ts"],"sourcesContent":["/**\n * BlockStorage Callback Implementations - wired to facade callbacks in BlockModelV3._done().\n *\n * Provides pure functions for storage operations (migration, initialization,\n * args derivation, updates, debug views). Each function takes its dependencies\n * explicitly as parameters.\n *\n * @module block_storage_callbacks\n * @internal\n */\n\nimport {\n BLOCK_STORAGE_KEY,\n BLOCK_STORAGE_SCHEMA_VERSION,\n type BlockStorage,\n type MutateStoragePayload,\n type StorageDebugView,\n type PluginRegistry,\n type VersionedData,\n createBlockStorage,\n getStorageData,\n isBlockStorage,\n migrateBlockStorage,\n normalizeBlockStorage,\n updateStorageData,\n} from \"./block_storage\";\nimport type { PluginHandle } from \"./plugin_handle\";\n\nimport { stringifyJson, type StringifiedJson } from \"@milaboratories/pl-model-common\";\nimport type { DataMigrationResult, DataVersioned } from \"./block_migrations\";\n\n// =============================================================================\n// Hook interfaces for dependency injection\n// =============================================================================\n\n/** Dependencies for storage migration */\nexport interface MigrationHooks {\n migrateBlockData: (versioned: DataVersioned<unknown>) => DataMigrationResult<unknown>;\n getPluginRegistry: () => PluginRegistry;\n migratePluginData: (\n handle: PluginHandle,\n versioned: DataVersioned<unknown>,\n ) => DataMigrationResult<unknown> | undefined;\n createPluginData: (handle: PluginHandle) => DataVersioned<unknown>;\n}\n\n/** Dependencies for initial storage creation */\nexport interface InitialStorageHooks {\n getDefaultBlockData: () => DataVersioned<unknown>;\n getPluginRegistry: () => PluginRegistry;\n createPluginData: (handle: PluginHandle) => DataVersioned<unknown>;\n}\n\n/**\n * Result of storage normalization\n */\nexport interface NormalizeStorageResult {\n /** The normalized BlockStorage object */\n storage: BlockStorage;\n /** The extracted data (what developers see) */\n data: unknown;\n}\n\n/**\n * Normalizes raw storage data and extracts state.\n * Handles all formats:\n * - New BlockStorage format (has discriminator)\n * - Legacy V1/V2 format ({ args, uiState })\n * - Raw V3 state (any other format)\n *\n * @param rawStorage - Raw data from blockStorage field (may be JSON string or object)\n * @returns Object with normalized storage and extracted state\n */\nfunction normalizeStorage(rawStorage: unknown): NormalizeStorageResult {\n // Handle undefined/null\n if (rawStorage === undefined || rawStorage === null) {\n const storage = createBlockStorage({});\n return { storage, data: {} };\n }\n\n // Parse JSON string if needed\n let parsed = rawStorage;\n if (typeof rawStorage === \"string\") {\n try {\n parsed = JSON.parse(rawStorage);\n } catch {\n // If parsing fails, treat string as the data\n const storage = createBlockStorage(rawStorage);\n return { storage, data: rawStorage };\n }\n }\n\n // Check for BlockStorage format (has discriminator)\n if (isBlockStorage(parsed)) {\n const storage = normalizeBlockStorage(parsed);\n return { storage, data: getStorageData(storage) };\n }\n\n // Check for legacy V1/V2 format: { args, uiState }\n if (isLegacyModelV1ApiFormat(parsed)) {\n // For legacy format, the whole object IS the data\n const storage = createBlockStorage(parsed);\n return { storage, data: parsed };\n }\n\n // Raw V3 data - wrap it\n const storage = createBlockStorage(parsed);\n return { storage, data: parsed };\n}\n\n/**\n * Applies a state update to existing storage.\n * Used when setData is called from the frontend.\n *\n * @param currentStorageJson - Current storage as JSON string (must be defined)\n * @param payload - Update payload with operation type and value\n * @returns Updated storage as StringifiedJson<BlockStorage>\n */\nexport function applyStorageUpdate(\n currentStorageJson: string,\n payload: MutateStoragePayload,\n): StringifiedJson<BlockStorage> {\n const { storage: currentStorage } = normalizeStorage(currentStorageJson);\n\n // Update data while preserving other storage fields (version, plugins)\n const updatedStorage = updateStorageData(currentStorage, payload);\n\n return stringifyJson(updatedStorage);\n}\n\n/**\n * Checks if data is in legacy Model API v1 format.\n * Legacy format has { args, uiState? } at top level without the BlockStorage discriminator.\n */\nfunction isLegacyModelV1ApiFormat(data: unknown): data is { args?: unknown } {\n if (data === null || typeof data !== \"object\") return false;\n if (isBlockStorage(data)) return false;\n\n const obj = data as Record<string, unknown>;\n return \"args\" in obj;\n}\n\n// =============================================================================\n// Facade Callback Implementations\n// =============================================================================\n\n/**\n * Gets storage debug view from raw storage data.\n * Returns structured debug info about the storage state.\n *\n * @param rawStorage - Raw data from blockStorage field (may be JSON string or object)\n * @returns JSON string with storage debug view\n */\nexport function getStorageDebugView(rawStorage: unknown): StringifiedJson<StorageDebugView> {\n const { storage } = normalizeStorage(rawStorage);\n const debugView: StorageDebugView = {\n dataVersion: storage.__dataVersion,\n data: storage.__data,\n };\n return stringifyJson(debugView);\n}\n\n// =============================================================================\n// Migration Support\n// =============================================================================\n\n/**\n * Result of storage migration.\n * Returned by __pl_storage_migrate callback.\n *\n * - Error result: { error: string } - serious failure (no context, etc.)\n * - Success result: { newStorageJson: StringifiedJson<BlockStorage>, info: string } - migration succeeded\n */\nexport type MigrationResult =\n | { error: string }\n | { error?: undefined; newStorageJson: StringifiedJson<BlockStorage>; info: string };\n\n/**\n * Runs storage migration using the provided hooks.\n * This is the main entry point for the middle layer to trigger migrations.\n *\n * @param currentStorageJson - Current storage as JSON string (or undefined)\n * @param hooks - Migration dependencies (block/plugin data migration and creation functions)\n * @returns MigrationResult\n */\nexport function migrateStorage(\n currentStorageJson: string | undefined,\n hooks: MigrationHooks,\n): MigrationResult {\n // Normalize current storage\n const { storage: currentStorage } = normalizeStorage(currentStorageJson);\n\n const newPluginRegistry = hooks.getPluginRegistry();\n\n // Perform atomic migration of block + all plugins\n const migrationResult = migrateBlockStorage(currentStorage, {\n migrateBlockData: hooks.migrateBlockData,\n migratePluginData: hooks.migratePluginData,\n newPluginRegistry,\n createPluginData: hooks.createPluginData,\n });\n\n if (!migrationResult.success) {\n return {\n error: `Migration failed at '${migrationResult.failedAt}': ${migrationResult.error}`,\n };\n }\n\n // Build info message\n const oldVersion = currentStorage.__dataVersion;\n const newVersion = migrationResult.storage.__dataVersion;\n const info =\n oldVersion === newVersion\n ? `No migration needed (${oldVersion})`\n : `Migrated ${oldVersion} -> ${newVersion}`;\n\n return {\n newStorageJson: stringifyJson(migrationResult.storage),\n info,\n };\n}\n\n// =============================================================================\n// Initial Storage Creation\n// =============================================================================\n\n/**\n * Creates complete initial storage (block data + all plugin data) atomically.\n *\n * @param hooks - Dependencies for creating initial block and plugin data\n * @returns Initial storage as branded JSON string\n */\nexport function createInitialStorage(hooks: InitialStorageHooks): StringifiedJson<BlockStorage> {\n const blockDefault = hooks.getDefaultBlockData();\n const pluginRegistry = hooks.getPluginRegistry();\n\n const plugins: Record<PluginHandle, VersionedData<unknown>> = {};\n for (const handle of Object.keys(pluginRegistry) as PluginHandle[]) {\n const initial = hooks.createPluginData(handle);\n plugins[handle] = { __dataVersion: initial.version, __data: initial.data };\n }\n\n const storage: BlockStorage = {\n [BLOCK_STORAGE_KEY]: BLOCK_STORAGE_SCHEMA_VERSION,\n __dataVersion: blockDefault.version,\n __data: blockDefault.data,\n __pluginRegistry: pluginRegistry,\n __plugins: plugins,\n };\n return stringifyJson(storage);\n}\n\n// =============================================================================\n// Args Derivation from Storage\n// =============================================================================\n\n/**\n * Result of args derivation from storage.\n * Returned by __pl_args_derive and __pl_prerunArgs_derive callbacks.\n */\nexport type ArgsDeriveResult = { error: string } | { error?: undefined; value: unknown };\n\n/**\n * Derives args from storage using the provided args function.\n * This extracts data from storage and passes it to the block's args() function.\n *\n * @param storageJson - Storage as JSON string\n * @param argsFunction - The block's args derivation function\n * @returns ArgsDeriveResult with derived args or error\n */\nexport function deriveArgsFromStorage(\n storageJson: string,\n argsFunction: (data: unknown) => unknown,\n): ArgsDeriveResult {\n // Extract data from storage\n const { data } = normalizeStorage(storageJson);\n\n // Call the args function with extracted data\n try {\n const result = argsFunction(data);\n return { value: result };\n } catch (e) {\n const errorMsg = e instanceof Error ? e.message : String(e);\n return { error: `args() threw: ${errorMsg}` };\n }\n}\n\n/**\n * Derives prerunArgs from storage.\n * Uses prerunArgsFunction if provided, otherwise falls back to argsFunction.\n *\n * @param storageJson - Storage as JSON string\n * @param argsFunction - The block's args derivation function (fallback)\n * @param prerunArgsFunction - Optional prerun args derivation function\n * @returns ArgsDeriveResult with derived prerunArgs or error\n */\nexport function derivePrerunArgsFromStorage(\n storageJson: string,\n argsFunction: (data: unknown) => unknown,\n prerunArgsFunction?: (data: unknown) => unknown,\n): ArgsDeriveResult {\n // Extract data from storage\n const { data } = normalizeStorage(storageJson);\n\n // Try prerunArgs function first if available\n if (prerunArgsFunction) {\n try {\n const result = prerunArgsFunction(data);\n return { value: result };\n } catch (e) {\n const errorMsg = e instanceof Error ? e.message : String(e);\n return { error: `prerunArgs() threw: ${errorMsg}` };\n }\n }\n\n // Fall back to args function\n try {\n const result = argsFunction(data);\n return { value: result };\n } catch (e) {\n const errorMsg = e instanceof Error ? e.message : String(e);\n return { error: `args() threw (fallback): ${errorMsg}` };\n }\n}\n\n// Export discriminator key and schema version for external checks\nexport { BLOCK_STORAGE_KEY, BLOCK_STORAGE_SCHEMA_VERSION };\n"],"names":["createBlockStorage","isBlockStorage","normalizeBlockStorage","getStorageData","updateStorageData","stringifyJson","migrateBlockStorage","BLOCK_STORAGE_KEY","BLOCK_STORAGE_SCHEMA_VERSION"],"mappings":"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"}
1
+ {"version":3,"file":"block_storage_callbacks.cjs","sources":["../src/block_storage_callbacks.ts"],"sourcesContent":["/**\n * BlockStorage Callback Implementations - wired to facade callbacks in BlockModelV3._done().\n *\n * Provides pure functions for storage operations (migration, initialization,\n * args derivation, updates, debug views). Each function takes its dependencies\n * explicitly as parameters.\n *\n * @module block_storage_callbacks\n * @internal\n */\n\nimport {\n BLOCK_STORAGE_KEY,\n BLOCK_STORAGE_SCHEMA_VERSION,\n type BlockStorage,\n type MutateStoragePayload,\n type StorageDebugView,\n type PluginRegistry,\n type VersionedData,\n createBlockStorage,\n getStorageData,\n isBlockStorage,\n migrateBlockStorage,\n normalizeBlockStorage,\n updateStorageData,\n} from \"./block_storage\";\nimport type { PluginHandle } from \"./plugin_handle\";\n\nimport { stringifyJson, type StringifiedJson } from \"@milaboratories/pl-model-common\";\nimport type { DataVersioned } from \"./block_migrations\";\n\n// =============================================================================\n// Hook interfaces for dependency injection\n// =============================================================================\n\n/** Dependencies for storage migration */\nexport interface MigrationHooks {\n migrateBlockData: (versioned: DataVersioned<unknown>) => DataVersioned<unknown>;\n getPluginRegistry: () => PluginRegistry;\n migratePluginData: (\n handle: PluginHandle,\n versioned: DataVersioned<unknown>,\n ) => DataVersioned<unknown> | undefined;\n createPluginData: (handle: PluginHandle) => DataVersioned<unknown>;\n}\n\n/** Dependencies for initial storage creation */\nexport interface InitialStorageHooks {\n getDefaultBlockData: () => DataVersioned<unknown>;\n getPluginRegistry: () => PluginRegistry;\n createPluginData: (handle: PluginHandle) => DataVersioned<unknown>;\n}\n\n/**\n * Result of storage normalization\n */\nexport interface NormalizeStorageResult {\n /** The normalized BlockStorage object */\n storage: BlockStorage;\n /** The extracted data (what developers see) */\n data: unknown;\n}\n\n/**\n * Normalizes raw storage data and extracts state.\n * Handles all formats:\n * - New BlockStorage format (has discriminator)\n * - Legacy V1/V2 format ({ args, uiState })\n * - Raw V3 state (any other format)\n *\n * @param rawStorage - Raw data from blockStorage field (may be JSON string or object)\n * @returns Object with normalized storage and extracted state\n */\nfunction normalizeStorage(rawStorage: unknown): NormalizeStorageResult {\n // Handle undefined/null\n if (rawStorage === undefined || rawStorage === null) {\n const storage = createBlockStorage({});\n return { storage, data: {} };\n }\n\n // Parse JSON string if needed\n let parsed = rawStorage;\n if (typeof rawStorage === \"string\") {\n try {\n parsed = JSON.parse(rawStorage);\n } catch {\n // If parsing fails, treat string as the data\n const storage = createBlockStorage(rawStorage);\n return { storage, data: rawStorage };\n }\n }\n\n // Check for BlockStorage format (has discriminator)\n if (isBlockStorage(parsed)) {\n const storage = normalizeBlockStorage(parsed);\n return { storage, data: getStorageData(storage) };\n }\n\n // Check for legacy V1/V2 format: { args, uiState }\n if (isLegacyModelV1ApiFormat(parsed)) {\n // For legacy format, the whole object IS the data\n const storage = createBlockStorage(parsed);\n return { storage, data: parsed };\n }\n\n // Raw V3 data - wrap it\n const storage = createBlockStorage(parsed);\n return { storage, data: parsed };\n}\n\n/**\n * Applies a state update to existing storage.\n * Used when setData is called from the frontend.\n *\n * @param currentStorageJson - Current storage as JSON string (must be defined)\n * @param payload - Update payload with operation type and value\n * @returns Updated storage as StringifiedJson<BlockStorage>\n */\nexport function applyStorageUpdate(\n currentStorageJson: string,\n payload: MutateStoragePayload,\n): StringifiedJson<BlockStorage> {\n const { storage: currentStorage } = normalizeStorage(currentStorageJson);\n\n // Update data while preserving other storage fields (version, plugins)\n const updatedStorage = updateStorageData(currentStorage, payload);\n\n return stringifyJson(updatedStorage);\n}\n\n/**\n * Checks if data is in legacy Model API v1 format.\n * Legacy format has { args, uiState? } at top level without the BlockStorage discriminator.\n */\nfunction isLegacyModelV1ApiFormat(data: unknown): data is { args?: unknown } {\n if (data === null || typeof data !== \"object\") return false;\n if (isBlockStorage(data)) return false;\n\n const obj = data as Record<string, unknown>;\n return \"args\" in obj;\n}\n\n// =============================================================================\n// Facade Callback Implementations\n// =============================================================================\n\n/**\n * Gets storage debug view from raw storage data.\n * Returns structured debug info about the storage state.\n *\n * @param rawStorage - Raw data from blockStorage field (may be JSON string or object)\n * @returns JSON string with storage debug view\n */\nexport function getStorageDebugView(rawStorage: unknown): StringifiedJson<StorageDebugView> {\n const { storage } = normalizeStorage(rawStorage);\n const debugView: StorageDebugView = {\n dataVersion: storage.__dataVersion,\n data: storage.__data,\n };\n return stringifyJson(debugView);\n}\n\n// =============================================================================\n// Migration Support\n// =============================================================================\n\n/**\n * Result of storage migration.\n * Returned by __pl_storage_migrate callback.\n *\n * - Error result: { error: string } - serious failure (no context, etc.)\n * - Success result: { newStorageJson: StringifiedJson<BlockStorage>, info: string } - migration succeeded\n */\nexport type MigrationResult =\n | { error: string }\n | { error?: undefined; newStorageJson: StringifiedJson<BlockStorage>; info: string };\n\n/**\n * Runs storage migration using the provided hooks.\n * This is the main entry point for the middle layer to trigger migrations.\n *\n * @param currentStorageJson - Current storage as JSON string (or undefined)\n * @param hooks - Migration dependencies (block/plugin data migration and creation functions)\n * @returns MigrationResult\n */\nexport function migrateStorage(\n currentStorageJson: string | undefined,\n hooks: MigrationHooks,\n): MigrationResult {\n // Normalize current storage\n const { storage: currentStorage } = normalizeStorage(currentStorageJson);\n\n const newPluginRegistry = hooks.getPluginRegistry();\n\n // Perform atomic migration of block + all plugins\n const migrationResult = migrateBlockStorage(currentStorage, {\n migrateBlockData: hooks.migrateBlockData,\n migratePluginData: hooks.migratePluginData,\n newPluginRegistry,\n createPluginData: hooks.createPluginData,\n });\n\n if (!migrationResult.success) {\n return {\n error: `Migration failed at '${migrationResult.failedAt}': ${migrationResult.error}`,\n };\n }\n\n // Build info message\n const oldVersion = currentStorage.__dataVersion;\n const newVersion = migrationResult.storage.__dataVersion;\n const info =\n oldVersion === newVersion\n ? `No migration needed (${oldVersion})`\n : `Migrated ${oldVersion} -> ${newVersion}`;\n\n return {\n newStorageJson: stringifyJson(migrationResult.storage),\n info,\n };\n}\n\n// =============================================================================\n// Initial Storage Creation\n// =============================================================================\n\n/**\n * Creates complete initial storage (block data + all plugin data) atomically.\n *\n * @param hooks - Dependencies for creating initial block and plugin data\n * @returns Initial storage as branded JSON string\n * @throws If initialDataFn or createPluginData throws\n */\nexport function createInitialStorage(hooks: InitialStorageHooks): StringifiedJson<BlockStorage> {\n const blockDefault = hooks.getDefaultBlockData();\n const pluginRegistry = hooks.getPluginRegistry();\n\n const plugins: Record<PluginHandle, VersionedData<unknown>> = {};\n for (const handle of Object.keys(pluginRegistry) as PluginHandle[]) {\n const initial = hooks.createPluginData(handle);\n plugins[handle] = { __dataVersion: initial.version, __data: initial.data };\n }\n\n const storage: BlockStorage = {\n [BLOCK_STORAGE_KEY]: BLOCK_STORAGE_SCHEMA_VERSION,\n __dataVersion: blockDefault.version,\n __data: blockDefault.data,\n __pluginRegistry: pluginRegistry,\n __plugins: plugins,\n };\n return stringifyJson(storage);\n}\n\n// =============================================================================\n// Args Derivation from Storage\n// =============================================================================\n\n/**\n * Result of args derivation from storage.\n * Returned by __pl_args_derive and __pl_prerunArgs_derive callbacks.\n */\nexport type ArgsDeriveResult = { error: string } | { error?: undefined; value: unknown };\n\n/**\n * Derives args from storage using the provided args function.\n * This extracts data from storage and passes it to the block's args() function.\n *\n * @param storageJson - Storage as JSON string\n * @param argsFunction - The block's args derivation function\n * @returns ArgsDeriveResult with derived args or error\n */\nexport function deriveArgsFromStorage(\n storageJson: string,\n argsFunction: (data: unknown) => unknown,\n): ArgsDeriveResult {\n // Extract data from storage\n const { data } = normalizeStorage(storageJson);\n\n // Call the args function with extracted data\n try {\n const result = argsFunction(data);\n return { value: result };\n } catch (e) {\n const errorMsg = e instanceof Error ? e.message : String(e);\n return { error: `args() threw: ${errorMsg}` };\n }\n}\n\n/**\n * Derives prerunArgs from storage.\n * Uses prerunArgsFunction if provided, otherwise falls back to argsFunction.\n *\n * @param storageJson - Storage as JSON string\n * @param argsFunction - The block's args derivation function (fallback)\n * @param prerunArgsFunction - Optional prerun args derivation function\n * @returns ArgsDeriveResult with derived prerunArgs or error\n */\nexport function derivePrerunArgsFromStorage(\n storageJson: string,\n argsFunction: (data: unknown) => unknown,\n prerunArgsFunction?: (data: unknown) => unknown,\n): ArgsDeriveResult {\n // Extract data from storage\n const { data } = normalizeStorage(storageJson);\n\n // Try prerunArgs function first if available\n if (prerunArgsFunction) {\n try {\n const result = prerunArgsFunction(data);\n return { value: result };\n } catch (e) {\n const errorMsg = e instanceof Error ? e.message : String(e);\n return { error: `prerunArgs() threw: ${errorMsg}` };\n }\n }\n\n // Fall back to args function\n try {\n const result = argsFunction(data);\n return { value: result };\n } catch (e) {\n const errorMsg = e instanceof Error ? e.message : String(e);\n return { error: `args() threw (fallback): ${errorMsg}` };\n }\n}\n\n// Export discriminator key and schema version for external checks\nexport { BLOCK_STORAGE_KEY, BLOCK_STORAGE_SCHEMA_VERSION };\n"],"names":["createBlockStorage","isBlockStorage","normalizeBlockStorage","getStorageData","updateStorageData","stringifyJson","migrateBlockStorage","BLOCK_STORAGE_KEY","BLOCK_STORAGE_SCHEMA_VERSION"],"mappings":"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"}