@platforma-sdk/model 1.54.7 → 1.54.8
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/annotations/converter.cjs +2 -2
- package/dist/annotations/converter.cjs.map +1 -1
- package/dist/annotations/converter.js +2 -2
- package/dist/annotations/converter.js.map +1 -1
- package/dist/components/PlDataTable/index.d.ts +1 -1
- package/dist/components/PlDataTable/index.d.ts.map +1 -1
- package/dist/components/PlDataTable/state-migration.cjs +1 -1
- package/dist/components/PlDataTable/state-migration.cjs.map +1 -1
- package/dist/components/PlDataTable/state-migration.js +2 -2
- package/dist/components/PlDataTable/state-migration.js.map +1 -1
- package/dist/components/PlDataTable/table.cjs +2 -3
- package/dist/components/PlDataTable/table.cjs.map +1 -1
- package/dist/components/PlDataTable/table.d.ts.map +1 -1
- package/dist/components/PlDataTable/table.js +2 -3
- package/dist/components/PlDataTable/table.js.map +1 -1
- package/dist/components/PlDataTable/v5.d.ts +6 -5
- package/dist/components/PlDataTable/v5.d.ts.map +1 -1
- package/dist/filters/distill.cjs +22 -7
- package/dist/filters/distill.cjs.map +1 -1
- package/dist/filters/distill.d.ts +6 -5
- package/dist/filters/distill.d.ts.map +1 -1
- package/dist/filters/distill.js +22 -7
- package/dist/filters/distill.js.map +1 -1
- package/dist/filters/types.d.ts +1 -1
- package/dist/filters/types.d.ts.map +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.js +1 -1
- package/dist/package.json.cjs +1 -1
- package/dist/package.json.js +1 -1
- package/package.json +6 -6
- package/src/annotations/converter.ts +2 -2
- package/src/components/PlDataTable/index.ts +2 -0
- package/src/components/PlDataTable/state-migration.ts +6 -6
- package/src/components/PlDataTable/table.ts +4 -5
- package/src/components/PlDataTable/v5.ts +5 -5
- package/src/filters/distill.test.ts +191 -0
- package/src/filters/distill.ts +26 -18
- package/src/filters/types.ts +0 -1
- package/dist/filters/converters/filterEmpty.cjs +0 -49
- package/dist/filters/converters/filterEmpty.cjs.map +0 -1
- package/dist/filters/converters/filterEmpty.d.ts +0 -4
- package/dist/filters/converters/filterEmpty.d.ts.map +0 -1
- package/dist/filters/converters/filterEmpty.js +0 -46
- package/dist/filters/converters/filterEmpty.js.map +0 -1
- package/src/filters/converters/filterEmpty.test.ts +0 -125
- package/src/filters/converters/filterEmpty.ts +0 -57
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { type FilterSpec, type FilterSpecLeaf
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
1
|
+
import { type FilterSpec, type FilterSpecLeaf } from "@milaboratories/pl-model-common";
|
|
2
|
+
/**
|
|
3
|
+
* Strips non-FilterSpec metadata (whitelist approach) and removes
|
|
4
|
+
* unfilled leaves (type is undefined or any required field is undefined).
|
|
5
|
+
*/
|
|
6
|
+
export declare function distillFilterSpec<T extends FilterSpecLeaf<unknown>>(filter: null | undefined | FilterSpec<T, unknown, unknown>): null | FilterSpec<T>;
|
|
6
7
|
//# sourceMappingURL=distill.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"distill.d.ts","sourceRoot":"","sources":["../../src/filters/distill.ts"],"names":[],"mappings":"AACA,OAAO,
|
|
1
|
+
{"version":3,"file":"distill.d.ts","sourceRoot":"","sources":["../../src/filters/distill.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,UAAU,EAAE,KAAK,cAAc,EAAE,MAAM,iCAAiC,CAAC;AAwDvF;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,CAAC,SAAS,cAAc,CAAC,OAAO,CAAC,EACjE,MAAM,EAAE,IAAI,GAAG,SAAS,GAAG,UAAU,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,GACzD,IAAI,GAAG,UAAU,CAAC,CAAC,CAAC,CAGtB"}
|
package/dist/filters/distill.js
CHANGED
|
@@ -13,6 +13,12 @@ const KNOWN_LEAF_KEYS_TUPLE = [
|
|
|
13
13
|
"substitutionsOnly",
|
|
14
14
|
];
|
|
15
15
|
const KNOWN_LEAF_KEYS = new Set(KNOWN_LEAF_KEYS_TUPLE);
|
|
16
|
+
/** Returns true if the leaf is filled — type is defined and no required fields are undefined. */
|
|
17
|
+
function isFilledLeaf(node) {
|
|
18
|
+
if (node.type == null)
|
|
19
|
+
return false;
|
|
20
|
+
return !Object.values(node).some((v) => v === undefined);
|
|
21
|
+
}
|
|
16
22
|
function distillLeaf(node) {
|
|
17
23
|
const result = {};
|
|
18
24
|
for (const [key, value] of Object.entries(node)) {
|
|
@@ -25,20 +31,29 @@ function distillLeaf(node) {
|
|
|
25
31
|
function distillNode(node) {
|
|
26
32
|
switch (node.type) {
|
|
27
33
|
case "and":
|
|
28
|
-
case "or":
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
34
|
+
case "or": {
|
|
35
|
+
const filtered = node.filters.map(distillNode).filter((f) => f !== null);
|
|
36
|
+
return filtered.length === 0 ? null : { type: node.type, filters: filtered };
|
|
37
|
+
}
|
|
38
|
+
case "not": {
|
|
39
|
+
const inner = distillNode(node.filter);
|
|
40
|
+
return inner === null ? null : { type: "not", filter: inner };
|
|
41
|
+
}
|
|
32
42
|
default:
|
|
43
|
+
if (!isFilledLeaf(node))
|
|
44
|
+
return null;
|
|
33
45
|
return distillLeaf(node);
|
|
34
46
|
}
|
|
35
47
|
}
|
|
36
|
-
/**
|
|
37
|
-
|
|
48
|
+
/**
|
|
49
|
+
* Strips non-FilterSpec metadata (whitelist approach) and removes
|
|
50
|
+
* unfilled leaves (type is undefined or any required field is undefined).
|
|
51
|
+
*/
|
|
52
|
+
function distillFilterSpec(filter) {
|
|
38
53
|
if (filter == null)
|
|
39
54
|
return null;
|
|
40
55
|
return distillNode(filter);
|
|
41
56
|
}
|
|
42
57
|
|
|
43
|
-
export {
|
|
58
|
+
export { distillFilterSpec };
|
|
44
59
|
//# sourceMappingURL=distill.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"distill.js","sources":["../../src/filters/distill.ts"],"sourcesContent":["import { DistributiveKeys, UnionToTuples } from \"@milaboratories/helpers\";\nimport {
|
|
1
|
+
{"version":3,"file":"distill.js","sources":["../../src/filters/distill.ts"],"sourcesContent":["import { DistributiveKeys, UnionToTuples } from \"@milaboratories/helpers\";\nimport { type FilterSpec, type FilterSpecLeaf } from \"@milaboratories/pl-model-common\";\n\n/** All possible field names that can appear in any FilterSpecLeaf variant. */\ntype FilterSpecLeafKey = DistributiveKeys<FilterSpecLeaf<string>>;\n\n/** Compile-time check: every key in the tuple is a valid leaf key (via satisfies). */\nconst KNOWN_LEAF_KEYS_TUPLE: UnionToTuples<FilterSpecLeafKey> = [\n \"n\",\n \"x\",\n \"rhs\",\n \"type\",\n \"value\",\n \"column\",\n \"minDiff\",\n \"maxEdits\",\n \"wildcard\",\n \"replacement\",\n \"substitutionsOnly\",\n];\nconst KNOWN_LEAF_KEYS: Set<FilterSpecLeafKey> = new Set(KNOWN_LEAF_KEYS_TUPLE);\n\n/** Returns true if the leaf is filled — type is defined and no required fields are undefined. */\nfunction isFilledLeaf(node: Record<string, unknown>): boolean {\n if (node.type == null) return false;\n return !Object.values(node).some((v) => v === undefined);\n}\n\nfunction distillLeaf(node: Record<string, unknown>): FilterSpecLeaf<string> {\n const result: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(node)) {\n if (KNOWN_LEAF_KEYS.has(key as FilterSpecLeafKey)) {\n result[key] = value;\n }\n }\n return result as FilterSpecLeaf<string>;\n}\n\nfunction distillNode<T extends FilterSpecLeaf<unknown>>(\n node: FilterSpec<T, unknown, unknown>,\n): FilterSpec<T> | null {\n switch (node.type) {\n case \"and\":\n case \"or\": {\n const filtered = node.filters.map(distillNode).filter((f): f is FilterSpec<T> => f !== null);\n return filtered.length === 0 ? null : { type: node.type, filters: filtered };\n }\n case \"not\": {\n const inner = distillNode(node.filter);\n return inner === null ? null : { type: \"not\", filter: inner };\n }\n default:\n if (!isFilledLeaf(node)) return null;\n return distillLeaf(node);\n }\n}\n\n/**\n * Strips non-FilterSpec metadata (whitelist approach) and removes\n * unfilled leaves (type is undefined or any required field is undefined).\n */\nexport function distillFilterSpec<T extends FilterSpecLeaf<unknown>>(\n filter: null | undefined | FilterSpec<T, unknown, unknown>,\n): null | FilterSpec<T> {\n if (filter == null) return null;\n return distillNode(filter);\n}\n"],"names":[],"mappings":"AAMA;AACA,MAAM,qBAAqB,GAAqC;IAC9D,GAAG;IACH,GAAG;IACH,KAAK;IACL,MAAM;IACN,OAAO;IACP,QAAQ;IACR,SAAS;IACT,UAAU;IACV,UAAU;IACV,aAAa;IACb,mBAAmB;CACpB;AACD,MAAM,eAAe,GAA2B,IAAI,GAAG,CAAC,qBAAqB,CAAC;AAE9E;AACA,SAAS,YAAY,CAAC,IAA6B,EAAA;AACjD,IAAA,IAAI,IAAI,CAAC,IAAI,IAAI,IAAI;AAAE,QAAA,OAAO,KAAK;AACnC,IAAA,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,SAAS,CAAC;AAC1D;AAEA,SAAS,WAAW,CAAC,IAA6B,EAAA;IAChD,MAAM,MAAM,GAA4B,EAAE;AAC1C,IAAA,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE;AAC/C,QAAA,IAAI,eAAe,CAAC,GAAG,CAAC,GAAwB,CAAC,EAAE;AACjD,YAAA,MAAM,CAAC,GAAG,CAAC,GAAG,KAAK;QACrB;IACF;AACA,IAAA,OAAO,MAAgC;AACzC;AAEA,SAAS,WAAW,CAClB,IAAqC,EAAA;AAErC,IAAA,QAAQ,IAAI,CAAC,IAAI;AACf,QAAA,KAAK,KAAK;QACV,KAAK,IAAI,EAAE;YACT,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,KAAyB,CAAC,KAAK,IAAI,CAAC;YAC5F,OAAO,QAAQ,CAAC,MAAM,KAAK,CAAC,GAAG,IAAI,GAAG,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAE;QAC9E;QACA,KAAK,KAAK,EAAE;YACV,MAAM,KAAK,GAAG,WAAW,CAAC,IAAI,CAAC,MAAM,CAAC;AACtC,YAAA,OAAO,KAAK,KAAK,IAAI,GAAG,IAAI,GAAG,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE;QAC/D;AACA,QAAA;AACE,YAAA,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC;AAAE,gBAAA,OAAO,IAAI;AACpC,YAAA,OAAO,WAAW,CAAC,IAAI,CAAC;;AAE9B;AAEA;;;AAGG;AACG,SAAU,iBAAiB,CAC/B,MAA0D,EAAA;IAE1D,IAAI,MAAM,IAAI,IAAI;AAAE,QAAA,OAAO,IAAI;AAC/B,IAAA,OAAO,WAAW,CAAC,MAAM,CAAC;AAC5B;;;;"}
|
package/dist/filters/types.d.ts
CHANGED
|
@@ -5,5 +5,5 @@ export type SimplifiedUniversalPColumnEntry = {
|
|
|
5
5
|
label: string;
|
|
6
6
|
obj: SimplifiedPColumnSpec;
|
|
7
7
|
};
|
|
8
|
-
export type { FilterSpecNode, FilterSpecLeaf, FilterSpec, FilterSpecType, FilterSpecOfType,
|
|
8
|
+
export type { FilterSpecNode, FilterSpecLeaf, FilterSpec, FilterSpecType, FilterSpecOfType, } from "@milaboratories/pl-model-common";
|
|
9
9
|
//# sourceMappingURL=types.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/filters/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,mBAAmB,EAAE,MAAM,iCAAiC,CAAC;AAExF,MAAM,MAAM,qBAAqB,GAAG,IAAI,CAAC,WAAW,EAAE,WAAW,GAAG,aAAa,CAAC,CAAC;AAEnF,MAAM,MAAM,+BAA+B,GAAG;IAC5C,EAAE,EAAE,mBAAmB,CAAC;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,qBAAqB,CAAC;CAC5B,CAAC;AAEF,YAAY,EACV,cAAc,EACd,cAAc,EACd,UAAU,EACV,cAAc,EACd,gBAAgB,
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/filters/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,mBAAmB,EAAE,MAAM,iCAAiC,CAAC;AAExF,MAAM,MAAM,qBAAqB,GAAG,IAAI,CAAC,WAAW,EAAE,WAAW,GAAG,aAAa,CAAC,CAAC;AAEnF,MAAM,MAAM,+BAA+B,GAAG;IAC5C,EAAE,EAAE,mBAAmB,CAAC;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,qBAAqB,CAAC;CAC5B,CAAC;AAEF,YAAY,EACV,cAAc,EACd,cAAc,EACd,UAAU,EACV,cAAc,EACd,gBAAgB,GACjB,MAAM,iCAAiC,CAAC"}
|
package/dist/index.cjs
CHANGED
|
@@ -157,7 +157,7 @@ exports.getRawPlatformaInstance = raw_globals.getRawPlatformaInstance;
|
|
|
157
157
|
exports.filterSpecToSpecQueryExpr = filterToQuery.filterSpecToSpecQueryExpr;
|
|
158
158
|
exports.convertFilterUiToExpressionImpl = filterUiToExpressionImpl.convertFilterUiToExpressionImpl;
|
|
159
159
|
exports.convertFilterUiToExpressions = filterUiToExpressionImpl.convertFilterUiToExpressions;
|
|
160
|
-
exports.
|
|
160
|
+
exports.distillFilterSpec = distill.distillFilterSpec;
|
|
161
161
|
exports.convertFilterSpecsToExpressionSpecs = converter.convertFilterSpecsToExpressionSpecs;
|
|
162
162
|
exports.getAxisUniqueValues = index.getAxisUniqueValues;
|
|
163
163
|
exports.getColumnOrAxisValueLabelsId = index.getColumnOrAxisValueLabelsId;
|
package/dist/index.js
CHANGED
|
@@ -25,7 +25,7 @@ export { CurrentSdkInfo } from './sdk_info.js';
|
|
|
25
25
|
export { getPlatformaApiVersion, getRawPlatformaInstance } from './raw_globals.js';
|
|
26
26
|
export { filterSpecToSpecQueryExpr } from './filters/converters/filterToQuery.js';
|
|
27
27
|
export { convertFilterUiToExpressionImpl, convertFilterUiToExpressions } from './filters/converters/filterUiToExpressionImpl.js';
|
|
28
|
-
export {
|
|
28
|
+
export { distillFilterSpec } from './filters/distill.js';
|
|
29
29
|
export { convertFilterSpecsToExpressionSpecs } from './annotations/converter.js';
|
|
30
30
|
export { getAxisUniqueValues, getColumnOrAxisValueLabelsId, getColumnSpecById, getColumnUniqueValues, getColumnsFull, getRequestColumnsFromSelectedSources, getSingleColumnData, getUniqueSourceValuesWithLabels } from './pframe_utils/index.js';
|
|
31
31
|
export * from '@milaboratories/pl-model-common';
|
package/dist/package.json.cjs
CHANGED
package/dist/package.json.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@platforma-sdk/model",
|
|
3
|
-
"version": "1.54.
|
|
3
|
+
"version": "1.54.8",
|
|
4
4
|
"description": "Platforma.bio SDK / Block Model",
|
|
5
5
|
"files": [
|
|
6
6
|
"./dist/**/*",
|
|
@@ -25,9 +25,9 @@
|
|
|
25
25
|
"utility-types": "^3.11.0",
|
|
26
26
|
"zod": "~3.23.8",
|
|
27
27
|
"@milaboratories/helpers": "1.13.4",
|
|
28
|
-
"@milaboratories/pl-
|
|
29
|
-
"@milaboratories/ptabler-expression-js": "1.1.
|
|
30
|
-
"@milaboratories/pl-
|
|
28
|
+
"@milaboratories/pl-error-like": "1.12.8",
|
|
29
|
+
"@milaboratories/ptabler-expression-js": "1.1.18",
|
|
30
|
+
"@milaboratories/pl-model-common": "1.24.8"
|
|
31
31
|
},
|
|
32
32
|
"devDependencies": {
|
|
33
33
|
"@vitest/coverage-istanbul": "^4.0.16",
|
|
@@ -35,8 +35,8 @@
|
|
|
35
35
|
"typescript": "~5.6.3",
|
|
36
36
|
"vitest": "^4.0.16",
|
|
37
37
|
"@milaboratories/build-configs": "1.4.4",
|
|
38
|
-
"@milaboratories/ts-
|
|
39
|
-
"@milaboratories/ts-
|
|
38
|
+
"@milaboratories/ts-builder": "1.2.10",
|
|
39
|
+
"@milaboratories/ts-configs": "1.2.1"
|
|
40
40
|
},
|
|
41
41
|
"scripts": {
|
|
42
42
|
"build": "ts-builder build --target node",
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { when } from "@milaboratories/ptabler-expression-js";
|
|
2
2
|
import { convertFilterUiToExpressionImpl } from "../filters/converters/filterUiToExpressionImpl";
|
|
3
3
|
import type { ExpressionSpec, FilterSpecUi } from "./types";
|
|
4
|
-
import {
|
|
4
|
+
import { distillFilterSpec } from "../filters/distill";
|
|
5
5
|
|
|
6
6
|
export function convertFilterSpecsToExpressionSpecs(
|
|
7
7
|
annotationsUI: FilterSpecUi[],
|
|
@@ -9,7 +9,7 @@ export function convertFilterSpecsToExpressionSpecs(
|
|
|
9
9
|
const validAnnotationsUI = annotationsUI
|
|
10
10
|
.map((step) => ({
|
|
11
11
|
label: step.label,
|
|
12
|
-
filter:
|
|
12
|
+
filter: distillFilterSpec(step.filter),
|
|
13
13
|
}))
|
|
14
14
|
.filter(
|
|
15
15
|
(step): step is { label: string; filter: NonNullable<typeof step.filter> } =>
|
|
@@ -8,10 +8,10 @@ import type {
|
|
|
8
8
|
PTableSorting,
|
|
9
9
|
} from "@milaboratories/pl-model-common";
|
|
10
10
|
import { canonicalizeJson } from "@milaboratories/pl-model-common";
|
|
11
|
-
import {
|
|
11
|
+
import { distillFilterSpec } from "../../filters";
|
|
12
12
|
import type { PlDataTableFilterState, PlTableFilter } from "./v4";
|
|
13
13
|
import type {
|
|
14
|
-
|
|
14
|
+
PlDataTableFiltersWithMeta,
|
|
15
15
|
PlDataTableGridStateCore,
|
|
16
16
|
PlDataTableSheetState,
|
|
17
17
|
PlDataTableStateV2CacheEntry,
|
|
@@ -158,14 +158,14 @@ function migrateV4toV5(
|
|
|
158
158
|
const nextId = () => ++idCounter;
|
|
159
159
|
|
|
160
160
|
const migratedCache: PlDataTableStateV2CacheEntry[] = state.stateCache.map((entry) => {
|
|
161
|
-
const leaves:
|
|
161
|
+
const leaves: PlDataTableFiltersWithMeta[] = [];
|
|
162
162
|
for (const f of entry.filtersState) {
|
|
163
163
|
if (f.filter !== null && !f.filter.disabled) {
|
|
164
164
|
const column = canonicalizeJson<PTableColumnId>(f.id);
|
|
165
165
|
leaves.push(migrateTableFilter(column, f.filter.value, nextId));
|
|
166
166
|
}
|
|
167
167
|
}
|
|
168
|
-
const filtersState:
|
|
168
|
+
const filtersState: PlDataTableFiltersWithMeta | null =
|
|
169
169
|
leaves.length > 0 ? { id: nextId(), type: "and", filters: leaves } : null;
|
|
170
170
|
|
|
171
171
|
return {
|
|
@@ -189,7 +189,7 @@ function migrateV4toV5(
|
|
|
189
189
|
? {
|
|
190
190
|
sourceId: oldSourceId,
|
|
191
191
|
hiddenColIds: state.pTableParams.hiddenColIds,
|
|
192
|
-
filters:
|
|
192
|
+
filters: distillFilterSpec(currentCache.filtersState),
|
|
193
193
|
sorting: state.pTableParams.sorting,
|
|
194
194
|
}
|
|
195
195
|
: createDefaultPTableParams(),
|
|
@@ -201,7 +201,7 @@ function migrateTableFilter(
|
|
|
201
201
|
column: string,
|
|
202
202
|
filter: PlTableFilter,
|
|
203
203
|
nextId: () => number,
|
|
204
|
-
):
|
|
204
|
+
): PlDataTableFiltersWithMeta {
|
|
205
205
|
const id = nextId();
|
|
206
206
|
switch (filter.type) {
|
|
207
207
|
case "isNA":
|
|
@@ -26,13 +26,12 @@ import {
|
|
|
26
26
|
uniqueBy,
|
|
27
27
|
isBooleanExpression,
|
|
28
28
|
} from "@milaboratories/pl-model-common";
|
|
29
|
-
import type { PTableFilters } from "../../filters";
|
|
30
29
|
import { filterSpecToSpecQueryExpr } from "../../filters";
|
|
31
30
|
import type { RenderCtxBase, TreeNodeAccessor, PColumnDataUniversal } from "../../render";
|
|
32
31
|
import { allPColumnsReady, deriveLabels } from "../../render";
|
|
33
32
|
import { identity, isFunction, isNil } from "es-toolkit";
|
|
34
|
-
import {
|
|
35
|
-
import type { CreatePlDataTableOps, PlDataTableModel } from "./v5";
|
|
33
|
+
import { distillFilterSpec } from "../../filters/distill";
|
|
34
|
+
import type { CreatePlDataTableOps, PlDataTableFilters, PlDataTableModel } from "./v5";
|
|
36
35
|
import { upgradePlDataTableStateV2 } from "./state-migration";
|
|
37
36
|
import type { PlDataTableStateV2 } from "./state-migration";
|
|
38
37
|
import type { PlDataTableSheet } from "./v5";
|
|
@@ -55,7 +54,7 @@ function createPTableDef(params: {
|
|
|
55
54
|
columns: PColumn<PColumnDataUniversal>[];
|
|
56
55
|
labelColumns: PColumn<PColumnDataUniversal>[];
|
|
57
56
|
coreJoinType: "inner" | "full";
|
|
58
|
-
filters:
|
|
57
|
+
filters: null | PlDataTableFilters;
|
|
59
58
|
sorting: PTableSorting[];
|
|
60
59
|
coreColumnPredicate?: (spec: PColumnIdAndSpec) => boolean;
|
|
61
60
|
}): PTableDefV2<PColumn<TreeNodeAccessor | PColumnValues | DataInfo<TreeNodeAccessor>>> {
|
|
@@ -87,7 +86,7 @@ function createPTableDef(params: {
|
|
|
87
86
|
|
|
88
87
|
// Apply filters
|
|
89
88
|
if (params.filters !== null) {
|
|
90
|
-
const nonEmpty =
|
|
89
|
+
const nonEmpty = distillFilterSpec(params.filters);
|
|
91
90
|
|
|
92
91
|
if (!isNil(nonEmpty)) {
|
|
93
92
|
const pridicate = filterSpecToSpecQueryExpr(nonEmpty);
|
|
@@ -5,7 +5,6 @@ import type {
|
|
|
5
5
|
ListOptionBase,
|
|
6
6
|
PObjectId,
|
|
7
7
|
PTableColumnSpec,
|
|
8
|
-
PTableFilters,
|
|
9
8
|
PTableRecordFilter,
|
|
10
9
|
PTableSorting,
|
|
11
10
|
PColumnIdAndSpec,
|
|
@@ -62,7 +61,8 @@ export type PlDataTableSheetState = {
|
|
|
62
61
|
};
|
|
63
62
|
|
|
64
63
|
/** Tree-based filter state compatible with PlAdvancedFilter's RootFilter */
|
|
65
|
-
export type
|
|
64
|
+
export type PlDataTableFilters = FilterSpec<FilterSpecLeaf<string>>;
|
|
65
|
+
export type PlDataTableFiltersWithMeta = FilterSpec<
|
|
66
66
|
FilterSpecLeaf<string>,
|
|
67
67
|
{ id: number; isExpanded?: boolean }
|
|
68
68
|
>;
|
|
@@ -75,7 +75,7 @@ export type PlDataTableStateV2CacheEntry = {
|
|
|
75
75
|
/** Sheets state */
|
|
76
76
|
sheetsState: PlDataTableSheetState[];
|
|
77
77
|
/** Filters state (tree-based, compatible with PlAdvancedFilter) */
|
|
78
|
-
filtersState:
|
|
78
|
+
filtersState: null | PlDataTableFiltersWithMeta;
|
|
79
79
|
};
|
|
80
80
|
|
|
81
81
|
export type PTableParamsV2 =
|
|
@@ -87,8 +87,8 @@ export type PTableParamsV2 =
|
|
|
87
87
|
}
|
|
88
88
|
| {
|
|
89
89
|
sourceId: string;
|
|
90
|
-
hiddenColIds: PObjectId[]
|
|
91
|
-
filters:
|
|
90
|
+
hiddenColIds: null | PObjectId[];
|
|
91
|
+
filters: null | PlDataTableFilters;
|
|
92
92
|
sorting: PTableSorting[];
|
|
93
93
|
};
|
|
94
94
|
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import type { FilterSpec, FilterSpecLeaf } from "@milaboratories/pl-model-common";
|
|
3
|
+
import { distillFilterSpec } from "./distill";
|
|
4
|
+
|
|
5
|
+
type F = FilterSpec<FilterSpecLeaf<string>>;
|
|
6
|
+
|
|
7
|
+
const leaf: F = { type: "equal", column: "c1", x: 1 };
|
|
8
|
+
const emptyLeaf: F = { type: undefined };
|
|
9
|
+
const incompleteLeaf: F = { type: "equal", column: "c1", x: undefined as unknown as number };
|
|
10
|
+
|
|
11
|
+
describe("distillFilterSpec", () => {
|
|
12
|
+
it("returns null for null input", () => {
|
|
13
|
+
expect(distillFilterSpec(null)).toBeNull();
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
it("returns null for undefined input", () => {
|
|
17
|
+
expect(distillFilterSpec(undefined)).toBeNull();
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it("returns valid leaf as-is", () => {
|
|
21
|
+
expect(distillFilterSpec(leaf)).toEqual(leaf);
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it("returns null for empty leaf", () => {
|
|
25
|
+
expect(distillFilterSpec(emptyLeaf)).toBeNull();
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it("returns null for incomplete leaf", () => {
|
|
29
|
+
expect(distillFilterSpec(incompleteLeaf)).toBeNull();
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
// --- distill (strips extra metadata) ---
|
|
33
|
+
|
|
34
|
+
it("strips non-FilterSpec metadata from leaf", () => {
|
|
35
|
+
const leafWithMeta = { type: "equal" as const, column: "c1", x: 1, _extra: true, label: "hi" };
|
|
36
|
+
expect(distillFilterSpec(leafWithMeta)).toEqual({ type: "equal", column: "c1", x: 1 });
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it("strips non-FilterSpec metadata from nested nodes", () => {
|
|
40
|
+
const filter = {
|
|
41
|
+
type: "and" as const,
|
|
42
|
+
filters: [{ type: "equal" as const, column: "c1", x: 1, _meta: "remove" }],
|
|
43
|
+
_rootMeta: 42,
|
|
44
|
+
};
|
|
45
|
+
expect(distillFilterSpec(filter)).toEqual({
|
|
46
|
+
type: "and",
|
|
47
|
+
filters: [{ type: "equal", column: "c1", x: 1 }],
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
// --- and ---
|
|
52
|
+
|
|
53
|
+
it("filters out empty children from and", () => {
|
|
54
|
+
const filter: F = { type: "and", filters: [emptyLeaf, leaf, emptyLeaf] };
|
|
55
|
+
expect(distillFilterSpec(filter)).toEqual({ type: "and", filters: [leaf] });
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
it("returns null when all and children are empty", () => {
|
|
59
|
+
const filter: F = { type: "and", filters: [emptyLeaf, emptyLeaf] };
|
|
60
|
+
expect(distillFilterSpec(filter)).toBeNull();
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
it("returns null for and with no children", () => {
|
|
64
|
+
const filter: F = { type: "and", filters: [] };
|
|
65
|
+
expect(distillFilterSpec(filter)).toBeNull();
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
// --- or ---
|
|
69
|
+
|
|
70
|
+
it("filters out empty children from or", () => {
|
|
71
|
+
const leaf2: F = { type: "equal", column: "c2", x: 2 };
|
|
72
|
+
const filter: F = { type: "or", filters: [leaf, emptyLeaf, leaf2] };
|
|
73
|
+
expect(distillFilterSpec(filter)).toEqual({ type: "or", filters: [leaf, leaf2] });
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
it("returns null when all or children are empty", () => {
|
|
77
|
+
const filter: F = { type: "or", filters: [emptyLeaf] };
|
|
78
|
+
expect(distillFilterSpec(filter)).toBeNull();
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
// --- not ---
|
|
82
|
+
|
|
83
|
+
it("preserves not with valid inner filter", () => {
|
|
84
|
+
const filter: F = { type: "not", filter: leaf };
|
|
85
|
+
expect(distillFilterSpec(filter)).toEqual({ type: "not", filter: leaf });
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
it("returns null for not wrapping an empty filter", () => {
|
|
89
|
+
const filter: F = { type: "not", filter: emptyLeaf };
|
|
90
|
+
expect(distillFilterSpec(filter)).toBeNull();
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
// --- nested collapse ---
|
|
94
|
+
|
|
95
|
+
it("returns null when nested and collapses", () => {
|
|
96
|
+
const filter: F = {
|
|
97
|
+
type: "not",
|
|
98
|
+
filter: { type: "and", filters: [emptyLeaf] },
|
|
99
|
+
};
|
|
100
|
+
expect(distillFilterSpec(filter)).toBeNull();
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
it("returns null when parent and has only a collapsing child", () => {
|
|
104
|
+
const filter: F = {
|
|
105
|
+
type: "and",
|
|
106
|
+
filters: [{ type: "or", filters: [emptyLeaf] }],
|
|
107
|
+
};
|
|
108
|
+
expect(distillFilterSpec(filter)).toBeNull();
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
it("keeps valid siblings when one child collapses", () => {
|
|
112
|
+
const filter: F = {
|
|
113
|
+
type: "and",
|
|
114
|
+
filters: [{ type: "or", filters: [emptyLeaf] }, leaf],
|
|
115
|
+
};
|
|
116
|
+
expect(distillFilterSpec(filter)).toEqual({ type: "and", filters: [leaf] });
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
// --- unfilled fields (at least one undefined field drops the leaf) ---
|
|
120
|
+
|
|
121
|
+
it("returns null for leaf with undefined column", () => {
|
|
122
|
+
const filter: F = { type: "equal", column: undefined as unknown as string, x: 1 };
|
|
123
|
+
expect(distillFilterSpec(filter)).toBeNull();
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
it("returns null for leaf with undefined value (string filter)", () => {
|
|
127
|
+
const filter: F = {
|
|
128
|
+
type: "patternEquals",
|
|
129
|
+
column: "c1",
|
|
130
|
+
value: undefined as unknown as string,
|
|
131
|
+
};
|
|
132
|
+
expect(distillFilterSpec(filter)).toBeNull();
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
it("returns null for greaterThan with undefined x", () => {
|
|
136
|
+
const filter: F = {
|
|
137
|
+
type: "greaterThan",
|
|
138
|
+
column: "c1",
|
|
139
|
+
x: undefined as unknown as number,
|
|
140
|
+
};
|
|
141
|
+
expect(distillFilterSpec(filter)).toBeNull();
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
it("returns null for topN with undefined n", () => {
|
|
145
|
+
const filter: F = {
|
|
146
|
+
type: "topN",
|
|
147
|
+
column: "c1",
|
|
148
|
+
n: undefined as unknown as number,
|
|
149
|
+
};
|
|
150
|
+
expect(distillFilterSpec(filter)).toBeNull();
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
it("returns null for inSet with undefined value", () => {
|
|
154
|
+
const filter: F = {
|
|
155
|
+
type: "inSet",
|
|
156
|
+
column: "c1",
|
|
157
|
+
value: undefined as unknown as string[],
|
|
158
|
+
};
|
|
159
|
+
expect(distillFilterSpec(filter)).toBeNull();
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
it("drops incomplete leaf from and, keeps filled siblings", () => {
|
|
163
|
+
const incomplete: F = { type: "greaterThan", column: "c1", x: undefined as unknown as number };
|
|
164
|
+
const filter: F = { type: "and", filters: [leaf, incomplete] };
|
|
165
|
+
expect(distillFilterSpec(filter)).toEqual({ type: "and", filters: [leaf] });
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
it("drops incomplete leaf from or, keeps filled siblings", () => {
|
|
169
|
+
const incomplete: F = {
|
|
170
|
+
type: "patternEquals",
|
|
171
|
+
column: "c1",
|
|
172
|
+
value: undefined as unknown as string,
|
|
173
|
+
};
|
|
174
|
+
const leaf2: F = { type: "equal", column: "c2", x: 2 };
|
|
175
|
+
const filter: F = { type: "or", filters: [incomplete, leaf2] };
|
|
176
|
+
expect(distillFilterSpec(filter)).toEqual({ type: "or", filters: [leaf2] });
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
it("returns null for not wrapping an incomplete leaf", () => {
|
|
180
|
+
const incomplete: F = { type: "equal", column: undefined as unknown as string, x: 1 };
|
|
181
|
+
const filter: F = { type: "not", filter: incomplete };
|
|
182
|
+
expect(distillFilterSpec(filter)).toBeNull();
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
it("returns null when and contains only incomplete leaves", () => {
|
|
186
|
+
const a: F = { type: "equal", column: "c1", x: undefined as unknown as number };
|
|
187
|
+
const b: F = { type: "greaterThan", column: undefined as unknown as string, x: 5 };
|
|
188
|
+
const filter: F = { type: "and", filters: [a, b] };
|
|
189
|
+
expect(distillFilterSpec(filter)).toBeNull();
|
|
190
|
+
});
|
|
191
|
+
});
|
package/src/filters/distill.ts
CHANGED
|
@@ -1,9 +1,5 @@
|
|
|
1
1
|
import { DistributiveKeys, UnionToTuples } from "@milaboratories/helpers";
|
|
2
|
-
import {
|
|
3
|
-
type FilterSpec,
|
|
4
|
-
type FilterSpecLeaf,
|
|
5
|
-
type PTableFilters,
|
|
6
|
-
} from "@milaboratories/pl-model-common";
|
|
2
|
+
import { type FilterSpec, type FilterSpecLeaf } from "@milaboratories/pl-model-common";
|
|
7
3
|
|
|
8
4
|
/** All possible field names that can appear in any FilterSpecLeaf variant. */
|
|
9
5
|
type FilterSpecLeafKey = DistributiveKeys<FilterSpecLeaf<string>>;
|
|
@@ -24,11 +20,11 @@ const KNOWN_LEAF_KEYS_TUPLE: UnionToTuples<FilterSpecLeafKey> = [
|
|
|
24
20
|
];
|
|
25
21
|
const KNOWN_LEAF_KEYS: Set<FilterSpecLeafKey> = new Set(KNOWN_LEAF_KEYS_TUPLE);
|
|
26
22
|
|
|
27
|
-
type
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
23
|
+
/** Returns true if the leaf is filled — type is defined and no required fields are undefined. */
|
|
24
|
+
function isFilledLeaf(node: Record<string, unknown>): boolean {
|
|
25
|
+
if (node.type == null) return false;
|
|
26
|
+
return !Object.values(node).some((v) => v === undefined);
|
|
27
|
+
}
|
|
32
28
|
|
|
33
29
|
function distillLeaf(node: Record<string, unknown>): FilterSpecLeaf<string> {
|
|
34
30
|
const result: Record<string, unknown> = {};
|
|
@@ -40,20 +36,32 @@ function distillLeaf(node: Record<string, unknown>): FilterSpecLeaf<string> {
|
|
|
40
36
|
return result as FilterSpecLeaf<string>;
|
|
41
37
|
}
|
|
42
38
|
|
|
43
|
-
function distillNode
|
|
39
|
+
function distillNode<T extends FilterSpecLeaf<unknown>>(
|
|
40
|
+
node: FilterSpec<T, unknown, unknown>,
|
|
41
|
+
): FilterSpec<T> | null {
|
|
44
42
|
switch (node.type) {
|
|
45
43
|
case "and":
|
|
46
|
-
case "or":
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
44
|
+
case "or": {
|
|
45
|
+
const filtered = node.filters.map(distillNode).filter((f): f is FilterSpec<T> => f !== null);
|
|
46
|
+
return filtered.length === 0 ? null : { type: node.type, filters: filtered };
|
|
47
|
+
}
|
|
48
|
+
case "not": {
|
|
49
|
+
const inner = distillNode(node.filter);
|
|
50
|
+
return inner === null ? null : { type: "not", filter: inner };
|
|
51
|
+
}
|
|
50
52
|
default:
|
|
51
|
-
|
|
53
|
+
if (!isFilledLeaf(node)) return null;
|
|
54
|
+
return distillLeaf(node);
|
|
52
55
|
}
|
|
53
56
|
}
|
|
54
57
|
|
|
55
|
-
/**
|
|
56
|
-
|
|
58
|
+
/**
|
|
59
|
+
* Strips non-FilterSpec metadata (whitelist approach) and removes
|
|
60
|
+
* unfilled leaves (type is undefined or any required field is undefined).
|
|
61
|
+
*/
|
|
62
|
+
export function distillFilterSpec<T extends FilterSpecLeaf<unknown>>(
|
|
63
|
+
filter: null | undefined | FilterSpec<T, unknown, unknown>,
|
|
64
|
+
): null | FilterSpec<T> {
|
|
57
65
|
if (filter == null) return null;
|
|
58
66
|
return distillNode(filter);
|
|
59
67
|
}
|
package/src/filters/types.ts
CHANGED
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
function filterPredicate(item) {
|
|
4
|
-
// No need to convert empty steps
|
|
5
|
-
if (item.type == null) {
|
|
6
|
-
return false;
|
|
7
|
-
}
|
|
8
|
-
if (item.type === "or") {
|
|
9
|
-
return item.filters.length > 0;
|
|
10
|
-
}
|
|
11
|
-
if (item.type === "and") {
|
|
12
|
-
return item.filters.length > 0;
|
|
13
|
-
}
|
|
14
|
-
if (item.type === "not") {
|
|
15
|
-
return filterPredicate(item.filter);
|
|
16
|
-
}
|
|
17
|
-
// Filter out any item that has undefined values in required fields
|
|
18
|
-
return !Object.values(item).some((v) => v === undefined);
|
|
19
|
-
}
|
|
20
|
-
function filterEmptyPieces(item) {
|
|
21
|
-
if (item.type === "or" || item.type === "and") {
|
|
22
|
-
const filtered = item.filters
|
|
23
|
-
.map(filterEmptyPieces)
|
|
24
|
-
.filter((f) => f !== null && filterPredicate(f));
|
|
25
|
-
return filtered.length === 0
|
|
26
|
-
? null
|
|
27
|
-
: {
|
|
28
|
-
...item,
|
|
29
|
-
filters: filtered,
|
|
30
|
-
};
|
|
31
|
-
}
|
|
32
|
-
if (item.type === "not") {
|
|
33
|
-
const inner = filterEmptyPieces(item.filter);
|
|
34
|
-
return inner === null || !filterPredicate(inner)
|
|
35
|
-
? null
|
|
36
|
-
: {
|
|
37
|
-
...item,
|
|
38
|
-
filter: inner,
|
|
39
|
-
};
|
|
40
|
-
}
|
|
41
|
-
if (!filterPredicate(item)) {
|
|
42
|
-
return null;
|
|
43
|
-
}
|
|
44
|
-
return item;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
exports.filterEmptyPieces = filterEmptyPieces;
|
|
48
|
-
exports.filterPredicate = filterPredicate;
|
|
49
|
-
//# sourceMappingURL=filterEmpty.cjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"filterEmpty.cjs","sources":["../../../src/filters/converters/filterEmpty.ts"],"sourcesContent":["import { FilterSpec, FilterSpecLeaf } from \"@milaboratories/pl-model-common\";\n\nexport function filterPredicate(\n item: FilterSpec<FilterSpecLeaf<unknown>, unknown, unknown>,\n): boolean {\n // No need to convert empty steps\n if (item.type == null) {\n return false;\n }\n\n if (item.type === \"or\") {\n return item.filters.length > 0;\n }\n\n if (item.type === \"and\") {\n return item.filters.length > 0;\n }\n\n if (item.type === \"not\") {\n return filterPredicate(item.filter);\n }\n\n // Filter out any item that has undefined values in required fields\n return !Object.values(item).some((v) => v === undefined);\n}\n\nexport function filterEmptyPieces<T extends FilterSpec<FilterSpecLeaf<unknown>, unknown, unknown>>(\n item: T,\n): null | T {\n if (item.type === \"or\" || item.type === \"and\") {\n const filtered = item.filters\n .map(filterEmptyPieces)\n .filter((f): f is T => f !== null && filterPredicate(f));\n\n return filtered.length === 0\n ? null\n : {\n ...item,\n filters: filtered,\n };\n }\n if (item.type === \"not\") {\n const inner = filterEmptyPieces(item.filter);\n return inner === null || !filterPredicate(inner)\n ? null\n : {\n ...item,\n filter: inner,\n };\n }\n\n if (!filterPredicate(item)) {\n return null;\n }\n\n return item;\n}\n"],"names":[],"mappings":";;AAEM,SAAU,eAAe,CAC7B,IAA2D,EAAA;;AAG3D,IAAA,IAAI,IAAI,CAAC,IAAI,IAAI,IAAI,EAAE;AACrB,QAAA,OAAO,KAAK;IACd;AAEA,IAAA,IAAI,IAAI,CAAC,IAAI,KAAK,IAAI,EAAE;AACtB,QAAA,OAAO,IAAI,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC;IAChC;AAEA,IAAA,IAAI,IAAI,CAAC,IAAI,KAAK,KAAK,EAAE;AACvB,QAAA,OAAO,IAAI,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC;IAChC;AAEA,IAAA,IAAI,IAAI,CAAC,IAAI,KAAK,KAAK,EAAE;AACvB,QAAA,OAAO,eAAe,CAAC,IAAI,CAAC,MAAM,CAAC;IACrC;;AAGA,IAAA,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,SAAS,CAAC;AAC1D;AAEM,SAAU,iBAAiB,CAC/B,IAAO,EAAA;AAEP,IAAA,IAAI,IAAI,CAAC,IAAI,KAAK,IAAI,IAAI,IAAI,CAAC,IAAI,KAAK,KAAK,EAAE;AAC7C,QAAA,MAAM,QAAQ,GAAG,IAAI,CAAC;aACnB,GAAG,CAAC,iBAAiB;AACrB,aAAA,MAAM,CAAC,CAAC,CAAC,KAAa,CAAC,KAAK,IAAI,IAAI,eAAe,CAAC,CAAC,CAAC,CAAC;AAE1D,QAAA,OAAO,QAAQ,CAAC,MAAM,KAAK;AACzB,cAAE;AACF,cAAE;AACE,gBAAA,GAAG,IAAI;AACP,gBAAA,OAAO,EAAE,QAAQ;aAClB;IACP;AACA,IAAA,IAAI,IAAI,CAAC,IAAI,KAAK,KAAK,EAAE;QACvB,MAAM,KAAK,GAAG,iBAAiB,CAAC,IAAI,CAAC,MAAM,CAAC;QAC5C,OAAO,KAAK,KAAK,IAAI,IAAI,CAAC,eAAe,CAAC,KAAK;AAC7C,cAAE;AACF,cAAE;AACE,gBAAA,GAAG,IAAI;AACP,gBAAA,MAAM,EAAE,KAAK;aACd;IACP;AAEA,IAAA,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,EAAE;AAC1B,QAAA,OAAO,IAAI;IACb;AAEA,IAAA,OAAO,IAAI;AACb;;;;;"}
|