@platforma-sdk/model 1.80.17 → 1.81.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.
- package/dist/columns/column.cjs +9 -6
- package/dist/columns/column.cjs.map +1 -1
- package/dist/columns/column.d.ts +5 -5
- package/dist/columns/column.d.ts.map +1 -1
- package/dist/columns/column.js +10 -7
- package/dist/columns/column.js.map +1 -1
- package/dist/columns/column_providers/index.cjs.map +1 -1
- package/dist/columns/column_providers/index.d.ts.map +1 -1
- package/dist/columns/column_providers/index.js.map +1 -1
- package/dist/columns/column_providers/providers.cjs +5 -5
- package/dist/columns/column_providers/providers.cjs.map +1 -1
- package/dist/columns/column_providers/providers.d.ts +8 -8
- package/dist/columns/column_providers/providers.d.ts.map +1 -1
- package/dist/columns/column_providers/providers.js +5 -5
- package/dist/columns/column_providers/providers.js.map +1 -1
- package/dist/columns/column_providers/types.d.ts +3 -3
- package/dist/columns/column_providers/types.d.ts.map +1 -1
- package/dist/columns/column_recipes/column_discovered_recipe.cjs +1 -1
- package/dist/columns/column_recipes/column_discovered_recipe.cjs.map +1 -1
- package/dist/columns/column_recipes/column_discovered_recipe.d.ts +1 -1
- package/dist/columns/column_recipes/column_discovered_recipe.js +1 -1
- package/dist/columns/column_recipes/column_discovered_recipe.js.map +1 -1
- package/dist/columns/column_recipes/column_overrided_recipe.cjs +47 -2
- package/dist/columns/column_recipes/column_overrided_recipe.cjs.map +1 -1
- package/dist/columns/column_recipes/column_overrided_recipe.d.ts +30 -3
- package/dist/columns/column_recipes/column_overrided_recipe.d.ts.map +1 -1
- package/dist/columns/column_recipes/column_overrided_recipe.js +47 -3
- package/dist/columns/column_recipes/column_overrided_recipe.js.map +1 -1
- package/dist/columns/column_recipes/index.cjs +4 -14
- package/dist/columns/column_recipes/index.cjs.map +1 -1
- package/dist/columns/column_recipes/index.d.ts +3 -10
- package/dist/columns/column_recipes/index.d.ts.map +1 -1
- package/dist/columns/column_recipes/index.js +5 -14
- package/dist/columns/column_recipes/index.js.map +1 -1
- package/dist/columns/{column_lazy.cjs → data_column.cjs} +57 -54
- package/dist/columns/data_column.cjs.map +1 -0
- package/dist/columns/data_column.d.ts +152 -0
- package/dist/columns/data_column.d.ts.map +1 -0
- package/dist/columns/{column_lazy.js → data_column.js} +55 -52
- package/dist/columns/data_column.js.map +1 -0
- package/dist/columns/derive_axis_values_labels.cjs +4 -4
- package/dist/columns/derive_axis_values_labels.cjs.map +1 -1
- package/dist/columns/derive_axis_values_labels.d.ts +2 -2
- package/dist/columns/derive_axis_values_labels.js +4 -4
- package/dist/columns/derive_axis_values_labels.js.map +1 -1
- package/dist/columns/index.cjs +9 -9
- package/dist/columns/index.d.ts +5 -5
- package/dist/columns/index.js +5 -5
- package/dist/columns/{expand_by_partition.cjs → split_by_axes.cjs} +13 -8
- package/dist/columns/split_by_axes.cjs.map +1 -0
- package/dist/columns/{expand_by_partition.d.ts → split_by_axes.d.ts} +18 -8
- package/dist/columns/split_by_axes.d.ts.map +1 -0
- package/dist/columns/{expand_by_partition.js → split_by_axes.js} +14 -9
- package/dist/columns/split_by_axes.js.map +1 -0
- package/dist/columns/utils.cjs +50 -43
- package/dist/columns/utils.cjs.map +1 -1
- package/dist/columns/utils.d.ts +44 -19
- package/dist/columns/utils.d.ts.map +1 -1
- package/dist/columns/utils.js +50 -44
- package/dist/columns/utils.js.map +1 -1
- package/dist/components/PlDataTable/createPlDataTable/createPTableDefV2.cjs +3 -3
- package/dist/components/PlDataTable/createPlDataTable/createPTableDefV2.cjs.map +1 -1
- package/dist/components/PlDataTable/createPlDataTable/createPTableDefV2.js +3 -3
- package/dist/components/PlDataTable/createPlDataTable/createPTableDefV2.js.map +1 -1
- package/dist/components/PlDataTable/createPlDataTable/createPlDataTableV2.cjs +2 -2
- package/dist/components/PlDataTable/createPlDataTable/createPlDataTableV2.cjs.map +1 -1
- package/dist/components/PlDataTable/createPlDataTable/createPlDataTableV2.js +2 -2
- package/dist/components/PlDataTable/createPlDataTable/createPlDataTableV2.js.map +1 -1
- package/dist/components/PlDataTable/createPlDataTable/createPlDataTableV3.cjs +1 -1
- package/dist/components/PlDataTable/createPlDataTable/createPlDataTableV3.cjs.map +1 -1
- package/dist/components/PlDataTable/createPlDataTable/createPlDataTableV3.js +2 -2
- package/dist/components/PlDataTable/createPlDataTable/createPlDataTableV3.js.map +1 -1
- package/dist/components/PlDataTable/createPlDataTable/discoverColumns.cjs +1 -1
- package/dist/components/PlDataTable/createPlDataTable/discoverColumns.cjs.map +1 -1
- package/dist/components/PlDataTable/createPlDataTable/discoverColumns.js +2 -2
- package/dist/components/PlDataTable/createPlDataTable/discoverColumns.js.map +1 -1
- package/dist/components/PlDataTable/createPlDataTable/utils.cjs +1 -1
- package/dist/components/PlDataTable/createPlDataTable/utils.cjs.map +1 -1
- package/dist/components/PlDataTable/createPlDataTable/utils.js +1 -1
- package/dist/components/PlDataTable/createPlDataTable/utils.js.map +1 -1
- package/dist/components/PlDatasetSelector/enrichment_discovery.cjs +1 -1
- package/dist/components/PlDatasetSelector/enrichment_discovery.cjs.map +1 -1
- package/dist/components/PlDatasetSelector/enrichment_discovery.js +2 -2
- package/dist/components/PlDatasetSelector/enrichment_discovery.js.map +1 -1
- package/dist/index.cjs +9 -9
- package/dist/index.d.ts +5 -5
- package/dist/index.js +5 -5
- package/dist/package.cjs +1 -1
- package/dist/package.js +1 -1
- package/package.json +8 -8
- package/src/columns/__test_helpers__/collection_driver.ts +6 -6
- package/src/columns/__test_helpers__/stub_registry.ts +4 -6
- package/src/columns/column.ts +20 -9
- package/src/columns/column_providers/index.ts +9 -3
- package/src/columns/column_providers/providers.ts +16 -15
- package/src/columns/column_providers/types.ts +3 -3
- package/src/columns/column_recipes/column_discovered_recipe.ts +1 -1
- package/src/columns/column_recipes/column_overrided_recipe.ts +58 -4
- package/src/columns/column_recipes/column_recipes.test.ts +2 -2
- package/src/columns/column_recipes/index.ts +5 -20
- package/src/columns/{column_lazy.test.ts → data_column.test.ts} +19 -19
- package/src/columns/{column_lazy.ts → data_column.ts} +105 -67
- package/src/columns/derive_axis_values_labels.ts +4 -4
- package/src/columns/index.ts +2 -2
- package/src/columns/predicates.test.ts +124 -0
- package/src/columns/{expand_by_partition.test.ts → split_by_axes.test.ts} +29 -21
- package/src/columns/{expand_by_partition.ts → split_by_axes.ts} +22 -9
- package/src/columns/utils.ts +53 -48
- package/src/components/PlDataTable/columnResolver.test.ts +3 -3
- package/src/components/PlDataTable/createPlDataTable/createPTableDefV2.ts +3 -3
- package/src/components/PlDataTable/createPlDataTable/createPlDataTableV2.ts +2 -2
- package/src/components/PlDataTable/createPlDataTable/createPlDataTableV3.ts +2 -2
- package/src/components/PlDataTable/createPlDataTable/discoverColumns.ts +2 -2
- package/src/components/PlDataTable/createPlDataTable/utils.test.ts +34 -3
- package/src/components/PlDataTable/createPlDataTable/utils.ts +4 -1
- package/src/components/PlDatasetSelector/enrichment_discovery.ts +2 -2
- package/dist/columns/column_lazy.cjs.map +0 -1
- package/dist/columns/column_lazy.d.ts +0 -137
- package/dist/columns/column_lazy.d.ts.map +0 -1
- package/dist/columns/column_lazy.js.map +0 -1
- package/dist/columns/expand_by_partition.cjs.map +0 -1
- package/dist/columns/expand_by_partition.d.ts.map +0 -1
- package/dist/columns/expand_by_partition.js.map +0 -1
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
import { afterEach, beforeEach, describe, expect, test } from "vitest";
|
|
8
8
|
import { deriveAllLabels, evaluateRules, type LabelableColumn } from "./utils";
|
|
9
9
|
import type { ColumnOrderRule, ColumnVisibilityRule } from "./createPlDataTableV3";
|
|
10
|
-
import {
|
|
10
|
+
import { DataColumn, type DataColumnRecipe } from "../../../columns";
|
|
11
11
|
import {
|
|
12
12
|
createTestCollectionDriver,
|
|
13
13
|
type TestCollectionDriverHandle,
|
|
@@ -156,11 +156,11 @@ function gid(name: string): PObjectId {
|
|
|
156
156
|
return createGlobalPObjectId("test-block", name);
|
|
157
157
|
}
|
|
158
158
|
|
|
159
|
-
function makeLazyColumn(name: string, spec: Partial<PColumnSpec> = {}):
|
|
159
|
+
function makeLazyColumn(name: string, spec: Partial<PColumnSpec> = {}): DataColumnRecipe {
|
|
160
160
|
const s = makeSpec({ axesSpec: [{ name: "id", type: "String" }], ...spec });
|
|
161
161
|
const id = gid(name);
|
|
162
162
|
driverHandle.register([{ id, spec: s }]);
|
|
163
|
-
return
|
|
163
|
+
return DataColumn.fromColumn({ id, spec: s, data: undefined as never });
|
|
164
164
|
}
|
|
165
165
|
|
|
166
166
|
describe("evaluateRules", () => {
|
|
@@ -207,6 +207,37 @@ describe("evaluateRules", () => {
|
|
|
207
207
|
expect(result.get(gid("b"))?.priority).toBe(50);
|
|
208
208
|
});
|
|
209
209
|
|
|
210
|
+
test("a rule matching zero columns does not suppress the other rules", () => {
|
|
211
|
+
const rules: ColumnOrderRule[] = [
|
|
212
|
+
{ match: { name: "^nothing-matches-this$" }, priority: 999 },
|
|
213
|
+
{ match: { name: "^alpha$" }, priority: 100 },
|
|
214
|
+
{ match: { name: "^beta$" }, priority: 50 },
|
|
215
|
+
];
|
|
216
|
+
const columns = [makeLazyColumn("a", { name: "alpha" }), makeLazyColumn("b", { name: "beta" })];
|
|
217
|
+
|
|
218
|
+
const result = evaluateRules(rules, columns);
|
|
219
|
+
|
|
220
|
+
expect(result.get(gid("a"))?.priority).toBe(100);
|
|
221
|
+
expect(result.get(gid("b"))?.priority).toBe(50);
|
|
222
|
+
expect(result.size).toBe(2);
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
test("rules matching zero columns are skipped regardless of their position", () => {
|
|
226
|
+
const rules: ColumnVisibilityRule[] = [
|
|
227
|
+
{ match: { name: "^ghost-a$" }, visibility: "hidden" },
|
|
228
|
+
{ match: { name: "^note$" }, visibility: "hidden" },
|
|
229
|
+
{ match: { name: "^ghost-b$" }, visibility: "hidden" },
|
|
230
|
+
{ match: { name: "^score$" }, visibility: "optional" },
|
|
231
|
+
{ match: { name: "^ghost-c$" }, visibility: "hidden" },
|
|
232
|
+
];
|
|
233
|
+
const columns = [makeLazyColumn("n", { name: "note" }), makeLazyColumn("s", { name: "score" })];
|
|
234
|
+
|
|
235
|
+
const result = evaluateRules(rules, columns);
|
|
236
|
+
|
|
237
|
+
expect(result.get(gid("n"))?.visibility).toBe("hidden");
|
|
238
|
+
expect(result.get(gid("s"))?.visibility).toBe("optional");
|
|
239
|
+
});
|
|
240
|
+
|
|
210
241
|
test("dedupes columns by id before building spec frame (no duplicate-key crash)", () => {
|
|
211
242
|
const rules: ColumnVisibilityRule[] = [{ match: { name: "^dup$" }, visibility: "hidden" }];
|
|
212
243
|
const dup = makeLazyColumn("d", { name: "dup" });
|
|
@@ -88,7 +88,10 @@ export function evaluateRules<R extends { match: ColumnSelector }>(
|
|
|
88
88
|
const baseCollection = ColumnsCollection([{ columns: baseColumns, isFinal: true }]);
|
|
89
89
|
for (const rule of selectorRules) {
|
|
90
90
|
const hitIds = baseCollection.filter({ include: rule.match }).getColumnIds();
|
|
91
|
-
|
|
91
|
+
// A rule matching nothing simply contributes no entries — it must not
|
|
92
|
+
// discard the rules evaluated alongside it. The lookup below is
|
|
93
|
+
// null-safe, so leaving this rule out of the map is enough.
|
|
94
|
+
if (hitIds.length === 0) continue;
|
|
92
95
|
selectorHitsByRule.set(rule, new Set(hitIds.map((id) => extractPObjectId(id))));
|
|
93
96
|
}
|
|
94
97
|
}
|
|
@@ -15,7 +15,7 @@ import type {
|
|
|
15
15
|
} from "@milaboratories/pl-model-common";
|
|
16
16
|
import type { ColumnRecipe, ColumnsSource } from "../../columns";
|
|
17
17
|
import {
|
|
18
|
-
|
|
18
|
+
hasSingleDataColumn,
|
|
19
19
|
ColumnsCollection,
|
|
20
20
|
collectLinkerIds,
|
|
21
21
|
hitQualifications,
|
|
@@ -67,7 +67,7 @@ export function findEnrichmentColumns(options: FindEnrichmentColumnsOptions): Co
|
|
|
67
67
|
|
|
68
68
|
const predicate = options.predicate;
|
|
69
69
|
return columns.filter((v) => {
|
|
70
|
-
if (
|
|
70
|
+
if (hasSingleDataColumn(v)) return false;
|
|
71
71
|
if (predicate !== undefined && !predicate(v.getSpec())) return false;
|
|
72
72
|
if (!isGlobalPObjectId(extractPObjectId(v.id))) return false;
|
|
73
73
|
if (collectLinkerIds(v).some((id) => !isGlobalPObjectId(id))) return false;
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"column_lazy.cjs","names":["ColumnOverriddenRecipe","ColumnRegistry","getCtxProviders","LRUCache"],"sources":["../../src/columns/column_lazy.ts"],"sourcesContent":["import {\n ColumnRegistry,\n createGlobalPObjectId,\n isPColumn,\n isPlRef,\n PlRef,\n PObjectId,\n type ColumnUniversalId,\n type LeafEntry,\n type PColumn,\n type PColumnSpec,\n type SpecOverrides,\n type SpecQuery,\n} from \"@milaboratories/pl-model-common\";\nimport type { GlobalCfgRenderCtx, PColumnDataUniversal } from \"../render/internal\";\nimport { getCtxProviders } from \"./column_providers\";\nimport { isNil } from \"es-toolkit\";\nimport { LRUCache } from \"lru-cache\";\nimport { TreeNodeAccessor } from \"../render\";\nimport type {\n ColumnFieldStatus,\n ColumnRecipe,\n ColumnResolutionStatus,\n} from \"./column_recipes/types\";\nimport { ColumnOverriddenRecipe } from \"./column_recipes/column_overrided_recipe\";\n\nexport type ColumnLazyId = PObjectId;\nexport type ColumnLazyData = undefined | PColumnDataUniversal;\nexport type { ColumnFieldStatus, ColumnResolutionStatus } from \"./column_recipes/types\";\n\n/**\n * Thrown by leaf-recipe factories when the requested column is provably\n * absent in the active render ctx — i.e. every relevant accessor reports\n * `inputsLocked` and the column did not appear. Distinct from the\n * `undefined` return that the factories still use for the \"resolving\" case\n * (the column may still appear later).\n *\n * Catch at the boundary (resolver / filter+sort wiring / data-table assembly)\n * when partial absence should be surfaced to the user rather than silently\n * producing an empty result.\n */\nexport class ColumnAbsentError extends Error {\n constructor(public readonly id: ColumnUniversalId) {\n super(`Column is absent in the active render ctx and will not appear: ${id}`);\n this.name = \"ColumnAbsentError\";\n }\n}\n\n/**\n * ColumnLazy is the leaf-recipe building block: a {@link ColumnRecipe} whose\n * `id` is a bare {@link PObjectId} and whose readers are bound to a single\n * tree-accessor leaf. Layered encodings (Overridden / Discovered / Filtered)\n * are reified through their dedicated recipe classes and reference leaf\n * columns by id.\n */\nexport class ColumnLazyImpl implements ColumnRecipe<PObjectId> {\n private specCache?: { readonly value: PColumnSpec };\n private dataCache?: { readonly value: ColumnLazyData };\n private dataStatusCache?: { readonly value: ColumnFieldStatus };\n\n private constructor(\n public readonly id: PObjectId,\n private readonly options: {\n getSpec: () => PColumnSpec;\n getData: () => ColumnLazyData;\n getDataStatus: () => ColumnFieldStatus;\n },\n ) {}\n\n /** A leaf-recipe references exactly one column — its own id. */\n getReferencedIds(): PObjectId[] {\n return [this.id];\n }\n\n getSpec(): PColumnSpec {\n if (this.specCache === undefined) {\n this.specCache = { value: this.options.getSpec() };\n }\n return this.specCache.value;\n }\n\n /** Leaf-shaped — points straight at `this.id`. */\n getQuery(): SpecQuery {\n return { type: \"column\", column: this.id } as SpecQuery;\n }\n\n /** Leaf-only: not on the recipe interface — only the leaf can read data directly. */\n getData(): ColumnLazyData {\n if (this.dataCache === undefined) this.dataCache = { value: this.options.getData() };\n return this.dataCache.value;\n }\n\n getDataStatus(): ColumnFieldStatus {\n if (this.dataStatusCache === undefined) {\n this.dataStatusCache = { value: this.options.getDataStatus() };\n }\n return this.dataStatusCache.value;\n }\n\n /**\n * Overlay overrides → produces a {@link ColumnOverriddenRecipe} wrapping\n * this leaf. ColumnLazy itself stays bare (id remains a plain PObjectId);\n * layering lives entirely in the recipe wrappers.\n */\n withSpecs(overrides: SpecOverrides): ColumnRecipe {\n return ColumnOverriddenRecipe.wrap(this, overrides);\n }\n\n /**\n * Resolve via the ambient {@link ColumnRegistry}. Spec is resolved eagerly:\n * returns `undefined` if the leaf isn't reachable yet (resolving). Throws\n * {@link ColumnAbsentError} when every relevant accessor is `inputsLocked`\n * and the column did not appear — the column will not exist in this ctx.\n * Data and dataStatus stay lazy.\n */\n static fromId(id: PObjectId, { ctx }: { ctx?: GlobalCfgRenderCtx } = {}): undefined | ColumnLazy {\n const registry = new ColumnRegistry(getCtxProviders({ ctx }));\n const leaf = registry.resolve(id);\n if (isNil(leaf)) {\n if (registry.isFinal()) throw new ColumnAbsentError(id);\n return undefined;\n }\n const spec = readSpecAccessor(leaf);\n if (isNil(spec)) {\n if (leaf.accessor.getInputsLocked()) throw new ColumnAbsentError(id);\n return undefined;\n }\n if (!spec.hasData()) return undefined;\n return new ColumnLazyImpl(id, {\n getSpec: () => spec.getDataAsJson<PColumnSpec>(),\n getData: () => readDataAccessor(leaf),\n getDataStatus: () => readDataStatus(leaf),\n });\n }\n\n /** {@link PlRef} wrapper over {@link fromId}. */\n static fromPlRef(ref: PlRef): undefined | ColumnLazy {\n return ColumnLazyImpl.fromId(createGlobalPObjectId(ref.blockId, ref.name));\n }\n\n /**\n * Bind directly to an accessor-backed {@link LeafEntry} — no registry.\n * Throws {@link ColumnAbsentError} if the leaf has no spec field and its\n * accessor is `inputsLocked`. Returns `undefined` while still resolving.\n */\n static fromAccessor(entry: LeafEntry<TreeNodeAccessor>): undefined | ColumnLazy {\n const spec = readSpecAccessor(entry);\n if (isNil(spec)) {\n if (entry.accessor.getInputsLocked()) throw new ColumnAbsentError(entry.id);\n return undefined;\n }\n if (!spec.hasData()) return undefined;\n return new ColumnLazyImpl(entry.id, {\n getSpec: () => spec.getDataAsJson<PColumnSpec>(),\n getData: () => readDataAccessor(entry),\n getDataStatus: () => readDataStatus(entry),\n });\n }\n\n /**\n * Wrap a materialised {@link PColumn}. If the input is already a\n * {@link ColumnLazy} it is returned as-is.\n */\n static fromColumn(column: PColumn<ColumnLazyData> | ColumnLazy): ColumnLazy {\n if (column instanceof ColumnLazyImpl) return column;\n return new ColumnLazyImpl(column.id, {\n getSpec: () => column.spec,\n getData: () => column.data,\n getDataStatus: () => \"present\",\n });\n }\n\n /**\n * Distinguishes `present` / `resolving` / `absent` for a {@link PObjectId}\n * in the active render ctx. Falls back to the registry's `isFinal()`\n * when the id has no entry — only then we can say `absent` instead of\n * `resolving`.\n */\n static getStatusById(\n id: PObjectId,\n { ctx }: { ctx?: GlobalCfgRenderCtx } = {},\n ): ColumnResolutionStatus {\n const registry = new ColumnRegistry(getCtxProviders({ ctx }));\n const leaf = registry.resolve(id);\n if (isNil(leaf)) return registry.isFinal() ? \"absent\" : \"resolving\";\n return getLeafEntryStatus(leaf);\n }\n\n /** {@link PlRef} wrapper over {@link getStatusById}. */\n static getStatusByPlRef(\n ref: PlRef,\n opts: { ctx?: GlobalCfgRenderCtx } = {},\n ): ColumnResolutionStatus {\n return ColumnLazyImpl.getStatusById(createGlobalPObjectId(ref.blockId, ref.name), opts);\n }\n\n /** No registry — reads straight off the entry's accessor. */\n static getStatusByAccessor(entry: LeafEntry<TreeNodeAccessor>): ColumnResolutionStatus {\n return getLeafEntryStatus(entry);\n }\n}\n\n/**\n * Public type alias — `ColumnLazy` (the type) refers to the underlying\n * {@link ColumnLazyImpl} class instance. `ColumnLazy` (the value) is the\n * callable below with the static factories attached.\n */\nexport type ColumnLazy = ColumnLazyImpl;\n\n/**\n * Unified dispatcher — picks the right `ColumnLazyImpl.fromX` by source\n * shape. For ambiguous inputs callers can still use the explicit factories\n * (also attached as properties: `ColumnLazy.fromId`, `.fromPlRef`,\n * `.fromAccessor`, `.fromColumn`).\n */\nfunction ColumnLazyDispatch(\n source: PObjectId | PlRef | LeafEntry<TreeNodeAccessor> | PColumn<ColumnLazyData> | ColumnLazy,\n opts: { ctx?: GlobalCfgRenderCtx } = {},\n): undefined | ColumnLazy {\n if (typeof source === \"string\") return ColumnLazyImpl.fromId(source, opts);\n if (source instanceof ColumnLazyImpl) return source;\n if (\"accessor\" in source) return ColumnLazyImpl.fromAccessor(source);\n if (isPlRef(source)) return ColumnLazyImpl.fromPlRef(source);\n if (isPColumn(source)) return ColumnLazyImpl.fromColumn(source);\n throw new Error(\"ColumnLazy: unknown source shape\");\n}\n\n/**\n * Polymorphic counterpart to {@link ColumnLazyDispatch}: returns the\n * {@link ColumnResolutionStatus} for any factory input without constructing\n * the recipe. For already-materialised sources ({@link PColumn} value,\n * existing {@link ColumnLazy}) status is `present` by construction.\n */\nfunction ColumnLazyGetStatus(\n source: PObjectId | PlRef | LeafEntry<TreeNodeAccessor> | PColumn<ColumnLazyData> | ColumnLazy,\n opts: { ctx?: GlobalCfgRenderCtx } = {},\n): ColumnResolutionStatus {\n if (typeof source === \"string\") return ColumnLazyImpl.getStatusById(source, opts);\n if (source instanceof ColumnLazyImpl) return \"present\";\n if (\"accessor\" in source) return ColumnLazyImpl.getStatusByAccessor(source);\n if (isPlRef(source)) return ColumnLazyImpl.getStatusByPlRef(source, opts);\n if (isPColumn(source)) return \"present\";\n throw new Error(\"ColumnLazy.getStatus: unknown source shape\");\n}\n\nexport const ColumnLazy = Object.assign(ColumnLazyDispatch, {\n fromId: ColumnLazyImpl.fromId,\n fromPlRef: ColumnLazyImpl.fromPlRef,\n fromAccessor: ColumnLazyImpl.fromAccessor,\n fromColumn: ColumnLazyImpl.fromColumn,\n getStatus: ColumnLazyGetStatus,\n getStatusById: ColumnLazyImpl.getStatusById,\n getStatusByPlRef: ColumnLazyImpl.getStatusByPlRef,\n getStatusByAccessor: ColumnLazyImpl.getStatusByAccessor,\n});\n\n/**\n * Type-guard narrowing to the leaf recipe. Prefer this over\n * `instanceof ColumnLazyImpl` — `ColumnLazy` (the value) is the dispatcher\n * with statics, not the class, so `instanceof` requires the impl symbol.\n */\nexport function isColumnLazy(value: unknown): value is ColumnLazy {\n return value instanceof ColumnLazyImpl;\n}\n\nconst readSpecAccessor = memoizeByEntry(\n ({ accessor, name }: LeafEntry<TreeNodeAccessor>): undefined | TreeNodeAccessor =>\n accessor.traverse({ field: `${name}.spec`, assertFieldType: \"Input\", ignoreError: true }),\n);\n\n/**\n * Per-entry counterpart to {@link readDataStatus}: tells whether the leaf's\n * **spec** can be read in this ctx, and — for the negative cases —\n * distinguishes `resolving` from `absent` via `getInputsLocked()`.\n *\n * - spec field not yet on the entry's accessor + inputs locked → `absent`\n * - spec field not yet on the entry's accessor + still resolving → `resolving`\n * - spec resource present but bytes not yet written → `resolving`\n * (transient — the spec resource is connected, just unfilled)\n * - spec resource present and `hasData()` → `present`\n */\nfunction getLeafEntryStatus(entry: LeafEntry<TreeNodeAccessor>): ColumnResolutionStatus {\n const spec = readSpecAccessor(entry);\n if (isNil(spec)) return entry.accessor.getInputsLocked() ? \"absent\" : \"resolving\";\n if (!spec.hasData()) return \"resolving\";\n return \"present\";\n}\n\nconst readDataAccessor = memoizeByEntry(\n ({ accessor, name }: LeafEntry<TreeNodeAccessor>): undefined | TreeNodeAccessor =>\n accessor.traverse({ field: `${name}.data`, assertFieldType: \"Input\", ignoreError: true }),\n);\n\nconst readDataStatus = memoizeByEntry(\n ({ accessor, name }: LeafEntry<TreeNodeAccessor>): ColumnFieldStatus => {\n if (accessor.listInputFields().includes(`${name}.data`)) return \"present\";\n return accessor.getInputsLocked() ? \"absent\" : \"resolving\";\n },\n);\n\nfunction memoizeByEntry<R>(\n fn: (entry: LeafEntry<TreeNodeAccessor>) => R,\n): (entry: LeafEntry<TreeNodeAccessor>) => R {\n const cache = new LRUCache<string, { value: R }>({ max: 1000 });\n return (entry) => {\n const key = `${entry.accessor.handle}:${entry.name}`;\n let hit = cache.get(key);\n if (!hit) cache.set(key, (hit = { value: fn(entry) }));\n return hit.value;\n };\n}\n"],"mappings":"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"}
|
|
@@ -1,137 +0,0 @@
|
|
|
1
|
-
import { GlobalCfgRenderCtx, PColumnDataUniversal } from "../render/internal.js";
|
|
2
|
-
import { TreeNodeAccessor } from "../render/accessor.js";
|
|
3
|
-
import { ColumnFieldStatus, ColumnRecipe, ColumnResolutionStatus } from "./column_recipes/types.js";
|
|
4
|
-
import { ColumnUniversalId, LeafEntry, PColumn, PColumnSpec, PObjectId, PlRef, SpecOverrides, SpecQuery } from "@milaboratories/pl-model-common";
|
|
5
|
-
|
|
6
|
-
//#region src/columns/column_lazy.d.ts
|
|
7
|
-
type ColumnLazyId = PObjectId;
|
|
8
|
-
type ColumnLazyData = undefined | PColumnDataUniversal;
|
|
9
|
-
/**
|
|
10
|
-
* Thrown by leaf-recipe factories when the requested column is provably
|
|
11
|
-
* absent in the active render ctx — i.e. every relevant accessor reports
|
|
12
|
-
* `inputsLocked` and the column did not appear. Distinct from the
|
|
13
|
-
* `undefined` return that the factories still use for the "resolving" case
|
|
14
|
-
* (the column may still appear later).
|
|
15
|
-
*
|
|
16
|
-
* Catch at the boundary (resolver / filter+sort wiring / data-table assembly)
|
|
17
|
-
* when partial absence should be surfaced to the user rather than silently
|
|
18
|
-
* producing an empty result.
|
|
19
|
-
*/
|
|
20
|
-
declare class ColumnAbsentError extends Error {
|
|
21
|
-
readonly id: ColumnUniversalId;
|
|
22
|
-
constructor(id: ColumnUniversalId);
|
|
23
|
-
}
|
|
24
|
-
/**
|
|
25
|
-
* ColumnLazy is the leaf-recipe building block: a {@link ColumnRecipe} whose
|
|
26
|
-
* `id` is a bare {@link PObjectId} and whose readers are bound to a single
|
|
27
|
-
* tree-accessor leaf. Layered encodings (Overridden / Discovered / Filtered)
|
|
28
|
-
* are reified through their dedicated recipe classes and reference leaf
|
|
29
|
-
* columns by id.
|
|
30
|
-
*/
|
|
31
|
-
declare class ColumnLazyImpl implements ColumnRecipe<PObjectId> {
|
|
32
|
-
readonly id: PObjectId;
|
|
33
|
-
private readonly options;
|
|
34
|
-
private specCache?;
|
|
35
|
-
private dataCache?;
|
|
36
|
-
private dataStatusCache?;
|
|
37
|
-
private constructor();
|
|
38
|
-
/** A leaf-recipe references exactly one column — its own id. */
|
|
39
|
-
getReferencedIds(): PObjectId[];
|
|
40
|
-
getSpec(): PColumnSpec;
|
|
41
|
-
/** Leaf-shaped — points straight at `this.id`. */
|
|
42
|
-
getQuery(): SpecQuery;
|
|
43
|
-
/** Leaf-only: not on the recipe interface — only the leaf can read data directly. */
|
|
44
|
-
getData(): ColumnLazyData;
|
|
45
|
-
getDataStatus(): ColumnFieldStatus;
|
|
46
|
-
/**
|
|
47
|
-
* Overlay overrides → produces a {@link ColumnOverriddenRecipe} wrapping
|
|
48
|
-
* this leaf. ColumnLazy itself stays bare (id remains a plain PObjectId);
|
|
49
|
-
* layering lives entirely in the recipe wrappers.
|
|
50
|
-
*/
|
|
51
|
-
withSpecs(overrides: SpecOverrides): ColumnRecipe;
|
|
52
|
-
/**
|
|
53
|
-
* Resolve via the ambient {@link ColumnRegistry}. Spec is resolved eagerly:
|
|
54
|
-
* returns `undefined` if the leaf isn't reachable yet (resolving). Throws
|
|
55
|
-
* {@link ColumnAbsentError} when every relevant accessor is `inputsLocked`
|
|
56
|
-
* and the column did not appear — the column will not exist in this ctx.
|
|
57
|
-
* Data and dataStatus stay lazy.
|
|
58
|
-
*/
|
|
59
|
-
static fromId(id: PObjectId, {
|
|
60
|
-
ctx
|
|
61
|
-
}?: {
|
|
62
|
-
ctx?: GlobalCfgRenderCtx;
|
|
63
|
-
}): undefined | ColumnLazy;
|
|
64
|
-
/** {@link PlRef} wrapper over {@link fromId}. */
|
|
65
|
-
static fromPlRef(ref: PlRef): undefined | ColumnLazy;
|
|
66
|
-
/**
|
|
67
|
-
* Bind directly to an accessor-backed {@link LeafEntry} — no registry.
|
|
68
|
-
* Throws {@link ColumnAbsentError} if the leaf has no spec field and its
|
|
69
|
-
* accessor is `inputsLocked`. Returns `undefined` while still resolving.
|
|
70
|
-
*/
|
|
71
|
-
static fromAccessor(entry: LeafEntry<TreeNodeAccessor>): undefined | ColumnLazy;
|
|
72
|
-
/**
|
|
73
|
-
* Wrap a materialised {@link PColumn}. If the input is already a
|
|
74
|
-
* {@link ColumnLazy} it is returned as-is.
|
|
75
|
-
*/
|
|
76
|
-
static fromColumn(column: PColumn<ColumnLazyData> | ColumnLazy): ColumnLazy;
|
|
77
|
-
/**
|
|
78
|
-
* Distinguishes `present` / `resolving` / `absent` for a {@link PObjectId}
|
|
79
|
-
* in the active render ctx. Falls back to the registry's `isFinal()`
|
|
80
|
-
* when the id has no entry — only then we can say `absent` instead of
|
|
81
|
-
* `resolving`.
|
|
82
|
-
*/
|
|
83
|
-
static getStatusById(id: PObjectId, {
|
|
84
|
-
ctx
|
|
85
|
-
}?: {
|
|
86
|
-
ctx?: GlobalCfgRenderCtx;
|
|
87
|
-
}): ColumnResolutionStatus;
|
|
88
|
-
/** {@link PlRef} wrapper over {@link getStatusById}. */
|
|
89
|
-
static getStatusByPlRef(ref: PlRef, opts?: {
|
|
90
|
-
ctx?: GlobalCfgRenderCtx;
|
|
91
|
-
}): ColumnResolutionStatus;
|
|
92
|
-
/** No registry — reads straight off the entry's accessor. */
|
|
93
|
-
static getStatusByAccessor(entry: LeafEntry<TreeNodeAccessor>): ColumnResolutionStatus;
|
|
94
|
-
}
|
|
95
|
-
/**
|
|
96
|
-
* Public type alias — `ColumnLazy` (the type) refers to the underlying
|
|
97
|
-
* {@link ColumnLazyImpl} class instance. `ColumnLazy` (the value) is the
|
|
98
|
-
* callable below with the static factories attached.
|
|
99
|
-
*/
|
|
100
|
-
type ColumnLazy = ColumnLazyImpl;
|
|
101
|
-
/**
|
|
102
|
-
* Unified dispatcher — picks the right `ColumnLazyImpl.fromX` by source
|
|
103
|
-
* shape. For ambiguous inputs callers can still use the explicit factories
|
|
104
|
-
* (also attached as properties: `ColumnLazy.fromId`, `.fromPlRef`,
|
|
105
|
-
* `.fromAccessor`, `.fromColumn`).
|
|
106
|
-
*/
|
|
107
|
-
declare function ColumnLazyDispatch(source: PObjectId | PlRef | LeafEntry<TreeNodeAccessor> | PColumn<ColumnLazyData> | ColumnLazy, opts?: {
|
|
108
|
-
ctx?: GlobalCfgRenderCtx;
|
|
109
|
-
}): undefined | ColumnLazy;
|
|
110
|
-
/**
|
|
111
|
-
* Polymorphic counterpart to {@link ColumnLazyDispatch}: returns the
|
|
112
|
-
* {@link ColumnResolutionStatus} for any factory input without constructing
|
|
113
|
-
* the recipe. For already-materialised sources ({@link PColumn} value,
|
|
114
|
-
* existing {@link ColumnLazy}) status is `present` by construction.
|
|
115
|
-
*/
|
|
116
|
-
declare function ColumnLazyGetStatus(source: PObjectId | PlRef | LeafEntry<TreeNodeAccessor> | PColumn<ColumnLazyData> | ColumnLazy, opts?: {
|
|
117
|
-
ctx?: GlobalCfgRenderCtx;
|
|
118
|
-
}): ColumnResolutionStatus;
|
|
119
|
-
declare const ColumnLazy: typeof ColumnLazyDispatch & {
|
|
120
|
-
fromId: typeof ColumnLazyImpl.fromId;
|
|
121
|
-
fromPlRef: typeof ColumnLazyImpl.fromPlRef;
|
|
122
|
-
fromAccessor: typeof ColumnLazyImpl.fromAccessor;
|
|
123
|
-
fromColumn: typeof ColumnLazyImpl.fromColumn;
|
|
124
|
-
getStatus: typeof ColumnLazyGetStatus;
|
|
125
|
-
getStatusById: typeof ColumnLazyImpl.getStatusById;
|
|
126
|
-
getStatusByPlRef: typeof ColumnLazyImpl.getStatusByPlRef;
|
|
127
|
-
getStatusByAccessor: typeof ColumnLazyImpl.getStatusByAccessor;
|
|
128
|
-
};
|
|
129
|
-
/**
|
|
130
|
-
* Type-guard narrowing to the leaf recipe. Prefer this over
|
|
131
|
-
* `instanceof ColumnLazyImpl` — `ColumnLazy` (the value) is the dispatcher
|
|
132
|
-
* with statics, not the class, so `instanceof` requires the impl symbol.
|
|
133
|
-
*/
|
|
134
|
-
declare function isColumnLazy(value: unknown): value is ColumnLazy;
|
|
135
|
-
//#endregion
|
|
136
|
-
export { ColumnAbsentError, type ColumnFieldStatus, ColumnLazy, ColumnLazyData, ColumnLazyId, ColumnLazyImpl, type ColumnResolutionStatus, isColumnLazy };
|
|
137
|
-
//# sourceMappingURL=column_lazy.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"column_lazy.d.ts","names":[],"sources":["../../src/columns/column_lazy.ts"],"mappings":";;;;;;KA0BY,YAAA,GAAe,SAAS;AAAA,KACxB,cAAA,eAA6B,oBAAoB;AAD7D;;;;AAAoC;AACpC;;;;AAA6D;AAc7D;AAfA,cAea,iBAAA,SAA0B,KAAA;EAAA,SACT,EAAA,EAAI,iBAAA;cAAJ,EAAA,EAAI,iBAAA;AAAA;;;;;;;;cAarB,cAAA,YAA0B,YAAA,CAAa,SAAA;EAAA,SAMhC,EAAA,EAAI,SAAA;EAAA,iBACH,OAAA;EAAA,QANX,SAAA;EAAA,QACA,SAAA;EAAA,QACA,eAAA;EAAA,QAED,WAAA;EAL2C;EAelD,gBAAA,IAAoB,SAAA;EAIpB,OAAA,IAAW,WAAA;EAAA;EAQX,QAAA,IAAY,SAAA;EAKD;EAAX,OAAA,IAAW,cAAA;EAKX,aAAA,IAAiB,iBAAA;EAYoB;;;;;EAArC,SAAA,CAAU,SAAA,EAAW,aAAA,GAAgB,YAAA;EAgCK;;;;;;;EAAA,OArBnC,MAAA,CAAO,EAAA,EAAI,SAAA;IAAa;EAAA;IAAS,GAAA,GAAM,kBAAA;EAAA,gBAAwC,UAAA;EAiEnE;EAAA,OA5CZ,SAAA,CAAU,GAAA,EAAK,KAAA,eAAoB,UAAA;EAsDnC;;;;;EAAA,OA7CA,YAAA,CAAa,KAAA,EAAO,SAAA,CAAU,gBAAA,gBAAgC,UAAA;EA1FhC;;;;EAAA,OA4G9B,UAAA,CAAW,MAAA,EAAQ,OAAA,CAAQ,cAAA,IAAkB,UAAA,GAAa,UAAA;EAtG/C;;;;;;EAAA,OAqHX,aAAA,CACL,EAAA,EAAI,SAAA;IACF;EAAA;IAAS,GAAA,GAAM,kBAAA;EAAA,IAChB,sBAAA;EA3GH;EAAA,OAmHO,gBAAA,CACL,GAAA,EAAK,KAAA,EACL,IAAA;IAAQ,GAAA,GAAM,kBAAA;EAAA,IACb,sBAAA;EAzGH;EAAA,OA8GO,mBAAA,CAAoB,KAAA,EAAO,SAAA,CAAU,gBAAA,IAAoB,sBAAA;AAAA;;;;;;KAUtD,UAAA,GAAa,cAAc;;;;;;;iBAQ9B,kBAAA,CACP,MAAA,EAAQ,SAAA,GAAY,KAAA,GAAQ,SAAA,CAAU,gBAAA,IAAoB,OAAA,CAAQ,cAAA,IAAkB,UAAA,EACpF,IAAA;EAAQ,GAAA,GAAM,kBAAA;AAAA,gBACD,UAAA;;;;;;;iBAeN,mBAAA,CACP,MAAA,EAAQ,SAAA,GAAY,KAAA,GAAQ,SAAA,CAAU,gBAAA,IAAoB,OAAA,CAAQ,cAAA,IAAkB,UAAA,EACpF,IAAA;EAAQ,GAAA,GAAM,kBAAA;AAAA,IACb,sBAAA;AAAA,cASU,UAAA,SAAU,kBAAA;;;;;;;;;;;;;;;iBAgBP,YAAA,CAAa,KAAA,YAAiB,KAAA,IAAS,UAAU"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"column_lazy.js","names":[],"sources":["../../src/columns/column_lazy.ts"],"sourcesContent":["import {\n ColumnRegistry,\n createGlobalPObjectId,\n isPColumn,\n isPlRef,\n PlRef,\n PObjectId,\n type ColumnUniversalId,\n type LeafEntry,\n type PColumn,\n type PColumnSpec,\n type SpecOverrides,\n type SpecQuery,\n} from \"@milaboratories/pl-model-common\";\nimport type { GlobalCfgRenderCtx, PColumnDataUniversal } from \"../render/internal\";\nimport { getCtxProviders } from \"./column_providers\";\nimport { isNil } from \"es-toolkit\";\nimport { LRUCache } from \"lru-cache\";\nimport { TreeNodeAccessor } from \"../render\";\nimport type {\n ColumnFieldStatus,\n ColumnRecipe,\n ColumnResolutionStatus,\n} from \"./column_recipes/types\";\nimport { ColumnOverriddenRecipe } from \"./column_recipes/column_overrided_recipe\";\n\nexport type ColumnLazyId = PObjectId;\nexport type ColumnLazyData = undefined | PColumnDataUniversal;\nexport type { ColumnFieldStatus, ColumnResolutionStatus } from \"./column_recipes/types\";\n\n/**\n * Thrown by leaf-recipe factories when the requested column is provably\n * absent in the active render ctx — i.e. every relevant accessor reports\n * `inputsLocked` and the column did not appear. Distinct from the\n * `undefined` return that the factories still use for the \"resolving\" case\n * (the column may still appear later).\n *\n * Catch at the boundary (resolver / filter+sort wiring / data-table assembly)\n * when partial absence should be surfaced to the user rather than silently\n * producing an empty result.\n */\nexport class ColumnAbsentError extends Error {\n constructor(public readonly id: ColumnUniversalId) {\n super(`Column is absent in the active render ctx and will not appear: ${id}`);\n this.name = \"ColumnAbsentError\";\n }\n}\n\n/**\n * ColumnLazy is the leaf-recipe building block: a {@link ColumnRecipe} whose\n * `id` is a bare {@link PObjectId} and whose readers are bound to a single\n * tree-accessor leaf. Layered encodings (Overridden / Discovered / Filtered)\n * are reified through their dedicated recipe classes and reference leaf\n * columns by id.\n */\nexport class ColumnLazyImpl implements ColumnRecipe<PObjectId> {\n private specCache?: { readonly value: PColumnSpec };\n private dataCache?: { readonly value: ColumnLazyData };\n private dataStatusCache?: { readonly value: ColumnFieldStatus };\n\n private constructor(\n public readonly id: PObjectId,\n private readonly options: {\n getSpec: () => PColumnSpec;\n getData: () => ColumnLazyData;\n getDataStatus: () => ColumnFieldStatus;\n },\n ) {}\n\n /** A leaf-recipe references exactly one column — its own id. */\n getReferencedIds(): PObjectId[] {\n return [this.id];\n }\n\n getSpec(): PColumnSpec {\n if (this.specCache === undefined) {\n this.specCache = { value: this.options.getSpec() };\n }\n return this.specCache.value;\n }\n\n /** Leaf-shaped — points straight at `this.id`. */\n getQuery(): SpecQuery {\n return { type: \"column\", column: this.id } as SpecQuery;\n }\n\n /** Leaf-only: not on the recipe interface — only the leaf can read data directly. */\n getData(): ColumnLazyData {\n if (this.dataCache === undefined) this.dataCache = { value: this.options.getData() };\n return this.dataCache.value;\n }\n\n getDataStatus(): ColumnFieldStatus {\n if (this.dataStatusCache === undefined) {\n this.dataStatusCache = { value: this.options.getDataStatus() };\n }\n return this.dataStatusCache.value;\n }\n\n /**\n * Overlay overrides → produces a {@link ColumnOverriddenRecipe} wrapping\n * this leaf. ColumnLazy itself stays bare (id remains a plain PObjectId);\n * layering lives entirely in the recipe wrappers.\n */\n withSpecs(overrides: SpecOverrides): ColumnRecipe {\n return ColumnOverriddenRecipe.wrap(this, overrides);\n }\n\n /**\n * Resolve via the ambient {@link ColumnRegistry}. Spec is resolved eagerly:\n * returns `undefined` if the leaf isn't reachable yet (resolving). Throws\n * {@link ColumnAbsentError} when every relevant accessor is `inputsLocked`\n * and the column did not appear — the column will not exist in this ctx.\n * Data and dataStatus stay lazy.\n */\n static fromId(id: PObjectId, { ctx }: { ctx?: GlobalCfgRenderCtx } = {}): undefined | ColumnLazy {\n const registry = new ColumnRegistry(getCtxProviders({ ctx }));\n const leaf = registry.resolve(id);\n if (isNil(leaf)) {\n if (registry.isFinal()) throw new ColumnAbsentError(id);\n return undefined;\n }\n const spec = readSpecAccessor(leaf);\n if (isNil(spec)) {\n if (leaf.accessor.getInputsLocked()) throw new ColumnAbsentError(id);\n return undefined;\n }\n if (!spec.hasData()) return undefined;\n return new ColumnLazyImpl(id, {\n getSpec: () => spec.getDataAsJson<PColumnSpec>(),\n getData: () => readDataAccessor(leaf),\n getDataStatus: () => readDataStatus(leaf),\n });\n }\n\n /** {@link PlRef} wrapper over {@link fromId}. */\n static fromPlRef(ref: PlRef): undefined | ColumnLazy {\n return ColumnLazyImpl.fromId(createGlobalPObjectId(ref.blockId, ref.name));\n }\n\n /**\n * Bind directly to an accessor-backed {@link LeafEntry} — no registry.\n * Throws {@link ColumnAbsentError} if the leaf has no spec field and its\n * accessor is `inputsLocked`. Returns `undefined` while still resolving.\n */\n static fromAccessor(entry: LeafEntry<TreeNodeAccessor>): undefined | ColumnLazy {\n const spec = readSpecAccessor(entry);\n if (isNil(spec)) {\n if (entry.accessor.getInputsLocked()) throw new ColumnAbsentError(entry.id);\n return undefined;\n }\n if (!spec.hasData()) return undefined;\n return new ColumnLazyImpl(entry.id, {\n getSpec: () => spec.getDataAsJson<PColumnSpec>(),\n getData: () => readDataAccessor(entry),\n getDataStatus: () => readDataStatus(entry),\n });\n }\n\n /**\n * Wrap a materialised {@link PColumn}. If the input is already a\n * {@link ColumnLazy} it is returned as-is.\n */\n static fromColumn(column: PColumn<ColumnLazyData> | ColumnLazy): ColumnLazy {\n if (column instanceof ColumnLazyImpl) return column;\n return new ColumnLazyImpl(column.id, {\n getSpec: () => column.spec,\n getData: () => column.data,\n getDataStatus: () => \"present\",\n });\n }\n\n /**\n * Distinguishes `present` / `resolving` / `absent` for a {@link PObjectId}\n * in the active render ctx. Falls back to the registry's `isFinal()`\n * when the id has no entry — only then we can say `absent` instead of\n * `resolving`.\n */\n static getStatusById(\n id: PObjectId,\n { ctx }: { ctx?: GlobalCfgRenderCtx } = {},\n ): ColumnResolutionStatus {\n const registry = new ColumnRegistry(getCtxProviders({ ctx }));\n const leaf = registry.resolve(id);\n if (isNil(leaf)) return registry.isFinal() ? \"absent\" : \"resolving\";\n return getLeafEntryStatus(leaf);\n }\n\n /** {@link PlRef} wrapper over {@link getStatusById}. */\n static getStatusByPlRef(\n ref: PlRef,\n opts: { ctx?: GlobalCfgRenderCtx } = {},\n ): ColumnResolutionStatus {\n return ColumnLazyImpl.getStatusById(createGlobalPObjectId(ref.blockId, ref.name), opts);\n }\n\n /** No registry — reads straight off the entry's accessor. */\n static getStatusByAccessor(entry: LeafEntry<TreeNodeAccessor>): ColumnResolutionStatus {\n return getLeafEntryStatus(entry);\n }\n}\n\n/**\n * Public type alias — `ColumnLazy` (the type) refers to the underlying\n * {@link ColumnLazyImpl} class instance. `ColumnLazy` (the value) is the\n * callable below with the static factories attached.\n */\nexport type ColumnLazy = ColumnLazyImpl;\n\n/**\n * Unified dispatcher — picks the right `ColumnLazyImpl.fromX` by source\n * shape. For ambiguous inputs callers can still use the explicit factories\n * (also attached as properties: `ColumnLazy.fromId`, `.fromPlRef`,\n * `.fromAccessor`, `.fromColumn`).\n */\nfunction ColumnLazyDispatch(\n source: PObjectId | PlRef | LeafEntry<TreeNodeAccessor> | PColumn<ColumnLazyData> | ColumnLazy,\n opts: { ctx?: GlobalCfgRenderCtx } = {},\n): undefined | ColumnLazy {\n if (typeof source === \"string\") return ColumnLazyImpl.fromId(source, opts);\n if (source instanceof ColumnLazyImpl) return source;\n if (\"accessor\" in source) return ColumnLazyImpl.fromAccessor(source);\n if (isPlRef(source)) return ColumnLazyImpl.fromPlRef(source);\n if (isPColumn(source)) return ColumnLazyImpl.fromColumn(source);\n throw new Error(\"ColumnLazy: unknown source shape\");\n}\n\n/**\n * Polymorphic counterpart to {@link ColumnLazyDispatch}: returns the\n * {@link ColumnResolutionStatus} for any factory input without constructing\n * the recipe. For already-materialised sources ({@link PColumn} value,\n * existing {@link ColumnLazy}) status is `present` by construction.\n */\nfunction ColumnLazyGetStatus(\n source: PObjectId | PlRef | LeafEntry<TreeNodeAccessor> | PColumn<ColumnLazyData> | ColumnLazy,\n opts: { ctx?: GlobalCfgRenderCtx } = {},\n): ColumnResolutionStatus {\n if (typeof source === \"string\") return ColumnLazyImpl.getStatusById(source, opts);\n if (source instanceof ColumnLazyImpl) return \"present\";\n if (\"accessor\" in source) return ColumnLazyImpl.getStatusByAccessor(source);\n if (isPlRef(source)) return ColumnLazyImpl.getStatusByPlRef(source, opts);\n if (isPColumn(source)) return \"present\";\n throw new Error(\"ColumnLazy.getStatus: unknown source shape\");\n}\n\nexport const ColumnLazy = Object.assign(ColumnLazyDispatch, {\n fromId: ColumnLazyImpl.fromId,\n fromPlRef: ColumnLazyImpl.fromPlRef,\n fromAccessor: ColumnLazyImpl.fromAccessor,\n fromColumn: ColumnLazyImpl.fromColumn,\n getStatus: ColumnLazyGetStatus,\n getStatusById: ColumnLazyImpl.getStatusById,\n getStatusByPlRef: ColumnLazyImpl.getStatusByPlRef,\n getStatusByAccessor: ColumnLazyImpl.getStatusByAccessor,\n});\n\n/**\n * Type-guard narrowing to the leaf recipe. Prefer this over\n * `instanceof ColumnLazyImpl` — `ColumnLazy` (the value) is the dispatcher\n * with statics, not the class, so `instanceof` requires the impl symbol.\n */\nexport function isColumnLazy(value: unknown): value is ColumnLazy {\n return value instanceof ColumnLazyImpl;\n}\n\nconst readSpecAccessor = memoizeByEntry(\n ({ accessor, name }: LeafEntry<TreeNodeAccessor>): undefined | TreeNodeAccessor =>\n accessor.traverse({ field: `${name}.spec`, assertFieldType: \"Input\", ignoreError: true }),\n);\n\n/**\n * Per-entry counterpart to {@link readDataStatus}: tells whether the leaf's\n * **spec** can be read in this ctx, and — for the negative cases —\n * distinguishes `resolving` from `absent` via `getInputsLocked()`.\n *\n * - spec field not yet on the entry's accessor + inputs locked → `absent`\n * - spec field not yet on the entry's accessor + still resolving → `resolving`\n * - spec resource present but bytes not yet written → `resolving`\n * (transient — the spec resource is connected, just unfilled)\n * - spec resource present and `hasData()` → `present`\n */\nfunction getLeafEntryStatus(entry: LeafEntry<TreeNodeAccessor>): ColumnResolutionStatus {\n const spec = readSpecAccessor(entry);\n if (isNil(spec)) return entry.accessor.getInputsLocked() ? \"absent\" : \"resolving\";\n if (!spec.hasData()) return \"resolving\";\n return \"present\";\n}\n\nconst readDataAccessor = memoizeByEntry(\n ({ accessor, name }: LeafEntry<TreeNodeAccessor>): undefined | TreeNodeAccessor =>\n accessor.traverse({ field: `${name}.data`, assertFieldType: \"Input\", ignoreError: true }),\n);\n\nconst readDataStatus = memoizeByEntry(\n ({ accessor, name }: LeafEntry<TreeNodeAccessor>): ColumnFieldStatus => {\n if (accessor.listInputFields().includes(`${name}.data`)) return \"present\";\n return accessor.getInputsLocked() ? \"absent\" : \"resolving\";\n },\n);\n\nfunction memoizeByEntry<R>(\n fn: (entry: LeafEntry<TreeNodeAccessor>) => R,\n): (entry: LeafEntry<TreeNodeAccessor>) => R {\n const cache = new LRUCache<string, { value: R }>({ max: 1000 });\n return (entry) => {\n const key = `${entry.accessor.handle}:${entry.name}`;\n let hit = cache.get(key);\n if (!hit) cache.set(key, (hit = { value: fn(entry) }));\n return hit.value;\n };\n}\n"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"expand_by_partition.cjs","names":["getLeafColumnData","TreeNodeAccessor","getUniquePartitionKeys","Annotation","ColumnFilteredRecipe","ColumnOverriddenRecipe"],"sources":["../../src/columns/expand_by_partition.ts"],"sourcesContent":["import type {\n AxisFilterByIdx,\n AxisFilterValue,\n AxisId,\n TraceEntry,\n} from \"@milaboratories/pl-model-common\";\nimport {\n Annotation,\n canonicalizeAxisId,\n getAxisId,\n isDataInfoEntries,\n readAnnotation,\n} from \"@milaboratories/pl-model-common\";\nimport { TreeNodeAccessor } from \"../render/accessor\";\nimport { getUniquePartitionKeys } from \"../render/util/pcolumn_data\";\nimport type { ColumnRecipe } from \"./column_recipes\";\nimport { ColumnFilteredRecipe } from \"./column_recipes/column_filtered_recipe\";\nimport { ColumnOverriddenRecipe } from \"./column_recipes/column_overrided_recipe\";\nimport { Column } from \"./column\";\nimport { getLeafColumnData } from \"./utils\";\n\n// --- Types ---\n\nexport interface SplitAxis {\n /** Index of the axis in the column's axesSpec to split by. */\n readonly idx: number;\n}\n\nexport interface ExpandByPartitionOpts {\n /** Resolve axis values to human-readable labels. */\n axisValuesLabels?: (axisId: AxisId) => undefined | Record<string | number, string>;\n}\n\n// --- Implementation ---\n\nconst MAX_KEY_COMBINATIONS = 10_000;\n\n/**\n * Expand each input column along the requested partition axes into one\n * {@link ColumnRecipe} per Cartesian combination of unique partition values.\n *\n * Each split is produced as `ColumnOverriddenRecipe.wrap(ColumnFilteredRecipe.wrap(inner, axisFilters), { domain, annotations })`:\n * - `ColumnFilteredRecipe` pins all split axes at once (one wrap call with\n * every `[idx, value]` pair) and removes them from `axesSpec`. The\n * engine performs the data slicing via the `sliceAxes` query node.\n * - `ColumnOverriddenRecipe` overlays a `domain[axisName] = String(value)`\n * entry per split axis and appends a `split:<canonicalAxisId>` trace\n * entry per split axis to the existing `pl7.app/trace` annotation.\n *\n * The recipe id is a canonical\n * `ColumnOverriddenId(source: ColumnFilteredId(source: inner.id, axisFilters), specOverrides)`\n * — distinct per split combination, parseable by `extractPObjectId`, and\n * traversable by recipe walkers.\n *\n * Returns `undefined` when any input's `getData()` is not a\n * {@link TreeNodeAccessor} — partition inspection is not yet possible.\n */\nexport function expandByPartition(\n inputs: Column[],\n splitAxes: SplitAxis[],\n opts?: ExpandByPartitionOpts,\n): ColumnRecipe[] | undefined {\n if (splitAxes.length === 0) {\n return [...inputs];\n }\n\n const splitAxisIdxs = splitAxes.map((a) => a.idx).sort((a, b) => a - b);\n const maxSplitIdx = splitAxisIdxs[splitAxisIdxs.length - 1];\n\n const result: ColumnRecipe[] = [];\n\n for (const inner of inputs) {\n const data = getLeafColumnData(inner);\n // Partition inspection requires either a live tree-accessor or already\n // parsed DataInfoEntries. Anything else means the input is not yet ready.\n if (!(data instanceof TreeNodeAccessor) && !isDataInfoEntries(data)) {\n return undefined;\n }\n\n const uniqueKeys = getUniquePartitionKeys(data);\n if (uniqueKeys === undefined) return undefined;\n\n if (maxSplitIdx >= uniqueKeys.length) {\n throw new Error(\n `Not enough partition keys (${uniqueKeys.length}) for requested split axes (max index ${maxSplitIdx}) in column ${inner.getSpec().name}`,\n );\n }\n\n const spec = inner.getSpec();\n const axesSpec = spec.axesSpec;\n const splitAxisSpecs = splitAxisIdxs.map((idx) => axesSpec[idx]);\n const splitAxisIds = splitAxisSpecs.map((axisSpec) => getAxisId(axisSpec));\n const axesLabels = splitAxisIds.map((axisId) => opts?.axisValuesLabels?.(axisId));\n\n const existingTraceRaw = readAnnotation(spec, Annotation.Trace);\n const baseTrace: TraceEntry[] = existingTraceRaw\n ? ((JSON.parse(existingTraceRaw) as TraceEntry[]) ?? [])\n : [];\n\n const keyCombinations = generateKeyCombinations(uniqueKeys, splitAxisIdxs);\n if (keyCombinations.length === 0) continue;\n\n for (const keyCombo of keyCombinations) {\n const axisFilters: AxisFilterByIdx[] = keyCombo.map(\n (value, sAxisIdx): AxisFilterByIdx => [splitAxisIdxs[sAxisIdx], value as AxisFilterValue],\n );\n\n const domain: Record<string, string> = {};\n const traceEntries: TraceEntry[] = [];\n for (let sAxisIdx = 0; sAxisIdx < keyCombo.length; sAxisIdx++) {\n const value = keyCombo[sAxisIdx];\n const axisSpec = splitAxisSpecs[sAxisIdx];\n const axisId = splitAxisIds[sAxisIdx];\n const labelMap = axesLabels[sAxisIdx];\n const label = labelMap?.[value] ?? String(value);\n domain[axisSpec.name] = String(value);\n traceEntries.push({\n type: `split:${canonicalizeAxisId(axisId)}`,\n label,\n importance: 1_000_000,\n });\n }\n\n const filtered = ColumnFilteredRecipe.wrap(inner, axisFilters);\n const overrided = ColumnOverriddenRecipe.wrap(filtered, {\n domain,\n annotations: {\n [Annotation.Trace]: JSON.stringify([...baseTrace, ...traceEntries]),\n },\n });\n\n result.push(overrided);\n }\n }\n\n return result;\n}\n\nfunction generateKeyCombinations(\n uniqueKeys: (string | number)[][],\n splitAxisIdxs: number[],\n): (string | number)[][] {\n const combinations: (string | number)[][] = [];\n\n function generate(currentCombo: (string | number)[], sAxisIdx: number): void {\n if (sAxisIdx >= splitAxisIdxs.length) {\n combinations.push([...currentCombo]);\n if (combinations.length > MAX_KEY_COMBINATIONS) {\n throw new Error(\"Too many key combinations, aborting.\");\n }\n return;\n }\n\n const axisIdx = splitAxisIdxs[sAxisIdx];\n if (axisIdx >= uniqueKeys.length) {\n throw new Error(\n `Axis index ${axisIdx} out of bounds for unique keys array (length ${uniqueKeys.length})`,\n );\n }\n\n const axisValues = uniqueKeys[axisIdx];\n if (!axisValues || axisValues.length === 0) {\n combinations.length = 0;\n return;\n }\n\n for (const val of axisValues) {\n currentCombo.push(val);\n generate(currentCombo, sAxisIdx + 1);\n currentCombo.pop();\n }\n }\n\n generate([], 0);\n return combinations;\n}\n"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"expand_by_partition.d.ts","names":[],"sources":["../../src/columns/expand_by_partition.ts"],"mappings":";;;;;UAuBiB,SAAA;;WAEN,GAAG;AAAA;AAAA,UAGG,qBAAA;EAHH;EAKZ,gBAAA,IAAoB,MAAA,EAAQ,MAAA,iBAAuB,MAAM;AAAA;;;;;;;;;AAAA;AA2B3D;;;;;;;;;;;iBAAgB,iBAAA,CACd,MAAA,EAAQ,MAAA,IACR,SAAA,EAAW,SAAA,IACX,IAAA,GAAO,qBAAA,GACN,YAAA"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"expand_by_partition.js","names":[],"sources":["../../src/columns/expand_by_partition.ts"],"sourcesContent":["import type {\n AxisFilterByIdx,\n AxisFilterValue,\n AxisId,\n TraceEntry,\n} from \"@milaboratories/pl-model-common\";\nimport {\n Annotation,\n canonicalizeAxisId,\n getAxisId,\n isDataInfoEntries,\n readAnnotation,\n} from \"@milaboratories/pl-model-common\";\nimport { TreeNodeAccessor } from \"../render/accessor\";\nimport { getUniquePartitionKeys } from \"../render/util/pcolumn_data\";\nimport type { ColumnRecipe } from \"./column_recipes\";\nimport { ColumnFilteredRecipe } from \"./column_recipes/column_filtered_recipe\";\nimport { ColumnOverriddenRecipe } from \"./column_recipes/column_overrided_recipe\";\nimport { Column } from \"./column\";\nimport { getLeafColumnData } from \"./utils\";\n\n// --- Types ---\n\nexport interface SplitAxis {\n /** Index of the axis in the column's axesSpec to split by. */\n readonly idx: number;\n}\n\nexport interface ExpandByPartitionOpts {\n /** Resolve axis values to human-readable labels. */\n axisValuesLabels?: (axisId: AxisId) => undefined | Record<string | number, string>;\n}\n\n// --- Implementation ---\n\nconst MAX_KEY_COMBINATIONS = 10_000;\n\n/**\n * Expand each input column along the requested partition axes into one\n * {@link ColumnRecipe} per Cartesian combination of unique partition values.\n *\n * Each split is produced as `ColumnOverriddenRecipe.wrap(ColumnFilteredRecipe.wrap(inner, axisFilters), { domain, annotations })`:\n * - `ColumnFilteredRecipe` pins all split axes at once (one wrap call with\n * every `[idx, value]` pair) and removes them from `axesSpec`. The\n * engine performs the data slicing via the `sliceAxes` query node.\n * - `ColumnOverriddenRecipe` overlays a `domain[axisName] = String(value)`\n * entry per split axis and appends a `split:<canonicalAxisId>` trace\n * entry per split axis to the existing `pl7.app/trace` annotation.\n *\n * The recipe id is a canonical\n * `ColumnOverriddenId(source: ColumnFilteredId(source: inner.id, axisFilters), specOverrides)`\n * — distinct per split combination, parseable by `extractPObjectId`, and\n * traversable by recipe walkers.\n *\n * Returns `undefined` when any input's `getData()` is not a\n * {@link TreeNodeAccessor} — partition inspection is not yet possible.\n */\nexport function expandByPartition(\n inputs: Column[],\n splitAxes: SplitAxis[],\n opts?: ExpandByPartitionOpts,\n): ColumnRecipe[] | undefined {\n if (splitAxes.length === 0) {\n return [...inputs];\n }\n\n const splitAxisIdxs = splitAxes.map((a) => a.idx).sort((a, b) => a - b);\n const maxSplitIdx = splitAxisIdxs[splitAxisIdxs.length - 1];\n\n const result: ColumnRecipe[] = [];\n\n for (const inner of inputs) {\n const data = getLeafColumnData(inner);\n // Partition inspection requires either a live tree-accessor or already\n // parsed DataInfoEntries. Anything else means the input is not yet ready.\n if (!(data instanceof TreeNodeAccessor) && !isDataInfoEntries(data)) {\n return undefined;\n }\n\n const uniqueKeys = getUniquePartitionKeys(data);\n if (uniqueKeys === undefined) return undefined;\n\n if (maxSplitIdx >= uniqueKeys.length) {\n throw new Error(\n `Not enough partition keys (${uniqueKeys.length}) for requested split axes (max index ${maxSplitIdx}) in column ${inner.getSpec().name}`,\n );\n }\n\n const spec = inner.getSpec();\n const axesSpec = spec.axesSpec;\n const splitAxisSpecs = splitAxisIdxs.map((idx) => axesSpec[idx]);\n const splitAxisIds = splitAxisSpecs.map((axisSpec) => getAxisId(axisSpec));\n const axesLabels = splitAxisIds.map((axisId) => opts?.axisValuesLabels?.(axisId));\n\n const existingTraceRaw = readAnnotation(spec, Annotation.Trace);\n const baseTrace: TraceEntry[] = existingTraceRaw\n ? ((JSON.parse(existingTraceRaw) as TraceEntry[]) ?? [])\n : [];\n\n const keyCombinations = generateKeyCombinations(uniqueKeys, splitAxisIdxs);\n if (keyCombinations.length === 0) continue;\n\n for (const keyCombo of keyCombinations) {\n const axisFilters: AxisFilterByIdx[] = keyCombo.map(\n (value, sAxisIdx): AxisFilterByIdx => [splitAxisIdxs[sAxisIdx], value as AxisFilterValue],\n );\n\n const domain: Record<string, string> = {};\n const traceEntries: TraceEntry[] = [];\n for (let sAxisIdx = 0; sAxisIdx < keyCombo.length; sAxisIdx++) {\n const value = keyCombo[sAxisIdx];\n const axisSpec = splitAxisSpecs[sAxisIdx];\n const axisId = splitAxisIds[sAxisIdx];\n const labelMap = axesLabels[sAxisIdx];\n const label = labelMap?.[value] ?? String(value);\n domain[axisSpec.name] = String(value);\n traceEntries.push({\n type: `split:${canonicalizeAxisId(axisId)}`,\n label,\n importance: 1_000_000,\n });\n }\n\n const filtered = ColumnFilteredRecipe.wrap(inner, axisFilters);\n const overrided = ColumnOverriddenRecipe.wrap(filtered, {\n domain,\n annotations: {\n [Annotation.Trace]: JSON.stringify([...baseTrace, ...traceEntries]),\n },\n });\n\n result.push(overrided);\n }\n }\n\n return result;\n}\n\nfunction generateKeyCombinations(\n uniqueKeys: (string | number)[][],\n splitAxisIdxs: number[],\n): (string | number)[][] {\n const combinations: (string | number)[][] = [];\n\n function generate(currentCombo: (string | number)[], sAxisIdx: number): void {\n if (sAxisIdx >= splitAxisIdxs.length) {\n combinations.push([...currentCombo]);\n if (combinations.length > MAX_KEY_COMBINATIONS) {\n throw new Error(\"Too many key combinations, aborting.\");\n }\n return;\n }\n\n const axisIdx = splitAxisIdxs[sAxisIdx];\n if (axisIdx >= uniqueKeys.length) {\n throw new Error(\n `Axis index ${axisIdx} out of bounds for unique keys array (length ${uniqueKeys.length})`,\n );\n }\n\n const axisValues = uniqueKeys[axisIdx];\n if (!axisValues || axisValues.length === 0) {\n combinations.length = 0;\n return;\n }\n\n for (const val of axisValues) {\n currentCombo.push(val);\n generate(currentCombo, sAxisIdx + 1);\n currentCombo.pop();\n }\n }\n\n generate([], 0);\n return combinations;\n}\n"],"mappings":";;;;;;;AAmCA,MAAM,uBAAuB;;;;;;;;;;;;;;;;;;;;;AAsB7B,SAAgB,kBACd,QACA,WACA,MAC4B;CAC5B,IAAI,UAAU,WAAW,GACvB,OAAO,CAAC,GAAG,MAAM;CAGnB,MAAM,gBAAgB,UAAU,KAAK,MAAM,EAAE,GAAG,CAAC,CAAC,MAAM,GAAG,MAAM,IAAI,CAAC;CACtE,MAAM,cAAc,cAAc,cAAc,SAAS;CAEzD,MAAM,SAAyB,CAAC;CAEhC,KAAK,MAAM,SAAS,QAAQ;EAC1B,MAAM,OAAO,kBAAkB,KAAK;EAGpC,IAAI,EAAE,gBAAgB,qBAAqB,CAAC,kBAAkB,IAAI,GAChE;EAGF,MAAM,aAAa,uBAAuB,IAAI;EAC9C,IAAI,eAAe,KAAA,GAAW,OAAO,KAAA;EAErC,IAAI,eAAe,WAAW,QAC5B,MAAM,IAAI,MACR,8BAA8B,WAAW,OAAO,wCAAwC,YAAY,cAAc,MAAM,QAAQ,CAAC,CAAC,MACpI;EAGF,MAAM,OAAO,MAAM,QAAQ;EAC3B,MAAM,WAAW,KAAK;EACtB,MAAM,iBAAiB,cAAc,KAAK,QAAQ,SAAS,IAAI;EAC/D,MAAM,eAAe,eAAe,KAAK,aAAa,UAAU,QAAQ,CAAC;EACzE,MAAM,aAAa,aAAa,KAAK,WAAW,MAAM,mBAAmB,MAAM,CAAC;EAEhF,MAAM,mBAAmB,eAAe,MAAM,WAAW,KAAK;EAC9D,MAAM,YAA0B,mBAC1B,KAAK,MAAM,gBAAgB,KAAsB,CAAC,IACpD,CAAC;EAEL,MAAM,kBAAkB,wBAAwB,YAAY,aAAa;EACzE,IAAI,gBAAgB,WAAW,GAAG;EAElC,KAAK,MAAM,YAAY,iBAAiB;GACtC,MAAM,cAAiC,SAAS,KAC7C,OAAO,aAA8B,CAAC,cAAc,WAAW,KAAwB,CAC1F;GAEA,MAAM,SAAiC,CAAC;GACxC,MAAM,eAA6B,CAAC;GACpC,KAAK,IAAI,WAAW,GAAG,WAAW,SAAS,QAAQ,YAAY;IAC7D,MAAM,QAAQ,SAAS;IACvB,MAAM,WAAW,eAAe;IAChC,MAAM,SAAS,aAAa;IAE5B,MAAM,QADW,WAAW,SACN,GAAG,UAAU,OAAO,KAAK;IAC/C,OAAO,SAAS,QAAQ,OAAO,KAAK;IACpC,aAAa,KAAK;KAChB,MAAM,SAAS,mBAAmB,MAAM;KACxC;KACA,YAAY;IACd,CAAC;GACH;GAEA,MAAM,WAAW,qBAAqB,KAAK,OAAO,WAAW;GAC7D,MAAM,YAAY,uBAAuB,KAAK,UAAU;IACtD;IACA,aAAa,GACV,WAAW,QAAQ,KAAK,UAAU,CAAC,GAAG,WAAW,GAAG,YAAY,CAAC,EACpE;GACF,CAAC;GAED,OAAO,KAAK,SAAS;EACvB;CACF;CAEA,OAAO;AACT;AAEA,SAAS,wBACP,YACA,eACuB;CACvB,MAAM,eAAsC,CAAC;CAE7C,SAAS,SAAS,cAAmC,UAAwB;EAC3E,IAAI,YAAY,cAAc,QAAQ;GACpC,aAAa,KAAK,CAAC,GAAG,YAAY,CAAC;GACnC,IAAI,aAAa,SAAS,sBACxB,MAAM,IAAI,MAAM,sCAAsC;GAExD;EACF;EAEA,MAAM,UAAU,cAAc;EAC9B,IAAI,WAAW,WAAW,QACxB,MAAM,IAAI,MACR,cAAc,QAAQ,+CAA+C,WAAW,OAAO,EACzF;EAGF,MAAM,aAAa,WAAW;EAC9B,IAAI,CAAC,cAAc,WAAW,WAAW,GAAG;GAC1C,aAAa,SAAS;GACtB;EACF;EAEA,KAAK,MAAM,OAAO,YAAY;GAC5B,aAAa,KAAK,GAAG;GACrB,SAAS,cAAc,WAAW,CAAC;GACnC,aAAa,IAAI;EACnB;CACF;CAEA,SAAS,CAAC,GAAG,CAAC;CACd,OAAO;AACT"}
|