@platforma-sdk/model 1.80.13 → 1.81.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.
- package/dist/columns/column.cjs +9 -6
- package/dist/columns/column.cjs.map +1 -1
- package/dist/columns/column.d.ts +5 -5
- package/dist/columns/column.d.ts.map +1 -1
- package/dist/columns/column.js +10 -7
- package/dist/columns/column.js.map +1 -1
- package/dist/columns/column_providers/index.cjs.map +1 -1
- package/dist/columns/column_providers/index.d.ts.map +1 -1
- package/dist/columns/column_providers/index.js.map +1 -1
- package/dist/columns/column_providers/providers.cjs +5 -5
- package/dist/columns/column_providers/providers.cjs.map +1 -1
- package/dist/columns/column_providers/providers.d.ts +8 -8
- package/dist/columns/column_providers/providers.d.ts.map +1 -1
- package/dist/columns/column_providers/providers.js +5 -5
- package/dist/columns/column_providers/providers.js.map +1 -1
- package/dist/columns/column_providers/types.d.ts +3 -3
- package/dist/columns/column_providers/types.d.ts.map +1 -1
- package/dist/columns/column_recipes/column_discovered_recipe.cjs +1 -1
- package/dist/columns/column_recipes/column_discovered_recipe.cjs.map +1 -1
- package/dist/columns/column_recipes/column_discovered_recipe.d.ts +1 -1
- package/dist/columns/column_recipes/column_discovered_recipe.js +1 -1
- package/dist/columns/column_recipes/column_discovered_recipe.js.map +1 -1
- package/dist/columns/column_recipes/column_overrided_recipe.cjs +47 -2
- package/dist/columns/column_recipes/column_overrided_recipe.cjs.map +1 -1
- package/dist/columns/column_recipes/column_overrided_recipe.d.ts +30 -3
- package/dist/columns/column_recipes/column_overrided_recipe.d.ts.map +1 -1
- package/dist/columns/column_recipes/column_overrided_recipe.js +47 -3
- package/dist/columns/column_recipes/column_overrided_recipe.js.map +1 -1
- package/dist/columns/column_recipes/index.cjs +4 -14
- package/dist/columns/column_recipes/index.cjs.map +1 -1
- package/dist/columns/column_recipes/index.d.ts +3 -10
- package/dist/columns/column_recipes/index.d.ts.map +1 -1
- package/dist/columns/column_recipes/index.js +5 -14
- package/dist/columns/column_recipes/index.js.map +1 -1
- package/dist/columns/{column_lazy.cjs → data_column.cjs} +57 -54
- package/dist/columns/data_column.cjs.map +1 -0
- package/dist/columns/data_column.d.ts +152 -0
- package/dist/columns/data_column.d.ts.map +1 -0
- package/dist/columns/{column_lazy.js → data_column.js} +55 -52
- package/dist/columns/data_column.js.map +1 -0
- package/dist/columns/derive_axis_values_labels.cjs +4 -4
- package/dist/columns/derive_axis_values_labels.cjs.map +1 -1
- package/dist/columns/derive_axis_values_labels.d.ts +2 -2
- package/dist/columns/derive_axis_values_labels.js +4 -4
- package/dist/columns/derive_axis_values_labels.js.map +1 -1
- package/dist/columns/index.cjs +9 -9
- package/dist/columns/index.d.ts +5 -5
- package/dist/columns/index.js +5 -5
- package/dist/columns/{expand_by_partition.cjs → split_by_axes.cjs} +13 -8
- package/dist/columns/split_by_axes.cjs.map +1 -0
- package/dist/columns/{expand_by_partition.d.ts → split_by_axes.d.ts} +18 -8
- package/dist/columns/split_by_axes.d.ts.map +1 -0
- package/dist/columns/{expand_by_partition.js → split_by_axes.js} +14 -9
- package/dist/columns/split_by_axes.js.map +1 -0
- package/dist/columns/utils.cjs +50 -43
- package/dist/columns/utils.cjs.map +1 -1
- package/dist/columns/utils.d.ts +44 -19
- package/dist/columns/utils.d.ts.map +1 -1
- package/dist/columns/utils.js +50 -44
- package/dist/columns/utils.js.map +1 -1
- package/dist/components/PlDataTable/createPlDataTable/createPTableDefV2.cjs +3 -3
- package/dist/components/PlDataTable/createPlDataTable/createPTableDefV2.cjs.map +1 -1
- package/dist/components/PlDataTable/createPlDataTable/createPTableDefV2.js +3 -3
- package/dist/components/PlDataTable/createPlDataTable/createPTableDefV2.js.map +1 -1
- package/dist/components/PlDataTable/createPlDataTable/createPlDataTableV2.cjs +2 -2
- package/dist/components/PlDataTable/createPlDataTable/createPlDataTableV2.cjs.map +1 -1
- package/dist/components/PlDataTable/createPlDataTable/createPlDataTableV2.js +2 -2
- package/dist/components/PlDataTable/createPlDataTable/createPlDataTableV2.js.map +1 -1
- package/dist/components/PlDataTable/createPlDataTable/createPlDataTableV3.cjs +1 -1
- package/dist/components/PlDataTable/createPlDataTable/createPlDataTableV3.cjs.map +1 -1
- package/dist/components/PlDataTable/createPlDataTable/createPlDataTableV3.js +2 -2
- package/dist/components/PlDataTable/createPlDataTable/createPlDataTableV3.js.map +1 -1
- package/dist/components/PlDataTable/createPlDataTable/discoverColumns.cjs +1 -1
- package/dist/components/PlDataTable/createPlDataTable/discoverColumns.cjs.map +1 -1
- package/dist/components/PlDataTable/createPlDataTable/discoverColumns.js +2 -2
- package/dist/components/PlDataTable/createPlDataTable/discoverColumns.js.map +1 -1
- package/dist/components/PlDatasetSelector/enrichment_discovery.cjs +1 -1
- package/dist/components/PlDatasetSelector/enrichment_discovery.cjs.map +1 -1
- package/dist/components/PlDatasetSelector/enrichment_discovery.js +2 -2
- package/dist/components/PlDatasetSelector/enrichment_discovery.js.map +1 -1
- package/dist/index.cjs +9 -9
- package/dist/index.d.ts +5 -5
- package/dist/index.js +5 -5
- package/dist/package.cjs +1 -1
- package/dist/package.js +1 -1
- package/package.json +9 -9
- package/src/columns/__test_helpers__/collection_driver.ts +6 -6
- package/src/columns/__test_helpers__/stub_registry.ts +4 -6
- package/src/columns/column.ts +20 -9
- package/src/columns/column_providers/index.ts +9 -3
- package/src/columns/column_providers/providers.ts +16 -15
- package/src/columns/column_providers/types.ts +3 -3
- package/src/columns/column_recipes/column_discovered_recipe.ts +1 -1
- package/src/columns/column_recipes/column_overrided_recipe.ts +58 -4
- package/src/columns/column_recipes/column_recipes.test.ts +2 -2
- package/src/columns/column_recipes/index.ts +5 -20
- package/src/columns/{column_lazy.test.ts → data_column.test.ts} +19 -19
- package/src/columns/{column_lazy.ts → data_column.ts} +105 -67
- package/src/columns/derive_axis_values_labels.ts +4 -4
- package/src/columns/index.ts +2 -2
- package/src/columns/predicates.test.ts +124 -0
- package/src/columns/{expand_by_partition.test.ts → split_by_axes.test.ts} +29 -21
- package/src/columns/{expand_by_partition.ts → split_by_axes.ts} +22 -9
- package/src/columns/utils.ts +53 -48
- package/src/components/PlDataTable/columnResolver.test.ts +3 -3
- package/src/components/PlDataTable/createPlDataTable/createPTableDefV2.ts +3 -3
- package/src/components/PlDataTable/createPlDataTable/createPlDataTableV2.ts +2 -2
- package/src/components/PlDataTable/createPlDataTable/createPlDataTableV3.ts +2 -2
- package/src/components/PlDataTable/createPlDataTable/discoverColumns.ts +2 -2
- package/src/components/PlDataTable/createPlDataTable/utils.test.ts +3 -3
- package/src/components/PlDatasetSelector/enrichment_discovery.ts +2 -2
- package/dist/columns/column_lazy.cjs.map +0 -1
- package/dist/columns/column_lazy.d.ts +0 -137
- package/dist/columns/column_lazy.d.ts.map +0 -1
- package/dist/columns/column_lazy.js.map +0 -1
- package/dist/columns/expand_by_partition.cjs.map +0 -1
- package/dist/columns/expand_by_partition.d.ts.map +0 -1
- package/dist/columns/expand_by_partition.js.map +0 -1
|
@@ -3,7 +3,7 @@ const require_index = require("./column_providers/index.cjs");
|
|
|
3
3
|
let _milaboratories_pl_model_common = require("@milaboratories/pl-model-common");
|
|
4
4
|
let es_toolkit = require("es-toolkit");
|
|
5
5
|
let lru_cache = require("lru-cache");
|
|
6
|
-
//#region src/columns/
|
|
6
|
+
//#region src/columns/data_column.ts
|
|
7
7
|
/**
|
|
8
8
|
* Thrown by leaf-recipe factories when the requested column is provably
|
|
9
9
|
* absent in the active render ctx — i.e. every relevant accessor reports
|
|
@@ -24,13 +24,15 @@ var ColumnAbsentError = class extends Error {
|
|
|
24
24
|
}
|
|
25
25
|
};
|
|
26
26
|
/**
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
27
|
+
* The leaf-recipe building block: a {@link DataColumnRecipe} whose `id` is a bare
|
|
28
|
+
* {@link PObjectId} and whose readers are bound to a single tree-accessor
|
|
29
|
+
* leaf. Layered encodings (Overridden / Discovered / Filtered) are reified
|
|
30
|
+
* through their dedicated recipe classes and reference leaf columns by id.
|
|
31
|
+
*
|
|
32
|
+
* Internal — consumers hold the {@link DataColumnRecipe} interface and construct
|
|
33
|
+
* through the {@link DataColumnRecipe} dispatcher.
|
|
32
34
|
*/
|
|
33
|
-
var
|
|
35
|
+
var DataColumnImpl = class DataColumnImpl {
|
|
34
36
|
id;
|
|
35
37
|
options;
|
|
36
38
|
specCache;
|
|
@@ -55,7 +57,6 @@ var ColumnLazyImpl = class ColumnLazyImpl {
|
|
|
55
57
|
column: this.id
|
|
56
58
|
};
|
|
57
59
|
}
|
|
58
|
-
/** Leaf-only: not on the recipe interface — only the leaf can read data directly. */
|
|
59
60
|
getData() {
|
|
60
61
|
if (this.dataCache === void 0) this.dataCache = { value: this.options.getData() };
|
|
61
62
|
return this.dataCache.value;
|
|
@@ -66,7 +67,7 @@ var ColumnLazyImpl = class ColumnLazyImpl {
|
|
|
66
67
|
}
|
|
67
68
|
/**
|
|
68
69
|
* Overlay overrides → produces a {@link ColumnOverriddenRecipe} wrapping
|
|
69
|
-
* this leaf.
|
|
70
|
+
* this leaf. The leaf itself stays bare (id remains a plain PObjectId);
|
|
70
71
|
* layering lives entirely in the recipe wrappers.
|
|
71
72
|
*/
|
|
72
73
|
withSpecs(overrides) {
|
|
@@ -92,7 +93,7 @@ var ColumnLazyImpl = class ColumnLazyImpl {
|
|
|
92
93
|
return;
|
|
93
94
|
}
|
|
94
95
|
if (!spec.hasData()) return void 0;
|
|
95
|
-
return new
|
|
96
|
+
return new DataColumnImpl(id, {
|
|
96
97
|
getSpec: () => spec.getDataAsJson(),
|
|
97
98
|
getData: () => readDataAccessor(leaf),
|
|
98
99
|
getDataStatus: () => readDataStatus(leaf)
|
|
@@ -100,7 +101,7 @@ var ColumnLazyImpl = class ColumnLazyImpl {
|
|
|
100
101
|
}
|
|
101
102
|
/** {@link PlRef} wrapper over {@link fromId}. */
|
|
102
103
|
static fromPlRef(ref) {
|
|
103
|
-
return
|
|
104
|
+
return DataColumnImpl.fromId((0, _milaboratories_pl_model_common.createGlobalPObjectId)(ref.blockId, ref.name));
|
|
104
105
|
}
|
|
105
106
|
/**
|
|
106
107
|
* Bind directly to an accessor-backed {@link LeafEntry} — no registry.
|
|
@@ -114,7 +115,7 @@ var ColumnLazyImpl = class ColumnLazyImpl {
|
|
|
114
115
|
return;
|
|
115
116
|
}
|
|
116
117
|
if (!spec.hasData()) return void 0;
|
|
117
|
-
return new
|
|
118
|
+
return new DataColumnImpl(entry.id, {
|
|
118
119
|
getSpec: () => spec.getDataAsJson(),
|
|
119
120
|
getData: () => readDataAccessor(entry),
|
|
120
121
|
getDataStatus: () => readDataStatus(entry)
|
|
@@ -122,13 +123,14 @@ var ColumnLazyImpl = class ColumnLazyImpl {
|
|
|
122
123
|
}
|
|
123
124
|
/**
|
|
124
125
|
* Wrap a materialised {@link PColumn}. If the input is already a
|
|
125
|
-
* {@link
|
|
126
|
+
* {@link DataColumnRecipe} leaf it is returned as-is.
|
|
126
127
|
*/
|
|
127
128
|
static fromColumn(column) {
|
|
128
|
-
if (column instanceof
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
129
|
+
if (column instanceof DataColumnImpl) return column;
|
|
130
|
+
const pColumn = column;
|
|
131
|
+
return new DataColumnImpl(pColumn.id, {
|
|
132
|
+
getSpec: () => pColumn.spec,
|
|
133
|
+
getData: () => pColumn.data,
|
|
132
134
|
getDataStatus: () => "present"
|
|
133
135
|
});
|
|
134
136
|
}
|
|
@@ -146,7 +148,7 @@ var ColumnLazyImpl = class ColumnLazyImpl {
|
|
|
146
148
|
}
|
|
147
149
|
/** {@link PlRef} wrapper over {@link getStatusById}. */
|
|
148
150
|
static getStatusByPlRef(ref, opts = {}) {
|
|
149
|
-
return
|
|
151
|
+
return DataColumnImpl.getStatusById((0, _milaboratories_pl_model_common.createGlobalPObjectId)(ref.blockId, ref.name), opts);
|
|
150
152
|
}
|
|
151
153
|
/** No registry — reads straight off the entry's accessor. */
|
|
152
154
|
static getStatusByAccessor(entry) {
|
|
@@ -154,50 +156,51 @@ var ColumnLazyImpl = class ColumnLazyImpl {
|
|
|
154
156
|
}
|
|
155
157
|
};
|
|
156
158
|
/**
|
|
157
|
-
* Unified dispatcher — picks the right `
|
|
159
|
+
* Unified dispatcher — picks the right `DataColumnImpl.fromX` by source
|
|
158
160
|
* shape. For ambiguous inputs callers can still use the explicit factories
|
|
159
|
-
* (also attached as properties: `
|
|
161
|
+
* (also attached as properties: `DataColumn.fromId`, `.fromPlRef`,
|
|
160
162
|
* `.fromAccessor`, `.fromColumn`).
|
|
161
163
|
*/
|
|
162
|
-
function
|
|
163
|
-
if (typeof source === "string") return
|
|
164
|
-
if (source instanceof
|
|
165
|
-
|
|
166
|
-
if (
|
|
167
|
-
if ((0, _milaboratories_pl_model_common.
|
|
168
|
-
|
|
164
|
+
function DataColumnDispatch(source, opts = {}) {
|
|
165
|
+
if (typeof source === "string") return DataColumnImpl.fromId(source, opts);
|
|
166
|
+
if (source instanceof DataColumnImpl) return source;
|
|
167
|
+
const rest = source;
|
|
168
|
+
if ("accessor" in rest) return DataColumnImpl.fromAccessor(rest);
|
|
169
|
+
if ((0, _milaboratories_pl_model_common.isPlRef)(rest)) return DataColumnImpl.fromPlRef(rest);
|
|
170
|
+
if ((0, _milaboratories_pl_model_common.isPColumn)(rest)) return DataColumnImpl.fromColumn(rest);
|
|
171
|
+
throw new Error("DataColumn: unknown source shape");
|
|
169
172
|
}
|
|
170
173
|
/**
|
|
171
|
-
* Polymorphic counterpart to {@link
|
|
174
|
+
* Polymorphic counterpart to {@link DataColumnDispatch}: returns the
|
|
172
175
|
* {@link ColumnResolutionStatus} for any factory input without constructing
|
|
173
176
|
* the recipe. For already-materialised sources ({@link PColumn} value,
|
|
174
|
-
* existing {@link
|
|
177
|
+
* existing {@link DataColumnRecipe}) status is `present` by construction.
|
|
175
178
|
*/
|
|
176
|
-
function
|
|
177
|
-
if (typeof source === "string") return
|
|
178
|
-
if (source instanceof
|
|
179
|
-
|
|
180
|
-
if (
|
|
181
|
-
if ((0, _milaboratories_pl_model_common.
|
|
182
|
-
|
|
179
|
+
function DataColumnGetStatus(source, opts = {}) {
|
|
180
|
+
if (typeof source === "string") return DataColumnImpl.getStatusById(source, opts);
|
|
181
|
+
if (source instanceof DataColumnImpl) return "present";
|
|
182
|
+
const rest = source;
|
|
183
|
+
if ("accessor" in rest) return DataColumnImpl.getStatusByAccessor(rest);
|
|
184
|
+
if ((0, _milaboratories_pl_model_common.isPlRef)(rest)) return DataColumnImpl.getStatusByPlRef(rest, opts);
|
|
185
|
+
if ((0, _milaboratories_pl_model_common.isPColumn)(rest)) return "present";
|
|
186
|
+
throw new Error("DataColumn.getStatus: unknown source shape");
|
|
183
187
|
}
|
|
184
|
-
const
|
|
185
|
-
fromId:
|
|
186
|
-
fromPlRef:
|
|
187
|
-
fromAccessor:
|
|
188
|
-
fromColumn:
|
|
189
|
-
getStatus:
|
|
190
|
-
getStatusById:
|
|
191
|
-
getStatusByPlRef:
|
|
192
|
-
getStatusByAccessor:
|
|
188
|
+
const DataColumn = Object.assign(DataColumnDispatch, {
|
|
189
|
+
fromId: DataColumnImpl.fromId,
|
|
190
|
+
fromPlRef: DataColumnImpl.fromPlRef,
|
|
191
|
+
fromAccessor: DataColumnImpl.fromAccessor,
|
|
192
|
+
fromColumn: DataColumnImpl.fromColumn,
|
|
193
|
+
getStatus: DataColumnGetStatus,
|
|
194
|
+
getStatusById: DataColumnImpl.getStatusById,
|
|
195
|
+
getStatusByPlRef: DataColumnImpl.getStatusByPlRef,
|
|
196
|
+
getStatusByAccessor: DataColumnImpl.getStatusByAccessor
|
|
193
197
|
});
|
|
194
198
|
/**
|
|
195
|
-
* Type-guard narrowing to
|
|
196
|
-
*
|
|
197
|
-
* with statics, not the class, so `instanceof` requires the impl symbol.
|
|
199
|
+
* Type-guard narrowing to a bare leaf — a {@link DataColumnRecipe} whose `id` is a
|
|
200
|
+
* plain {@link PObjectId}.
|
|
198
201
|
*/
|
|
199
|
-
function
|
|
200
|
-
return value instanceof
|
|
202
|
+
function isDataColumn(value) {
|
|
203
|
+
return value instanceof DataColumnImpl;
|
|
201
204
|
}
|
|
202
205
|
const readSpecAccessor = memoizeByEntry(({ accessor, name }) => accessor.traverse({
|
|
203
206
|
field: `${name}.spec`,
|
|
@@ -241,8 +244,8 @@ function memoizeByEntry(fn) {
|
|
|
241
244
|
}
|
|
242
245
|
//#endregion
|
|
243
246
|
exports.ColumnAbsentError = ColumnAbsentError;
|
|
244
|
-
exports.
|
|
245
|
-
exports.
|
|
246
|
-
exports.
|
|
247
|
+
exports.DataColumn = DataColumn;
|
|
248
|
+
exports.DataColumnImpl = DataColumnImpl;
|
|
249
|
+
exports.isDataColumn = isDataColumn;
|
|
247
250
|
|
|
248
|
-
//# sourceMappingURL=
|
|
251
|
+
//# sourceMappingURL=data_column.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"data_column.cjs","names":["ColumnOverriddenRecipe","ColumnRegistry","getCtxProviders","LRUCache"],"sources":["../../src/columns/data_column.ts"],"sourcesContent":["import {\n ColumnRegistry,\n createGlobalPObjectId,\n isPColumn,\n isPlRef,\n PlRef,\n PObjectId,\n type ColumnUniversalId,\n type LeafEntry,\n type PColumn,\n type PColumnSpec,\n type SpecOverrides,\n type SpecQuery,\n} from \"@milaboratories/pl-model-common\";\nimport type { GlobalCfgRenderCtx, PColumnDataUniversal } from \"../render/internal\";\nimport { getCtxProviders } from \"./column_providers\";\nimport { isNil } from \"es-toolkit\";\nimport { LRUCache } from \"lru-cache\";\nimport { TreeNodeAccessor } from \"../render\";\nimport type {\n ColumnFieldStatus,\n ColumnRecipe,\n ColumnRecipeId,\n ColumnResolutionStatus,\n} from \"./column_recipes/types\";\nimport { ColumnOverriddenRecipe } from \"./column_recipes/column_overrided_recipe\";\n\nexport type DataColumnId = PObjectId;\nexport type ColumnData = undefined | PColumnDataUniversal;\nexport type { ColumnFieldStatus, ColumnResolutionStatus } from \"./column_recipes/types\";\n\n/**\n * A {@link ColumnRecipe} that can be read directly in the sandbox: its\n * {@link getData} is guaranteed to be consistent with {@link getSpec}.\n *\n * This is a *capability*, not a class. Only two recipe shapes carry it — a\n * bare leaf and a spec-override over a bare leaf — because those are the only\n * ones whose data still matches their spec. An axis-filtered recipe drops axes\n * from its spec while the underlying data keeps them, and a discovered recipe\n * only materialises after a join; neither can be read here, so neither\n * implements this interface.\n *\n * Narrow to it with `hasReachableData(recipe)` rather than testing for a concrete\n * class — which recipe classes exist is an implementation detail behind\n * `recipe.id`.\n */\nexport interface DataColumnRecipe<\n ID extends ColumnRecipeId = ColumnRecipeId,\n> extends ColumnRecipe<ID> {\n /** Column data, consistent with {@link ColumnRecipe.getSpec}. */\n getData(): ColumnData;\n}\n\n/**\n * Thrown by leaf-recipe factories when the requested column is provably\n * absent in the active render ctx — i.e. every relevant accessor reports\n * `inputsLocked` and the column did not appear. Distinct from the\n * `undefined` return that the factories still use for the \"resolving\" case\n * (the column may still appear later).\n *\n * Catch at the boundary (resolver / filter+sort wiring / data-table assembly)\n * when partial absence should be surfaced to the user rather than silently\n * producing an empty result.\n */\nexport class ColumnAbsentError extends Error {\n constructor(public readonly id: ColumnUniversalId) {\n super(`Column is absent in the active render ctx and will not appear: ${id}`);\n this.name = \"ColumnAbsentError\";\n }\n}\n\n/**\n * The leaf-recipe building block: a {@link DataColumnRecipe} whose `id` is a bare\n * {@link PObjectId} and whose readers are bound to a single tree-accessor\n * leaf. Layered encodings (Overridden / Discovered / Filtered) are reified\n * through their dedicated recipe classes and reference leaf columns by id.\n *\n * Internal — consumers hold the {@link DataColumnRecipe} interface and construct\n * through the {@link DataColumnRecipe} dispatcher.\n */\nexport class DataColumnImpl implements DataColumnRecipe<PObjectId> {\n private specCache?: { readonly value: PColumnSpec };\n private dataCache?: { readonly value: ColumnData };\n private dataStatusCache?: { readonly value: ColumnFieldStatus };\n\n private constructor(\n public readonly id: PObjectId,\n private readonly options: {\n getSpec: () => PColumnSpec;\n getData: () => ColumnData;\n getDataStatus: () => ColumnFieldStatus;\n },\n ) {}\n\n /** A leaf-recipe references exactly one column — its own id. */\n getReferencedIds(): PObjectId[] {\n return [this.id];\n }\n\n getSpec(): PColumnSpec {\n if (this.specCache === undefined) {\n this.specCache = { value: this.options.getSpec() };\n }\n return this.specCache.value;\n }\n\n /** Leaf-shaped — points straight at `this.id`. */\n getQuery(): SpecQuery {\n return { type: \"column\", column: this.id } as SpecQuery;\n }\n\n getData(): ColumnData {\n if (this.dataCache === undefined) this.dataCache = { value: this.options.getData() };\n return this.dataCache.value;\n }\n\n getDataStatus(): ColumnFieldStatus {\n if (this.dataStatusCache === undefined) {\n this.dataStatusCache = { value: this.options.getDataStatus() };\n }\n return this.dataStatusCache.value;\n }\n\n /**\n * Overlay overrides → produces a {@link ColumnOverriddenRecipe} wrapping\n * this leaf. The leaf itself stays bare (id remains a plain PObjectId);\n * layering lives entirely in the recipe wrappers.\n */\n withSpecs(overrides: SpecOverrides): ColumnRecipe {\n return ColumnOverriddenRecipe.wrap(this, overrides);\n }\n\n /**\n * Resolve via the ambient {@link ColumnRegistry}. Spec is resolved eagerly:\n * returns `undefined` if the leaf isn't reachable yet (resolving). Throws\n * {@link ColumnAbsentError} when every relevant accessor is `inputsLocked`\n * and the column did not appear — the column will not exist in this ctx.\n * Data and dataStatus stay lazy.\n */\n static fromId(\n id: PObjectId,\n { ctx }: { ctx?: GlobalCfgRenderCtx } = {},\n ): undefined | DataColumnRecipe<PObjectId> {\n const registry = new ColumnRegistry(getCtxProviders({ ctx }));\n const leaf = registry.resolve(id);\n if (isNil(leaf)) {\n if (registry.isFinal()) throw new ColumnAbsentError(id);\n return undefined;\n }\n const spec = readSpecAccessor(leaf);\n if (isNil(spec)) {\n if (leaf.accessor.getInputsLocked()) throw new ColumnAbsentError(id);\n return undefined;\n }\n if (!spec.hasData()) return undefined;\n return new DataColumnImpl(id, {\n getSpec: () => spec.getDataAsJson<PColumnSpec>(),\n getData: () => readDataAccessor(leaf),\n getDataStatus: () => readDataStatus(leaf),\n });\n }\n\n /** {@link PlRef} wrapper over {@link fromId}. */\n static fromPlRef(ref: PlRef): undefined | DataColumnRecipe<PObjectId> {\n return DataColumnImpl.fromId(createGlobalPObjectId(ref.blockId, ref.name));\n }\n\n /**\n * Bind directly to an accessor-backed {@link LeafEntry} — no registry.\n * Throws {@link ColumnAbsentError} if the leaf has no spec field and its\n * accessor is `inputsLocked`. Returns `undefined` while still resolving.\n */\n static fromAccessor(entry: LeafEntry<TreeNodeAccessor>): undefined | DataColumnRecipe<PObjectId> {\n const spec = readSpecAccessor(entry);\n if (isNil(spec)) {\n if (entry.accessor.getInputsLocked()) throw new ColumnAbsentError(entry.id);\n return undefined;\n }\n if (!spec.hasData()) return undefined;\n return new DataColumnImpl(entry.id, {\n getSpec: () => spec.getDataAsJson<PColumnSpec>(),\n getData: () => readDataAccessor(entry),\n getDataStatus: () => readDataStatus(entry),\n });\n }\n\n /**\n * Wrap a materialised {@link PColumn}. If the input is already a\n * {@link DataColumnRecipe} leaf it is returned as-is.\n */\n static fromColumn(\n column: PColumn<ColumnData> | DataColumnRecipe<PObjectId>,\n ): DataColumnRecipe<PObjectId> {\n if (column instanceof DataColumnImpl) return column;\n // `DataColumnRecipe` is an interface, so `instanceof` above does not remove it\n // from the union. The only implementation carrying a bare `PObjectId` is\n // `DataColumnImpl` (wrappers expose `ColumnUniversalId`), so anything\n // reaching here is a materialised `PColumn`.\n const pColumn = column as PColumn<ColumnData>;\n return new DataColumnImpl(pColumn.id, {\n getSpec: () => pColumn.spec,\n getData: () => pColumn.data,\n getDataStatus: () => \"present\",\n });\n }\n\n /**\n * Distinguishes `present` / `resolving` / `absent` for a {@link PObjectId}\n * in the active render ctx. Falls back to the registry's `isFinal()`\n * when the id has no entry — only then we can say `absent` instead of\n * `resolving`.\n */\n static getStatusById(\n id: PObjectId,\n { ctx }: { ctx?: GlobalCfgRenderCtx } = {},\n ): ColumnResolutionStatus {\n const registry = new ColumnRegistry(getCtxProviders({ ctx }));\n const leaf = registry.resolve(id);\n if (isNil(leaf)) return registry.isFinal() ? \"absent\" : \"resolving\";\n return getLeafEntryStatus(leaf);\n }\n\n /** {@link PlRef} wrapper over {@link getStatusById}. */\n static getStatusByPlRef(\n ref: PlRef,\n opts: { ctx?: GlobalCfgRenderCtx } = {},\n ): ColumnResolutionStatus {\n return DataColumnImpl.getStatusById(createGlobalPObjectId(ref.blockId, ref.name), opts);\n }\n\n /** No registry — reads straight off the entry's accessor. */\n static getStatusByAccessor(entry: LeafEntry<TreeNodeAccessor>): ColumnResolutionStatus {\n return getLeafEntryStatus(entry);\n }\n}\n\n/** Anything the {@link DataColumnRecipe} dispatcher can build a leaf from. */\nexport type DataColumnSource =\n | PObjectId\n | PlRef\n | LeafEntry<TreeNodeAccessor>\n | PColumn<ColumnData>\n | DataColumnRecipe<PObjectId>;\n\n/**\n * Unified dispatcher — picks the right `DataColumnImpl.fromX` by source\n * shape. For ambiguous inputs callers can still use the explicit factories\n * (also attached as properties: `DataColumn.fromId`, `.fromPlRef`,\n * `.fromAccessor`, `.fromColumn`).\n */\nfunction DataColumnDispatch(\n source: DataColumnSource,\n opts: { ctx?: GlobalCfgRenderCtx } = {},\n): undefined | DataColumnRecipe<PObjectId> {\n if (typeof source === \"string\") return DataColumnImpl.fromId(source, opts);\n if (source instanceof DataColumnImpl) return source;\n // `DataColumnRecipe` is an interface — `instanceof` above cannot remove it from\n // the union, and only `DataColumnImpl` implements it with a bare id.\n const rest = source as LeafEntry<TreeNodeAccessor> | PlRef | PColumn<ColumnData>;\n if (\"accessor\" in rest) return DataColumnImpl.fromAccessor(rest);\n if (isPlRef(rest)) return DataColumnImpl.fromPlRef(rest);\n if (isPColumn(rest)) return DataColumnImpl.fromColumn(rest);\n throw new Error(\"DataColumn: unknown source shape\");\n}\n\n/**\n * Polymorphic counterpart to {@link DataColumnDispatch}: returns the\n * {@link ColumnResolutionStatus} for any factory input without constructing\n * the recipe. For already-materialised sources ({@link PColumn} value,\n * existing {@link DataColumnRecipe}) status is `present` by construction.\n */\nfunction DataColumnGetStatus(\n source: DataColumnSource,\n opts: { ctx?: GlobalCfgRenderCtx } = {},\n): ColumnResolutionStatus {\n if (typeof source === \"string\") return DataColumnImpl.getStatusById(source, opts);\n if (source instanceof DataColumnImpl) return \"present\";\n const rest = source as LeafEntry<TreeNodeAccessor> | PlRef | PColumn<ColumnData>;\n if (\"accessor\" in rest) return DataColumnImpl.getStatusByAccessor(rest);\n if (isPlRef(rest)) return DataColumnImpl.getStatusByPlRef(rest, opts);\n if (isPColumn(rest)) return \"present\";\n throw new Error(\"DataColumn.getStatus: unknown source shape\");\n}\n\nexport const DataColumn = Object.assign(DataColumnDispatch, {\n fromId: DataColumnImpl.fromId,\n fromPlRef: DataColumnImpl.fromPlRef,\n fromAccessor: DataColumnImpl.fromAccessor,\n fromColumn: DataColumnImpl.fromColumn,\n getStatus: DataColumnGetStatus,\n getStatusById: DataColumnImpl.getStatusById,\n getStatusByPlRef: DataColumnImpl.getStatusByPlRef,\n getStatusByAccessor: DataColumnImpl.getStatusByAccessor,\n});\n\n/**\n * Type-guard narrowing to a bare leaf — a {@link DataColumnRecipe} whose `id` is a\n * plain {@link PObjectId}.\n */\nexport function isDataColumn(value: unknown): value is DataColumnRecipe<PObjectId> {\n return value instanceof DataColumnImpl;\n}\n\nconst readSpecAccessor = memoizeByEntry(\n ({ accessor, name }: LeafEntry<TreeNodeAccessor>): undefined | TreeNodeAccessor =>\n accessor.traverse({ field: `${name}.spec`, assertFieldType: \"Input\", ignoreError: true }),\n);\n\n/**\n * Per-entry counterpart to {@link readDataStatus}: tells whether the leaf's\n * **spec** can be read in this ctx, and — for the negative cases —\n * distinguishes `resolving` from `absent` via `getInputsLocked()`.\n *\n * - spec field not yet on the entry's accessor + inputs locked → `absent`\n * - spec field not yet on the entry's accessor + still resolving → `resolving`\n * - spec resource present but bytes not yet written → `resolving`\n * (transient — the spec resource is connected, just unfilled)\n * - spec resource present and `hasData()` → `present`\n */\nfunction getLeafEntryStatus(entry: LeafEntry<TreeNodeAccessor>): ColumnResolutionStatus {\n const spec = readSpecAccessor(entry);\n if (isNil(spec)) return entry.accessor.getInputsLocked() ? \"absent\" : \"resolving\";\n if (!spec.hasData()) return \"resolving\";\n return \"present\";\n}\n\nconst readDataAccessor = memoizeByEntry(\n ({ accessor, name }: LeafEntry<TreeNodeAccessor>): undefined | TreeNodeAccessor =>\n accessor.traverse({ field: `${name}.data`, assertFieldType: \"Input\", ignoreError: true }),\n);\n\nconst readDataStatus = memoizeByEntry(\n ({ accessor, name }: LeafEntry<TreeNodeAccessor>): ColumnFieldStatus => {\n if (accessor.listInputFields().includes(`${name}.data`)) return \"present\";\n return accessor.getInputsLocked() ? \"absent\" : \"resolving\";\n },\n);\n\nfunction memoizeByEntry<R>(\n fn: (entry: LeafEntry<TreeNodeAccessor>) => R,\n): (entry: LeafEntry<TreeNodeAccessor>) => R {\n const cache = new LRUCache<string, { value: R }>({ max: 1000 });\n return (entry) => {\n const key = `${entry.accessor.handle}:${entry.name}`;\n let hit = cache.get(key);\n if (!hit) cache.set(key, (hit = { value: fn(entry) }));\n return hit.value;\n };\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import { GlobalCfgRenderCtx, PColumnDataUniversal } from "../render/internal.js";
|
|
2
|
+
import { TreeNodeAccessor } from "../render/accessor.js";
|
|
3
|
+
import { ColumnFieldStatus, ColumnRecipe, ColumnRecipeId, ColumnResolutionStatus } from "./column_recipes/types.js";
|
|
4
|
+
import { ColumnUniversalId, LeafEntry, PColumn, PColumnSpec, PObjectId, PlRef, SpecOverrides, SpecQuery } from "@milaboratories/pl-model-common";
|
|
5
|
+
|
|
6
|
+
//#region src/columns/data_column.d.ts
|
|
7
|
+
type DataColumnId = PObjectId;
|
|
8
|
+
type ColumnData = undefined | PColumnDataUniversal;
|
|
9
|
+
/**
|
|
10
|
+
* A {@link ColumnRecipe} that can be read directly in the sandbox: its
|
|
11
|
+
* {@link getData} is guaranteed to be consistent with {@link getSpec}.
|
|
12
|
+
*
|
|
13
|
+
* This is a *capability*, not a class. Only two recipe shapes carry it — a
|
|
14
|
+
* bare leaf and a spec-override over a bare leaf — because those are the only
|
|
15
|
+
* ones whose data still matches their spec. An axis-filtered recipe drops axes
|
|
16
|
+
* from its spec while the underlying data keeps them, and a discovered recipe
|
|
17
|
+
* only materialises after a join; neither can be read here, so neither
|
|
18
|
+
* implements this interface.
|
|
19
|
+
*
|
|
20
|
+
* Narrow to it with `hasReachableData(recipe)` rather than testing for a concrete
|
|
21
|
+
* class — which recipe classes exist is an implementation detail behind
|
|
22
|
+
* `recipe.id`.
|
|
23
|
+
*/
|
|
24
|
+
interface DataColumnRecipe<ID extends ColumnRecipeId = ColumnRecipeId> extends ColumnRecipe<ID> {
|
|
25
|
+
/** Column data, consistent with {@link ColumnRecipe.getSpec}. */
|
|
26
|
+
getData(): ColumnData;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Thrown by leaf-recipe factories when the requested column is provably
|
|
30
|
+
* absent in the active render ctx — i.e. every relevant accessor reports
|
|
31
|
+
* `inputsLocked` and the column did not appear. Distinct from the
|
|
32
|
+
* `undefined` return that the factories still use for the "resolving" case
|
|
33
|
+
* (the column may still appear later).
|
|
34
|
+
*
|
|
35
|
+
* Catch at the boundary (resolver / filter+sort wiring / data-table assembly)
|
|
36
|
+
* when partial absence should be surfaced to the user rather than silently
|
|
37
|
+
* producing an empty result.
|
|
38
|
+
*/
|
|
39
|
+
declare class ColumnAbsentError extends Error {
|
|
40
|
+
readonly id: ColumnUniversalId;
|
|
41
|
+
constructor(id: ColumnUniversalId);
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* The leaf-recipe building block: a {@link DataColumnRecipe} whose `id` is a bare
|
|
45
|
+
* {@link PObjectId} and whose readers are bound to a single tree-accessor
|
|
46
|
+
* leaf. Layered encodings (Overridden / Discovered / Filtered) are reified
|
|
47
|
+
* through their dedicated recipe classes and reference leaf columns by id.
|
|
48
|
+
*
|
|
49
|
+
* Internal — consumers hold the {@link DataColumnRecipe} interface and construct
|
|
50
|
+
* through the {@link DataColumnRecipe} dispatcher.
|
|
51
|
+
*/
|
|
52
|
+
declare class DataColumnImpl implements DataColumnRecipe<PObjectId> {
|
|
53
|
+
readonly id: PObjectId;
|
|
54
|
+
private readonly options;
|
|
55
|
+
private specCache?;
|
|
56
|
+
private dataCache?;
|
|
57
|
+
private dataStatusCache?;
|
|
58
|
+
private constructor();
|
|
59
|
+
/** A leaf-recipe references exactly one column — its own id. */
|
|
60
|
+
getReferencedIds(): PObjectId[];
|
|
61
|
+
getSpec(): PColumnSpec;
|
|
62
|
+
/** Leaf-shaped — points straight at `this.id`. */
|
|
63
|
+
getQuery(): SpecQuery;
|
|
64
|
+
getData(): ColumnData;
|
|
65
|
+
getDataStatus(): ColumnFieldStatus;
|
|
66
|
+
/**
|
|
67
|
+
* Overlay overrides → produces a {@link ColumnOverriddenRecipe} wrapping
|
|
68
|
+
* this leaf. The leaf itself stays bare (id remains a plain PObjectId);
|
|
69
|
+
* layering lives entirely in the recipe wrappers.
|
|
70
|
+
*/
|
|
71
|
+
withSpecs(overrides: SpecOverrides): ColumnRecipe;
|
|
72
|
+
/**
|
|
73
|
+
* Resolve via the ambient {@link ColumnRegistry}. Spec is resolved eagerly:
|
|
74
|
+
* returns `undefined` if the leaf isn't reachable yet (resolving). Throws
|
|
75
|
+
* {@link ColumnAbsentError} when every relevant accessor is `inputsLocked`
|
|
76
|
+
* and the column did not appear — the column will not exist in this ctx.
|
|
77
|
+
* Data and dataStatus stay lazy.
|
|
78
|
+
*/
|
|
79
|
+
static fromId(id: PObjectId, {
|
|
80
|
+
ctx
|
|
81
|
+
}?: {
|
|
82
|
+
ctx?: GlobalCfgRenderCtx;
|
|
83
|
+
}): undefined | DataColumnRecipe<PObjectId>;
|
|
84
|
+
/** {@link PlRef} wrapper over {@link fromId}. */
|
|
85
|
+
static fromPlRef(ref: PlRef): undefined | DataColumnRecipe<PObjectId>;
|
|
86
|
+
/**
|
|
87
|
+
* Bind directly to an accessor-backed {@link LeafEntry} — no registry.
|
|
88
|
+
* Throws {@link ColumnAbsentError} if the leaf has no spec field and its
|
|
89
|
+
* accessor is `inputsLocked`. Returns `undefined` while still resolving.
|
|
90
|
+
*/
|
|
91
|
+
static fromAccessor(entry: LeafEntry<TreeNodeAccessor>): undefined | DataColumnRecipe<PObjectId>;
|
|
92
|
+
/**
|
|
93
|
+
* Wrap a materialised {@link PColumn}. If the input is already a
|
|
94
|
+
* {@link DataColumnRecipe} leaf it is returned as-is.
|
|
95
|
+
*/
|
|
96
|
+
static fromColumn(column: PColumn<ColumnData> | DataColumnRecipe<PObjectId>): DataColumnRecipe<PObjectId>;
|
|
97
|
+
/**
|
|
98
|
+
* Distinguishes `present` / `resolving` / `absent` for a {@link PObjectId}
|
|
99
|
+
* in the active render ctx. Falls back to the registry's `isFinal()`
|
|
100
|
+
* when the id has no entry — only then we can say `absent` instead of
|
|
101
|
+
* `resolving`.
|
|
102
|
+
*/
|
|
103
|
+
static getStatusById(id: PObjectId, {
|
|
104
|
+
ctx
|
|
105
|
+
}?: {
|
|
106
|
+
ctx?: GlobalCfgRenderCtx;
|
|
107
|
+
}): ColumnResolutionStatus;
|
|
108
|
+
/** {@link PlRef} wrapper over {@link getStatusById}. */
|
|
109
|
+
static getStatusByPlRef(ref: PlRef, opts?: {
|
|
110
|
+
ctx?: GlobalCfgRenderCtx;
|
|
111
|
+
}): ColumnResolutionStatus;
|
|
112
|
+
/** No registry — reads straight off the entry's accessor. */
|
|
113
|
+
static getStatusByAccessor(entry: LeafEntry<TreeNodeAccessor>): ColumnResolutionStatus;
|
|
114
|
+
}
|
|
115
|
+
/** Anything the {@link DataColumnRecipe} dispatcher can build a leaf from. */
|
|
116
|
+
type DataColumnSource = PObjectId | PlRef | LeafEntry<TreeNodeAccessor> | PColumn<ColumnData> | DataColumnRecipe<PObjectId>;
|
|
117
|
+
/**
|
|
118
|
+
* Unified dispatcher — picks the right `DataColumnImpl.fromX` by source
|
|
119
|
+
* shape. For ambiguous inputs callers can still use the explicit factories
|
|
120
|
+
* (also attached as properties: `DataColumn.fromId`, `.fromPlRef`,
|
|
121
|
+
* `.fromAccessor`, `.fromColumn`).
|
|
122
|
+
*/
|
|
123
|
+
declare function DataColumnDispatch(source: DataColumnSource, opts?: {
|
|
124
|
+
ctx?: GlobalCfgRenderCtx;
|
|
125
|
+
}): undefined | DataColumnRecipe<PObjectId>;
|
|
126
|
+
/**
|
|
127
|
+
* Polymorphic counterpart to {@link DataColumnDispatch}: returns the
|
|
128
|
+
* {@link ColumnResolutionStatus} for any factory input without constructing
|
|
129
|
+
* the recipe. For already-materialised sources ({@link PColumn} value,
|
|
130
|
+
* existing {@link DataColumnRecipe}) status is `present` by construction.
|
|
131
|
+
*/
|
|
132
|
+
declare function DataColumnGetStatus(source: DataColumnSource, opts?: {
|
|
133
|
+
ctx?: GlobalCfgRenderCtx;
|
|
134
|
+
}): ColumnResolutionStatus;
|
|
135
|
+
declare const DataColumn: typeof DataColumnDispatch & {
|
|
136
|
+
fromId: typeof DataColumnImpl.fromId;
|
|
137
|
+
fromPlRef: typeof DataColumnImpl.fromPlRef;
|
|
138
|
+
fromAccessor: typeof DataColumnImpl.fromAccessor;
|
|
139
|
+
fromColumn: typeof DataColumnImpl.fromColumn;
|
|
140
|
+
getStatus: typeof DataColumnGetStatus;
|
|
141
|
+
getStatusById: typeof DataColumnImpl.getStatusById;
|
|
142
|
+
getStatusByPlRef: typeof DataColumnImpl.getStatusByPlRef;
|
|
143
|
+
getStatusByAccessor: typeof DataColumnImpl.getStatusByAccessor;
|
|
144
|
+
};
|
|
145
|
+
/**
|
|
146
|
+
* Type-guard narrowing to a bare leaf — a {@link DataColumnRecipe} whose `id` is a
|
|
147
|
+
* plain {@link PObjectId}.
|
|
148
|
+
*/
|
|
149
|
+
declare function isDataColumn(value: unknown): value is DataColumnRecipe<PObjectId>;
|
|
150
|
+
//#endregion
|
|
151
|
+
export { ColumnAbsentError, ColumnData, type ColumnFieldStatus, type ColumnResolutionStatus, DataColumn, DataColumnId, DataColumnImpl, DataColumnRecipe, DataColumnSource, isDataColumn };
|
|
152
|
+
//# sourceMappingURL=data_column.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"data_column.d.ts","names":[],"sources":["../../src/columns/data_column.ts"],"mappings":";;;;;;KA2BY,YAAA,GAAe,SAAS;AAAA,KACxB,UAAA,eAAyB,oBAAoB;AADzD;;;;AAAoC;AACpC;;;;AAAyD;AAkBzD;;;;;AAnBA,UAmBiB,gBAAA,YACJ,cAAA,GAAiB,cAAA,UACpB,YAAA,CAAa,EAAA;EAEV;EAAX,OAAA,IAAW,UAAA;AAAA;;;;;;;;;;AAAU;AAcvB;cAAa,iBAAA,SAA0B,KAAA;EAAA,SACT,EAAA,EAAI,iBAAA;cAAJ,EAAA,EAAI,iBAAA;AAAA;;;;;;;;;;cAerB,cAAA,YAA0B,gBAAA,CAAiB,SAAA;EAAA,SAMpC,EAAA,EAAI,SAAA;EAAA,iBACH,OAAA;EAAA,QANX,SAAA;EAAA,QACA,SAAA;EAAA,QACA,eAAA;EAAA,QAED,WAAA;EAUa;EAApB,gBAAA,IAAoB,SAAA;EAIpB,OAAA,IAAW,WAAA;EAYA;EAJX,QAAA,IAAY,SAAA;EAIZ,OAAA,IAAW,UAAA;EAKX,aAAA,IAAiB,iBAAA;EAwBX;;;;;EAZN,SAAA,CAAU,SAAA,EAAW,aAAA,GAAgB,YAAA;EAmCsB;;;;;;;EAAA,OAxBpD,MAAA,CACL,EAAA,EAAI,SAAA;IACF;EAAA;IAAS,GAAA,GAAM,kBAAA;EAAA,gBACJ,gBAAA,CAAiB,SAAA;EAkDZ;EAAA,OA7Bb,SAAA,CAAU,GAAA,EAAK,KAAA,eAAoB,gBAAA,CAAiB,SAAA;EAkDrD;;;;;EAAA,OAzCC,YAAA,CAAa,KAAA,EAAO,SAAA,CAAU,gBAAA,gBAAgC,gBAAA,CAAiB,SAAA;EAsDnF;;;;EAAA,OApCI,UAAA,CACL,MAAA,EAAQ,OAAA,CAAQ,UAAA,IAAc,gBAAA,CAAiB,SAAA,IAC9C,gBAAA,CAAiB,SAAA;EAhHiC;;;;;;EAAA,OAoI9C,aAAA,CACL,EAAA,EAAI,SAAA;IACF;EAAA;IAAS,GAAA,GAAM,kBAAA;EAAA,IAChB,sBAAA;EApIK;EAAA,OA4ID,gBAAA,CACL,GAAA,EAAK,KAAA,EACL,IAAA;IAAQ,GAAA,GAAM,kBAAA;EAAA,IACb,sBAAA;EA/HH;EAAA,OAoIO,mBAAA,CAAoB,KAAA,EAAO,SAAA,CAAU,gBAAA,IAAoB,sBAAA;AAAA;;KAMtD,gBAAA,GACR,SAAA,GACA,KAAA,GACA,SAAA,CAAU,gBAAA,IACV,OAAA,CAAQ,UAAA,IACR,gBAAA,CAAiB,SAAA;;;;;;;iBAQZ,kBAAA,CACP,MAAA,EAAQ,gBAAA,EACR,IAAA;EAAQ,GAAA,GAAM,kBAAA;AAAA,gBACD,gBAAA,CAAiB,SAAA;;;;;;;iBAkBvB,mBAAA,CACP,MAAA,EAAQ,gBAAA,EACR,IAAA;EAAQ,GAAA,GAAM,kBAAA;AAAA,IACb,sBAAA;AAAA,cAUU,UAAA,SAAU,kBAAA;;;;;;;;;;;;;;iBAeP,YAAA,CAAa,KAAA,YAAiB,KAAA,IAAS,gBAAgB,CAAC,SAAA"}
|
|
@@ -3,7 +3,7 @@ import { getCtxProviders } from "./column_providers/index.js";
|
|
|
3
3
|
import { ColumnRegistry, createGlobalPObjectId, isPColumn, isPlRef } from "@milaboratories/pl-model-common";
|
|
4
4
|
import { isNil } from "es-toolkit";
|
|
5
5
|
import { LRUCache } from "lru-cache";
|
|
6
|
-
//#region src/columns/
|
|
6
|
+
//#region src/columns/data_column.ts
|
|
7
7
|
/**
|
|
8
8
|
* Thrown by leaf-recipe factories when the requested column is provably
|
|
9
9
|
* absent in the active render ctx — i.e. every relevant accessor reports
|
|
@@ -24,13 +24,15 @@ var ColumnAbsentError = class extends Error {
|
|
|
24
24
|
}
|
|
25
25
|
};
|
|
26
26
|
/**
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
27
|
+
* The leaf-recipe building block: a {@link DataColumnRecipe} whose `id` is a bare
|
|
28
|
+
* {@link PObjectId} and whose readers are bound to a single tree-accessor
|
|
29
|
+
* leaf. Layered encodings (Overridden / Discovered / Filtered) are reified
|
|
30
|
+
* through their dedicated recipe classes and reference leaf columns by id.
|
|
31
|
+
*
|
|
32
|
+
* Internal — consumers hold the {@link DataColumnRecipe} interface and construct
|
|
33
|
+
* through the {@link DataColumnRecipe} dispatcher.
|
|
32
34
|
*/
|
|
33
|
-
var
|
|
35
|
+
var DataColumnImpl = class DataColumnImpl {
|
|
34
36
|
id;
|
|
35
37
|
options;
|
|
36
38
|
specCache;
|
|
@@ -55,7 +57,6 @@ var ColumnLazyImpl = class ColumnLazyImpl {
|
|
|
55
57
|
column: this.id
|
|
56
58
|
};
|
|
57
59
|
}
|
|
58
|
-
/** Leaf-only: not on the recipe interface — only the leaf can read data directly. */
|
|
59
60
|
getData() {
|
|
60
61
|
if (this.dataCache === void 0) this.dataCache = { value: this.options.getData() };
|
|
61
62
|
return this.dataCache.value;
|
|
@@ -66,7 +67,7 @@ var ColumnLazyImpl = class ColumnLazyImpl {
|
|
|
66
67
|
}
|
|
67
68
|
/**
|
|
68
69
|
* Overlay overrides → produces a {@link ColumnOverriddenRecipe} wrapping
|
|
69
|
-
* this leaf.
|
|
70
|
+
* this leaf. The leaf itself stays bare (id remains a plain PObjectId);
|
|
70
71
|
* layering lives entirely in the recipe wrappers.
|
|
71
72
|
*/
|
|
72
73
|
withSpecs(overrides) {
|
|
@@ -92,7 +93,7 @@ var ColumnLazyImpl = class ColumnLazyImpl {
|
|
|
92
93
|
return;
|
|
93
94
|
}
|
|
94
95
|
if (!spec.hasData()) return void 0;
|
|
95
|
-
return new
|
|
96
|
+
return new DataColumnImpl(id, {
|
|
96
97
|
getSpec: () => spec.getDataAsJson(),
|
|
97
98
|
getData: () => readDataAccessor(leaf),
|
|
98
99
|
getDataStatus: () => readDataStatus(leaf)
|
|
@@ -100,7 +101,7 @@ var ColumnLazyImpl = class ColumnLazyImpl {
|
|
|
100
101
|
}
|
|
101
102
|
/** {@link PlRef} wrapper over {@link fromId}. */
|
|
102
103
|
static fromPlRef(ref) {
|
|
103
|
-
return
|
|
104
|
+
return DataColumnImpl.fromId(createGlobalPObjectId(ref.blockId, ref.name));
|
|
104
105
|
}
|
|
105
106
|
/**
|
|
106
107
|
* Bind directly to an accessor-backed {@link LeafEntry} — no registry.
|
|
@@ -114,7 +115,7 @@ var ColumnLazyImpl = class ColumnLazyImpl {
|
|
|
114
115
|
return;
|
|
115
116
|
}
|
|
116
117
|
if (!spec.hasData()) return void 0;
|
|
117
|
-
return new
|
|
118
|
+
return new DataColumnImpl(entry.id, {
|
|
118
119
|
getSpec: () => spec.getDataAsJson(),
|
|
119
120
|
getData: () => readDataAccessor(entry),
|
|
120
121
|
getDataStatus: () => readDataStatus(entry)
|
|
@@ -122,13 +123,14 @@ var ColumnLazyImpl = class ColumnLazyImpl {
|
|
|
122
123
|
}
|
|
123
124
|
/**
|
|
124
125
|
* Wrap a materialised {@link PColumn}. If the input is already a
|
|
125
|
-
* {@link
|
|
126
|
+
* {@link DataColumnRecipe} leaf it is returned as-is.
|
|
126
127
|
*/
|
|
127
128
|
static fromColumn(column) {
|
|
128
|
-
if (column instanceof
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
129
|
+
if (column instanceof DataColumnImpl) return column;
|
|
130
|
+
const pColumn = column;
|
|
131
|
+
return new DataColumnImpl(pColumn.id, {
|
|
132
|
+
getSpec: () => pColumn.spec,
|
|
133
|
+
getData: () => pColumn.data,
|
|
132
134
|
getDataStatus: () => "present"
|
|
133
135
|
});
|
|
134
136
|
}
|
|
@@ -146,7 +148,7 @@ var ColumnLazyImpl = class ColumnLazyImpl {
|
|
|
146
148
|
}
|
|
147
149
|
/** {@link PlRef} wrapper over {@link getStatusById}. */
|
|
148
150
|
static getStatusByPlRef(ref, opts = {}) {
|
|
149
|
-
return
|
|
151
|
+
return DataColumnImpl.getStatusById(createGlobalPObjectId(ref.blockId, ref.name), opts);
|
|
150
152
|
}
|
|
151
153
|
/** No registry — reads straight off the entry's accessor. */
|
|
152
154
|
static getStatusByAccessor(entry) {
|
|
@@ -154,50 +156,51 @@ var ColumnLazyImpl = class ColumnLazyImpl {
|
|
|
154
156
|
}
|
|
155
157
|
};
|
|
156
158
|
/**
|
|
157
|
-
* Unified dispatcher — picks the right `
|
|
159
|
+
* Unified dispatcher — picks the right `DataColumnImpl.fromX` by source
|
|
158
160
|
* shape. For ambiguous inputs callers can still use the explicit factories
|
|
159
|
-
* (also attached as properties: `
|
|
161
|
+
* (also attached as properties: `DataColumn.fromId`, `.fromPlRef`,
|
|
160
162
|
* `.fromAccessor`, `.fromColumn`).
|
|
161
163
|
*/
|
|
162
|
-
function
|
|
163
|
-
if (typeof source === "string") return
|
|
164
|
-
if (source instanceof
|
|
165
|
-
|
|
166
|
-
if (
|
|
167
|
-
if (
|
|
168
|
-
|
|
164
|
+
function DataColumnDispatch(source, opts = {}) {
|
|
165
|
+
if (typeof source === "string") return DataColumnImpl.fromId(source, opts);
|
|
166
|
+
if (source instanceof DataColumnImpl) return source;
|
|
167
|
+
const rest = source;
|
|
168
|
+
if ("accessor" in rest) return DataColumnImpl.fromAccessor(rest);
|
|
169
|
+
if (isPlRef(rest)) return DataColumnImpl.fromPlRef(rest);
|
|
170
|
+
if (isPColumn(rest)) return DataColumnImpl.fromColumn(rest);
|
|
171
|
+
throw new Error("DataColumn: unknown source shape");
|
|
169
172
|
}
|
|
170
173
|
/**
|
|
171
|
-
* Polymorphic counterpart to {@link
|
|
174
|
+
* Polymorphic counterpart to {@link DataColumnDispatch}: returns the
|
|
172
175
|
* {@link ColumnResolutionStatus} for any factory input without constructing
|
|
173
176
|
* the recipe. For already-materialised sources ({@link PColumn} value,
|
|
174
|
-
* existing {@link
|
|
177
|
+
* existing {@link DataColumnRecipe}) status is `present` by construction.
|
|
175
178
|
*/
|
|
176
|
-
function
|
|
177
|
-
if (typeof source === "string") return
|
|
178
|
-
if (source instanceof
|
|
179
|
-
|
|
180
|
-
if (
|
|
181
|
-
if (
|
|
182
|
-
|
|
179
|
+
function DataColumnGetStatus(source, opts = {}) {
|
|
180
|
+
if (typeof source === "string") return DataColumnImpl.getStatusById(source, opts);
|
|
181
|
+
if (source instanceof DataColumnImpl) return "present";
|
|
182
|
+
const rest = source;
|
|
183
|
+
if ("accessor" in rest) return DataColumnImpl.getStatusByAccessor(rest);
|
|
184
|
+
if (isPlRef(rest)) return DataColumnImpl.getStatusByPlRef(rest, opts);
|
|
185
|
+
if (isPColumn(rest)) return "present";
|
|
186
|
+
throw new Error("DataColumn.getStatus: unknown source shape");
|
|
183
187
|
}
|
|
184
|
-
const
|
|
185
|
-
fromId:
|
|
186
|
-
fromPlRef:
|
|
187
|
-
fromAccessor:
|
|
188
|
-
fromColumn:
|
|
189
|
-
getStatus:
|
|
190
|
-
getStatusById:
|
|
191
|
-
getStatusByPlRef:
|
|
192
|
-
getStatusByAccessor:
|
|
188
|
+
const DataColumn = Object.assign(DataColumnDispatch, {
|
|
189
|
+
fromId: DataColumnImpl.fromId,
|
|
190
|
+
fromPlRef: DataColumnImpl.fromPlRef,
|
|
191
|
+
fromAccessor: DataColumnImpl.fromAccessor,
|
|
192
|
+
fromColumn: DataColumnImpl.fromColumn,
|
|
193
|
+
getStatus: DataColumnGetStatus,
|
|
194
|
+
getStatusById: DataColumnImpl.getStatusById,
|
|
195
|
+
getStatusByPlRef: DataColumnImpl.getStatusByPlRef,
|
|
196
|
+
getStatusByAccessor: DataColumnImpl.getStatusByAccessor
|
|
193
197
|
});
|
|
194
198
|
/**
|
|
195
|
-
* Type-guard narrowing to
|
|
196
|
-
*
|
|
197
|
-
* with statics, not the class, so `instanceof` requires the impl symbol.
|
|
199
|
+
* Type-guard narrowing to a bare leaf — a {@link DataColumnRecipe} whose `id` is a
|
|
200
|
+
* plain {@link PObjectId}.
|
|
198
201
|
*/
|
|
199
|
-
function
|
|
200
|
-
return value instanceof
|
|
202
|
+
function isDataColumn(value) {
|
|
203
|
+
return value instanceof DataColumnImpl;
|
|
201
204
|
}
|
|
202
205
|
const readSpecAccessor = memoizeByEntry(({ accessor, name }) => accessor.traverse({
|
|
203
206
|
field: `${name}.spec`,
|
|
@@ -240,6 +243,6 @@ function memoizeByEntry(fn) {
|
|
|
240
243
|
};
|
|
241
244
|
}
|
|
242
245
|
//#endregion
|
|
243
|
-
export { ColumnAbsentError,
|
|
246
|
+
export { ColumnAbsentError, DataColumn, DataColumnImpl, isDataColumn };
|
|
244
247
|
|
|
245
|
-
//# sourceMappingURL=
|
|
248
|
+
//# sourceMappingURL=data_column.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"data_column.js","names":[],"sources":["../../src/columns/data_column.ts"],"sourcesContent":["import {\n ColumnRegistry,\n createGlobalPObjectId,\n isPColumn,\n isPlRef,\n PlRef,\n PObjectId,\n type ColumnUniversalId,\n type LeafEntry,\n type PColumn,\n type PColumnSpec,\n type SpecOverrides,\n type SpecQuery,\n} from \"@milaboratories/pl-model-common\";\nimport type { GlobalCfgRenderCtx, PColumnDataUniversal } from \"../render/internal\";\nimport { getCtxProviders } from \"./column_providers\";\nimport { isNil } from \"es-toolkit\";\nimport { LRUCache } from \"lru-cache\";\nimport { TreeNodeAccessor } from \"../render\";\nimport type {\n ColumnFieldStatus,\n ColumnRecipe,\n ColumnRecipeId,\n ColumnResolutionStatus,\n} from \"./column_recipes/types\";\nimport { ColumnOverriddenRecipe } from \"./column_recipes/column_overrided_recipe\";\n\nexport type DataColumnId = PObjectId;\nexport type ColumnData = undefined | PColumnDataUniversal;\nexport type { ColumnFieldStatus, ColumnResolutionStatus } from \"./column_recipes/types\";\n\n/**\n * A {@link ColumnRecipe} that can be read directly in the sandbox: its\n * {@link getData} is guaranteed to be consistent with {@link getSpec}.\n *\n * This is a *capability*, not a class. Only two recipe shapes carry it — a\n * bare leaf and a spec-override over a bare leaf — because those are the only\n * ones whose data still matches their spec. An axis-filtered recipe drops axes\n * from its spec while the underlying data keeps them, and a discovered recipe\n * only materialises after a join; neither can be read here, so neither\n * implements this interface.\n *\n * Narrow to it with `hasReachableData(recipe)` rather than testing for a concrete\n * class — which recipe classes exist is an implementation detail behind\n * `recipe.id`.\n */\nexport interface DataColumnRecipe<\n ID extends ColumnRecipeId = ColumnRecipeId,\n> extends ColumnRecipe<ID> {\n /** Column data, consistent with {@link ColumnRecipe.getSpec}. */\n getData(): ColumnData;\n}\n\n/**\n * Thrown by leaf-recipe factories when the requested column is provably\n * absent in the active render ctx — i.e. every relevant accessor reports\n * `inputsLocked` and the column did not appear. Distinct from the\n * `undefined` return that the factories still use for the \"resolving\" case\n * (the column may still appear later).\n *\n * Catch at the boundary (resolver / filter+sort wiring / data-table assembly)\n * when partial absence should be surfaced to the user rather than silently\n * producing an empty result.\n */\nexport class ColumnAbsentError extends Error {\n constructor(public readonly id: ColumnUniversalId) {\n super(`Column is absent in the active render ctx and will not appear: ${id}`);\n this.name = \"ColumnAbsentError\";\n }\n}\n\n/**\n * The leaf-recipe building block: a {@link DataColumnRecipe} whose `id` is a bare\n * {@link PObjectId} and whose readers are bound to a single tree-accessor\n * leaf. Layered encodings (Overridden / Discovered / Filtered) are reified\n * through their dedicated recipe classes and reference leaf columns by id.\n *\n * Internal — consumers hold the {@link DataColumnRecipe} interface and construct\n * through the {@link DataColumnRecipe} dispatcher.\n */\nexport class DataColumnImpl implements DataColumnRecipe<PObjectId> {\n private specCache?: { readonly value: PColumnSpec };\n private dataCache?: { readonly value: ColumnData };\n private dataStatusCache?: { readonly value: ColumnFieldStatus };\n\n private constructor(\n public readonly id: PObjectId,\n private readonly options: {\n getSpec: () => PColumnSpec;\n getData: () => ColumnData;\n getDataStatus: () => ColumnFieldStatus;\n },\n ) {}\n\n /** A leaf-recipe references exactly one column — its own id. */\n getReferencedIds(): PObjectId[] {\n return [this.id];\n }\n\n getSpec(): PColumnSpec {\n if (this.specCache === undefined) {\n this.specCache = { value: this.options.getSpec() };\n }\n return this.specCache.value;\n }\n\n /** Leaf-shaped — points straight at `this.id`. */\n getQuery(): SpecQuery {\n return { type: \"column\", column: this.id } as SpecQuery;\n }\n\n getData(): ColumnData {\n if (this.dataCache === undefined) this.dataCache = { value: this.options.getData() };\n return this.dataCache.value;\n }\n\n getDataStatus(): ColumnFieldStatus {\n if (this.dataStatusCache === undefined) {\n this.dataStatusCache = { value: this.options.getDataStatus() };\n }\n return this.dataStatusCache.value;\n }\n\n /**\n * Overlay overrides → produces a {@link ColumnOverriddenRecipe} wrapping\n * this leaf. The leaf itself stays bare (id remains a plain PObjectId);\n * layering lives entirely in the recipe wrappers.\n */\n withSpecs(overrides: SpecOverrides): ColumnRecipe {\n return ColumnOverriddenRecipe.wrap(this, overrides);\n }\n\n /**\n * Resolve via the ambient {@link ColumnRegistry}. Spec is resolved eagerly:\n * returns `undefined` if the leaf isn't reachable yet (resolving). Throws\n * {@link ColumnAbsentError} when every relevant accessor is `inputsLocked`\n * and the column did not appear — the column will not exist in this ctx.\n * Data and dataStatus stay lazy.\n */\n static fromId(\n id: PObjectId,\n { ctx }: { ctx?: GlobalCfgRenderCtx } = {},\n ): undefined | DataColumnRecipe<PObjectId> {\n const registry = new ColumnRegistry(getCtxProviders({ ctx }));\n const leaf = registry.resolve(id);\n if (isNil(leaf)) {\n if (registry.isFinal()) throw new ColumnAbsentError(id);\n return undefined;\n }\n const spec = readSpecAccessor(leaf);\n if (isNil(spec)) {\n if (leaf.accessor.getInputsLocked()) throw new ColumnAbsentError(id);\n return undefined;\n }\n if (!spec.hasData()) return undefined;\n return new DataColumnImpl(id, {\n getSpec: () => spec.getDataAsJson<PColumnSpec>(),\n getData: () => readDataAccessor(leaf),\n getDataStatus: () => readDataStatus(leaf),\n });\n }\n\n /** {@link PlRef} wrapper over {@link fromId}. */\n static fromPlRef(ref: PlRef): undefined | DataColumnRecipe<PObjectId> {\n return DataColumnImpl.fromId(createGlobalPObjectId(ref.blockId, ref.name));\n }\n\n /**\n * Bind directly to an accessor-backed {@link LeafEntry} — no registry.\n * Throws {@link ColumnAbsentError} if the leaf has no spec field and its\n * accessor is `inputsLocked`. Returns `undefined` while still resolving.\n */\n static fromAccessor(entry: LeafEntry<TreeNodeAccessor>): undefined | DataColumnRecipe<PObjectId> {\n const spec = readSpecAccessor(entry);\n if (isNil(spec)) {\n if (entry.accessor.getInputsLocked()) throw new ColumnAbsentError(entry.id);\n return undefined;\n }\n if (!spec.hasData()) return undefined;\n return new DataColumnImpl(entry.id, {\n getSpec: () => spec.getDataAsJson<PColumnSpec>(),\n getData: () => readDataAccessor(entry),\n getDataStatus: () => readDataStatus(entry),\n });\n }\n\n /**\n * Wrap a materialised {@link PColumn}. If the input is already a\n * {@link DataColumnRecipe} leaf it is returned as-is.\n */\n static fromColumn(\n column: PColumn<ColumnData> | DataColumnRecipe<PObjectId>,\n ): DataColumnRecipe<PObjectId> {\n if (column instanceof DataColumnImpl) return column;\n // `DataColumnRecipe` is an interface, so `instanceof` above does not remove it\n // from the union. The only implementation carrying a bare `PObjectId` is\n // `DataColumnImpl` (wrappers expose `ColumnUniversalId`), so anything\n // reaching here is a materialised `PColumn`.\n const pColumn = column as PColumn<ColumnData>;\n return new DataColumnImpl(pColumn.id, {\n getSpec: () => pColumn.spec,\n getData: () => pColumn.data,\n getDataStatus: () => \"present\",\n });\n }\n\n /**\n * Distinguishes `present` / `resolving` / `absent` for a {@link PObjectId}\n * in the active render ctx. Falls back to the registry's `isFinal()`\n * when the id has no entry — only then we can say `absent` instead of\n * `resolving`.\n */\n static getStatusById(\n id: PObjectId,\n { ctx }: { ctx?: GlobalCfgRenderCtx } = {},\n ): ColumnResolutionStatus {\n const registry = new ColumnRegistry(getCtxProviders({ ctx }));\n const leaf = registry.resolve(id);\n if (isNil(leaf)) return registry.isFinal() ? \"absent\" : \"resolving\";\n return getLeafEntryStatus(leaf);\n }\n\n /** {@link PlRef} wrapper over {@link getStatusById}. */\n static getStatusByPlRef(\n ref: PlRef,\n opts: { ctx?: GlobalCfgRenderCtx } = {},\n ): ColumnResolutionStatus {\n return DataColumnImpl.getStatusById(createGlobalPObjectId(ref.blockId, ref.name), opts);\n }\n\n /** No registry — reads straight off the entry's accessor. */\n static getStatusByAccessor(entry: LeafEntry<TreeNodeAccessor>): ColumnResolutionStatus {\n return getLeafEntryStatus(entry);\n }\n}\n\n/** Anything the {@link DataColumnRecipe} dispatcher can build a leaf from. */\nexport type DataColumnSource =\n | PObjectId\n | PlRef\n | LeafEntry<TreeNodeAccessor>\n | PColumn<ColumnData>\n | DataColumnRecipe<PObjectId>;\n\n/**\n * Unified dispatcher — picks the right `DataColumnImpl.fromX` by source\n * shape. For ambiguous inputs callers can still use the explicit factories\n * (also attached as properties: `DataColumn.fromId`, `.fromPlRef`,\n * `.fromAccessor`, `.fromColumn`).\n */\nfunction DataColumnDispatch(\n source: DataColumnSource,\n opts: { ctx?: GlobalCfgRenderCtx } = {},\n): undefined | DataColumnRecipe<PObjectId> {\n if (typeof source === \"string\") return DataColumnImpl.fromId(source, opts);\n if (source instanceof DataColumnImpl) return source;\n // `DataColumnRecipe` is an interface — `instanceof` above cannot remove it from\n // the union, and only `DataColumnImpl` implements it with a bare id.\n const rest = source as LeafEntry<TreeNodeAccessor> | PlRef | PColumn<ColumnData>;\n if (\"accessor\" in rest) return DataColumnImpl.fromAccessor(rest);\n if (isPlRef(rest)) return DataColumnImpl.fromPlRef(rest);\n if (isPColumn(rest)) return DataColumnImpl.fromColumn(rest);\n throw new Error(\"DataColumn: unknown source shape\");\n}\n\n/**\n * Polymorphic counterpart to {@link DataColumnDispatch}: returns the\n * {@link ColumnResolutionStatus} for any factory input without constructing\n * the recipe. For already-materialised sources ({@link PColumn} value,\n * existing {@link DataColumnRecipe}) status is `present` by construction.\n */\nfunction DataColumnGetStatus(\n source: DataColumnSource,\n opts: { ctx?: GlobalCfgRenderCtx } = {},\n): ColumnResolutionStatus {\n if (typeof source === \"string\") return DataColumnImpl.getStatusById(source, opts);\n if (source instanceof DataColumnImpl) return \"present\";\n const rest = source as LeafEntry<TreeNodeAccessor> | PlRef | PColumn<ColumnData>;\n if (\"accessor\" in rest) return DataColumnImpl.getStatusByAccessor(rest);\n if (isPlRef(rest)) return DataColumnImpl.getStatusByPlRef(rest, opts);\n if (isPColumn(rest)) return \"present\";\n throw new Error(\"DataColumn.getStatus: unknown source shape\");\n}\n\nexport const DataColumn = Object.assign(DataColumnDispatch, {\n fromId: DataColumnImpl.fromId,\n fromPlRef: DataColumnImpl.fromPlRef,\n fromAccessor: DataColumnImpl.fromAccessor,\n fromColumn: DataColumnImpl.fromColumn,\n getStatus: DataColumnGetStatus,\n getStatusById: DataColumnImpl.getStatusById,\n getStatusByPlRef: DataColumnImpl.getStatusByPlRef,\n getStatusByAccessor: DataColumnImpl.getStatusByAccessor,\n});\n\n/**\n * Type-guard narrowing to a bare leaf — a {@link DataColumnRecipe} whose `id` is a\n * plain {@link PObjectId}.\n */\nexport function isDataColumn(value: unknown): value is DataColumnRecipe<PObjectId> {\n return value instanceof DataColumnImpl;\n}\n\nconst readSpecAccessor = memoizeByEntry(\n ({ accessor, name }: LeafEntry<TreeNodeAccessor>): undefined | TreeNodeAccessor =>\n accessor.traverse({ field: `${name}.spec`, assertFieldType: \"Input\", ignoreError: true }),\n);\n\n/**\n * Per-entry counterpart to {@link readDataStatus}: tells whether the leaf's\n * **spec** can be read in this ctx, and — for the negative cases —\n * distinguishes `resolving` from `absent` via `getInputsLocked()`.\n *\n * - spec field not yet on the entry's accessor + inputs locked → `absent`\n * - spec field not yet on the entry's accessor + still resolving → `resolving`\n * - spec resource present but bytes not yet written → `resolving`\n * (transient — the spec resource is connected, just unfilled)\n * - spec resource present and `hasData()` → `present`\n */\nfunction getLeafEntryStatus(entry: LeafEntry<TreeNodeAccessor>): ColumnResolutionStatus {\n const spec = readSpecAccessor(entry);\n if (isNil(spec)) return entry.accessor.getInputsLocked() ? \"absent\" : \"resolving\";\n if (!spec.hasData()) return \"resolving\";\n return \"present\";\n}\n\nconst readDataAccessor = memoizeByEntry(\n ({ accessor, name }: LeafEntry<TreeNodeAccessor>): undefined | TreeNodeAccessor =>\n accessor.traverse({ field: `${name}.data`, assertFieldType: \"Input\", ignoreError: true }),\n);\n\nconst readDataStatus = memoizeByEntry(\n ({ accessor, name }: LeafEntry<TreeNodeAccessor>): ColumnFieldStatus => {\n if (accessor.listInputFields().includes(`${name}.data`)) return \"present\";\n return accessor.getInputsLocked() ? \"absent\" : \"resolving\";\n },\n);\n\nfunction memoizeByEntry<R>(\n fn: (entry: LeafEntry<TreeNodeAccessor>) => R,\n): (entry: LeafEntry<TreeNodeAccessor>) => R {\n const cache = new LRUCache<string, { value: R }>({ max: 1000 });\n return (entry) => {\n const key = `${entry.accessor.handle}:${entry.name}`;\n let hit = cache.get(key);\n if (!hit) cache.set(key, (hit = { value: fn(entry) }));\n return hit.value;\n };\n}\n"],"mappings":"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"}
|