diff --git a/.changeset/sequence-properties-trace-label-and-meta.md b/.changeset/sequence-properties-trace-label-and-meta.md new file mode 100644 index 0000000..5215ff2 --- /dev/null +++ b/.changeset/sequence-properties-trace-label-and-meta.md @@ -0,0 +1,14 @@ +--- +'@platforma-open/milaboratories.sequence-properties.workflow': minor +'@platforma-open/milaboratories.sequence-properties.model': minor +'@platforma-open/milaboratories.sequence-properties.ui': minor +'@platforma-open/milaboratories.sequence-properties': minor +'@platforma-open/milaboratories.sequence-properties.test': patch +--- + +Per-instance trace label, broader plot pickers, locked-in test coverage. + +- **Trace label is per-instance.** The workflow's `pl7.app/trace.label` resolves to `customBlockLabel || defaultBlockLabel || "Sequence Properties"` (centralised in `model/src/label.ts`). Two sequence-properties blocks on the same dataset show distinguishable entries in Lead Selection and other downstream pickers once the user customises the `PlBlockPage` subtitle. Same pattern as clonotype-clustering and titeseq-analysis PR #13. +- **Scatter and Histogram metadata pickers accept own-block columns.** Filter, Grouping/Color, Highlight, Size, Tab, Tooltip, Label, and Additional-curves now treat every column in the property pframe as a candidate — own scalars and upstream metadata alike. Users can color the Property Relationships scatter by Aromaticity while plotting Charge vs Hydrophobicity. X/Y axis defaults unchanged. +- **Migration backfill.** A new `Ver_2026_05_18` step fills the new label fields onto projects tagged at the deployed `Ver_2026_05_05`, preserving any interim-deployed value via `?? ""`. Without the split, already-V2 projects would skip the migration and the workflow would receive `args.customBlockLabel === undefined`. +- **Test coverage.** Model vitest locks the resolution chain (6 cases) and the migration backfill (4 cases). A subprocess-based Python byte-compare test guards Python output determinism. `build.yaml` enables `test: true` so block-level tests exercise on every PR. diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index ab5e10c..b39e54e 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -32,7 +32,7 @@ jobs: build-script-name: 'build' pnpm-recursive-build: false - test: false + test: true test-script-name: 'test' pnpm-recursive-tests: false team-id: 'ciplopen' diff --git a/block/package.json b/block/package.json index aaf7914..e0017bb 100644 --- a/block/package.json +++ b/block/package.json @@ -47,5 +47,5 @@ "devDependencies": { "@platforma-sdk/block-tools": "catalog:" }, - "packageManager": "pnpm@9.12.0" + "packageManager": "pnpm@9.15.0" } diff --git a/model/package.json b/model/package.json index 3db9ee2..3f67b0d 100644 --- a/model/package.json +++ b/model/package.json @@ -17,7 +17,9 @@ "fmt": "ts-builder format", "watch": "ts-builder build --target block-model --watch", "build": "ts-builder build --target block-model && block-tools build-model", - "check": "ts-builder check --target block-model" + "check": "ts-builder check --target block-model", + "test": "vitest --run --passWithNoTests", + "test:watch": "vitest" }, "dependencies": { "@milaboratories/graph-maker": "catalog:", @@ -27,7 +29,8 @@ "devDependencies": { "@milaboratories/ts-builder": "catalog:", "@milaboratories/ts-configs": "catalog:", - "@platforma-sdk/block-tools": "catalog:" + "@platforma-sdk/block-tools": "catalog:", + "vitest": "catalog:" }, "peerDependencies": { "@types/node": "*", diff --git a/model/src/dataModel.test.ts b/model/src/dataModel.test.ts new file mode 100644 index 0000000..5e80700 --- /dev/null +++ b/model/src/dataModel.test.ts @@ -0,0 +1,63 @@ +// Tests drive the migration callbacks directly because @platforma-sdk/model's +// DataModelBuilder exposes no introspection / external-apply API. The builder +// wiring in dataModel.ts (the two `.migrate(...)` calls) is only exercised +// when a real block loads — these unit tests would still pass if someone +// deleted a `.migrate(...)` line while keeping the named callback exported. + +import { describe, expect, it } from "vitest"; +import type { BlockData, BlockDataV1, BlockDataV2 } from "./types"; +import { migrateV1toV2, migrateV2toV2_1 } from "./dataModel"; + +const tableState = { + pTableParams: { + defaultFilters: null, + filters: null, + hiddenColIds: null, + sorting: [], + sourceId: null, + }, + stateCache: [], + version: 6, +} as BlockDataV2["tableState"]; + +const v2Graph: Pick = { + graphStateScatter: { currentTab: null, template: "dots", title: "Property Relationships" }, + graphStateHistogram: { + currentTab: null, + layersSettings: { bins: { fillColor: "#99e099" } }, + template: "bins", + title: "Property Distribution", + }, +}; + +describe("blockDataModel Ver_2026_05_18 backfill", () => { + it("backfills both label fields to '' on a bare V2 payload", () => { + const v2: BlockDataV2 = { tableState, ...v2Graph }; + const upgraded: BlockData = migrateV2toV2_1(v2); + expect(upgraded.customBlockLabel).toBe(""); + expect(upgraded.defaultBlockLabel).toBe(""); + }); + + it("preserves an interim-deployed customBlockLabel", () => { + const v2: BlockDataV2 = { tableState, ...v2Graph, customBlockLabel: "X" }; + const upgraded: BlockData = migrateV2toV2_1(v2); + expect(upgraded.customBlockLabel).toBe("X"); + expect(upgraded.defaultBlockLabel).toBe(""); + }); + + it("preserves an interim-deployed defaultBlockLabel", () => { + const v2: BlockDataV2 = { tableState, ...v2Graph, defaultBlockLabel: "Y" }; + const upgraded: BlockData = migrateV2toV2_1(v2); + expect(upgraded.defaultBlockLabel).toBe("Y"); + expect(upgraded.customBlockLabel).toBe(""); + }); + + it("runs the full V1 → V2 → V2.1 chain on legacy data", () => { + const v1: BlockDataV1 = { tableState, defaultBlockLabel: "Old" }; + const upgraded: BlockData = migrateV2toV2_1(migrateV1toV2(v1)); + expect(upgraded.defaultBlockLabel).toBe("Old"); + expect(upgraded.customBlockLabel).toBe(""); + expect(upgraded.graphStateScatter).toBeDefined(); + expect(upgraded.graphStateHistogram).toBeDefined(); + }); +}); diff --git a/model/src/dataModel.ts b/model/src/dataModel.ts index 01f26b9..3033520 100644 --- a/model/src/dataModel.ts +++ b/model/src/dataModel.ts @@ -1,6 +1,6 @@ import type { GraphMakerState } from "@milaboratories/graph-maker"; import { createPlDataTableStateV2, DataModelBuilder } from "@platforma-sdk/model"; -import type { BlockData, BlockDataV1 } from "./types"; +import type { BlockData, BlockDataV1, BlockDataV2 } from "./types"; const DEFAULT_SCATTER_STATE: GraphMakerState = { title: "Property Relationships", @@ -17,15 +17,33 @@ const DEFAULT_HISTOGRAM_STATE: GraphMakerState = { }, }; +export const migrateV1toV2 = (v1: BlockDataV1): BlockDataV2 => ({ + ...v1, + graphStateScatter: { ...DEFAULT_SCATTER_STATE }, + graphStateHistogram: { ...DEFAULT_HISTOGRAM_STATE }, +}); + +export const migrateV2toV2_1 = (v2: BlockDataV2): BlockData => ({ + ...v2, + defaultBlockLabel: v2.defaultBlockLabel ?? "", + customBlockLabel: v2.customBlockLabel ?? "", +}); + export const blockDataModel = new DataModelBuilder() .from("Ver_2026_04_28") - .migrate("Ver_2026_05_05", (v1) => ({ - ...v1, - graphStateScatter: { ...DEFAULT_SCATTER_STATE }, - graphStateHistogram: { ...DEFAULT_HISTOGRAM_STATE }, - })) + // Already-deployed step. Future field additions must go into a new step + // below — editing this body has no effect on projects already tagged + // Ver_2026_05_05 (DataModelBuilder skips matching-version migrations). + .migrate("Ver_2026_05_05", migrateV1toV2) + // Backfills label fields onto V2-tagged projects. `?? ""` preserves any + // interim-deployed value; missing fields default to "". The args + // projection (resolveTraceLabel in label.ts) requires both fields to be + // strings, never undefined. + .migrate("Ver_2026_05_18", migrateV2toV2_1) .init(() => ({ tableState: createPlDataTableStateV2(), + defaultBlockLabel: "", + customBlockLabel: "", graphStateScatter: { ...DEFAULT_SCATTER_STATE }, graphStateHistogram: { ...DEFAULT_HISTOGRAM_STATE }, })); diff --git a/model/src/index.ts b/model/src/index.ts index 8ee0f47..bc1eb47 100644 --- a/model/src/index.ts +++ b/model/src/index.ts @@ -6,6 +6,7 @@ import { createPlDataTableV2, } from "@platforma-sdk/model"; import { blockDataModel } from "./dataModel"; +import { resolveSubtitle, resolveTraceLabel } from "./label"; import type { BlockArgs, WorkflowInfo } from "./types"; export type * from "@milaboratories/helpers"; @@ -41,6 +42,7 @@ export const platforma = BlockModelV3.create(blockDataModel) } return { inputAnchor: data.inputAnchor, + traceLabel: resolveTraceLabel(data), }; }) .output("inputOptions", (ctx) => ctx.resultPool.getOptions(inputAnchorSpecs)) @@ -107,7 +109,7 @@ export const platforma = BlockModelV3.create(blockDataModel) return pCols.map((c) => ({ columnId: c.id, spec: c.spec }) satisfies PColumnIdAndSpec); }) .title(() => "Sequence Properties") - .subtitle((ctx) => ctx.data.defaultBlockLabel ?? "") + .subtitle((ctx) => resolveSubtitle(ctx.data)) .sections(() => [ { type: "link", href: "/", label: "Main" }, { type: "link", href: "/scatter", label: "Property Relationships" }, diff --git a/model/src/label.test.ts b/model/src/label.test.ts new file mode 100644 index 0000000..1232efd --- /dev/null +++ b/model/src/label.test.ts @@ -0,0 +1,62 @@ +import { describe, expect, it } from "vitest"; +import type { BlockData } from "./types"; +import { resolveSubtitle, resolveTraceLabel } from "./label"; + +const base: Omit = { + tableState: { + pTableParams: { + defaultFilters: null, + filters: null, + hiddenColIds: null, + sorting: [], + sourceId: null, + }, + stateCache: [], + version: 6, + } as BlockData["tableState"], + graphStateScatter: { + currentTab: null, + template: "dots", + title: "Property Relationships", + } as BlockData["graphStateScatter"], + graphStateHistogram: { + currentTab: null, + layersSettings: { bins: { fillColor: "#99e099" } }, + template: "bins", + title: "Property Distribution", + } as BlockData["graphStateHistogram"], +}; + +const make = (custom: string, def: string): BlockData => ({ + ...(base as BlockData), + customBlockLabel: custom, + defaultBlockLabel: def, +}); + +describe("resolveSubtitle", () => { + it("uses customBlockLabel when set", () => { + expect(resolveSubtitle(make("My label", "Dataset"))).toBe("My label"); + }); + + it("falls back to defaultBlockLabel when customBlockLabel is empty", () => { + expect(resolveSubtitle(make("", "Dataset"))).toBe("Dataset"); + }); + + it("returns empty string when both label fields are empty", () => { + expect(resolveSubtitle(make("", ""))).toBe(""); + }); +}); + +describe("resolveTraceLabel", () => { + it("uses customBlockLabel when set", () => { + expect(resolveTraceLabel(make("My label", "Dataset"))).toBe("My label"); + }); + + it("falls back to defaultBlockLabel when customBlockLabel is empty", () => { + expect(resolveTraceLabel(make("", "Dataset"))).toBe("Dataset"); + }); + + it("falls back to the block-type default when both label fields are empty", () => { + expect(resolveTraceLabel(make("", ""))).toBe("Sequence Properties"); + }); +}); diff --git a/model/src/label.ts b/model/src/label.ts new file mode 100644 index 0000000..62ed052 --- /dev/null +++ b/model/src/label.ts @@ -0,0 +1,17 @@ +import type { BlockData } from "./types"; + +const STATIC_FALLBACK = "Sequence Properties"; + +// Subtitle resolution for the PlBlockPage. Empty string is a valid result +// (the page renders the title alone). +export function resolveSubtitle(data: BlockData): string { + return data.customBlockLabel || data.defaultBlockLabel; +} + +// Trace-label resolution for the workflow's pl7.app/trace.label. Same chain +// as resolveSubtitle plus a static block-type last-resort, so automated +// pipelines that run before the UI populates defaultBlockLabel still emit +// a non-empty label downstream. +export function resolveTraceLabel(data: BlockData): string { + return data.customBlockLabel || data.defaultBlockLabel || STATIC_FALLBACK; +} diff --git a/model/src/types.ts b/model/src/types.ts index 5d684d2..6fff0bd 100644 --- a/model/src/types.ts +++ b/model/src/types.ts @@ -6,19 +6,31 @@ import type { PlDataTableStateV2, PlRef } from "@platforma-sdk/model"; export type BlockDataV1 = { inputAnchor?: PlRef; tableState: PlDataTableStateV2; - // UI-only state. Tracks the selected input dataset's label so the block - // subtitle can reflect it — populated by the UI watcher in app.ts. Not - // projected into BlockArgs because the workflow does not consume it. + // Historically optional UI-only state. Required in the current BlockData + // shape; consumed by the label helpers in label.ts (resolveSubtitle for + // the PlBlockPage subtitle, resolveTraceLabel for the workflow trace). defaultBlockLabel?: string; }; -export type BlockData = BlockDataV1 & { +// V2 shape — what the deployed Ver_2026_05_05 migration produces. Input to +// the new Ver_2026_05_18 step that backfills the label fields. Both label +// fields are optional here so the V2→V2.1 migration can read-or-default any +// value an interim deployment may have written. +export type BlockDataV2 = Omit & { + defaultBlockLabel?: string; + customBlockLabel?: string; graphStateScatter: GraphMakerState; graphStateHistogram: GraphMakerState; }; +export type BlockData = Omit & { + defaultBlockLabel: string; + customBlockLabel: string; +}; + export type BlockArgs = { inputAnchor: PlRef; + traceLabel: string; }; export type WorkflowMode = diff --git a/model/vitest.config.mts b/model/vitest.config.mts new file mode 100644 index 0000000..c6016f2 --- /dev/null +++ b/model/vitest.config.mts @@ -0,0 +1,8 @@ +import { defineConfig } from "vitest/config"; + +export default defineConfig({ + test: { + watch: false, + testTimeout: 5000, + }, +}); diff --git a/package.json b/package.json index 3f9f67d..10f9491 100644 --- a/package.json +++ b/package.json @@ -27,5 +27,5 @@ "oxfmt": "*", "oxlint": "*" }, - "packageManager": "pnpm@10.33.2" + "packageManager": "pnpm@9.15.0" } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 07198d8..161cf40 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -25,8 +25,8 @@ catalogs: specifier: 1.8.2 version: 1.8.2 '@platforma-sdk/block-tools': - specifier: 2.7.25 - version: 2.7.25 + specifier: 2.8.1 + version: 2.8.1 '@platforma-sdk/eslint-config': specifier: 1.2.0 version: 1.2.0 @@ -86,7 +86,7 @@ importers: version: 1.4.0(@types/node@25.3.2)(rollup@4.53.3)(vue@3.5.25(typescript@5.9.3))(yaml@2.8.1) '@platforma-sdk/block-tools': specifier: 'catalog:' - version: 2.7.25 + version: 2.8.1 shx: specifier: 'catalog:' version: 0.4.0 @@ -111,13 +111,13 @@ importers: devDependencies: '@platforma-sdk/block-tools': specifier: 'catalog:' - version: 2.7.25 + version: 2.8.1 model: dependencies: '@milaboratories/graph-maker': specifier: 'catalog:' - version: 1.4.2(@milaboratories/pl-model-common@1.39.0)(@platforma-sdk/model@1.77.0)(@platforma-sdk/ui-vue@1.73.3(@bytecodealliance/preview2-shim@0.17.9)(typescript@5.6.3))(d3-dispatch@3.0.1)(d3-path@3.1.0)(d3-scale-chromatic@3.1.0)(typescript@5.6.3) + version: 1.4.2(@milaboratories/pl-model-common@1.42.0)(@platforma-sdk/model@1.77.0)(@platforma-sdk/ui-vue@1.77.0(@bytecodealliance/preview2-shim@0.17.8)(typescript@5.6.3))(d3-dispatch@3.0.1)(d3-path@3.1.0)(d3-scale-chromatic@3.1.0)(typescript@5.6.3) '@milaboratories/helpers': specifier: 'catalog:' version: 1.14.2 @@ -139,7 +139,10 @@ importers: version: 1.2.3 '@platforma-sdk/block-tools': specifier: 'catalog:' - version: 2.7.25 + version: 2.8.1 + vitest: + specifier: 'catalog:' + version: 4.1.4(@types/node@25.3.2)(@vitest/coverage-istanbul@4.1.4)(vite@8.0.8(@types/node@25.3.2)(yaml@2.8.1)) software: devDependencies: @@ -188,7 +191,7 @@ importers: version: 1.2.0(@eslint/js@9.39.4)(@stylistic/eslint-plugin@2.13.0(eslint@9.39.4)(typescript@5.6.3))(eslint-plugin-n@17.24.0(eslint@9.39.4)(typescript@5.6.3))(eslint-plugin-vue@9.33.0(eslint@9.39.4))(eslint@9.39.4)(globals@15.15.0)(typescript-eslint@8.59.1(eslint@9.39.4)(typescript@5.6.3))(typescript@5.6.3) '@platforma-sdk/test': specifier: 'catalog:' - version: 1.77.1(@bytecodealliance/preview2-shim@0.17.9)(@types/node@25.3.2)(vite@8.0.8(@types/node@25.3.2)(yaml@2.8.1)) + version: 1.77.1(@bytecodealliance/preview2-shim@0.17.8)(@types/node@25.3.2)(vite@8.0.8(@types/node@25.3.2)(yaml@2.8.1)) eslint: specifier: 'catalog:' version: 9.39.4 @@ -203,7 +206,7 @@ importers: dependencies: '@milaboratories/graph-maker': specifier: 'catalog:' - version: 1.4.2(@milaboratories/pl-model-common@1.39.0)(@platforma-sdk/model@1.77.0)(@platforma-sdk/ui-vue@1.77.0(@bytecodealliance/preview2-shim@0.17.9)(typescript@5.6.3))(d3-dispatch@3.0.1)(d3-path@3.1.0)(d3-scale-chromatic@3.1.0)(typescript@5.6.3) + version: 1.4.2(@milaboratories/pl-model-common@1.42.0)(@platforma-sdk/model@1.77.0)(@platforma-sdk/ui-vue@1.77.0(@bytecodealliance/preview2-shim@0.17.8)(typescript@5.6.3))(d3-dispatch@3.0.1)(d3-path@3.1.0)(d3-scale-chromatic@3.1.0)(typescript@5.6.3) '@platforma-open/milaboratories.sequence-properties.model': specifier: workspace:* version: link:../model @@ -212,7 +215,7 @@ importers: version: 1.77.0 '@platforma-sdk/ui-vue': specifier: 'catalog:' - version: 1.77.0(@bytecodealliance/preview2-shim@0.17.9)(typescript@5.6.3) + version: 1.77.0(@bytecodealliance/preview2-shim@0.17.8)(typescript@5.6.3) typescript: specifier: '*' version: 5.6.3 @@ -250,7 +253,7 @@ importers: version: 2.5.29 '@platforma-sdk/test': specifier: 'catalog:' - version: 1.77.1(@bytecodealliance/preview2-shim@0.17.9)(@types/node@25.3.2)(vite@8.0.8(@types/node@25.3.2)(yaml@2.8.1)) + version: 1.77.1(@bytecodealliance/preview2-shim@0.17.8)(@types/node@25.3.2)(vite@8.0.8(@types/node@25.3.2)(yaml@2.8.1)) packages: @@ -274,28 +277,24 @@ packages: engines: {node: '>= 10'} cpu: [arm64] os: [linux] - libc: [glibc] '@ast-grep/napi-linux-arm64-musl@0.36.3': resolution: {integrity: sha512-2XRmNYuovZu0Pa4J3or4PKMkQZnXXfpVcCrPwWB/2ytX7XUo+TWLgYE8rPVnJOyw5zujkveFb0XUrro9mQgLzw==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] - libc: [musl] '@ast-grep/napi-linux-x64-gnu@0.36.3': resolution: {integrity: sha512-mTwPRbBi1feGqR2b5TWC5gkEDeRi8wfk4euF5sKNihfMGHj6pdfINHQ3QvLVO4C7z0r/wgWLAvditFA0b997dg==} engines: {node: '>= 10'} cpu: [x64] os: [linux] - libc: [glibc] '@ast-grep/napi-linux-x64-musl@0.36.3': resolution: {integrity: sha512-tMGPrT+zuZzJK6n1cD1kOii7HYZE9gUXjwtVNE/uZqXEaWP6lmkfoTMbLjnxEe74VQbmaoDGh1/cjrDBnqC6Uw==} engines: {node: '>= 10'} cpu: [x64] os: [linux] - libc: [musl] '@ast-grep/napi-win32-arm64-msvc@0.36.3': resolution: {integrity: sha512-7pFyr9+dyV+4cBJJ1I57gg6PDXP3GBQeVAsEEitzEruxx4Hb4cyNro54gGtlsS+6ty+N0t004tPQxYO2VrsPIg==} @@ -583,8 +582,8 @@ packages: '@bufbuild/protoplugin@2.7.0': resolution: {integrity: sha512-yUdg8hXzFGR6K8ren7aXly2hT9BxClId814VB142YeZPatY0wqD3c0D8KfIz5nIeMdoPt0/Pm/RycFJCNGMD6w==} - '@bytecodealliance/preview2-shim@0.17.9': - resolution: {integrity: sha512-i0R3eQBe6PA/o/1EFE3Owe4In2rcccb6QxnjpntM/lPe3/duJ0bRQTVZM2Ufpo99X4eofGeltQUkape1C91FFA==} + '@bytecodealliance/preview2-shim@0.17.8': + resolution: {integrity: sha512-wS5kg8u0KCML1UeHQPJ1IuOI24x/XLentCzsqPER1+gDNC5Cz2hG4G2blLOZap+3CEGhIhnJ9mmZYj6a2W0Lww==} '@changesets/apply-release-plan@7.0.14': resolution: {integrity: sha512-ddBvf9PHdy2YY0OUiEl3TV78mH9sckndJR14QAt87KLEbIov81XO0q0QAmvooBxXlqRRP8I9B7XOzZwQG7JkWA==} @@ -1046,9 +1045,6 @@ packages: '@milaboratories/pf-spec-driver@1.3.16': resolution: {integrity: sha512-Podb1eNvcl1nQXG/LeT+ZiesSq17z4ixBNDk0Kj92RRYQ6IZDeDyCgUgrVH7/A/UZ/j/dyRzbjkOLBWmk3DDmQ==} - '@milaboratories/pf-spec-driver@1.3.9': - resolution: {integrity: sha512-IdUFwaxWdOjwmRixGJ/QeENsL0/7JjvSUXN6YhLbD5iWir6TY/Rk6xXTg1tk+IpriVmoPzncHQTRxifwsvJ/gA==} - '@milaboratories/pframes-rs-node@1.1.35': resolution: {integrity: sha512-XGLRa29bOmpEUeHgpWNDYZDGDFvR7OfXqaodxaIAVtXnsrsjxzDz063z1sjRPDOx/Pz9T+5n4iRNuLyygwcQ5A==} @@ -1056,9 +1052,6 @@ packages: resolution: {integrity: sha512-xr0zztZZX9V7i6iRpbqy12TiFRP3q73UDkjX6sn5KuP7kdmQLExVzhud7Mgd1G293vVAkD2ZBmMRiMoOu2bsMQ==} hasBin: true - '@milaboratories/pframes-rs-wasi@1.1.31': - resolution: {integrity: sha512-pueH0fOgiCusD2YzlvZAD6FL1Qo2AvFc6+Jmxms+ymADgpTcd0ABYeaxGZih4hfgyJc5Zcnkea9dy+pemju8TA==} - '@milaboratories/pframes-rs-wasip2@1.1.35': resolution: {integrity: sha512-kcR9YLWAbKYSpksPOoJWkcrkSiUUAEKj/Wuyc9aFsd2bNbWcIb7mLGptFOuvhLn07bZHn0oNcVgupEqd/6Ftbw==} @@ -1069,13 +1062,6 @@ packages: '@milaboratories/pl-model-common': 1.28.0 '@milaboratories/pl-model-middle-layer': 1.15.0 - '@milaboratories/pframes-rs-wasm@1.1.31': - resolution: {integrity: sha512-tLUf8FkRvrWOV6uSP1rwFlpNl0DUXkSNrrlTp5qFNFzHZSBtcVlw8bjfrJS7aCWuSMvKHQd49aNitEXF7aNbKw==} - peerDependencies: - '@bytecodealliance/preview2-shim': 0.17.9 - '@milaboratories/pl-model-common': 1.36.0 - '@milaboratories/pl-model-middle-layer': 1.18.5 - '@milaboratories/pframes-rs-wasm@1.1.35': resolution: {integrity: sha512-wmnEujKa47y+CguYFbeYPjzYAsdhOQO/oNKIyCl0cG/RDwi3qtioKj6DMIBQgjC+/yLPuMsPkjXh7aaPn9cvpA==} peerDependencies: @@ -1087,6 +1073,10 @@ packages: resolution: {integrity: sha512-eVDnXExhKB4DYzqkWD49MYyi6AyBxWyG8WoDMFrB23FjyHgMTR7rSoep0iUsWEoLLGnwq4Qd8l89/hBYpAVg0A==} engines: {node: '>=22.19.0'} + '@milaboratories/pl-client@3.8.0': + resolution: {integrity: sha512-ADUFHvwtGDC/sOYd4btCw2GdR58gq3+Nm9yV3UUYhmH5dv3J/1tXsxJH15mv1mT9YvK0IS4vMw0eDteeQUmaiQ==} + engines: {node: '>=22.19.0'} + '@milaboratories/pl-config@1.8.1': resolution: {integrity: sha512-moh8YNeSXRkBaH5IQhrzcWoehM6EMwfVFx2Y4vP6eEwphbHDqPLdhn6COkEJlYijH6kT00JdWjlUrvx+MZD9Cw==} @@ -1117,6 +1107,9 @@ packages: '@milaboratories/pl-model-backend@1.2.29': resolution: {integrity: sha512-0jL+k6z6MJha0XMFXq/e814THyvNZZNeBB6zcLYtiWKVYRCKp/iK43xOFCzGAgT1uQVz4AdhKsqgqQv5B9deBw==} + '@milaboratories/pl-model-backend@1.3.1': + resolution: {integrity: sha512-zDWTa/AqI2YSmD3FpQavWI9OnZzMRoNQS+E8YtdSadC9LkvetqY5YI9Uu/IAGrWVonlvjD6uA0PrPX5hACOxow==} + '@milaboratories/pl-model-common@1.31.1': resolution: {integrity: sha512-MLQvhXXFOykABZr8aVgzt5x0htT7ye4cvvnVy0TgOITXpct+lEmWvaVj29zirK/0VFw7wnTXDGnLwusr77NZFA==} @@ -1126,9 +1119,6 @@ packages: '@milaboratories/pl-model-common@1.36.0': resolution: {integrity: sha512-BbFs3sTmy12ZKfTY6rFep0C5pfQoLvsjACN8EYaNIjXqOjQajt6velh4uGpB47+Uyp78k0cIWH4T04MIlixQrw==} - '@milaboratories/pl-model-common@1.39.0': - resolution: {integrity: sha512-4IdmhTdHnmPnAPlaQYApNV5rswV60hUMeLEjmQm/s8y/IeuaW9RUkYUkl32PhsKxfbb6BQMlahU0XcpXRPLBPg==} - '@milaboratories/pl-model-common@1.42.0': resolution: {integrity: sha512-ttX9OcQ9kgEhgyXZNkC7+vtsCaxjz+uNwmU8d2LlA56cpWgWV9WONi91w8nCRGFf9WboSgUVVaFj2XxiT9QHXQ==} @@ -1138,22 +1128,19 @@ packages: '@milaboratories/pl-model-middle-layer@1.16.4': resolution: {integrity: sha512-uFTZHEjRmfRvY97tRbBuTvNzQnTYIj58S8HBzyMsFkzxbUI8vTBPtvEqbTU2IbQ31IEGvv8T0BBl9b7/vdud0A==} - '@milaboratories/pl-model-middle-layer@1.18.10': - resolution: {integrity: sha512-DpCtmdIN1xqapE1QpYxeWutH4O11sjyd1FAWpGLY/CKf3MPFomQ4PS0ny9/wGEMnE+uYH19zEqGfRnt9byxtkQ==} - '@milaboratories/pl-model-middle-layer@1.18.5': resolution: {integrity: sha512-eIYE77rBva0k2KWDUmKJBHnGcyi/Tnc5rhlwZVb8q3hS3L4Upf4Pk7/lBL4ZLxYVMZ3/Da0Wp3EKYspUagi9Zg==} '@milaboratories/pl-model-middle-layer@1.19.4': resolution: {integrity: sha512-2SzgfHmTpSewPAv3WqbS6XHpObh69Mull3b1ec2bhn11ij6zSEDcBrMz0fFQ1XViAVQcafC4CjNTDZ/6s92kgQ==} + '@milaboratories/pl-model-middle-layer@1.20.0': + resolution: {integrity: sha512-RGEJD0avNEBejl1SjCqn7RWNiK15xCNWMXfNziiHUcG4n+QxYm1sk5AezfWsKE3j3y1HdzZnYaoGto9Z1RoV7A==} + '@milaboratories/pl-tree@1.11.0': resolution: {integrity: sha512-L6GYK0fff9ZsuZcuKW1wbu7uJl4I1S6zRhp4bpcnuCcLZuo3Qqf3zvmvax2bAl7e/rsRa6cMhz409PXS0c5Cww==} engines: {node: '>=22.19.0'} - '@milaboratories/ptabler-expression-js@1.2.20': - resolution: {integrity: sha512-Q+9LlIC4AxXdcMMngiQ0YtGthQhVpRhzxWgU4x2az/3CzVARUI4bCitJUXIGrs2a8WfddapmgQ5MT2qctveSqg==} - '@milaboratories/ptabler-expression-js@1.2.25': resolution: {integrity: sha512-dFx+gNoDssA7dQZTr0y93TWuNwlyNY9tzvUaeH0F7BYu/03ZzyLZ4N2iM7ai44bisqAe9o0ppmNM9LtoeTn5Gg==} @@ -1191,9 +1178,6 @@ packages: '@milaboratories/uikit@2.11.7': resolution: {integrity: sha512-qCgxfOHUhp3E1EfuoKtY314ZvdmFa+xHVQzMVC0cFj5YLJFPlgRc3ZiiEHQGLnrUsoICZxqtICia99fcHoyJrw==} - '@milaboratories/uikit@2.13.5': - resolution: {integrity: sha512-4YzUxHqCSjZjZCeWlIvQQFmmEiEcm2ES7rBqdp6paW2fncsL/kKLhhVwwx67QwKxUhmPFRU+fh8onc5aLV1JPw==} - '@milaboratories/uikit@2.14.10': resolution: {integrity: sha512-Nssg54Pk5EViOY04qscmU9MlS4fk0B0paUcivuKQidmgHKXvJWsmNIp0XIbRMb8Uo8Kb0tGi9mqu3v7yghbVnA==} @@ -1283,56 +1267,48 @@ packages: engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] - libc: [glibc] '@oxfmt/binding-linux-arm64-musl@0.35.0': resolution: {integrity: sha512-5Okqi+uhYFxwKz8hcnUftNNwdm8BCkf6GSCbcz9xJxYMm87k1E4p7PEmAAbhLTk7cjSdDre6TDL0pDzNX+Y22Q==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] - libc: [musl] '@oxfmt/binding-linux-ppc64-gnu@0.35.0': resolution: {integrity: sha512-9k66pbZQXM/lBJWys3Xbc5yhl4JexyfqkEf/tvtq8976VIJnLAAL3M127xHA3ifYSqxdVHfVGTg84eiBHCGcNw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ppc64] os: [linux] - libc: [glibc] '@oxfmt/binding-linux-riscv64-gnu@0.35.0': resolution: {integrity: sha512-aUcY9ofKPtjO52idT6t0SAQvEF6ctjzUQa1lLp7GDsRpSBvuTrBQGeq0rYKz3gN8dMIQ7mtMdGD9tT4LhR8jAQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [riscv64] os: [linux] - libc: [glibc] '@oxfmt/binding-linux-riscv64-musl@0.35.0': resolution: {integrity: sha512-C6yhY5Hvc2sGM+mCPek9ZLe5xRUOC/BvhAt2qIWFAeXMn4il04EYIjl3DsWiJr0xDMTJhvMOmD55xTRPlNp39w==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [riscv64] os: [linux] - libc: [musl] '@oxfmt/binding-linux-s390x-gnu@0.35.0': resolution: {integrity: sha512-RG2hlvOMK4OMZpO3mt8MpxLQ0AAezlFqhn5mI/g5YrVbPFyoCv9a34AAvbSJS501ocOxlFIRcKEuw5hFvddf9g==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [s390x] os: [linux] - libc: [glibc] '@oxfmt/binding-linux-x64-gnu@0.35.0': resolution: {integrity: sha512-wzmh90Pwvqj9xOKHJjkQYBpydRkaXG77ZvDz+iFDRRQpnqIEqGm5gmim2s6vnZIkDGsvKCuTdtxm0GFmBjM1+w==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] - libc: [glibc] '@oxfmt/binding-linux-x64-musl@0.35.0': resolution: {integrity: sha512-+HCqYCJPCUy5I+b2cf+gUVaApfgtoQT3HdnSg/l7NIcLHOhKstlYaGyrFZLmUpQt4WkFbpGKZZayG6zjRU0KFA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] - libc: [musl] '@oxfmt/binding-openharmony-arm64@0.35.0': resolution: {integrity: sha512-kFYmWfR9YL78XyO5ws+1dsxNvZoD973qfVMNFOS4e9bcHXGF7DvGC2tY5UDFwyMCeB33t3sDIuGONKggnVNSJA==} @@ -1405,56 +1381,48 @@ packages: engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] - libc: [glibc] '@oxlint/binding-linux-arm64-musl@1.50.0': resolution: {integrity: sha512-++B3k/HEPFVlj89cOz8kWfQccMZB/aWL9AhsW7jPIkG++63Mpwb2cE9XOEsd0PATbIan78k2Gky+09uWM1d/gQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] - libc: [musl] '@oxlint/binding-linux-ppc64-gnu@1.50.0': resolution: {integrity: sha512-Z9b/KpFMkx66w3gVBqjIC1AJBTZAGoI9+U+K5L4QM0CB/G0JSNC1es9b3Y0Vcrlvtdn8A+IQTkYjd/Q0uCSaZw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ppc64] os: [linux] - libc: [glibc] '@oxlint/binding-linux-riscv64-gnu@1.50.0': resolution: {integrity: sha512-jvmuIw8wRSohsQlFNIST5uUwkEtEJmOQYr33bf/K2FrFPXHhM4KqGekI3ShYJemFS/gARVacQFgBzzJKCAyJjg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [riscv64] os: [linux] - libc: [glibc] '@oxlint/binding-linux-riscv64-musl@1.50.0': resolution: {integrity: sha512-x+UrN47oYNh90nmAAyql8eQaaRpHbDPu5guasDg10+OpszUQ3/1+1J6zFMmV4xfIEgTcUXG/oI5fxJhF4eWCNA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [riscv64] os: [linux] - libc: [musl] '@oxlint/binding-linux-s390x-gnu@1.50.0': resolution: {integrity: sha512-i/JLi2ljLUIVfekMj4ISmdt+Hn11wzYUdRRrkVUYsCWw7zAy5xV7X9iA+KMyM156LTFympa7s3oKBjuCLoTAUQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [s390x] os: [linux] - libc: [glibc] '@oxlint/binding-linux-x64-gnu@1.50.0': resolution: {integrity: sha512-/C7brhn6c6UUPccgSPCcpLQXcp+xKIW/3sji/5VZ8/OItL3tQ2U7KalHz887UxxSQeEOmd1kY6lrpuwFnmNqOA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] - libc: [glibc] '@oxlint/binding-linux-x64-musl@1.50.0': resolution: {integrity: sha512-oDR1f+bGOYU8LfgtEW8XtotWGB63ghtcxk5Jm6IDTCk++rTA/IRMsjOid2iMd+1bW+nP9Mdsmcdc7VbPD3+iyQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] - libc: [musl] '@oxlint/binding-openharmony-arm64@1.50.0': resolution: {integrity: sha512-4CmRGPp5UpvXyu4jjP9Tey/SrXDQLRvZXm4pb4vdZBxAzbFZkCyh0KyRy4txld/kZKTJlW4TO8N1JKrNEk+mWw==} @@ -1494,25 +1462,21 @@ packages: resolution: {integrity: sha512-75tf1HvwdZ3ebk83yMbSB+moAEWK98mYqpXiaFAi6Zshie7r+Cx5PLXZFUEqkscenoZ+fcNXakHxfn94V6nf1g==} cpu: [arm64] os: [linux] - libc: [glibc] '@oxlint/linux-arm64-musl@1.43.0': resolution: {integrity: sha512-BHV4fb36T2p/7bpA9fiJ5ayt7oJbiYX10nklW5arYp4l9/9yG/FQC5J4G1evzbJ/YbipF9UH0vYBAm5xbqGrvw==} cpu: [arm64] os: [linux] - libc: [musl] '@oxlint/linux-x64-gnu@1.43.0': resolution: {integrity: sha512-1l3nvnzWWse1YHibzZ4HQXdF/ibfbKZhp9IguElni3bBqEyPEyurzZ0ikWynDxKGXqZa+UNXTFuU1NRVX1RJ3g==} cpu: [x64] os: [linux] - libc: [glibc] '@oxlint/linux-x64-musl@1.43.0': resolution: {integrity: sha512-+jNYgLGRFTJxJuaSOZJBwlYo5M0TWRw0+3y5MHOL4ArrIdHyCthg6r4RbVWrsR1qUfUE1VSSHQ2bfbC99RXqMg==} cpu: [x64] os: [linux] - libc: [musl] '@oxlint/win32-arm64@1.43.0': resolution: {integrity: sha512-dvs1C/HCjCyGTURMagiHprsOvVTT3omDiSzi5Qw0D4QFJ1pEaNlfBhVnOUYgUfS6O7Mcmj4+G+sidRsQcWQ/kA==} @@ -1625,9 +1589,6 @@ packages: '@platforma-open/milaboratories.software-ptabler.schema@1.14.8': resolution: {integrity: sha512-wSrsHJB8zgeglndBJBUXnIrJjaeUNKbMhj9uZAFg8OOec8rZXSdoPN6vJcJr6Qf9m4dwsLCM7pplnyO6VzCfxg==} - '@platforma-open/milaboratories.software-ptabler.schema@1.15.4': - resolution: {integrity: sha512-3sIWl5ub5eZLdmACRTYhSkZcVziGE1UqXQRPPUkwY+T+g3y071RNGbjomeeAOL1WEWMKHetD37PG5o9hIIAc2g==} - '@platforma-open/milaboratories.software-ptabler.schema@1.15.9': resolution: {integrity: sha512-DtCxrXCaDzjRzEPhlbnJnLfTQatHnGau72D/b8nUtxIgGTNZXG9S3WfRS+VgtaITETbh1x78k4/Qi1o5hUPQHQ==} @@ -1665,6 +1626,10 @@ packages: resolution: {integrity: sha512-7msHgkgr3uFTitgokcOFAqdO9mtAUr9rHnmjk26WzIzO1HY/uyrs2AHWpdc/skchJHr1U7HHzNt7oQ3RdzZWpQ==} hasBin: true + '@platforma-sdk/block-tools@2.8.1': + resolution: {integrity: sha512-5PA8iAdndsxlbk4YMAJDnueFBNracOHPDV8a6Zw10mkxP6+YFvsnlA4+kJoh7ULTgI6qPpLmCYwvPdA6KFwd7g==} + hasBin: true + '@platforma-sdk/blocks-deps-updater@2.2.0': resolution: {integrity: sha512-p9lBxhFXM9WoRsrJO7dfkiXSK+1m63yIn1sKhBO71eMbhrLMyVYHEOeNf3w5OCdbRF5QsNhXzWuiTmFK3zHFsA==} hasBin: true @@ -1687,9 +1652,6 @@ packages: '@platforma-sdk/model@1.65.4': resolution: {integrity: sha512-OVZBOeN9LCQt1AuOd6a4kBrXqTRD+tUukDQxqcsqHdG1lG2y9kJ2RxqUWXVbzIEv2qD0m8owg99KNWEVG35nAQ==} - '@platforma-sdk/model@1.73.3': - resolution: {integrity: sha512-1XzFjPmNaYujDEIPHH5j8PF+35ePyITFPaXxjQ4fRffFx/nXYZMfGfE5t9KaItsSJR8HWlEF1sZQYUDF3LDY8w==} - '@platforma-sdk/model@1.77.0': resolution: {integrity: sha512-XPpYMwRtsIXH91K2IBvzE8RzGJ/CXICQgDUBix6/ZjK+NNjBlGAqAiu6PiJZNTDF8xwd2TWQVSzorz8MxuJlnQ==} @@ -1708,9 +1670,6 @@ packages: '@platforma-sdk/ui-vue@1.63.8': resolution: {integrity: sha512-Nv/QhhTlGE8vfw3es+FNiHxrvRBn7M4xP5JPta0qIuDgCV7s0y/yK4hJQkkYuSVmigOGLQuPJGqgAV+wJVQK+g==} - '@platforma-sdk/ui-vue@1.73.3': - resolution: {integrity: sha512-QgLwTB8kKWw5ZbobNzhzBiIhj1wwJwA1+hxcxznuwNM6Tf6zVssDW365sFfbJjfhjFFRRgn//UUp8ZtlT/M/bQ==} - '@platforma-sdk/ui-vue@1.77.0': resolution: {integrity: sha512-ih+oV/haDjLO9Qk8B+/JpPkKbhXyWDVsk58wHKUqR1l80mGslr9wwOUf+h99uhWTMa6jV1cmxHpqToV4obmmjw==} @@ -1837,84 +1796,72 @@ packages: engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] - libc: [glibc] '@rolldown/binding-linux-arm64-gnu@1.0.0-rc.16': resolution: {integrity: sha512-+tHktCHWV8BDQSjemUqm/Jl/TPk3QObCTIjmdDy/nlupcujZghmKK2962LYrqFpWu+ai01AN/REOH3NEpqvYQg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] - libc: [glibc] '@rolldown/binding-linux-arm64-musl@1.0.0-rc.15': resolution: {integrity: sha512-ZvRYMGrAklV9PEkgt4LQM6MjQX2P58HPAuecwYObY2DhS2t35R0I810bKi0wmaYORt6m/2Sm+Z+nFgb0WhXNcQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] - libc: [musl] '@rolldown/binding-linux-arm64-musl@1.0.0-rc.16': resolution: {integrity: sha512-3fPzdREH806oRLxpTWW1Gt4tQHs0TitZFOECB2xzCFLPKnSOy90gwA7P29cksYilFO6XVRY1kzga0cL2nRjKPg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] - libc: [musl] '@rolldown/binding-linux-ppc64-gnu@1.0.0-rc.15': resolution: {integrity: sha512-VDpgGBzgfg5hLg+uBpCLoFG5kVvEyafmfxGUV0UHLcL5irxAK7PKNeC2MwClgk6ZAiNhmo9FLhRYgvMmedLtnQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ppc64] os: [linux] - libc: [glibc] '@rolldown/binding-linux-ppc64-gnu@1.0.0-rc.16': resolution: {integrity: sha512-EKwI1tSrLs7YVw+JPJT/G2dJQ1jl9qlTTTEG0V2Ok/RdOenRfBw2PQdLPyjhIu58ocdBfP7vIRN/pvMsPxs/AQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ppc64] os: [linux] - libc: [glibc] '@rolldown/binding-linux-s390x-gnu@1.0.0-rc.15': resolution: {integrity: sha512-y1uXY3qQWCzcPgRJATPSOUP4tCemh4uBdY7e3EZbVwCJTY3gLJWnQABgeUetvED+bt1FQ01OeZwvhLS2bpNrAQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [s390x] os: [linux] - libc: [glibc] '@rolldown/binding-linux-s390x-gnu@1.0.0-rc.16': resolution: {integrity: sha512-Uknladnb3Sxqu6SEcqBldQyJUpk8NleooZEc0MbRBJ4inEhRYWZX0NJu12vNf2mqAq7gsofAxHrGghiUYjhaLQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [s390x] os: [linux] - libc: [glibc] '@rolldown/binding-linux-x64-gnu@1.0.0-rc.15': resolution: {integrity: sha512-023bTPBod7J3Y/4fzAN6QtpkSABR0rigtrwaP+qSEabUh5zf6ELr9Nc7GujaROuPY3uwdSIXWrvhn1KxOvurWA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] - libc: [glibc] '@rolldown/binding-linux-x64-gnu@1.0.0-rc.16': resolution: {integrity: sha512-FIb8+uG49sZBtLTn+zt1AJ20TqVcqWeSIyoVt0or7uAWesgKaHbiBh6OpA/k9v0LTt+PTrb1Lao133kP4uVxkg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] - libc: [glibc] '@rolldown/binding-linux-x64-musl@1.0.0-rc.15': resolution: {integrity: sha512-witB2O0/hU4CgfOOKUoeFgQ4GktPi1eEbAhaLAIpgD6+ZnhcPkUtPsoKKHRzmOoWPZue46IThdSgdo4XneOLYw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] - libc: [musl] '@rolldown/binding-linux-x64-musl@1.0.0-rc.16': resolution: {integrity: sha512-RuERhF9/EgWxZEXYWCOaViUWHIboceK4/ivdtQ3R0T44NjLkIIlGIAVAuCddFxsZ7vnRHtNQUrt2vR2n2slB2w==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] - libc: [musl] '@rolldown/binding-openharmony-arm64@1.0.0-rc.15': resolution: {integrity: sha512-UCL68NJ0Ud5zRipXZE9dF5PmirzJE4E4BCIOOssEnM7wLDsxjc6Qb0sGDxTNRTP53I6MZpygyCpY8Aa8sPfKPg==} @@ -2014,67 +1961,56 @@ packages: resolution: {integrity: sha512-k9oD15soC/Ln6d2Wv/JOFPzZXIAIFLp6B+i14KhxAfnq76ajt0EhYc5YPeX6W1xJkAdItcVT+JhKl1QZh44/qw==} cpu: [arm] os: [linux] - libc: [glibc] '@rollup/rollup-linux-arm-musleabihf@4.53.3': resolution: {integrity: sha512-vTNlKq+N6CK/8UktsrFuc+/7NlEYVxgaEgRXVUVK258Z5ymho29skzW1sutgYjqNnquGwVUObAaxae8rZ6YMhg==} cpu: [arm] os: [linux] - libc: [musl] '@rollup/rollup-linux-arm64-gnu@4.53.3': resolution: {integrity: sha512-RGrFLWgMhSxRs/EWJMIFM1O5Mzuz3Xy3/mnxJp/5cVhZ2XoCAxJnmNsEyeMJtpK+wu0FJFWz+QF4mjCA7AUQ3w==} cpu: [arm64] os: [linux] - libc: [glibc] '@rollup/rollup-linux-arm64-musl@4.53.3': resolution: {integrity: sha512-kASyvfBEWYPEwe0Qv4nfu6pNkITLTb32p4yTgzFCocHnJLAHs+9LjUu9ONIhvfT/5lv4YS5muBHyuV84epBo/A==} cpu: [arm64] os: [linux] - libc: [musl] '@rollup/rollup-linux-loong64-gnu@4.53.3': resolution: {integrity: sha512-JiuKcp2teLJwQ7vkJ95EwESWkNRFJD7TQgYmCnrPtlu50b4XvT5MOmurWNrCj3IFdyjBQ5p9vnrX4JM6I8OE7g==} cpu: [loong64] os: [linux] - libc: [glibc] '@rollup/rollup-linux-ppc64-gnu@4.53.3': resolution: {integrity: sha512-EoGSa8nd6d3T7zLuqdojxC20oBfNT8nexBbB/rkxgKj5T5vhpAQKKnD+h3UkoMuTyXkP5jTjK/ccNRmQrPNDuw==} cpu: [ppc64] os: [linux] - libc: [glibc] '@rollup/rollup-linux-riscv64-gnu@4.53.3': resolution: {integrity: sha512-4s+Wped2IHXHPnAEbIB0YWBv7SDohqxobiiPA1FIWZpX+w9o2i4LezzH/NkFUl8LRci/8udci6cLq+jJQlh+0g==} cpu: [riscv64] os: [linux] - libc: [glibc] '@rollup/rollup-linux-riscv64-musl@4.53.3': resolution: {integrity: sha512-68k2g7+0vs2u9CxDt5ktXTngsxOQkSEV/xBbwlqYcUrAVh6P9EgMZvFsnHy4SEiUl46Xf0IObWVbMvPrr2gw8A==} cpu: [riscv64] os: [linux] - libc: [musl] '@rollup/rollup-linux-s390x-gnu@4.53.3': resolution: {integrity: sha512-VYsFMpULAz87ZW6BVYw3I6sWesGpsP9OPcyKe8ofdg9LHxSbRMd7zrVrr5xi/3kMZtpWL/wC+UIJWJYVX5uTKg==} cpu: [s390x] os: [linux] - libc: [glibc] '@rollup/rollup-linux-x64-gnu@4.53.3': resolution: {integrity: sha512-3EhFi1FU6YL8HTUJZ51imGJWEX//ajQPfqWLI3BQq4TlvHy4X0MOr5q3D2Zof/ka0d5FNdPwZXm3Yyib/UEd+w==} cpu: [x64] os: [linux] - libc: [glibc] '@rollup/rollup-linux-x64-musl@4.53.3': resolution: {integrity: sha512-eoROhjcc6HbZCJr+tvVT8X4fW3/5g/WkGvvmwz/88sDtSJzO7r/blvoBDgISDiCjDRZmHpwud7h+6Q9JxFwq1Q==} cpu: [x64] os: [linux] - libc: [musl] '@rollup/rollup-openharmony-arm64@4.53.3': resolution: {integrity: sha512-OueLAWgrNSPGAdUdIjSWXw+u/02BRTcnfw9PN41D2vq/JSEPnJnVuBgw18VkN8wcd4fjUs+jFHVM4t9+kBSNLw==} @@ -5547,28 +5483,24 @@ packages: engines: {node: '>= 12.0.0'} cpu: [arm64] os: [linux] - libc: [glibc] lightningcss-linux-arm64-musl@1.32.0: resolution: {integrity: sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==} engines: {node: '>= 12.0.0'} cpu: [arm64] os: [linux] - libc: [musl] lightningcss-linux-x64-gnu@1.32.0: resolution: {integrity: sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==} engines: {node: '>= 12.0.0'} cpu: [x64] os: [linux] - libc: [glibc] lightningcss-linux-x64-musl@1.32.0: resolution: {integrity: sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==} engines: {node: '>= 12.0.0'} cpu: [x64] os: [linux] - libc: [musl] lightningcss-win32-arm64-msvc@1.32.0: resolution: {integrity: sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==} @@ -7536,7 +7468,7 @@ snapshots: transitivePeerDependencies: - supports-color - '@bytecodealliance/preview2-shim@0.17.9': {} + '@bytecodealliance/preview2-shim@0.17.8': {} '@changesets/apply-release-plan@7.0.14': dependencies: @@ -8052,38 +7984,14 @@ snapshots: '@types/node': 24.5.2 utility-types: 3.11.0 - '@milaboratories/graph-maker@1.4.2(@milaboratories/pl-model-common@1.39.0)(@platforma-sdk/model@1.77.0)(@platforma-sdk/ui-vue@1.73.3(@bytecodealliance/preview2-shim@0.17.9)(typescript@5.6.3))(d3-dispatch@3.0.1)(d3-path@3.1.0)(d3-scale-chromatic@3.1.0)(typescript@5.6.3)': + '@milaboratories/graph-maker@1.4.2(@milaboratories/pl-model-common@1.42.0)(@platforma-sdk/model@1.77.0)(@platforma-sdk/ui-vue@1.77.0(@bytecodealliance/preview2-shim@0.17.8)(typescript@5.6.3))(d3-dispatch@3.0.1)(d3-path@3.1.0)(d3-scale-chromatic@3.1.0)(typescript@5.6.3)': dependencies: '@ag-grid-community/core': 32.3.9 '@milaboratories/helpers': 1.14.2 '@milaboratories/miplots4': 1.2.0(d3-dispatch@3.0.1)(d3-path@3.1.0)(d3-scale-chromatic@3.1.0) - '@milaboratories/pf-plots': 1.4.1(@milaboratories/pl-model-common@1.39.0)(@platforma-sdk/model@1.77.0) + '@milaboratories/pf-plots': 1.4.1(@milaboratories/pl-model-common@1.42.0)(@platforma-sdk/model@1.77.0) '@platforma-sdk/model': 1.77.0 - '@platforma-sdk/ui-vue': 1.73.3(@bytecodealliance/preview2-shim@0.17.9)(typescript@5.6.3) - '@types/d3-hierarchy': 3.1.7 - '@types/d3-scale': 4.0.9 - '@vueuse/core': 13.8.0(vue@3.5.25(typescript@5.6.3)) - ag-grid-vue3: 34.1.2(vue@3.5.25(typescript@5.6.3)) - canonicalize: 2.1.0 - d3-hierarchy: 3.1.2 - d3-scale: 4.0.2 - vue: 3.5.25(typescript@5.6.3) - transitivePeerDependencies: - - '@milaboratories/pl-model-common' - - d3-dispatch - - d3-path - - d3-scale-chromatic - - supports-color - - typescript - - '@milaboratories/graph-maker@1.4.2(@milaboratories/pl-model-common@1.39.0)(@platforma-sdk/model@1.77.0)(@platforma-sdk/ui-vue@1.77.0(@bytecodealliance/preview2-shim@0.17.9)(typescript@5.6.3))(d3-dispatch@3.0.1)(d3-path@3.1.0)(d3-scale-chromatic@3.1.0)(typescript@5.6.3)': - dependencies: - '@ag-grid-community/core': 32.3.9 - '@milaboratories/helpers': 1.14.2 - '@milaboratories/miplots4': 1.2.0(d3-dispatch@3.0.1)(d3-path@3.1.0)(d3-scale-chromatic@3.1.0) - '@milaboratories/pf-plots': 1.4.1(@milaboratories/pl-model-common@1.39.0)(@platforma-sdk/model@1.77.0) - '@platforma-sdk/model': 1.77.0 - '@platforma-sdk/ui-vue': 1.77.0(@bytecodealliance/preview2-shim@0.17.9)(typescript@5.6.3) + '@platforma-sdk/ui-vue': 1.77.0(@bytecodealliance/preview2-shim@0.17.8)(typescript@5.6.3) '@types/d3-hierarchy': 3.1.7 '@types/d3-scale': 4.0.9 '@vueuse/core': 13.8.0(vue@3.5.25(typescript@5.6.3)) @@ -8142,11 +8050,11 @@ snapshots: - d3-scale-chromatic - supports-color - '@milaboratories/pf-driver@1.4.11(@bytecodealliance/preview2-shim@0.17.9)': + '@milaboratories/pf-driver@1.4.11(@bytecodealliance/preview2-shim@0.17.8)': dependencies: '@milaboratories/helpers': 1.14.2 '@milaboratories/pframes-rs-node': 1.1.35 - '@milaboratories/pframes-rs-wasm': 1.1.35(@bytecodealliance/preview2-shim@0.17.9)(@milaboratories/pl-model-common@1.42.0)(@milaboratories/pl-model-middle-layer@1.19.4) + '@milaboratories/pframes-rs-wasm': 1.1.35(@bytecodealliance/preview2-shim@0.17.8)(@milaboratories/pl-model-common@1.42.0)(@milaboratories/pl-model-middle-layer@1.19.4) '@milaboratories/pl-model-common': 1.42.0 '@milaboratories/pl-model-middle-layer': 1.19.4 '@milaboratories/ts-helpers': 1.8.2 @@ -8157,10 +8065,10 @@ snapshots: - encoding - supports-color - '@milaboratories/pf-plots@1.4.1(@milaboratories/pl-model-common@1.39.0)(@platforma-sdk/model@1.77.0)': + '@milaboratories/pf-plots@1.4.1(@milaboratories/pl-model-common@1.42.0)(@platforma-sdk/model@1.77.0)': dependencies: '@milaboratories/helpers': 1.14.2 - '@milaboratories/pl-model-common': 1.39.0 + '@milaboratories/pl-model-common': 1.42.0 '@platforma-sdk/model': 1.77.0 canonicalize: 2.1.0 lodash: 4.17.23 @@ -8175,26 +8083,16 @@ snapshots: transitivePeerDependencies: - '@bytecodealliance/preview2-shim' - '@milaboratories/pf-spec-driver@1.3.16(@bytecodealliance/preview2-shim@0.17.9)': + '@milaboratories/pf-spec-driver@1.3.16(@bytecodealliance/preview2-shim@0.17.8)': dependencies: '@milaboratories/helpers': 1.14.2 - '@milaboratories/pframes-rs-wasm': 1.1.35(@bytecodealliance/preview2-shim@0.17.9)(@milaboratories/pl-model-common@1.42.0)(@milaboratories/pl-model-middle-layer@1.19.4) + '@milaboratories/pframes-rs-wasm': 1.1.35(@bytecodealliance/preview2-shim@0.17.8)(@milaboratories/pl-model-common@1.42.0)(@milaboratories/pl-model-middle-layer@1.19.4) '@milaboratories/pl-model-common': 1.42.0 '@milaboratories/pl-model-middle-layer': 1.19.4 '@noble/hashes': 2.2.0 transitivePeerDependencies: - '@bytecodealliance/preview2-shim' - '@milaboratories/pf-spec-driver@1.3.9(@bytecodealliance/preview2-shim@0.17.9)': - dependencies: - '@milaboratories/helpers': 1.14.1 - '@milaboratories/pframes-rs-wasm': 1.1.31(@bytecodealliance/preview2-shim@0.17.9)(@milaboratories/pl-model-common@1.39.0)(@milaboratories/pl-model-middle-layer@1.18.10) - '@milaboratories/pl-model-common': 1.39.0 - '@milaboratories/pl-model-middle-layer': 1.18.10 - '@noble/hashes': 2.2.0 - transitivePeerDependencies: - - '@bytecodealliance/preview2-shim' - '@milaboratories/pframes-rs-node@1.1.35': dependencies: '@mapbox/node-pre-gyp': 2.0.3 @@ -8214,8 +8112,6 @@ snapshots: commander: 14.0.3 selfsigned: 5.5.0 - '@milaboratories/pframes-rs-wasi@1.1.31': {} - '@milaboratories/pframes-rs-wasip2@1.1.35': {} '@milaboratories/pframes-rs-wasm@1.1.18(@milaboratories/pl-model-common@1.31.1)(@milaboratories/pl-model-middle-layer@1.16.3)': @@ -8223,16 +8119,9 @@ snapshots: '@milaboratories/pl-model-common': 1.31.1 '@milaboratories/pl-model-middle-layer': 1.16.3 - '@milaboratories/pframes-rs-wasm@1.1.31(@bytecodealliance/preview2-shim@0.17.9)(@milaboratories/pl-model-common@1.39.0)(@milaboratories/pl-model-middle-layer@1.18.10)': - dependencies: - '@bytecodealliance/preview2-shim': 0.17.9 - '@milaboratories/pframes-rs-wasi': 1.1.31 - '@milaboratories/pl-model-common': 1.39.0 - '@milaboratories/pl-model-middle-layer': 1.18.10 - - '@milaboratories/pframes-rs-wasm@1.1.35(@bytecodealliance/preview2-shim@0.17.9)(@milaboratories/pl-model-common@1.42.0)(@milaboratories/pl-model-middle-layer@1.19.4)': + '@milaboratories/pframes-rs-wasm@1.1.35(@bytecodealliance/preview2-shim@0.17.8)(@milaboratories/pl-model-common@1.42.0)(@milaboratories/pl-model-middle-layer@1.19.4)': dependencies: - '@bytecodealliance/preview2-shim': 0.17.9 + '@bytecodealliance/preview2-shim': 0.17.8 '@milaboratories/pframes-rs-wasip2': 1.1.35 '@milaboratories/pl-model-common': 1.42.0 '@milaboratories/pl-model-middle-layer': 1.19.4 @@ -8255,6 +8144,24 @@ snapshots: utility-types: 3.11.0 yaml: 2.8.1 + '@milaboratories/pl-client@3.8.0': + dependencies: + '@grpc/grpc-js': 1.13.4 + '@milaboratories/pl-http': 1.2.4 + '@milaboratories/pl-model-common': 1.42.0 + '@milaboratories/ts-helpers': 1.8.2 + '@protobuf-ts/grpc-transport': 2.11.1(@grpc/grpc-js@1.13.4) + '@protobuf-ts/runtime': 2.11.1 + '@protobuf-ts/runtime-rpc': 2.11.1 + canonicalize: 2.1.0 + denque: 2.1.0 + long: 5.3.2 + lru-cache: 11.2.4 + openapi-fetch: 0.15.0 + undici: 7.16.0 + utility-types: 3.11.0 + yaml: 2.8.1 + '@milaboratories/pl-config@1.8.1': dependencies: '@milaboratories/ts-helpers': 1.8.2 @@ -8317,14 +8224,14 @@ snapshots: dependencies: undici: 7.16.0 - '@milaboratories/pl-middle-layer@1.60.3(@bytecodealliance/preview2-shim@0.17.9)': + '@milaboratories/pl-middle-layer@1.60.3(@bytecodealliance/preview2-shim@0.17.8)': dependencies: '@milaboratories/computable': 2.9.4 '@milaboratories/helpers': 1.14.2 - '@milaboratories/pf-driver': 1.4.11(@bytecodealliance/preview2-shim@0.17.9) - '@milaboratories/pf-spec-driver': 1.3.16(@bytecodealliance/preview2-shim@0.17.9) + '@milaboratories/pf-driver': 1.4.11(@bytecodealliance/preview2-shim@0.17.8) + '@milaboratories/pf-spec-driver': 1.3.16(@bytecodealliance/preview2-shim@0.17.8) '@milaboratories/pframes-rs-node': 1.1.35 - '@milaboratories/pframes-rs-wasm': 1.1.35(@bytecodealliance/preview2-shim@0.17.9)(@milaboratories/pl-model-common@1.42.0)(@milaboratories/pl-model-middle-layer@1.19.4) + '@milaboratories/pframes-rs-wasm': 1.1.35(@bytecodealliance/preview2-shim@0.17.8)(@milaboratories/pl-model-common@1.42.0)(@milaboratories/pl-model-middle-layer@1.19.4) '@milaboratories/pl-client': 3.5.0 '@milaboratories/pl-deployments': 2.17.18 '@milaboratories/pl-drivers': 1.14.7 @@ -8360,6 +8267,12 @@ snapshots: canonicalize: 2.1.0 zod: 3.25.76 + '@milaboratories/pl-model-backend@1.3.1': + dependencies: + '@milaboratories/pl-client': 3.8.0 + canonicalize: 2.1.0 + zod: 3.25.76 + '@milaboratories/pl-model-common@1.31.1': dependencies: '@milaboratories/helpers': 1.14.1 @@ -8381,13 +8294,6 @@ snapshots: canonicalize: 2.1.0 zod: 3.25.76 - '@milaboratories/pl-model-common@1.39.0': - dependencies: - '@milaboratories/helpers': 1.14.1 - '@milaboratories/pl-error-like': 1.12.10 - canonicalize: 2.1.0 - zod: 3.25.76 - '@milaboratories/pl-model-common@1.42.0': dependencies: '@milaboratories/helpers': 1.14.2 @@ -8411,23 +8317,23 @@ snapshots: utility-types: 3.11.0 zod: 3.25.76 - '@milaboratories/pl-model-middle-layer@1.18.10': + '@milaboratories/pl-model-middle-layer@1.18.5': dependencies: '@milaboratories/helpers': 1.14.1 - '@milaboratories/pl-model-common': 1.39.0 + '@milaboratories/pl-model-common': 1.36.0 es-toolkit: 1.42.0 utility-types: 3.11.0 zod: 3.25.76 - '@milaboratories/pl-model-middle-layer@1.18.5': + '@milaboratories/pl-model-middle-layer@1.19.4': dependencies: - '@milaboratories/helpers': 1.14.1 - '@milaboratories/pl-model-common': 1.36.0 + '@milaboratories/helpers': 1.14.2 + '@milaboratories/pl-model-common': 1.42.0 es-toolkit: 1.42.0 utility-types: 3.11.0 zod: 3.25.76 - '@milaboratories/pl-model-middle-layer@1.19.4': + '@milaboratories/pl-model-middle-layer@1.20.0': dependencies: '@milaboratories/helpers': 1.14.2 '@milaboratories/pl-model-common': 1.42.0 @@ -8445,10 +8351,6 @@ snapshots: utility-types: 3.11.0 zod: 3.25.76 - '@milaboratories/ptabler-expression-js@1.2.20': - dependencies: - '@platforma-open/milaboratories.software-ptabler.schema': 1.15.4 - '@milaboratories/ptabler-expression-js@1.2.25': dependencies: '@platforma-open/milaboratories.software-ptabler.schema': 1.15.9 @@ -8634,40 +8536,6 @@ snapshots: - typescript - universal-cookie - '@milaboratories/uikit@2.13.5(typescript@5.6.3)': - dependencies: - '@milaboratories/helpers': 1.14.1 - '@platforma-sdk/model': 1.73.3 - '@types/d3-array': 3.2.1 - '@types/d3-axis': 3.0.6 - '@types/d3-scale': 4.0.9 - '@types/d3-selection': 3.0.11 - '@types/sortablejs': 1.15.8 - '@vue/test-utils': 2.4.6 - '@vueuse/core': 13.8.0(vue@3.5.25(typescript@5.6.3)) - '@vueuse/integrations': 13.8.0(sortablejs@1.15.6)(vue@3.5.25(typescript@5.6.3)) - canonicalize: 2.1.0 - d3-array: 3.2.4 - d3-axis: 3.0.0 - d3-scale: 4.0.2 - d3-selection: 3.0.0 - resize-observer-polyfill: 1.5.1 - sortablejs: 1.15.6 - vue: 3.5.25(typescript@5.6.3) - transitivePeerDependencies: - - async-validator - - axios - - change-case - - drauu - - focus-trap - - fuse.js - - idb-keyval - - jwt-decode - - nprogress - - qrcode - - typescript - - universal-cookie - '@milaboratories/uikit@2.14.10(typescript@5.6.3)': dependencies: '@milaboratories/helpers': 1.14.2 @@ -9099,10 +8967,6 @@ snapshots: dependencies: '@milaboratories/pl-model-common': 1.31.2 - '@platforma-open/milaboratories.software-ptabler.schema@1.15.4': - dependencies: - '@milaboratories/pl-model-common': 1.39.0 - '@platforma-open/milaboratories.software-ptabler.schema@1.15.9': dependencies: '@milaboratories/pl-model-common': 1.42.0 @@ -9156,6 +9020,28 @@ snapshots: transitivePeerDependencies: - aws-crt + '@platforma-sdk/block-tools@2.8.1': + dependencies: + '@aws-sdk/client-s3': 3.859.0 + '@milaboratories/pl-http': 1.2.4 + '@milaboratories/pl-model-backend': 1.3.1 + '@milaboratories/pl-model-common': 1.42.0 + '@milaboratories/pl-model-middle-layer': 1.20.0 + '@milaboratories/resolve-helper': 1.1.3 + '@milaboratories/ts-helpers': 1.8.2 + '@milaboratories/ts-helpers-oclif': 1.1.41 + '@oclif/core': 4.2.6 + '@platforma-sdk/blocks-deps-updater': 2.2.0 + canonicalize: 2.1.0 + lru-cache: 11.2.4 + mime-types: 2.1.35 + tar: 7.4.3 + undici: 7.16.0 + yaml: 2.8.1 + zod: 3.25.76 + transitivePeerDependencies: + - aws-crt + '@platforma-sdk/blocks-deps-updater@2.2.0': dependencies: yaml: 2.8.1 @@ -9197,19 +9083,6 @@ snapshots: utility-types: 3.11.0 zod: 3.25.76 - '@platforma-sdk/model@1.73.3': - dependencies: - '@milaboratories/helpers': 1.14.1 - '@milaboratories/pl-error-like': 1.12.10 - '@milaboratories/pl-model-common': 1.39.0 - '@milaboratories/pl-model-middle-layer': 1.18.10 - '@milaboratories/ptabler-expression-js': 1.2.20 - canonicalize: 2.1.0 - es-toolkit: 1.42.0 - fast-json-patch: 3.1.1 - utility-types: 3.11.0 - zod: 3.25.76 - '@platforma-sdk/model@1.77.0': dependencies: '@milaboratories/helpers': 1.14.2 @@ -9248,11 +9121,11 @@ snapshots: '@oclif/core': 4.2.6 winston: 3.17.0 - '@platforma-sdk/test@1.77.1(@bytecodealliance/preview2-shim@0.17.9)(@types/node@25.3.2)(vite@8.0.8(@types/node@25.3.2)(yaml@2.8.1))': + '@platforma-sdk/test@1.77.1(@bytecodealliance/preview2-shim@0.17.8)(@types/node@25.3.2)(vite@8.0.8(@types/node@25.3.2)(yaml@2.8.1))': dependencies: '@milaboratories/computable': 2.9.4 '@milaboratories/pl-client': 3.5.0 - '@milaboratories/pl-middle-layer': 1.60.3(@bytecodealliance/preview2-shim@0.17.9) + '@milaboratories/pl-middle-layer': 1.60.3(@bytecodealliance/preview2-shim@0.17.8) '@milaboratories/pl-tree': 1.11.0 '@platforma-sdk/model': 1.77.0 '@vitest/coverage-istanbul': 4.1.4(vitest@4.1.4) @@ -9310,45 +9183,9 @@ snapshots: - typescript - universal-cookie - '@platforma-sdk/ui-vue@1.73.3(@bytecodealliance/preview2-shim@0.17.9)(typescript@5.6.3)': - dependencies: - '@milaboratories/pf-spec-driver': 1.3.9(@bytecodealliance/preview2-shim@0.17.9) - '@milaboratories/pl-model-common': 1.39.0 - '@milaboratories/uikit': 2.13.5(typescript@5.6.3) - '@platforma-sdk/model': 1.73.3 - '@types/d3-format': 3.0.4 - '@types/node': 24.5.2 - '@types/semver': 7.7.0 - '@vueuse/core': 13.8.0(vue@3.5.25(typescript@5.6.3)) - '@zip.js/zip.js': 2.8.11 - ag-grid-enterprise: 34.1.2 - ag-grid-vue3: 34.1.2(vue@3.5.25(typescript@5.6.3)) - canonicalize: 2.1.0 - d3-format: 3.1.0 - es-toolkit: 1.42.0 - fast-json-patch: 3.1.1 - immer: 11.1.4 - lru-cache: 11.2.4 - vue: 3.5.25(typescript@5.6.3) - zod: 3.25.76 - transitivePeerDependencies: - - '@bytecodealliance/preview2-shim' - - async-validator - - axios - - change-case - - drauu - - focus-trap - - fuse.js - - idb-keyval - - jwt-decode - - nprogress - - qrcode - - typescript - - universal-cookie - - '@platforma-sdk/ui-vue@1.77.0(@bytecodealliance/preview2-shim@0.17.9)(typescript@5.6.3)': + '@platforma-sdk/ui-vue@1.77.0(@bytecodealliance/preview2-shim@0.17.8)(typescript@5.6.3)': dependencies: - '@milaboratories/pf-spec-driver': 1.3.16(@bytecodealliance/preview2-shim@0.17.9) + '@milaboratories/pf-spec-driver': 1.3.16(@bytecodealliance/preview2-shim@0.17.8) '@milaboratories/pl-model-common': 1.42.0 '@milaboratories/uikit': 2.14.10(typescript@5.6.3) '@platforma-sdk/model': 1.77.0 @@ -12769,14 +12606,14 @@ snapshots: '@vue/compiler-sfc@3.5.25': dependencies: - '@babel/parser': 7.28.5 + '@babel/parser': 7.29.0 '@vue/compiler-core': 3.5.25 '@vue/compiler-dom': 3.5.25 '@vue/compiler-ssr': 3.5.25 '@vue/shared': 3.5.25 estree-walker: 2.0.2 magic-string: 0.30.21 - postcss: 8.5.6 + postcss: 8.5.10 source-map-js: 1.2.1 '@vue/compiler-ssr@3.5.24': diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index b41df96..59b43ec 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -11,7 +11,7 @@ catalog: "@milaboratories/ts-builder": 1.4.0 "@milaboratories/ts-configs": 1.2.3 "@platforma-sdk/workflow-tengo": 5.24.0 - "@platforma-sdk/block-tools": 2.7.25 + "@platforma-sdk/block-tools": 2.8.1 "@platforma-sdk/model": 1.77.0 "@platforma-sdk/ui-vue": 1.77.0 "@platforma-sdk/test": 1.77.1 diff --git a/software/tests/integration/test_determinism.py b/software/tests/integration/test_determinism.py new file mode 100644 index 0000000..2f20601 --- /dev/null +++ b/software/tests/integration/test_determinism.py @@ -0,0 +1,197 @@ +"""Cross-process byte-stability regression test. + +`test_cli.py` runs byte-comparison checks via in-process `main()` calls. Those +share the Python process's randomized ahash seed across runs, so a future +change that re-introduces hash-based ordering (Polars `group_by` without +`maintain_order=True`, `set()` iteration into output bytes, etc.) could pass +the in-process checks while still breaking production — two block instances +run in separate processes with independent hash seeds. + +This file spawns the CLI via `subprocess.run` for each run. Fresh process, +fresh hash seed. Closes the subprocess-isolation gap titeseq-analysis PR #13's +reviewer flagged on its determinism test. + +Every output the tool writes — `properties.tsv`, `aa_fraction.tsv`, +`stats.json` — gets its own byte-compare. A determinism fix on one file does +not protect the others; siblings inherit the same upstream non-determinism +sources. +""" + +from __future__ import annotations + +import hashlib +import json +import subprocess +import sys +from pathlib import Path + +import pytest + +# main.py is loaded via pythonpath = ["src"] (see software/pyproject.toml). +# That import-path setup applies to in-process imports, not subprocess +# invocations — for those we point Python at the script directly. +_MAIN_PY = Path(__file__).resolve().parents[2] / "src" / "main.py" + +_OUTPUT_FILE_NAMES = ("properties.tsv", "aa_fraction.tsv", "stats.json") + + +def _sha256(path: Path) -> str: + return hashlib.sha256(path.read_bytes()).hexdigest() + + +def _write_tsv(path: Path, rows: list[dict[str, str]], columns: list[str]) -> None: + lines = ["\t".join(columns)] + for row in rows: + lines.append("\t".join(row.get(c, "") for c in columns)) + path.write_text("\n".join(lines) + "\n") + + +def _run_cli_subprocess( + *, + input_tsv: Path, + plan_json: Path, + out_tsv: Path, + aa_tsv: Path, + stats_json: Path, +) -> None: + """Invoke main.py in a fresh subprocess. Independent hash seed per call.""" + result = subprocess.run( + [ + sys.executable, + str(_MAIN_PY), + "--input", + str(input_tsv), + "--plan", + str(plan_json), + "--output", + str(out_tsv), + "--aa-fraction", + str(aa_tsv), + "--stats", + str(stats_json), + ], + capture_output=True, + text=True, + check=False, + ) + assert result.returncode == 0, f"main.py failed (rc={result.returncode}); stderr=\n{result.stderr}" + + +def _run_paths(tmp_path: Path, suffix: str) -> dict[str, Path]: + return { + "out_tsv": tmp_path / f"properties{suffix}.tsv", + "aa_tsv": tmp_path / f"aa_fraction{suffix}.tsv", + "stats_json": tmp_path / f"stats{suffix}.json", + } + + +def _assert_all_three_byte_identical(a: dict[str, Path], b: dict[str, Path]) -> None: + """Sibling-output rule: every output file the CLI writes gets its own check.""" + hashes_a = { + "properties.tsv": _sha256(a["out_tsv"]), + "aa_fraction.tsv": _sha256(a["aa_tsv"]), + "stats.json": _sha256(a["stats_json"]), + } + hashes_b = { + "properties.tsv": _sha256(b["out_tsv"]), + "aa_fraction.tsv": _sha256(b["aa_tsv"]), + "stats.json": _sha256(b["stats_json"]), + } + for name in _OUTPUT_FILE_NAMES: + assert hashes_a[name] == hashes_b[name], ( + f"{name} diverged across subprocess runs: sha256(A)={hashes_a[name]} sha256(B)={hashes_b[name]}" + ) + + +# Peptide mode exercises the scalar-properties + AA-fraction + stats paths. +_PEPTIDE_ROWS: list[dict[str, str]] = [ + {"entity_key": "p1", "sequence": "ACDEFGHIKL"}, + {"entity_key": "p2", "sequence": "MNPQRSTVWY"}, + {"entity_key": "p3", "sequence": "GFTFSSYAMS"}, + {"entity_key": "p4", "sequence": "KKKKHHHHHH"}, + {"entity_key": "p5", "sequence": "DDDDEEEEEE"}, +] +_PEPTIDE_COLUMNS = ["entity_key", "sequence"] +_PEPTIDE_PLAN: dict[str, object] = {"mode": "peptide"} + +# Antibody mode exercises per-chain CDR3 + full-chain + Fv computation paths +# that peptide mode does not touch. Same sibling-rule coverage. +_ANTIBODY_COLUMNS = ( + ["entity_key"] + + [f"A_{f}" for f in ("FR1", "CDR1", "FR2", "CDR2", "FR3", "CDR3", "FR4")] + + [f"B_{f}" for f in ("FR1", "CDR1", "FR2", "CDR2", "FR3", "CDR3", "FR4")] +) +_ANTIBODY_ROWS: list[dict[str, str]] = [ + { + "entity_key": "c1", + "A_FR1": "EVQLVES", + "A_CDR1": "GFTFSSY", + "A_FR2": "AMSWVRQ", + "A_CDR2": "ISGSGGS", + "A_FR3": "TYYAESVKGRFTI", + "A_CDR3": "CARDYW", + "A_FR4": "WGQGTLV", + "B_FR1": "DIQMTQS", + "B_CDR1": "QSISSY", + "B_FR2": "LNWYQQK", + "B_CDR2": "AASSLQS", + "B_FR3": "GVPSRFSGSG", + "B_CDR3": "CQQYNS", + "B_FR4": "FGQGTKV", + }, + { + "entity_key": "c2", + "A_FR1": "EVQLVES", + "A_CDR1": "GFTFSSY", + "A_FR2": "AMSWVRQ", + "A_CDR2": "ISGSGGS", + "A_FR3": "TYYAESVKGRFTI", + "A_CDR3": "CARGFW", + "A_FR4": "WGQGTLV", + "B_FR1": "DIQMTQS", + "B_CDR1": "QSISSY", + "B_FR2": "LNWYQQK", + "B_CDR2": "AASSLQS", + "B_FR3": "GVPSRFSGSG", + "B_CDR3": "CQHFSS", + "B_FR4": "FGQGTKV", + }, +] +_ANTIBODY_PLAN: dict[str, object] = { + "mode": "antibody_tcr_legacy_bulk", + "receptor": "IG", + "chains": ["A", "B"], + "fullChains": ["A", "B"], + "hasFv": True, +} + + +# Two subprocess runs on the same input must produce byte-identical output +# files. Catches future hash-order regressions (Polars group_by, set() iter, +# etc.) that pass in-process tests but break across separate worker processes. +@pytest.mark.parametrize( + "rows, columns, plan", + [ + (_PEPTIDE_ROWS, _PEPTIDE_COLUMNS, _PEPTIDE_PLAN), + (_ANTIBODY_ROWS, _ANTIBODY_COLUMNS, _ANTIBODY_PLAN), + ], + ids=["peptide", "antibody"], +) +def test_outputs_byte_stable_across_subprocess_runs( + tmp_path: Path, + rows: list[dict[str, str]], + columns: list[str], + plan: dict[str, object], +) -> None: + in_tsv = tmp_path / "input.tsv" + plan_json = tmp_path / "plan.json" + _write_tsv(in_tsv, rows, columns) + plan_json.write_text(json.dumps(plan)) + + a = _run_paths(tmp_path, "_a") + b = _run_paths(tmp_path, "_b") + + _run_cli_subprocess(input_tsv=in_tsv, plan_json=plan_json, **a) + _run_cli_subprocess(input_tsv=in_tsv, plan_json=plan_json, **b) + + _assert_all_three_byte_identical(a, b) diff --git a/test/src/helpers.ts b/test/src/helpers.ts index 4bd708f..afd03ef 100644 --- a/test/src/helpers.ts +++ b/test/src/helpers.ts @@ -11,23 +11,29 @@ * be imported. Resurrect once a fixed version is published or another * synthetic-publisher block is identified. * - * 2. MiXCR canary — `setupMixcrAnchor` runs the real samples-and-data + - * mixcr-clonotyping-2 pipeline against fastq fixtures. Slow, but is - * the only path currently runnable end-to-end. Used by the canary - * test (kept as `it.todo` until the upstream awaitBlockDone timing is - * confirmed against this workspace's local platforma). + * 2. MiXCR canary — runs the real samples-and-data + mixcr-clonotyping-2 + * pipeline against fastq fixtures. Slow, but is the only path currently + * runnable end-to-end. Used by the canary test and by + * `setupTwoSeqPropsCoInstances` to seed the two-instance dedup test. + * + * V3 plumbing: both upstreams (`samples-and-data@^1.17` and + * `mixcr-clonotyping-2@^2.18`) are `PlatformaV3` blocks and reject + * `setBlockArgs` with `ModelAPIVersionMismatchError`. The helpers below use + * `mutateBlockStorage({ operation: 'update-block-data', value: })` + * which is the V3 update path. * * Each blockTest spins up a fresh platforma container, so co-locating - * multiple assertions per test is the standard cost optimization. Helpers - * here are deliberately small composable steps so individual tests can - * inline the parts they need to assert on. + * multiple assertions per test is the standard cost optimization. */ import { blockSpec as samplesAndDataBlockSpec } from '@platforma-open/milaboratories.samples-and-data'; +import type { BlockData as SamplesAndDataBlockData } from '@platforma-open/milaboratories.samples-and-data.model'; import { blockSpec as mixcrClonotypingBlockSpec } from '@platforma-open/milaboratories.mixcr-clonotyping-2'; +import type { BlockData as MixcrClonotypingBlockData } from '@platforma-open/milaboratories.mixcr-clonotyping-2.model'; import { blockSpec as seqPropsBlockSpec } from 'this-block'; import { uniquePlId } from '@platforma-sdk/model'; import type { ML, RawHelpers } from '@platforma-sdk/test'; +import { awaitStableState } from '@platforma-sdk/test'; import type { expect as vitestExpect } from 'vitest'; export type TestCtx = { @@ -40,21 +46,115 @@ export type TestCtx = { /** * Add the sequence-properties block under test. */ -export async function addSequenceProperties(ctx: TestCtx): Promise { - return await ctx.rawPrj.addBlock('Sequence Properties', seqPropsBlockSpec); +export async function addSequenceProperties(ctx: TestCtx, label = 'Sequence Properties'): Promise { + return await ctx.rawPrj.addBlock(label, seqPropsBlockSpec); } /** - * Add samples-and-data + mixcr-clonotyping-2 wired to fastq fixtures. - * Single-cell IG preset by default — for the MiXCR canary test. - * - * Fastq fixtures are NOT bundled — drop appropriate small paired-end - * fastq.gz files into `test/assets/` (e.g. a single-cell IG slice from - * SRA) and pass their paths via `opts.r1Path` / `opts.r2Path`. Sibling - * blocks like mixcr-clonotyping vendor SRR-prefixed fixtures in their - * own test/assets — those can be reused here when the canary lands. - * - * Returns block ids; caller drives runs + assertions. + * Configure samples-and-data with a one-sample fastq dataset. Uses V3 + * mutateBlockStorage with the full BlockData payload (V1 setBlockArgs is + * rejected by samples-and-data@^1.17 — `PlatformaV3` model). + */ +async function configureSamplesAndData( + ctx: TestCtx, + sndBlockId: string, + opts: { r1Path: string; r2Path: string }, +): Promise { + const { rawPrj, helpers } = ctx; + const sample1Id = uniquePlId(); + const dataset1Id = uniquePlId(); + const r1Handle = await helpers.getLocalFileHandle(opts.r1Path); + const r2Handle = await helpers.getLocalFileHandle(opts.r2Path); + + await rawPrj.mutateBlockStorage(sndBlockId, { + operation: 'update-block-data', + value: { + metadata: [], + sampleIds: [sample1Id], + sampleLabelColumnLabel: 'Sample Name', + sampleLabels: { [sample1Id]: 'Sample 1' }, + datasets: [ + { + id: dataset1Id, + label: 'Dataset 1', + content: { + type: 'Fastq', + readIndices: ['R1', 'R2'], + gzipped: true, + data: { [sample1Id]: { R1: r1Handle, R2: r2Handle } }, + }, + }, + ], + h5adFilesToPreprocess: [], + seuratFilesToPreprocess: [], + suggestedImport: false, + } satisfies SamplesAndDataBlockData, + }); +} + +/** + * Configure mixcr-clonotyping-2 with the preset + chains, wired to + * samples-and-data's published output. V3 — requires the upstream input + * ref, which is read from clonotyping's inputOptions after samples-and-data + * has run to Done. + */ +async function configureMixcrClonotyping( + ctx: TestCtx, + clonotypingBlockId: string, + preset: string, + chains: string[], +): Promise { + const { rawPrj } = ctx; + + // After samples-and-data Done, clonotyping's inputOptions populates from + // the result pool. Wait for it to stabilize, then take the first option. + type ClonotypingInputOption = { ref: { __isRef: true; blockId: string; name: string } }; + const clonotypingState = await awaitStableState( + rawPrj.getBlockState(clonotypingBlockId), + 25000, + ); + const inputOptions = ( + clonotypingState.outputs as Record + ).inputOptions?.value; + if (!inputOptions || inputOptions.length === 0) { + throw new Error('mixcr-clonotyping-2 inputOptions did not populate after samples-and-data'); + } + + // mixcr-clonotyping-2@2.18 pins @platforma-sdk/model@1.63.1, whose + // PlDataTableStateV2 is version 5. Our catalog SDK (1.77.0) exposes + // createPlDataTableStateV2 that emits version 7. The two shapes are + // structurally close, but the version literal differs and TS treats + // them as incompatible. Constructing the v5 literal directly avoids + // the cross-version helper and keeps the type fully checked. + const tableState: MixcrClonotypingBlockData['tableState'] = { + version: 5, + stateCache: [], + pTableParams: { + sourceId: null, + hiddenColIds: null, + filters: null, + sorting: [], + }, + }; + + await rawPrj.mutateBlockStorage(clonotypingBlockId, { + operation: 'update-block-data', + value: { + defaultBlockLabel: '', + customBlockLabel: '', + input: inputOptions[0].ref, + preset: { type: 'name', name: preset }, + chains, + tableState, + runMode: 'full', + } satisfies MixcrClonotypingBlockData, + }); +} + +/** + * Older helper retained for the MiXCR canary test scaffolding. Same V3 + * configuration flow as setupTwoSeqPropsCoInstances but with a single + * sequence-properties block. */ export async function setupMixcrAnchor( ctx: TestCtx, @@ -73,34 +173,11 @@ export async function setupMixcrAnchor( const clonotypingBlockId = await rawPrj.addBlock('MiXCR Clonotyping', mixcrClonotypingBlockSpec); const seqPropsBlockId = await addSequenceProperties(ctx); - const sample1Id = uniquePlId(); - const dataset1Id = uniquePlId(); - const r1Handle = await helpers.getLocalFileHandle(opts.r1Path); - const r2Handle = await helpers.getLocalFileHandle(opts.r2Path); + await configureSamplesAndData(ctx, sndBlockId, opts); + await rawPrj.runBlock(sndBlockId); + await helpers.awaitBlockDone(sndBlockId, 30000); - await rawPrj.setBlockArgs(sndBlockId, { - metadata: [], - sampleIds: [sample1Id], - sampleLabelColumnLabel: 'Sample Name', - sampleLabels: { [sample1Id]: 'Sample 1' }, - datasets: [ - { - id: dataset1Id, - label: 'Dataset 1', - content: { - type: 'Fastq', - readIndices: ['R1', 'R2'], - gzipped: true, - data: { [sample1Id]: { R1: r1Handle, R2: r2Handle } }, - }, - }, - ], - }); - - await rawPrj.setBlockArgs(clonotypingBlockId, { - preset: { type: 'name', name: preset }, - chains, - }); + await configureMixcrClonotyping(ctx, clonotypingBlockId, preset, chains); return { sndBlockId, clonotypingBlockId, seqPropsBlockId }; } diff --git a/test/src/wf.test.ts b/test/src/wf.test.ts index 5ce361f..4719d14 100644 --- a/test/src/wf.test.ts +++ b/test/src/wf.test.ts @@ -150,6 +150,7 @@ describe('model + UI', () => { describe('dedup', () => { it.todo('second project on identical upstream lands on Done via dedup'); it.todo('changed upstream input breaks dedup and triggers fresh run'); + it.todo('two co-instances on identical upstream run without CID conflicts'); }); // --------------------------------------------------------------------------- diff --git a/ui/src/app.ts b/ui/src/app.ts index cef3edc..4242d95 100644 --- a/ui/src/app.ts +++ b/ui/src/app.ts @@ -6,6 +6,8 @@ import MainPage from "./pages/MainPage.vue"; import ScatterPage from "./pages/ScatterPage.vue"; export const sdkPlugin = defineAppV3(platforma, (app) => { + app.model.data.customBlockLabel ??= ""; + watchEffect(() => { const anchor = app.model.data.inputAnchor; const opts = app.model.outputs.inputOptions ?? []; diff --git a/ui/src/pages/HistogramPage.vue b/ui/src/pages/HistogramPage.vue index a7621ab..75a6682 100644 --- a/ui/src/pages/HistogramPage.vue +++ b/ui/src/pages/HistogramPage.vue @@ -24,17 +24,22 @@ const defaultOptions = computed((): PredefinedGraphOption<"histogram">[] | null return [{ inputName: "value", selectedSource: metric }]; }); -// Data = this block's own scalar properties. The propertiesPfHandle contains -// pCols (ours, trace-injected) ∪ upstreamMeta (filtered out our trace at the -// model layer). Our trace identifies our data candidates. +// Data = own scalar properties. propertiesPfHandle holds our trace-injected +// pCols plus single-axis upstream metadata; the trace match isolates ours +// to drive the default value-axis pick. const dataColumnPredicate = (spec: PColumnSpec) => isNumericScalar(spec) && spec.annotations?.["pl7.app/trace"]?.includes("sequence-properties") === true; -// Meta = upstream columns only (sample groups, patient IDs, etc.) — anything -// without our trace. -const metaColumnPredicate = (spec: PColumnSpec) => - !spec.annotations?.["pl7.app/trace"]?.includes("sequence-properties"); +// Meta = every column in the pframe. The model layer already curates the +// set (own scalars + single-axis upstream metadata; aaFraction excluded for +// the cell-count guard), so anything that reaches us is a valid dimension +// for Filter / Grouping-Color / Highlight / Size / Tab / Tooltip / Label / +// Additional-curves. Own scalars deliberately appear in both data and meta +// roles — users bin the histogram by, e.g., chain while plotting Aromaticity +// values. If multi-axis columns later enter the pframe, add an +// `isSingleAxis` guard here. +const metaColumnPredicate = (_spec: PColumnSpec) => true;