@milaboratories/pl-model-common 1.19.4 → 1.19.6

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/json.d.ts CHANGED
@@ -3,14 +3,14 @@ type JsonValue = JsonPrimitive | JsonValue[] | {
3
3
  [key: string]: JsonValue;
4
4
  };
5
5
  type NotAssignableToJson = bigint | symbol | Function;
6
- export type JsonCompatible<T> = unknown extends T ? never : {
6
+ export type JsonCompatible<T> = unknown extends T ? unknown : {
7
7
  [P in keyof T]: T[P] extends JsonValue ? T[P] : T[P] extends NotAssignableToJson ? never : JsonCompatible<T[P]>;
8
8
  };
9
- export type StringifiedJson<T> = JsonCompatible<T> extends never ? never : string & {
9
+ export type StringifiedJson<T = unknown> = JsonCompatible<T> extends never ? never : string & {
10
10
  __json_stringified: T;
11
11
  };
12
12
  export declare function stringifyJson<T>(value: JsonCompatible<T>): StringifiedJson<T>;
13
- export type CanonicalizedJson<T> = JsonCompatible<T> extends never ? never : string & {
13
+ export type CanonicalizedJson<T = unknown> = JsonCompatible<T> extends never ? never : string & {
14
14
  __json_canonicalized: T;
15
15
  };
16
16
  export declare function canonicalizeJson<T>(value: JsonCompatible<T>): CanonicalizedJson<T>;
@@ -1 +1 @@
1
- {"version":3,"file":"json.d.ts","sourceRoot":"","sources":["../src/json.ts"],"names":[],"mappings":"AAEA,KAAK,aAAa,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,IAAI,GAAG,SAAS,CAAC;AAElE,KAAK,SAAS,GAAG,aAAa,GAAG,SAAS,EAAE,GAAG;IAC7C,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,CAAC;CAC1B,CAAC;AAGF,KAAK,mBAAmB,GAAG,MAAM,GAAG,MAAM,GAAG,QAAQ,CAAC;AAEtD,MAAM,MAAM,cAAc,CAAC,CAAC,IAAI,OAAO,SAAS,CAAC,GAAG,KAAK,GAAG;KACzD,CAAC,IAAI,MAAM,CAAC,GACb,CAAC,CAAC,CAAC,CAAC,SAAS,SAAS,GAAG,CAAC,CAAC,CAAC,CAAC,GAC3B,CAAC,CAAC,CAAC,CAAC,SAAS,mBAAmB,GAAG,KAAK,GACtC,cAAc,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;CACzB,CAAC;AAEF,MAAM,MAAM,eAAe,CAAC,CAAC,IAAI,cAAc,CAAC,CAAC,CAAC,SAAS,KAAK,GAAG,KAAK,GAAG,MAAM,GAAG;IAClF,kBAAkB,EAAE,CAAC,CAAC;CACvB,CAAC;AAEF,wBAAgB,aAAa,CAAC,CAAC,EAAE,KAAK,EAAE,cAAc,CAAC,CAAC,CAAC,GAAG,eAAe,CAAC,CAAC,CAAC,CAE7E;AAED,MAAM,MAAM,iBAAiB,CAAC,CAAC,IAAI,cAAc,CAAC,CAAC,CAAC,SAAS,KAAK,GAAG,KAAK,GAAG,MAAM,GAAG;IACpF,oBAAoB,EAAE,CAAC,CAAC;CACzB,CAAC;AAEF,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,KAAK,EAAE,cAAc,CAAC,CAAC,CAAC,GAAG,iBAAiB,CAAC,CAAC,CAAC,CAElF;AAED,wBAAgB,SAAS,CAAC,CAAC,EAAE,KAAK,EAAE,eAAe,CAAC,CAAC,CAAC,GAAG,iBAAiB,CAAC,CAAC,CAAC,GAAG,CAAC,CAEhF"}
1
+ {"version":3,"file":"json.d.ts","sourceRoot":"","sources":["../src/json.ts"],"names":[],"mappings":"AAEA,KAAK,aAAa,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,IAAI,GAAG,SAAS,CAAC;AAElE,KAAK,SAAS,GAAG,aAAa,GAAG,SAAS,EAAE,GAAG;IAC7C,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,CAAC;CAC1B,CAAC;AAGF,KAAK,mBAAmB,GAAG,MAAM,GAAG,MAAM,GAAG,QAAQ,CAAC;AAEtD,MAAM,MAAM,cAAc,CAAC,CAAC,IAAI,OAAO,SAAS,CAAC,GAAG,OAAO,GAAG;KAC3D,CAAC,IAAI,MAAM,CAAC,GACb,CAAC,CAAC,CAAC,CAAC,SAAS,SAAS,GAAG,CAAC,CAAC,CAAC,CAAC,GAC3B,CAAC,CAAC,CAAC,CAAC,SAAS,mBAAmB,GAAG,KAAK,GACtC,cAAc,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;CACzB,CAAC;AAEF,MAAM,MAAM,eAAe,CAAC,CAAC,GAAG,OAAO,IAAI,cAAc,CAAC,CAAC,CAAC,SAAS,KAAK,GAAG,KAAK,GAAG,MAAM,GAAG;IAC5F,kBAAkB,EAAE,CAAC,CAAC;CACvB,CAAC;AAEF,wBAAgB,aAAa,CAAC,CAAC,EAAE,KAAK,EAAE,cAAc,CAAC,CAAC,CAAC,GAAG,eAAe,CAAC,CAAC,CAAC,CAE7E;AAED,MAAM,MAAM,iBAAiB,CAAC,CAAC,GAAG,OAAO,IAAI,cAAc,CAAC,CAAC,CAAC,SAAS,KAAK,GAAG,KAAK,GAAG,MAAM,GAAG;IAC9F,oBAAoB,EAAE,CAAC,CAAC;CACzB,CAAC;AAEF,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,KAAK,EAAE,cAAc,CAAC,CAAC,CAAC,GAAG,iBAAiB,CAAC,CAAC,CAAC,CAElF;AAED,wBAAgB,SAAS,CAAC,CAAC,EAAE,KAAK,EAAE,eAAe,CAAC,CAAC,CAAC,GAAG,iBAAiB,CAAC,CAAC,CAAC,GAAG,CAAC,CAEhF"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@milaboratories/pl-model-common",
3
- "version": "1.19.4",
3
+ "version": "1.19.6",
4
4
  "description": "Platforma SDK Model",
5
5
  "types": "./dist/index.d.ts",
6
6
  "main": "./dist/index.js",
@@ -26,8 +26,8 @@
26
26
  "typescript": "~5.6.3",
27
27
  "vite": "^6.3.5",
28
28
  "vitest": "^2.1.9",
29
- "@milaboratories/build-configs": "1.0.5",
30
- "@platforma-sdk/eslint-config": "1.0.3"
29
+ "@platforma-sdk/eslint-config": "1.0.3",
30
+ "@milaboratories/build-configs": "1.0.5"
31
31
  },
32
32
  "scripts": {
33
33
  "type-check": "tsc --noEmit --composite false",
@@ -204,6 +204,14 @@ export function isDataInfo<Blob>(value: unknown): value is DataInfo<Blob> {
204
204
  * @param mapFn - Function to transform blobs from type B1 to type B2
205
205
  * @returns A new DataInfo object with transformed blob references
206
206
  */
207
+ export function mapDataInfo<B1, B2>(
208
+ dataInfo: ParquetPartitionedDataInfo<B1>,
209
+ mapFn: (blob: B1) => B2,
210
+ ): ParquetPartitionedDataInfo<B2>;
211
+ export function mapDataInfo<B1, B2>(
212
+ dataInfo: Exclude<DataInfo<B1>, ParquetPartitionedDataInfo<B1>>,
213
+ mapFn: (blob: B1) => B2,
214
+ ): Exclude<DataInfo<B2>, ParquetPartitionedDataInfo<B2>>;
207
215
  export function mapDataInfo<B1, B2>(
208
216
  dataInfo: DataInfo<B1>,
209
217
  mapFn: (blob: B1) => B2,
@@ -1,8 +1,5 @@
1
1
  import type { Branded } from '../../branding';
2
- import {
3
- type ValueType,
4
- type ValueTypeBytes,
5
- } from './spec/spec';
2
+ import { ValueType } from './spec/spec';
6
3
 
7
4
  export type PVectorDataInt = Int32Array;
8
5
  export type PVectorDataLong = BigInt64Array;
@@ -11,12 +8,12 @@ export type PVectorDataDouble = Float64Array;
11
8
  export type PVectorDataString = (null | string)[];
12
9
  export type PVectorDataBytes = (null | Uint8Array)[];
13
10
  export type PVectorDataTyped<DataType extends ValueType> =
14
- DataType extends 'Int' ? PVectorDataInt :
15
- DataType extends 'Long' ? PVectorDataLong :
16
- DataType extends 'Float' ? PVectorDataFloat :
17
- DataType extends 'Double' ? PVectorDataDouble :
18
- DataType extends 'String' ? PVectorDataString :
19
- DataType extends 'Bytes' ? PVectorDataBytes :
11
+ DataType extends typeof ValueType.Int ? PVectorDataInt :
12
+ DataType extends typeof ValueType.Long ? PVectorDataLong :
13
+ DataType extends typeof ValueType.Float ? PVectorDataFloat :
14
+ DataType extends typeof ValueType.Double ? PVectorDataDouble :
15
+ DataType extends typeof ValueType.String ? PVectorDataString :
16
+ DataType extends typeof ValueType.Bytes ? PVectorDataBytes :
20
17
  never;
21
18
  export type PVectorData = PVectorDataTyped<ValueType>;
22
19
 
@@ -62,17 +59,17 @@ function isValueNA(vector: PTableVector, row: number): boolean {
62
59
  const valueType = vector.type;
63
60
  const value = vector.data[row];
64
61
  switch (valueType) {
65
- case 'Int':
62
+ case ValueType.Int:
66
63
  return (value as PVectorDataInt[number]) === -2147483648;
67
- case 'Long':
64
+ case ValueType.Long:
68
65
  return (value as PVectorDataLong[number]) === -9007199254740991n;
69
- case 'Float':
66
+ case ValueType.Float:
70
67
  return Number.isNaN((value as PVectorDataFloat[number]));
71
- case 'Double':
68
+ case ValueType.Double:
72
69
  return Number.isNaN((value as PVectorDataDouble[number]));
73
- case 'String':
70
+ case ValueType.String:
74
71
  return (value as PVectorDataString[number]) === null;
75
- case 'Bytes':
72
+ case ValueType.Bytes:
76
73
  return (value as PVectorDataBytes[number]) === null;
77
74
  default:
78
75
  throw Error(`unsupported data type: ${valueType satisfies never}`);
@@ -95,7 +92,7 @@ export function isPTableNA(value: unknown): value is PTableNA {
95
92
  return value === PTableNA;
96
93
  }
97
94
 
98
- export type ValueTypeSupported = Exclude<ValueType, ValueTypeBytes>;
95
+ export type ValueTypeSupported = Exclude<ValueType, typeof ValueType.Bytes>;
99
96
 
100
97
  export type PTableValueInt = number;
101
98
  export type PTableValueLong = number;
@@ -103,11 +100,11 @@ export type PTableValueFloat = number;
103
100
  export type PTableValueDouble = number;
104
101
  export type PTableValueString = string;
105
102
  export type PTableValueData<DataType extends ValueTypeSupported> =
106
- DataType extends 'Int' ? PTableValueInt :
107
- DataType extends 'Long' ? PTableValueLong :
108
- DataType extends 'Float' ? PTableValueFloat :
109
- DataType extends 'Double' ? PTableValueDouble :
110
- DataType extends 'String' ? PTableValueString :
103
+ DataType extends typeof ValueType.Int ? PTableValueInt :
104
+ DataType extends typeof ValueType.Long ? PTableValueLong :
105
+ DataType extends typeof ValueType.Float ? PTableValueFloat :
106
+ DataType extends typeof ValueType.Double ? PTableValueDouble :
107
+ DataType extends typeof ValueType.String ? PTableValueString :
111
108
  never;
112
109
  export type PTableValueDataBranded<DataType extends ValueTypeSupported> = Branded<PTableValueData<DataType>, DataType>;
113
110
  export type PTableValue<
@@ -157,8 +154,8 @@ function pTableValueImpl<
157
154
  dataType?: DataType;
158
155
  },
159
156
  ) {
160
- const valueType: ValueType = column.type;
161
- if (valueType === 'Bytes') {
157
+ const valueType = column.type;
158
+ if (valueType === ValueType.Bytes) {
162
159
  throw Error('Bytes not yet supported');
163
160
  }
164
161
 
@@ -176,15 +173,15 @@ function pTableValueImpl<
176
173
 
177
174
  const value = column.data[row]!;
178
175
  switch (valueType) {
179
- case 'Int':
176
+ case ValueType.Int:
180
177
  return value as PVectorDataInt[number];
181
- case 'Long':
178
+ case ValueType.Long:
182
179
  return Number(value as PVectorDataLong[number]);
183
- case 'Float':
180
+ case ValueType.Float:
184
181
  return value as PVectorDataFloat[number];
185
- case 'Double':
182
+ case ValueType.Double:
186
183
  return value as PVectorDataDouble[number];
187
- case 'String':
184
+ case ValueType.String:
188
185
  return (value as PVectorDataString[number])!;
189
186
  }
190
187
  }
@@ -12,3 +12,5 @@ export * from './unique_values';
12
12
  export * from './spec';
13
13
 
14
14
  export * from './driver';
15
+
16
+ export * from './linker_columns';
@@ -0,0 +1,238 @@
1
+ import {
2
+ Annotation,
3
+ AxisSpec,
4
+ AxisSpecNormalized,
5
+ getArrayFromAxisTree,
6
+ getAxesTree,
7
+ getNormalizedAxesList,
8
+ getSetFromAxisTree,
9
+ PColumnIdAndSpec,
10
+ ValueType,
11
+ } from './spec/index';
12
+ import { PObjectId } from '../../pool';
13
+ import { stringifyJson } from '../../json'
14
+ import {
15
+ describe,
16
+ expect,
17
+ test,
18
+ } from 'vitest';
19
+ import { LinkerMap } from './linker_columns';
20
+
21
+ function makeTestAxis(params: {
22
+ name: string;
23
+ parents?: AxisSpec[];
24
+ }): AxisSpec {
25
+ return {
26
+ type: ValueType.Int,
27
+ name: params.name,
28
+ annotations: {
29
+ [Annotation.Label]: `${params.name} axis`,
30
+ ...(params.parents && params.parents.length > 0
31
+ ? { [Annotation.Parents]: stringifyJson(params.parents) }
32
+ : {}
33
+ ),
34
+ } satisfies Annotation,
35
+ };
36
+ }
37
+
38
+ function makeLinkerColumn(params: {
39
+ name: string;
40
+ from: AxisSpec[];
41
+ to: AxisSpec[];
42
+ }): PColumnIdAndSpec {
43
+ return {
44
+ columnId: params.name as PObjectId,
45
+ spec: {
46
+ kind: 'PColumn',
47
+ valueType: ValueType.String,
48
+ name: params.name,
49
+ axesSpec: [...params.from, ...params.to],
50
+ annotations: {
51
+ [Annotation.Label]: `${params.name} column`,
52
+ [Annotation.IsLinkerColumn]: stringifyJson(true),
53
+ } satisfies Annotation,
54
+ },
55
+ };
56
+ }
57
+
58
+ /** Returns all permutations of initial array */
59
+ function allPermutations<T>(arr: T[]): T[][] {
60
+ switch (arr.length) {
61
+ case 0: return [];
62
+ case 1: return [arr];
63
+ case 2: return [arr, [arr[1], arr[0]]];
64
+ default: return arr.reduce(
65
+ (acc, item, i) => acc.concat(
66
+ allPermutations<T>([...arr.slice(0, i), ...arr.slice(i + 1)])
67
+ .map(val => [item, ...val])
68
+ ),
69
+ [] as T[][],
70
+ );
71
+ }
72
+ };
73
+
74
+ describe('Linker columns', () => {
75
+ test('Search in linker columns map', () => {
76
+ const [axis1, axis2, axis3, axis4, axis5] = getNormalizedAxesList([
77
+ makeTestAxis({ name: 'id1' }),
78
+ makeTestAxis({ name: 'id2' }),
79
+ makeTestAxis({ name: 'id3' }),
80
+ makeTestAxis({ name: 'id4' }),
81
+ makeTestAxis({ name: 'id5' })
82
+ ]);
83
+ const linkerMap = LinkerMap.fromColumns([
84
+ makeLinkerColumn({ name: 'c12', from: [axis1], to: [axis2] }),
85
+ makeLinkerColumn({ name: 'c13', from: [axis1], to: [axis3] }),
86
+ makeLinkerColumn({ name: 'c45', from: [axis4], to: [axis5] }),
87
+ ]);
88
+
89
+ let testCase = (params: {
90
+ from: AxisSpecNormalized[];
91
+ to: AxisSpecNormalized[];
92
+ expected: string[];
93
+ }) => {
94
+ const linkers = linkerMap.getLinkerColumnsForAxes({
95
+ from: params.from,
96
+ to: params.to,
97
+ throwWhenNoLinkExists: false,
98
+ });
99
+ expect(linkers.map(item => item.spec.name).sort()).toEqual(params.expected);
100
+ }
101
+
102
+ testCase({ from: [axis2], to: [axis3], expected: ['c12', 'c13'] });
103
+ testCase({ from: [axis1], to: [axis2], expected: ['c12'] });
104
+ testCase({ from: [axis1], to: [axis4], expected: []});
105
+ });
106
+
107
+ test('Axis tree - without parents', () => {
108
+ const [axisA, axisB] = getNormalizedAxesList([
109
+ makeTestAxis({ name: 'a' }),
110
+ makeTestAxis({ name: 'b' })
111
+ ]);
112
+ const tree = getAxesTree(axisA);
113
+ expect(getSetFromAxisTree(tree).size).toBe(1);
114
+ expect(getArrayFromAxisTree(tree).length).toBe(1);
115
+
116
+ expect(LinkerMap.getAxesGroups([axisA, axisB]).length).toBe(2);
117
+ })
118
+
119
+ test('Axis tree - with parents', () => {
120
+ const axisD = makeTestAxis({ name: 'd' });
121
+ const axisC = makeTestAxis({ name: 'c', parents: [axisD] });
122
+ const axisB = makeTestAxis({ name: 'b', parents: [axisC] });
123
+ const axisA = makeTestAxis({ name: 'a', parents: [axisB] });
124
+ const [axisDn, axisCn, axisBn, axisAn] = getNormalizedAxesList([axisD, axisC, axisB, axisA])
125
+
126
+ const tree = getAxesTree(axisAn);
127
+ expect(getSetFromAxisTree(tree).size).toBe(4);
128
+ expect(getArrayFromAxisTree(tree).length).toBe(4);
129
+
130
+ for (const group of allPermutations([axisAn, axisBn, axisCn, axisDn])) {
131
+ expect(LinkerMap.getAxesGroups(group).length).toBe(1);
132
+ }
133
+
134
+ const axisD2 = makeTestAxis({ name: 'd' });
135
+ const axisC2 = makeTestAxis({ name: 'c', parents: [axisD2] });
136
+ const axisB2 = makeTestAxis({ name: 'b' });
137
+ const axisA2 = makeTestAxis({ name: 'a', parents: [axisB2] });
138
+ const normalized2 = getNormalizedAxesList([axisD2, axisC2, axisB2, axisA2])
139
+
140
+ for (const group of allPermutations(normalized2)) {
141
+ expect(LinkerMap.getAxesGroups(group).length).toBe(2);
142
+ }
143
+
144
+ const axisD3 = makeTestAxis({ name: 'd' });
145
+ const axisC3 = makeTestAxis({ name: 'c' });
146
+ const axisB3 = makeTestAxis({ name: 'b' });
147
+ const axisA3 = makeTestAxis({ name: 'a', parents: [axisB3] });
148
+ const normalized3 = getNormalizedAxesList([axisD3, axisC3, axisB3, axisA3])
149
+
150
+ for (const group of allPermutations(normalized3)) {
151
+ expect(LinkerMap.getAxesGroups(group).length).toBe(3);
152
+ }
153
+
154
+ const axisD4 = makeTestAxis({ name: 'd' });
155
+ const axisC4 = makeTestAxis({ name: 'c' });
156
+ const axisB4 = makeTestAxis({ name: 'b' });
157
+ const axisA4 = makeTestAxis({ name: 'a' });
158
+ const normalized4 = getNormalizedAxesList([axisD4, axisC4, axisB4, axisA4])
159
+
160
+ for (const group of allPermutations(normalized4)) {
161
+ expect(LinkerMap.getAxesGroups(group).length).toBe(4);
162
+ }
163
+ })
164
+
165
+ test('Generate partial trees', () => {
166
+ // Axes graph of parents (A, E - roots, C, B, D - parents) in some column:
167
+ // A - C
168
+ // \_ B _ D
169
+ // E/
170
+ //
171
+ // If the column is not a linker: trees to search linkers should be:
172
+ // 1 C
173
+ // 2 D
174
+ // 3 B - D
175
+ // 4 A - C
176
+ // \_ B - D
177
+ // 5 E - B - D
178
+
179
+ // If the axes are in a linker: trees must be in the linkers map:
180
+
181
+ // 1 A - C
182
+ // \_ B _ D
183
+ // 2 E - B - D
184
+
185
+ const axisD = makeTestAxis({ name: 'd' });
186
+ const axisC = makeTestAxis({ name: 'c' });
187
+ const axisB = makeTestAxis({ name: 'b', parents: [axisD] });
188
+ const axisA = makeTestAxis({ name: 'a', parents: [axisB, axisC] });
189
+ const axisE = makeTestAxis({ name: 'e', parents: [axisB, axisC] });
190
+ const axisF = makeTestAxis({ name: 'f' });
191
+ const axisH = makeTestAxis({ name: 'h' });
192
+
193
+ const group1 = [axisA, axisB, axisC, axisD, axisE];
194
+ const group2 = [axisF];
195
+ const group3 = [axisH];
196
+ const group1Normalized = getNormalizedAxesList(group1);
197
+ const group2Normalized = getNormalizedAxesList(group2);
198
+ const [axisAn, axisBn, axisCn, axisDn, axisEn] = group1Normalized;
199
+
200
+ const linker1 = makeLinkerColumn({ name: 'linker1', from: group1, to: group2 });
201
+ const linker2 = makeLinkerColumn({ name: 'linker2', from: group2, to: group3 });
202
+
203
+ const roots = LinkerMap.getAxesRoots(group1Normalized);
204
+
205
+ expect(roots).toEqual([axisAn, axisEn]);
206
+
207
+ const groups = LinkerMap.getAxesGroups([...group1Normalized, ...group2Normalized]);
208
+ expect(groups.length).toBe(2);
209
+ expect(groups[0]).toEqual(group1Normalized);
210
+ expect(groups[1]).toEqual(group2Normalized);
211
+
212
+ const linkersMap = LinkerMap.fromColumns([linker1, linker2]);
213
+
214
+ expect(
215
+ new Set(linkersMap.getReachableByLinkersAxesFromAxes(group2Normalized).map(a => a.name))
216
+ ).toEqual(
217
+ new Set(([...group1, ...group3]).map(a => a.name))
218
+ );
219
+ expect(linkersMap.getReachableByLinkersAxesFromAxes([axisDn])).toEqual([]);
220
+ expect(linkersMap.getReachableByLinkersAxesFromAxes([axisBn])).toEqual([]);
221
+ });
222
+
223
+ test('Order of parents should not matter', () => {
224
+ const axisA = makeTestAxis({ name: 'a' });
225
+ const axisB = makeTestAxis({ name: 'b' });
226
+ const axisC1 = makeTestAxis({ name: 'c', parents: [axisA, axisB] });
227
+ const axisC2 = makeTestAxis({ name: 'c', parents: [axisB, axisA] });
228
+ const axisD = makeTestAxis({ name: 'd' });
229
+
230
+ const [a, b, c1, c2, d] = getNormalizedAxesList([axisA, axisB, axisC1, axisC2, axisD]);
231
+ const linkerMap = LinkerMap.fromColumns([
232
+ makeLinkerColumn({ name: 'linker1', from: [axisA, axisB, axisC1], to: [axisD] })
233
+ ]);
234
+
235
+ expect(linkerMap.getReachableByLinkersAxesFromAxes([c2])).not.toHaveLength(0);
236
+ expect(linkerMap.getReachableByLinkersAxesFromAxes([c1])).not.toHaveLength(0);
237
+ })
238
+ });