@milaboratories/pl-model-common 1.26.0 → 1.27.0

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 (37) hide show
  1. package/dist/drivers/index.d.ts +2 -1
  2. package/dist/drivers/index.js +1 -1
  3. package/dist/drivers/pframe/index.d.ts +1 -1
  4. package/dist/drivers/pframe/index.js +1 -1
  5. package/dist/drivers/pframe/linker_columns.cjs +2 -5
  6. package/dist/drivers/pframe/linker_columns.cjs.map +1 -1
  7. package/dist/drivers/pframe/linker_columns.js +2 -5
  8. package/dist/drivers/pframe/linker_columns.js.map +1 -1
  9. package/dist/drivers/pframe/spec/anchored.cjs.map +1 -1
  10. package/dist/drivers/pframe/spec/anchored.js.map +1 -1
  11. package/dist/drivers/pframe/spec/index.d.ts +1 -1
  12. package/dist/drivers/pframe/spec/index.js +1 -1
  13. package/dist/drivers/pframe/spec/selectors.cjs +2 -2
  14. package/dist/drivers/pframe/spec/selectors.cjs.map +1 -1
  15. package/dist/drivers/pframe/spec/selectors.d.ts +12 -6
  16. package/dist/drivers/pframe/spec/selectors.js +2 -2
  17. package/dist/drivers/pframe/spec/selectors.js.map +1 -1
  18. package/dist/drivers/pspec.d.ts +127 -0
  19. package/dist/index.cjs +4 -1
  20. package/dist/index.d.ts +4 -2
  21. package/dist/index.js +3 -2
  22. package/dist/resource_types.cjs +53 -0
  23. package/dist/resource_types.cjs.map +1 -0
  24. package/dist/resource_types.d.ts +50 -0
  25. package/dist/resource_types.js +51 -0
  26. package/dist/resource_types.js.map +1 -0
  27. package/package.json +5 -4
  28. package/src/common_types.ts +0 -1
  29. package/src/drivers/index.ts +1 -0
  30. package/src/drivers/pframe/linker_columns.test.ts +22 -3
  31. package/src/drivers/pframe/linker_columns.ts +2 -2
  32. package/src/drivers/pframe/spec/anchored.ts +2 -2
  33. package/src/drivers/pframe/spec/selectors.ts +11 -5
  34. package/src/drivers/pframe/type_util.ts +0 -1
  35. package/src/drivers/pspec.ts +143 -0
  36. package/src/index.ts +1 -0
  37. package/src/resource_types.ts +47 -0
@@ -98,7 +98,7 @@ describe("Linker columns", () => {
98
98
  expect(linkers.map((item) => item.spec.name).sort()).toEqual(params.expected);
99
99
  };
100
100
 
101
- testCase({ from: [axis2], to: [axis3], expected: ["c12", "c13"] });
101
+ testCase({ from: [axis2], to: [axis3], expected: [] });
102
102
  testCase({ from: [axis1], to: [axis2], expected: ["c12"] });
103
103
  testCase({ from: [axis1], to: [axis4], expected: [] });
104
104
  });
@@ -239,11 +239,13 @@ describe("Linker columns", () => {
239
239
 
240
240
  const linkersMap = LinkerMap.fromColumns([linker1, linker2]);
241
241
 
242
+ // Forward-only: from group2 we only reach group3 (no back edge to group1)
242
243
  expect(
243
244
  new Set(
244
245
  linkersMap.getReachableByLinkersAxesFromAxesNormalized(group2Normalized).map((a) => a.name),
245
246
  ),
246
- ).toEqual(new Set([...group1, ...group3].map((a) => a.name)));
247
+ ).toEqual(new Set([...group3].map((a) => a.name)));
248
+ // Non-root axes (axisDn, axisBn) don't match linker map keys, so no forward reachability
247
249
  expect(linkersMap.getReachableByLinkersAxesFromAxesNormalized([axisDn])).toEqual([]);
248
250
  expect(linkersMap.getReachableByLinkersAxesFromAxesNormalized([axisBn])).toEqual([]);
249
251
  });
@@ -282,7 +284,7 @@ describe("Linker columns", () => {
282
284
  getNormalizedAxesList([axisA, axisB, axisC, axisE]),
283
285
  )
284
286
  .map((v) => v.name),
285
- ).toEqual(["a", "b", "e"]);
287
+ ).toEqual(["a", "b", "c", "e"]);
286
288
 
287
289
  expect(
288
290
  linkerMap
@@ -290,4 +292,21 @@ describe("Linker columns", () => {
290
292
  .map((v) => v.name),
291
293
  ).toEqual(["a", "b", "c", "e"]);
292
294
  });
295
+
296
+ test("getReachableByLinkersAxesFromAxes", () => {
297
+ const axisA = makeTestAxis({ name: "a" });
298
+ const axisB = makeTestAxis({ name: "b" });
299
+ const axisC = makeTestAxis({ name: "c" });
300
+ const axisD = makeTestAxis({ name: "d" });
301
+ const linkerMap = LinkerMap.fromColumns([
302
+ makeLinkerColumn({ name: "linker1", from: [axisA], to: [axisB] }),
303
+ makeLinkerColumn({ name: "linker2", from: [axisB], to: [axisC] }),
304
+ makeLinkerColumn({ name: "linker3", from: [axisC], to: [axisD] }),
305
+ ]);
306
+
307
+ expect(linkerMap.getReachableByLinkersAxesFromAxes([axisA])).toEqual(
308
+ getNormalizedAxesList([axisB, axisC, axisD]),
309
+ );
310
+ expect(linkerMap.getReachableByLinkersAxesFromAxes([axisD])).toEqual([]);
311
+ });
293
312
  });
@@ -89,7 +89,6 @@ export class LinkerMap implements LinkersData {
89
89
  for (const [keyLeft] of leftKeyVariants) {
90
90
  for (const [keyRight] of rightKeyVariants) {
91
91
  result.get(keyLeft)?.linkWith.set(keyRight, linker);
92
- result.get(keyRight)?.linkWith.set(keyLeft, linker);
93
92
  }
94
93
  }
95
94
  }
@@ -138,7 +137,8 @@ export class LinkerMap implements LinkersData {
138
137
  current = previous[current];
139
138
  }
140
139
  ids.push(current);
141
- return ids.map((id: LinkerKey) => this.data.get(id)!.linkWith.get(previous[id])!);
140
+ // Edge (previous[id] -> id) is stored at the "from" node in one-directional map
141
+ return ids.map((id: LinkerKey) => this.data.get(previous[id])!.linkWith.get(id)!);
142
142
  } else if (!visited.has(availableId)) {
143
143
  next.add(availableId);
144
144
  visited.add(availableId);
@@ -8,7 +8,7 @@ import type {
8
8
  AnchorAxisRefByIdx,
9
9
  AnchoredPColumnId,
10
10
  AnchoredPColumnSelector,
11
- AxisSelector,
11
+ LegacyAxisSelector,
12
12
  PColumnSelector,
13
13
  } from "./selectors";
14
14
  import type { AxisId, PColumnSpec } from "./spec";
@@ -323,7 +323,7 @@ export function resolveAnchors(
323
323
  function resolveAxisReference(
324
324
  anchors: Record<string, PColumnSpec>,
325
325
  axisRef: AAxisSelector,
326
- ): AxisSelector {
326
+ ): LegacyAxisSelector {
327
327
  if (!isAnchorAxisRef(axisRef)) return axisRef;
328
328
 
329
329
  // It's an anchored reference
@@ -12,8 +12,14 @@ import { getAxisId } from "./spec";
12
12
  *
13
13
  * This interface is used in various selection and matching operations
14
14
  * throughout the PFrame system, such as column queries and axis lookups.
15
+ *
16
+ * @deprecated This selector is part of the legacy column matching API.
17
+ * The new Columns API (see sdk/model/src/columns/) now handles column and axis
18
+ * selection via {@link AxisSelector} and {@link ColumnSelector}, providing
19
+ * stricter matching semantics (StringMatcher-based) and a unified approach
20
+ * to working with columns, including domain and annotation matching.
15
21
  */
16
- export interface AxisSelector {
22
+ export interface LegacyAxisSelector {
17
23
  /**
18
24
  * Optional value type to match against.
19
25
  * When specified, only axes with this exact type will match.
@@ -94,7 +100,7 @@ export type ADomain = string | AnchorDomainRef;
94
100
  * Axis identifier that can be either a direct AxisId or a reference to an axis through an anchor
95
101
  * Allows referring to axes in a way that can be resolved in different contexts
96
102
  */
97
- export type AAxisSelector = AxisSelector | AnchorAxisRef;
103
+ export type AAxisSelector = LegacyAxisSelector | AnchorAxisRef;
98
104
 
99
105
  /**
100
106
  * Match resolution strategy for PColumns
@@ -144,7 +150,7 @@ export interface PColumnSelector extends AnchoredPColumnSelector {
144
150
  domain?: Record<string, string>;
145
151
  contextDomainAnchor?: never;
146
152
  contextDomain?: Record<string, string>;
147
- axes?: AxisSelector[];
153
+ axes?: LegacyAxisSelector[];
148
154
  }
149
155
 
150
156
  /**
@@ -187,7 +193,7 @@ export function isAnchoredPColumnId(id: unknown): id is AnchoredPColumnId {
187
193
  * @param axis - The AxisId to check against the selector
188
194
  * @returns true if the AxisId matches all specified criteria in the selector, false otherwise
189
195
  */
190
- export function matchAxis(selector: AxisSelector, axis: AxisId): boolean {
196
+ export function matchAxis(selector: LegacyAxisSelector, axis: AxisId): boolean {
191
197
  // Match name if specified
192
198
  if (selector.name !== undefined && selector.name !== axis.name) return false;
193
199
 
@@ -298,7 +304,7 @@ export function matchPColumn(pcolumn: PColumnSpec, selector: PColumnSelector): b
298
304
  * or an array of PColumnSelectors, or a single PColumnSelector
299
305
  * @returns A function that takes a PColumnSpec and returns a boolean
300
306
  */
301
- export function selectorsToPredicate(
307
+ export function legacyColumnSelectorsToPredicate(
302
308
  predicateOrSelectors: PColumnSelector | PColumnSelector[],
303
309
  ): (spec: PObjectSpec) => boolean {
304
310
  if (Array.isArray(predicateOrSelectors))
@@ -1,4 +1,3 @@
1
- /* eslint-disable @typescript-eslint/no-explicit-any */
2
1
  export type AddParameters<
3
2
  TParameters extends [...args: any],
4
3
  TFunction extends (...args: any) => any,
@@ -0,0 +1,143 @@
1
+ import type { Branded } from "@milaboratories/helpers";
2
+ import type {
3
+ ValueType,
4
+ SingleAxisSelector,
5
+ AxisSpec,
6
+ PColumnIdAndSpec,
7
+ PColumnSpec,
8
+ } from "./pframe";
9
+
10
+ // --- Discover columns types (duplicated from middle-layer internal_api) ---
11
+
12
+ /** Matches a string value either exactly or by regex pattern */
13
+ export type StringMatcher = { type: "exact"; value: string } | { type: "regex"; value: string };
14
+
15
+ /** Map of key to array of string matchers (OR-ed per key, AND-ed across keys) */
16
+ export type MatcherMap = Record<string, StringMatcher[]>;
17
+
18
+ /** Selector for matching axes by various criteria */
19
+ export interface MultiAxisSelector {
20
+ /** Match any of the axis types listed here */
21
+ readonly type?: ValueType[];
22
+ /** Match any of the axis names listed here */
23
+ readonly name?: StringMatcher[];
24
+ /** Match requires all the domains listed here */
25
+ readonly domain?: MatcherMap;
26
+ /** Match requires all the context domains listed here */
27
+ readonly contextDomain?: MatcherMap;
28
+ /** Match requires all the annotations listed here */
29
+ readonly annotations?: MatcherMap;
30
+ }
31
+
32
+ /** Column selector for discover columns request, matching columns by various criteria.
33
+ * Multiple selectors are OR-ed: a column matches if it satisfies any selector. */
34
+ export interface MultiColumnSelector {
35
+ /** Match any of the value types listed here */
36
+ readonly type?: ValueType[];
37
+ /** Match any of the names listed here */
38
+ readonly name?: StringMatcher[];
39
+ /** Match requires all the domains listed here */
40
+ readonly domain?: MatcherMap;
41
+ /** Match requires all the context domains listed here */
42
+ readonly contextDomain?: MatcherMap;
43
+ /** Match requires all the annotations listed here */
44
+ readonly annotations?: MatcherMap;
45
+ /** Match any of the axis selectors listed here */
46
+ readonly axes?: MultiAxisSelector[];
47
+ /** When true (default), allows matching if only a subset of axes match */
48
+ readonly partialAxesMatch?: boolean;
49
+ }
50
+
51
+ /** Qualification applied to a single axis to make it compatible during integration. */
52
+ export interface AxisQualification {
53
+ /** Axis selector identifying which axis is qualified. */
54
+ readonly axis: SingleAxisSelector;
55
+ /** Additional context domain entries applied to the axis. */
56
+ readonly contextDomain: Record<string, string>;
57
+ }
58
+
59
+ /** Qualifications needed for both query (already-integrated) columns and the hit column. */
60
+ export interface ColumnAxesWithQualifications {
61
+ /** Already integrated (query) columns with their qualifications. */
62
+ axesSpec: AxisSpec[];
63
+ /** Qualifications for each already integrated (query) column. */
64
+ qualifications: AxisQualification[];
65
+ }
66
+
67
+ /** Fine-grained constraints controlling axes matching and qualification behavior */
68
+ export interface DiscoverColumnsConstraints {
69
+ /** Allow source (query) axes that have no match in the hit column */
70
+ allowFloatingSourceAxes: boolean;
71
+ /** Allow hit column axes that have no match in the source (query) */
72
+ allowFloatingHitAxes: boolean;
73
+ /** Allow source (query) axes to be qualified (contextDomain extended) */
74
+ allowSourceQualifications: boolean;
75
+ /** Allow hit column axes to be qualified (contextDomain extended) */
76
+ allowHitQualifications: boolean;
77
+ }
78
+
79
+ /** Request for discovering columns compatible with a given axes integration */
80
+ export interface DiscoverColumnsRequest {
81
+ /** Column filters (OR-ed); empty array matches all columns */
82
+ columnFilter?: MultiColumnSelector[];
83
+ /** Already integrated axes with qualifications */
84
+ axes: ColumnAxesWithQualifications[];
85
+ /** Constraints controlling axes matching and qualification behavior */
86
+ constraints: DiscoverColumnsConstraints;
87
+ }
88
+
89
+ /** Qualifications info for a discover columns response mapping variant */
90
+ export interface DiscoverColumnsResponseQualifications {
91
+ /** Qualifications for each query (already-integrated) column set */
92
+ forQueries: AxisQualification[][];
93
+ /** Qualifications for the hit column */
94
+ forHit: AxisQualification[];
95
+ }
96
+
97
+ /** A single mapping variant describing how a hit column can be integrated */
98
+ export interface DiscoverColumnsMappingVariant {
99
+ /** Full qualifications needed for integration */
100
+ qualifications: DiscoverColumnsResponseQualifications;
101
+ /** Distinctive (minimal) qualifications needed for integration */
102
+ distinctiveQualifications: DiscoverColumnsResponseQualifications;
103
+ }
104
+
105
+ /** A single hit in the discover columns response */
106
+ export interface DiscoverColumnsResponseHit {
107
+ /** The column that was found compatible */
108
+ hit: PColumnIdAndSpec;
109
+ /** Possible ways to integrate this column with the existing set */
110
+ mappingVariants: DiscoverColumnsMappingVariant[];
111
+ }
112
+
113
+ /** Response from discover columns */
114
+ export interface DiscoverColumnsResponse {
115
+ /** Columns that could be integrated and possible ways to integrate them */
116
+ hits: DiscoverColumnsResponseHit[];
117
+ }
118
+
119
+ // --- Spec driver ---
120
+
121
+ /** Handle to a spec-only PFrame (no data, synchronous operations). */
122
+ export type SpecFrameHandle = Branded<string, "SpecFrameHandle">;
123
+
124
+ /**
125
+ * Synchronous driver for spec-level PFrame operations.
126
+ *
127
+ * Unlike the async PFrameDriver (which works with data), this driver
128
+ * operates on column specifications only. All methods are synchronous
129
+ * because the underlying WASM PFrame computes results immediately.
130
+ */
131
+ export interface PSpecDriver {
132
+ /** Create a spec-only PFrame from column specs. Returns a handle. */
133
+ createSpecFrame(specs: Record<string, PColumnSpec>): SpecFrameHandle;
134
+
135
+ /** Discover columns compatible with given axes integration. */
136
+ specFrameDiscoverColumns(
137
+ handle: SpecFrameHandle,
138
+ request: DiscoverColumnsRequest,
139
+ ): DiscoverColumnsResponse;
140
+
141
+ /** Dispose a spec frame, freeing WASM resources. */
142
+ disposeSpecFrame(handle: SpecFrameHandle): void;
143
+ }
package/src/index.ts CHANGED
@@ -17,3 +17,4 @@ export * from "./value_or_error";
17
17
  export * from "./base64";
18
18
  export * from "./util";
19
19
  export * from "./httpAuth";
20
+ export * from "./resource_types";
@@ -0,0 +1,47 @@
1
+ /** Well-known resource type names used across the platform. */
2
+ export const ResourceTypeName = {
3
+ StreamManager: "StreamManager",
4
+ StdMap: "StdMap",
5
+ StdMapSlash: "std/map",
6
+ EphStdMap: "EphStdMap",
7
+ PFrame: "PFrame",
8
+ ParquetChunk: "ParquetChunk",
9
+ BContext: "BContext",
10
+ BlockPackCustom: "BlockPackCustom",
11
+ BinaryMap: "BinaryMap",
12
+ BinaryValue: "BinaryValue",
13
+ BlobMap: "BlobMap",
14
+ BResolveSingle: "BResolveSingle",
15
+ BResolveSingleNoResult: "BResolveSingleNoResult",
16
+ BQueryResult: "BQueryResult",
17
+ TengoTemplate: "TengoTemplate",
18
+ TengoLib: "TengoLib",
19
+ SoftwareInfo: "SoftwareInfo",
20
+ Dummy: "Dummy",
21
+ JsonResourceError: "json/resourceError",
22
+ JsonObject: "json/object",
23
+ JsonGzObject: "json-gz/object",
24
+ JsonString: "json/string",
25
+ JsonArray: "json/array",
26
+ JsonNumber: "json/number",
27
+ BContextEnd: "BContextEnd",
28
+ FrontendFromUrl: "Frontend/FromUrl",
29
+ FrontendFromFolder: "Frontend/FromFolder",
30
+ BObjectSpec: "BObjectSpec",
31
+ Blob: "Blob",
32
+ Null: "Null",
33
+ Binary: "binary",
34
+ LSProvider: "LSProvider",
35
+ UserProject: "UserProject",
36
+ Projects: "Projects",
37
+ ClientRoot: "ClientRoot",
38
+ } as const;
39
+
40
+ /** Resource type name prefix constants. */
41
+ export const ResourceTypePrefix = {
42
+ Blob: "Blob/",
43
+ BlobUpload: "BlobUpload/",
44
+ BlobIndex: "BlobIndex/",
45
+ PColumnData: "PColumnData/",
46
+ StreamWorkdir: "StreamWorkdir/",
47
+ } as const;