@platforma-sdk/model 1.58.3 → 1.58.5

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.
@@ -1,6 +1,7 @@
1
1
  'use strict';
2
2
 
3
3
  var traverse = require('./traverse.cjs');
4
+ var compat = require('es-toolkit/compat');
4
5
 
5
6
  /** Compile-time check: every key in the tuple is a valid leaf key (via satisfies). */
6
7
  const KNOWN_LEAF_KEYS_TUPLE = [
@@ -21,7 +22,17 @@ const KNOWN_LEAF_KEYS = new Set(KNOWN_LEAF_KEYS_TUPLE);
21
22
  function isFilledLeaf(node) {
22
23
  if (node.type == null)
23
24
  return false;
24
- return !Object.values(node).some((v) => v === undefined);
25
+ return !Object.values(node).some((value) => {
26
+ switch (typeof value) {
27
+ case "number":
28
+ case "boolean":
29
+ return false;
30
+ case "string":
31
+ return value.trim() === "";
32
+ default: // undefined, null, empty objects/arrays
33
+ return compat.isEmpty(value);
34
+ }
35
+ });
25
36
  }
26
37
  function distillLeaf(node) {
27
38
  const result = {};
@@ -41,9 +52,8 @@ function distillFilterSpec(filter) {
41
52
  return null;
42
53
  return traverse.traverseFilterSpec(filter, {
43
54
  leaf: (leaf) => {
44
- if (!isFilledLeaf(leaf))
45
- return null;
46
- return distillLeaf(leaf);
55
+ const distilled = distillLeaf(leaf);
56
+ return isFilledLeaf(distilled) ? distilled : null;
47
57
  },
48
58
  and: (results) => {
49
59
  const filtered = results.filter((f) => f !== null);
@@ -1 +1 @@
1
- {"version":3,"file":"distill.cjs","sources":["../../src/filters/distill.ts"],"sourcesContent":["import { DistributiveKeys, UnionToTuples } from \"@milaboratories/helpers\";\nimport {\n RootFilterSpec,\n type FilterSpec,\n type FilterSpecLeaf,\n} from \"@milaboratories/pl-model-common\";\nimport { traverseFilterSpec } from \"./traverse\";\nimport { InferFilterSpecLeaf } 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\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<\n FS extends FilterSpec<FilterSpecLeaf<unknown>, unknown, unknown>,\n R extends FS extends RootFilterSpec<FilterSpecLeaf<unknown>, unknown, unknown>\n ? RootFilterSpec<InferFilterSpecLeaf<FS>>\n : FilterSpec<InferFilterSpecLeaf<FS>>,\n>(filter: null | undefined | FS): null | R {\n if (filter == null) return null;\n return traverseFilterSpec<FS, null | R>(filter, {\n leaf: (leaf) => {\n if (!isFilledLeaf(leaf as Record<string, unknown>)) return null;\n return distillLeaf(leaf as Record<string, unknown>) as R;\n },\n and: (results) => {\n const filtered = results.filter((f): f is NonNullable<typeof f> => f !== null);\n return filtered.length === 0 ? null : ({ type: \"and\", filters: filtered } as R);\n },\n or: (results) => {\n const filtered = results.filter((f): f is NonNullable<typeof f> => f !== null);\n return filtered.length === 0 ? null : ({ type: \"or\", filters: filtered } as R);\n },\n not: (result) => (result === null ? null : ({ type: \"not\", filter: result } as R)),\n });\n}\n"],"names":["traverseFilterSpec"],"mappings":";;;;AAYA;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;;;AAGG;AACG,SAAU,iBAAiB,CAK/B,MAA6B,EAAA;IAC7B,IAAI,MAAM,IAAI,IAAI;AAAE,QAAA,OAAO,IAAI;IAC/B,OAAOA,2BAAkB,CAAe,MAAM,EAAE;AAC9C,QAAA,IAAI,EAAE,CAAC,IAAI,KAAI;AACb,YAAA,IAAI,CAAC,YAAY,CAAC,IAA+B,CAAC;AAAE,gBAAA,OAAO,IAAI;AAC/D,YAAA,OAAO,WAAW,CAAC,IAA+B,CAAM;QAC1D,CAAC;AACD,QAAA,GAAG,EAAE,CAAC,OAAO,KAAI;AACf,YAAA,MAAM,QAAQ,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,KAAiC,CAAC,KAAK,IAAI,CAAC;YAC9E,OAAO,QAAQ,CAAC,MAAM,KAAK,CAAC,GAAG,IAAI,GAAI,EAAE,IAAI,EAAE,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAQ;QACjF,CAAC;AACD,QAAA,EAAE,EAAE,CAAC,OAAO,KAAI;AACd,YAAA,MAAM,QAAQ,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,KAAiC,CAAC,KAAK,IAAI,CAAC;YAC9E,OAAO,QAAQ,CAAC,MAAM,KAAK,CAAC,GAAG,IAAI,GAAI,EAAE,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAQ;QAChF,CAAC;QACD,GAAG,EAAE,CAAC,MAAM,MAAM,MAAM,KAAK,IAAI,GAAG,IAAI,GAAI,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAQ,CAAC;AACnF,KAAA,CAAC;AACJ;;;;"}
1
+ {"version":3,"file":"distill.cjs","sources":["../../src/filters/distill.ts"],"sourcesContent":["import { DistributiveKeys, UnionToTuples } from \"@milaboratories/helpers\";\nimport {\n RootFilterSpec,\n type FilterSpec,\n type FilterSpecLeaf,\n} from \"@milaboratories/pl-model-common\";\nimport { traverseFilterSpec } from \"./traverse\";\nimport { InferFilterSpecLeaf } from \"@milaboratories/pl-model-common\";\nimport { isEmpty } from \"es-toolkit/compat\";\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<T>(node: FilterSpecLeaf<T>): boolean {\n if (node.type == null) return false;\n return !Object.values(node).some((value) => {\n switch (typeof value) {\n case \"number\":\n case \"boolean\":\n return false;\n case \"string\":\n return value.trim() === \"\";\n default: // undefined, null, empty objects/arrays\n return isEmpty(value);\n }\n });\n}\n\nfunction distillLeaf<T>(node: FilterSpecLeaf<T>): FilterSpecLeaf<T> {\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<T>;\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<\n FS extends FilterSpec<FilterSpecLeaf<unknown>, unknown, unknown>,\n R extends FS extends RootFilterSpec<FilterSpecLeaf<unknown>, unknown, unknown>\n ? RootFilterSpec<InferFilterSpecLeaf<FS>>\n : FilterSpec<InferFilterSpecLeaf<FS>>,\n>(filter: null | undefined | FS): null | R {\n if (filter == null) return null;\n return traverseFilterSpec<FS, null | R>(filter, {\n leaf: (leaf) => {\n const distilled = distillLeaf(leaf);\n return isFilledLeaf(distilled) ? (distilled as R) : null;\n },\n and: (results) => {\n const filtered = results.filter((f): f is NonNullable<typeof f> => f !== null);\n return filtered.length === 0 ? null : ({ type: \"and\", filters: filtered } as R);\n },\n or: (results) => {\n const filtered = results.filter((f): f is NonNullable<typeof f> => f !== null);\n return filtered.length === 0 ? null : ({ type: \"or\", filters: filtered } as R);\n },\n not: (result) => (result === null ? null : ({ type: \"not\", filter: result } as R)),\n });\n}\n"],"names":["isEmpty","traverseFilterSpec"],"mappings":";;;;;AAaA;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,CAAI,IAAuB,EAAA;AAC9C,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,KAAK,KAAI;QACzC,QAAQ,OAAO,KAAK;AAClB,YAAA,KAAK,QAAQ;AACb,YAAA,KAAK,SAAS;AACZ,gBAAA,OAAO,KAAK;AACd,YAAA,KAAK,QAAQ;AACX,gBAAA,OAAO,KAAK,CAAC,IAAI,EAAE,KAAK,EAAE;AAC5B,YAAA;AACE,gBAAA,OAAOA,cAAO,CAAC,KAAK,CAAC;;AAE3B,IAAA,CAAC,CAAC;AACJ;AAEA,SAAS,WAAW,CAAI,IAAuB,EAAA;IAC7C,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,MAA2B;AACpC;AAEA;;;AAGG;AACG,SAAU,iBAAiB,CAK/B,MAA6B,EAAA;IAC7B,IAAI,MAAM,IAAI,IAAI;AAAE,QAAA,OAAO,IAAI;IAC/B,OAAOC,2BAAkB,CAAe,MAAM,EAAE;AAC9C,QAAA,IAAI,EAAE,CAAC,IAAI,KAAI;AACb,YAAA,MAAM,SAAS,GAAG,WAAW,CAAC,IAAI,CAAC;AACnC,YAAA,OAAO,YAAY,CAAC,SAAS,CAAC,GAAI,SAAe,GAAG,IAAI;QAC1D,CAAC;AACD,QAAA,GAAG,EAAE,CAAC,OAAO,KAAI;AACf,YAAA,MAAM,QAAQ,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,KAAiC,CAAC,KAAK,IAAI,CAAC;YAC9E,OAAO,QAAQ,CAAC,MAAM,KAAK,CAAC,GAAG,IAAI,GAAI,EAAE,IAAI,EAAE,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAQ;QACjF,CAAC;AACD,QAAA,EAAE,EAAE,CAAC,OAAO,KAAI;AACd,YAAA,MAAM,QAAQ,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,KAAiC,CAAC,KAAK,IAAI,CAAC;YAC9E,OAAO,QAAQ,CAAC,MAAM,KAAK,CAAC,GAAG,IAAI,GAAI,EAAE,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAQ;QAChF,CAAC;QACD,GAAG,EAAE,CAAC,MAAM,MAAM,MAAM,KAAK,IAAI,GAAG,IAAI,GAAI,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAQ,CAAC;AACnF,KAAA,CAAC;AACJ;;;;"}
@@ -1 +1 @@
1
- {"version":3,"file":"distill.d.ts","sourceRoot":"","sources":["../../src/filters/distill.ts"],"names":[],"mappings":"AACA,OAAO,EACL,cAAc,EACd,KAAK,UAAU,EACf,KAAK,cAAc,EACpB,MAAM,iCAAiC,CAAC;AAEzC,OAAO,EAAE,mBAAmB,EAAE,MAAM,iCAAiC,CAAC;AAqCtE;;;GAGG;AACH,wBAAgB,iBAAiB,CAC/B,EAAE,SAAS,UAAU,CAAC,cAAc,CAAC,OAAO,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,EAChE,CAAC,SAAS,EAAE,SAAS,cAAc,CAAC,cAAc,CAAC,OAAO,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,GAC1E,cAAc,CAAC,mBAAmB,CAAC,EAAE,CAAC,CAAC,GACvC,UAAU,CAAC,mBAAmB,CAAC,EAAE,CAAC,CAAC,EACvC,MAAM,EAAE,IAAI,GAAG,SAAS,GAAG,EAAE,GAAG,IAAI,GAAG,CAAC,CAiBzC"}
1
+ {"version":3,"file":"distill.d.ts","sourceRoot":"","sources":["../../src/filters/distill.ts"],"names":[],"mappings":"AACA,OAAO,EACL,cAAc,EACd,KAAK,UAAU,EACf,KAAK,cAAc,EACpB,MAAM,iCAAiC,CAAC;AAEzC,OAAO,EAAE,mBAAmB,EAAE,MAAM,iCAAiC,CAAC;AAgDtE;;;GAGG;AACH,wBAAgB,iBAAiB,CAC/B,EAAE,SAAS,UAAU,CAAC,cAAc,CAAC,OAAO,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,EAChE,CAAC,SAAS,EAAE,SAAS,cAAc,CAAC,cAAc,CAAC,OAAO,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,GAC1E,cAAc,CAAC,mBAAmB,CAAC,EAAE,CAAC,CAAC,GACvC,UAAU,CAAC,mBAAmB,CAAC,EAAE,CAAC,CAAC,EACvC,MAAM,EAAE,IAAI,GAAG,SAAS,GAAG,EAAE,GAAG,IAAI,GAAG,CAAC,CAiBzC"}
@@ -1,4 +1,5 @@
1
1
  import { traverseFilterSpec } from './traverse.js';
2
+ import { isEmpty } from 'es-toolkit/compat';
2
3
 
3
4
  /** Compile-time check: every key in the tuple is a valid leaf key (via satisfies). */
4
5
  const KNOWN_LEAF_KEYS_TUPLE = [
@@ -19,7 +20,17 @@ const KNOWN_LEAF_KEYS = new Set(KNOWN_LEAF_KEYS_TUPLE);
19
20
  function isFilledLeaf(node) {
20
21
  if (node.type == null)
21
22
  return false;
22
- return !Object.values(node).some((v) => v === undefined);
23
+ return !Object.values(node).some((value) => {
24
+ switch (typeof value) {
25
+ case "number":
26
+ case "boolean":
27
+ return false;
28
+ case "string":
29
+ return value.trim() === "";
30
+ default: // undefined, null, empty objects/arrays
31
+ return isEmpty(value);
32
+ }
33
+ });
23
34
  }
24
35
  function distillLeaf(node) {
25
36
  const result = {};
@@ -39,9 +50,8 @@ function distillFilterSpec(filter) {
39
50
  return null;
40
51
  return traverseFilterSpec(filter, {
41
52
  leaf: (leaf) => {
42
- if (!isFilledLeaf(leaf))
43
- return null;
44
- return distillLeaf(leaf);
53
+ const distilled = distillLeaf(leaf);
54
+ return isFilledLeaf(distilled) ? distilled : null;
45
55
  },
46
56
  and: (results) => {
47
57
  const filtered = results.filter((f) => f !== null);
@@ -1 +1 @@
1
- {"version":3,"file":"distill.js","sources":["../../src/filters/distill.ts"],"sourcesContent":["import { DistributiveKeys, UnionToTuples } from \"@milaboratories/helpers\";\nimport {\n RootFilterSpec,\n type FilterSpec,\n type FilterSpecLeaf,\n} from \"@milaboratories/pl-model-common\";\nimport { traverseFilterSpec } from \"./traverse\";\nimport { InferFilterSpecLeaf } 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\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<\n FS extends FilterSpec<FilterSpecLeaf<unknown>, unknown, unknown>,\n R extends FS extends RootFilterSpec<FilterSpecLeaf<unknown>, unknown, unknown>\n ? RootFilterSpec<InferFilterSpecLeaf<FS>>\n : FilterSpec<InferFilterSpecLeaf<FS>>,\n>(filter: null | undefined | FS): null | R {\n if (filter == null) return null;\n return traverseFilterSpec<FS, null | R>(filter, {\n leaf: (leaf) => {\n if (!isFilledLeaf(leaf as Record<string, unknown>)) return null;\n return distillLeaf(leaf as Record<string, unknown>) as R;\n },\n and: (results) => {\n const filtered = results.filter((f): f is NonNullable<typeof f> => f !== null);\n return filtered.length === 0 ? null : ({ type: \"and\", filters: filtered } as R);\n },\n or: (results) => {\n const filtered = results.filter((f): f is NonNullable<typeof f> => f !== null);\n return filtered.length === 0 ? null : ({ type: \"or\", filters: filtered } as R);\n },\n not: (result) => (result === null ? null : ({ type: \"not\", filter: result } as R)),\n });\n}\n"],"names":[],"mappings":";;AAYA;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;;;AAGG;AACG,SAAU,iBAAiB,CAK/B,MAA6B,EAAA;IAC7B,IAAI,MAAM,IAAI,IAAI;AAAE,QAAA,OAAO,IAAI;IAC/B,OAAO,kBAAkB,CAAe,MAAM,EAAE;AAC9C,QAAA,IAAI,EAAE,CAAC,IAAI,KAAI;AACb,YAAA,IAAI,CAAC,YAAY,CAAC,IAA+B,CAAC;AAAE,gBAAA,OAAO,IAAI;AAC/D,YAAA,OAAO,WAAW,CAAC,IAA+B,CAAM;QAC1D,CAAC;AACD,QAAA,GAAG,EAAE,CAAC,OAAO,KAAI;AACf,YAAA,MAAM,QAAQ,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,KAAiC,CAAC,KAAK,IAAI,CAAC;YAC9E,OAAO,QAAQ,CAAC,MAAM,KAAK,CAAC,GAAG,IAAI,GAAI,EAAE,IAAI,EAAE,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAQ;QACjF,CAAC;AACD,QAAA,EAAE,EAAE,CAAC,OAAO,KAAI;AACd,YAAA,MAAM,QAAQ,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,KAAiC,CAAC,KAAK,IAAI,CAAC;YAC9E,OAAO,QAAQ,CAAC,MAAM,KAAK,CAAC,GAAG,IAAI,GAAI,EAAE,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAQ;QAChF,CAAC;QACD,GAAG,EAAE,CAAC,MAAM,MAAM,MAAM,KAAK,IAAI,GAAG,IAAI,GAAI,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAQ,CAAC;AACnF,KAAA,CAAC;AACJ;;;;"}
1
+ {"version":3,"file":"distill.js","sources":["../../src/filters/distill.ts"],"sourcesContent":["import { DistributiveKeys, UnionToTuples } from \"@milaboratories/helpers\";\nimport {\n RootFilterSpec,\n type FilterSpec,\n type FilterSpecLeaf,\n} from \"@milaboratories/pl-model-common\";\nimport { traverseFilterSpec } from \"./traverse\";\nimport { InferFilterSpecLeaf } from \"@milaboratories/pl-model-common\";\nimport { isEmpty } from \"es-toolkit/compat\";\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<T>(node: FilterSpecLeaf<T>): boolean {\n if (node.type == null) return false;\n return !Object.values(node).some((value) => {\n switch (typeof value) {\n case \"number\":\n case \"boolean\":\n return false;\n case \"string\":\n return value.trim() === \"\";\n default: // undefined, null, empty objects/arrays\n return isEmpty(value);\n }\n });\n}\n\nfunction distillLeaf<T>(node: FilterSpecLeaf<T>): FilterSpecLeaf<T> {\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<T>;\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<\n FS extends FilterSpec<FilterSpecLeaf<unknown>, unknown, unknown>,\n R extends FS extends RootFilterSpec<FilterSpecLeaf<unknown>, unknown, unknown>\n ? RootFilterSpec<InferFilterSpecLeaf<FS>>\n : FilterSpec<InferFilterSpecLeaf<FS>>,\n>(filter: null | undefined | FS): null | R {\n if (filter == null) return null;\n return traverseFilterSpec<FS, null | R>(filter, {\n leaf: (leaf) => {\n const distilled = distillLeaf(leaf);\n return isFilledLeaf(distilled) ? (distilled as R) : null;\n },\n and: (results) => {\n const filtered = results.filter((f): f is NonNullable<typeof f> => f !== null);\n return filtered.length === 0 ? null : ({ type: \"and\", filters: filtered } as R);\n },\n or: (results) => {\n const filtered = results.filter((f): f is NonNullable<typeof f> => f !== null);\n return filtered.length === 0 ? null : ({ type: \"or\", filters: filtered } as R);\n },\n not: (result) => (result === null ? null : ({ type: \"not\", filter: result } as R)),\n });\n}\n"],"names":[],"mappings":";;;AAaA;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,CAAI,IAAuB,EAAA;AAC9C,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,KAAK,KAAI;QACzC,QAAQ,OAAO,KAAK;AAClB,YAAA,KAAK,QAAQ;AACb,YAAA,KAAK,SAAS;AACZ,gBAAA,OAAO,KAAK;AACd,YAAA,KAAK,QAAQ;AACX,gBAAA,OAAO,KAAK,CAAC,IAAI,EAAE,KAAK,EAAE;AAC5B,YAAA;AACE,gBAAA,OAAO,OAAO,CAAC,KAAK,CAAC;;AAE3B,IAAA,CAAC,CAAC;AACJ;AAEA,SAAS,WAAW,CAAI,IAAuB,EAAA;IAC7C,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,MAA2B;AACpC;AAEA;;;AAGG;AACG,SAAU,iBAAiB,CAK/B,MAA6B,EAAA;IAC7B,IAAI,MAAM,IAAI,IAAI;AAAE,QAAA,OAAO,IAAI;IAC/B,OAAO,kBAAkB,CAAe,MAAM,EAAE;AAC9C,QAAA,IAAI,EAAE,CAAC,IAAI,KAAI;AACb,YAAA,MAAM,SAAS,GAAG,WAAW,CAAC,IAAI,CAAC;AACnC,YAAA,OAAO,YAAY,CAAC,SAAS,CAAC,GAAI,SAAe,GAAG,IAAI;QAC1D,CAAC;AACD,QAAA,GAAG,EAAE,CAAC,OAAO,KAAI;AACf,YAAA,MAAM,QAAQ,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,KAAiC,CAAC,KAAK,IAAI,CAAC;YAC9E,OAAO,QAAQ,CAAC,MAAM,KAAK,CAAC,GAAG,IAAI,GAAI,EAAE,IAAI,EAAE,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAQ;QACjF,CAAC;AACD,QAAA,EAAE,EAAE,CAAC,OAAO,KAAI;AACd,YAAA,MAAM,QAAQ,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,KAAiC,CAAC,KAAK,IAAI,CAAC;YAC9E,OAAO,QAAQ,CAAC,MAAM,KAAK,CAAC,GAAG,IAAI,GAAI,EAAE,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAQ;QAChF,CAAC;QACD,GAAG,EAAE,CAAC,MAAM,MAAM,MAAM,KAAK,IAAI,GAAG,IAAI,GAAI,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAQ,CAAC;AACnF,KAAA,CAAC;AACJ;;;;"}
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- var version = "1.58.3";
3
+ var version = "1.58.5";
4
4
 
5
5
  exports.version = version;
6
6
  //# sourceMappingURL=package.json.cjs.map
@@ -1,4 +1,4 @@
1
- var version = "1.58.3";
1
+ var version = "1.58.5";
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.58.3",
3
+ "version": "1.58.5",
4
4
  "description": "Platforma.bio SDK / Block Model",
5
5
  "files": [
6
6
  "./dist/**/*",
@@ -25,8 +25,8 @@
25
25
  "utility-types": "^3.11.0",
26
26
  "zod": "~3.23.8",
27
27
  "@milaboratories/helpers": "1.13.5",
28
- "@milaboratories/pl-model-common": "1.25.1",
29
28
  "@milaboratories/pl-error-like": "1.12.8",
29
+ "@milaboratories/pl-model-common": "1.25.1",
30
30
  "@milaboratories/ptabler-expression-js": "1.1.23"
31
31
  },
32
32
  "devDependencies": {
@@ -34,8 +34,8 @@
34
34
  "fast-json-patch": "^3.1.1",
35
35
  "typescript": "~5.6.3",
36
36
  "vitest": "^4.0.18",
37
- "@milaboratories/build-configs": "1.5.0",
38
37
  "@milaboratories/ts-builder": "1.2.11",
38
+ "@milaboratories/build-configs": "1.5.0",
39
39
  "@milaboratories/ts-configs": "1.2.1"
40
40
  },
41
41
  "scripts": {
@@ -6,6 +6,7 @@ import {
6
6
  } from "@milaboratories/pl-model-common";
7
7
  import { traverseFilterSpec } from "./traverse";
8
8
  import { InferFilterSpecLeaf } from "@milaboratories/pl-model-common";
9
+ import { isEmpty } from "es-toolkit/compat";
9
10
 
10
11
  /** All possible field names that can appear in any FilterSpecLeaf variant. */
11
12
  type FilterSpecLeafKey = DistributiveKeys<FilterSpecLeaf<string>>;
@@ -27,19 +28,29 @@ const KNOWN_LEAF_KEYS_TUPLE: UnionToTuples<FilterSpecLeafKey> = [
27
28
  const KNOWN_LEAF_KEYS: Set<FilterSpecLeafKey> = new Set(KNOWN_LEAF_KEYS_TUPLE);
28
29
 
29
30
  /** Returns true if the leaf is filled — type is defined and no required fields are undefined. */
30
- function isFilledLeaf(node: Record<string, unknown>): boolean {
31
+ function isFilledLeaf<T>(node: FilterSpecLeaf<T>): boolean {
31
32
  if (node.type == null) return false;
32
- return !Object.values(node).some((v) => v === undefined);
33
+ return !Object.values(node).some((value) => {
34
+ switch (typeof value) {
35
+ case "number":
36
+ case "boolean":
37
+ return false;
38
+ case "string":
39
+ return value.trim() === "";
40
+ default: // undefined, null, empty objects/arrays
41
+ return isEmpty(value);
42
+ }
43
+ });
33
44
  }
34
45
 
35
- function distillLeaf(node: Record<string, unknown>): FilterSpecLeaf<string> {
46
+ function distillLeaf<T>(node: FilterSpecLeaf<T>): FilterSpecLeaf<T> {
36
47
  const result: Record<string, unknown> = {};
37
48
  for (const [key, value] of Object.entries(node)) {
38
49
  if (KNOWN_LEAF_KEYS.has(key as FilterSpecLeafKey)) {
39
50
  result[key] = value;
40
51
  }
41
52
  }
42
- return result as FilterSpecLeaf<string>;
53
+ return result as FilterSpecLeaf<T>;
43
54
  }
44
55
 
45
56
  /**
@@ -55,8 +66,8 @@ export function distillFilterSpec<
55
66
  if (filter == null) return null;
56
67
  return traverseFilterSpec<FS, null | R>(filter, {
57
68
  leaf: (leaf) => {
58
- if (!isFilledLeaf(leaf as Record<string, unknown>)) return null;
59
- return distillLeaf(leaf as Record<string, unknown>) as R;
69
+ const distilled = distillLeaf(leaf);
70
+ return isFilledLeaf(distilled) ? (distilled as R) : null;
60
71
  },
61
72
  and: (results) => {
62
73
  const filtered = results.filter((f): f is NonNullable<typeof f> => f !== null);