@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.
Files changed (46) hide show
  1. package/dist/annotations/converter.cjs +2 -2
  2. package/dist/annotations/converter.cjs.map +1 -1
  3. package/dist/annotations/converter.js +2 -2
  4. package/dist/annotations/converter.js.map +1 -1
  5. package/dist/components/PlDataTable/index.d.ts +1 -1
  6. package/dist/components/PlDataTable/index.d.ts.map +1 -1
  7. package/dist/components/PlDataTable/state-migration.cjs +1 -1
  8. package/dist/components/PlDataTable/state-migration.cjs.map +1 -1
  9. package/dist/components/PlDataTable/state-migration.js +2 -2
  10. package/dist/components/PlDataTable/state-migration.js.map +1 -1
  11. package/dist/components/PlDataTable/table.cjs +2 -3
  12. package/dist/components/PlDataTable/table.cjs.map +1 -1
  13. package/dist/components/PlDataTable/table.d.ts.map +1 -1
  14. package/dist/components/PlDataTable/table.js +2 -3
  15. package/dist/components/PlDataTable/table.js.map +1 -1
  16. package/dist/components/PlDataTable/v5.d.ts +6 -5
  17. package/dist/components/PlDataTable/v5.d.ts.map +1 -1
  18. package/dist/filters/distill.cjs +22 -7
  19. package/dist/filters/distill.cjs.map +1 -1
  20. package/dist/filters/distill.d.ts +6 -5
  21. package/dist/filters/distill.d.ts.map +1 -1
  22. package/dist/filters/distill.js +22 -7
  23. package/dist/filters/distill.js.map +1 -1
  24. package/dist/filters/types.d.ts +1 -1
  25. package/dist/filters/types.d.ts.map +1 -1
  26. package/dist/index.cjs +1 -1
  27. package/dist/index.js +1 -1
  28. package/dist/package.json.cjs +1 -1
  29. package/dist/package.json.js +1 -1
  30. package/package.json +6 -6
  31. package/src/annotations/converter.ts +2 -2
  32. package/src/components/PlDataTable/index.ts +2 -0
  33. package/src/components/PlDataTable/state-migration.ts +6 -6
  34. package/src/components/PlDataTable/table.ts +4 -5
  35. package/src/components/PlDataTable/v5.ts +5 -5
  36. package/src/filters/distill.test.ts +191 -0
  37. package/src/filters/distill.ts +26 -18
  38. package/src/filters/types.ts +0 -1
  39. package/dist/filters/converters/filterEmpty.cjs +0 -49
  40. package/dist/filters/converters/filterEmpty.cjs.map +0 -1
  41. package/dist/filters/converters/filterEmpty.d.ts +0 -4
  42. package/dist/filters/converters/filterEmpty.d.ts.map +0 -1
  43. package/dist/filters/converters/filterEmpty.js +0 -46
  44. package/dist/filters/converters/filterEmpty.js.map +0 -1
  45. package/src/filters/converters/filterEmpty.test.ts +0 -125
  46. package/src/filters/converters/filterEmpty.ts +0 -57
@@ -1,6 +1,7 @@
1
- import { type FilterSpec, type FilterSpecLeaf, type PTableFilters } from "@milaboratories/pl-model-common";
2
- type UnknownFilterNode = FilterSpec<FilterSpecLeaf<string>, Record<string, unknown>, Record<string, unknown>>;
3
- /** Strips all non-FilterSpec metadata from the filter tree (whitelist approach). */
4
- export declare function distillFilter(filter: UnknownFilterNode | null | undefined): PTableFilters;
5
- export {};
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,EACL,KAAK,UAAU,EACf,KAAK,cAAc,EACnB,KAAK,aAAa,EACnB,MAAM,iCAAiC,CAAC;AAqBzC,KAAK,iBAAiB,GAAG,UAAU,CACjC,cAAc,CAAC,MAAM,CAAC,EACtB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACvB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CACxB,CAAC;AAwBF,oFAAoF;AACpF,wBAAgB,aAAa,CAAC,MAAM,EAAE,iBAAiB,GAAG,IAAI,GAAG,SAAS,GAAG,aAAa,CAGzF"}
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"}
@@ -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
- return { type: node.type, filters: node.filters.map(distillNode) };
30
- case "not":
31
- return { type: "not", filter: distillNode(node.filter) };
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
- /** Strips all non-FilterSpec metadata from the filter tree (whitelist approach). */
37
- function distillFilter(filter) {
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 { distillFilter };
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 {\n type FilterSpec,\n type FilterSpecLeaf,\n type PTableFilters,\n} 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\ntype UnknownFilterNode = FilterSpec<\n FilterSpecLeaf<string>,\n Record<string, unknown>,\n Record<string, unknown>\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(node: UnknownFilterNode): FilterSpec<FilterSpecLeaf<string>> {\n switch (node.type) {\n case \"and\":\n case \"or\":\n return { type: node.type, filters: node.filters.map(distillNode) };\n case \"not\":\n return { type: \"not\", filter: distillNode(node.filter) };\n default:\n return distillLeaf(node as Record<string, unknown>);\n }\n}\n\n/** Strips all non-FilterSpec metadata from the filter tree (whitelist approach). */\nexport function distillFilter(filter: UnknownFilterNode | null | undefined): PTableFilters {\n if (filter == null) return null;\n return distillNode(filter);\n}\n"],"names":[],"mappings":"AAUA;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;AAQ9E,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,CAAC,IAAuB,EAAA;AAC1C,IAAA,QAAQ,IAAI,CAAC,IAAI;AACf,QAAA,KAAK,KAAK;AACV,QAAA,KAAK,IAAI;AACP,YAAA,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC,EAAE;AACpE,QAAA,KAAK,KAAK;AACR,YAAA,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,WAAW,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE;AAC1D,QAAA;AACE,YAAA,OAAO,WAAW,CAAC,IAA+B,CAAC;;AAEzD;AAEA;AACM,SAAU,aAAa,CAAC,MAA4C,EAAA;IACxE,IAAI,MAAM,IAAI,IAAI;AAAE,QAAA,OAAO,IAAI;AAC/B,IAAA,OAAO,WAAW,CAAC,MAAM,CAAC;AAC5B;;;;"}
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;;;;"}
@@ -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, PTableFilters, } from "@milaboratories/pl-model-common";
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,EAChB,aAAa,GACd,MAAM,iCAAiC,CAAC"}
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.distillFilter = distill.distillFilter;
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 { distillFilter } from './filters/distill.js';
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';
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- var version = "1.54.7";
3
+ var version = "1.54.8";
4
4
 
5
5
  exports.version = version;
6
6
  //# sourceMappingURL=package.json.cjs.map
@@ -1,4 +1,4 @@
1
- var version = "1.54.7";
1
+ var version = "1.54.8";
2
2
 
3
3
  export { version };
4
4
  //# sourceMappingURL=package.json.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@platforma-sdk/model",
3
- "version": "1.54.7",
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-model-common": "1.24.7",
29
- "@milaboratories/ptabler-expression-js": "1.1.17",
30
- "@milaboratories/pl-error-like": "1.12.8"
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-configs": "1.2.1",
39
- "@milaboratories/ts-builder": "1.2.10"
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 { filterEmptyPieces } from "../filters/converters/filterEmpty";
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: filterEmptyPieces(step.filter),
12
+ filter: distillFilterSpec(step.filter),
13
13
  }))
14
14
  .filter(
15
15
  (step): step is { label: string; filter: NonNullable<typeof step.filter> } =>
@@ -10,6 +10,8 @@ export type {
10
10
  PTableParamsV2,
11
11
  PlDataTableStateV2Normalized,
12
12
  PlDataTableModel,
13
+ PlDataTableFilters,
14
+ PlDataTableFiltersWithMeta,
13
15
  } from "./v5";
14
16
 
15
17
  export type { PlDataTableStateV2 } from "./state-migration";
@@ -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 { distillFilter } from "../../filters";
11
+ import { distillFilterSpec } from "../../filters";
12
12
  import type { PlDataTableFilterState, PlTableFilter } from "./v4";
13
13
  import type {
14
- PlDataTableAdvancedFilter,
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: PlDataTableAdvancedFilter[] = [];
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: PlDataTableAdvancedFilter | null =
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: distillFilter(currentCache.filtersState),
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
- ): PlDataTableAdvancedFilter {
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 { filterEmptyPieces } from "../../filters/converters/filterEmpty";
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: PTableFilters;
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 = filterEmptyPieces(params.filters);
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 PlDataTableAdvancedFilter = FilterSpec<
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: PlDataTableAdvancedFilter | null;
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[] | null;
91
- filters: PTableFilters;
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
+ });
@@ -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 UnknownFilterNode = FilterSpec<
28
- FilterSpecLeaf<string>,
29
- Record<string, unknown>,
30
- Record<string, unknown>
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(node: UnknownFilterNode): FilterSpec<FilterSpecLeaf<string>> {
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
- return { type: node.type, filters: node.filters.map(distillNode) };
48
- case "not":
49
- return { type: "not", filter: distillNode(node.filter) };
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
- return distillLeaf(node as Record<string, unknown>);
53
+ if (!isFilledLeaf(node)) return null;
54
+ return distillLeaf(node);
52
55
  }
53
56
  }
54
57
 
55
- /** Strips all non-FilterSpec metadata from the filter tree (whitelist approach). */
56
- export function distillFilter(filter: UnknownFilterNode | null | undefined): PTableFilters {
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
  }
@@ -14,5 +14,4 @@ export type {
14
14
  FilterSpec,
15
15
  FilterSpecType,
16
16
  FilterSpecOfType,
17
- PTableFilters,
18
17
  } from "@milaboratories/pl-model-common";
@@ -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;;;;;"}